@cryptotaxi247 / kubo / commits / dafe49537

extract ipns record logic to go-ipns

License: MIT Signed-off-by: Steven Allen <steven@stebalien.com>

Steven Allen committed Jun 15, 2018 at 16:53 UTC dafe495376c28d305950cf0ed14581f1f10ac3ca
15 files changed +56 -704
Rules.mk
-3
@@ -47,9 +47,6 @@ ifneq ($(filter coverage% clean distclean,$(MAKECMDGOALS)),)
47 include $(dir)/Rules.mk
48 endif
49
50 -dir := namesys/pb
51 -include $(dir)/Rules.mk
52 -
50 dir := unixfs/pb
51 include $(dir)/Rules.mk
52
core/commands/dht_test.go
+6 -3
@@ -4,12 +4,15 @@ import (
4 "testing"
5
6 "github.com/ipfs/go-ipfs/namesys"
7 +
8 tu "gx/ipfs/QmPdxCaVp4jZ9RbxqZADvKH6kiCR5jHvdR5f2ycjAY6T2a/go-testutil"
9 + ipns "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns"
10 )
11
12 func TestKeyTranslation(t *testing.T) {
13 pid := tu.RandPeerIDFatal(t)
12 - a, b := namesys.IpnsKeysForID(pid)
14 + pkname := namesys.PkKeyForID(pid)
15 + ipnsname := ipns.RecordKey(pid)
16
17 pkk, err := escapeDhtKey("/pk/" + pid.Pretty())
18 if err != nil {
@@ -21,11 +24,11 @@ func TestKeyTranslation(t *testing.T) {
24 t.Fatal(err)
25 }
26
24 - if pkk != a {
27 + if pkk != pkname {
28 t.Fatal("keys didnt match!")
29 }
30
28 - if ipnsk != b {
31 + if ipnsk != ipnsname {
32 t.Fatal("keys didnt match!")
33 }
34 }
core/core.go
+2 -1
@@ -42,6 +42,7 @@ import (
42 circuit "gx/ipfs/QmPavh4h3Edx5cv8GJQPC35AQAws7faXmzEZyru7k9b9Mn/go-libp2p-circuit"
43 u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util"
44 p2phost "gx/ipfs/QmQQGtcp6nVUrQjNsnU53YWV1q8fK1Kd9S7FEkYbRZzxry/go-libp2p-host"
45 + ipns "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns"
46 floodsub "gx/ipfs/QmRFEBGcNjtWPupwHA7zGHeGVLuUyE4ZRFi2MgtrPM6pfb/go-libp2p-floodsub"
47 pnet "gx/ipfs/QmRGvSwDpN4eunxgDNfmQhayZ6Z9F5a2v31V2D7y77osLg/go-libp2p-pnet"
48 goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess"
@@ -459,7 +460,7 @@ func (n *IpfsNode) startOnlineServicesWithHost(ctx context.Context, host p2phost
460
461 validator := record.NamespacedValidator{
462 "pk": record.PublicKeyValidator{},
462 - "ipns": namesys.IpnsValidator{KeyBook: host.Peerstore()},
463 + "ipns": ipns.Validator{KeyBook: host.Peerstore()},
464 }
465
466 // setup routing service
namesys/ipns_resolver_validation_test.go renamed
+8 -154
@@ -2,23 +2,19 @@ package namesys
2
3 import (
4 "context"
5 - "fmt"
6 - "math/rand"
7 - "strings"
5 "testing"
6 "time"
7
8 opts "github.com/ipfs/go-ipfs/namesys/opts"
12 - pb "github.com/ipfs/go-ipfs/namesys/pb"
9 path "github.com/ipfs/go-ipfs/path"
10
11 record "gx/ipfs/QmPWjVzxHeJdrjp4Jr2R2sPxBrMbBgGPWQtKwCKHHCBF7x/go-libp2p-record"
12 u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util"
13 testutil "gx/ipfs/QmPdxCaVp4jZ9RbxqZADvKH6kiCR5jHvdR5f2ycjAY6T2a/go-testutil"
14 + ipns "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns"
15 routing "gx/ipfs/QmUV9hDAAyjeGbxbXkJ2sYqZ6dTd1DXJ2REhYEkRm178Tg/go-libp2p-routing"
16 ropts "gx/ipfs/QmUV9hDAAyjeGbxbXkJ2sYqZ6dTd1DXJ2REhYEkRm178Tg/go-libp2p-routing/options"
17 peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer"
21 - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto"
18 pstore "gx/ipfs/QmZhsmorLpD9kmQ4ynbAu4vbKv2goMUnXazwGA4gnWHDjB/go-libp2p-peerstore"
19 mockrouting "gx/ipfs/Qmb1N7zdjG2FexpzWNj8T289u9QnQLEiSsTRadDGQxX32D/go-ipfs-routing/mock"
20 ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto"
@@ -26,147 +22,6 @@ import (
22 dssync "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore/sync"
23 )
24
29 -func testValidatorCase(t *testing.T, priv ci.PrivKey, kbook pstore.KeyBook, key string, val []byte, eol time.Time, exp error) {
30 - t.Helper()
31 -
32 - match := func(t *testing.T, err error) {
33 - t.Helper()
34 - if err != exp {
35 - params := fmt.Sprintf("key: %s\neol: %s\n", key, eol)
36 - if exp == nil {
37 - t.Fatalf("Unexpected error %s for params %s", err, params)
38 - } else if err == nil {
39 - t.Fatalf("Expected error %s but there was no error for params %s", exp, params)
40 - } else {
41 - t.Fatalf("Expected error %s but got %s for params %s", exp, err, params)
42 - }
43 - }
44 - }
45 -
46 - testValidatorCaseMatchFunc(t, priv, kbook, key, val, eol, match)
47 -}
48 -
49 -func testValidatorCaseMatchFunc(t *testing.T, priv ci.PrivKey, kbook pstore.KeyBook, key string, val []byte, eol time.Time, matchf func(*testing.T, error)) {
50 - t.Helper()
51 - validator := IpnsValidator{kbook}
52 -
53 - data := val
54 - if data == nil {
55 - p := path.Path("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG")
56 - entry, err := CreateRoutingEntryData(priv, p, 1, eol)
57 - if err != nil {
58 - t.Fatal(err)
59 - }
60 -
61 - data, err = proto.Marshal(entry)
62 - if err != nil {
63 - t.Fatal(err)
64 - }
65 - }
66 -
67 - matchf(t, validator.Validate(key, data))
68 -}
69 -
70 -func TestValidator(t *testing.T) {
71 - ts := time.Now()
72 -
73 - priv, id, _, _ := genKeys(t)
74 - priv2, id2, _, _ := genKeys(t)
75 - kbook := pstore.NewPeerstore()
76 - kbook.AddPubKey(id, priv.GetPublic())
77 - emptyKbook := pstore.NewPeerstore()
78 -
79 - testValidatorCase(t, priv, kbook, "/ipns/"+string(id), nil, ts.Add(time.Hour), nil)
80 - testValidatorCase(t, priv, kbook, "/ipns/"+string(id), nil, ts.Add(time.Hour*-1), ErrExpiredRecord)
81 - testValidatorCase(t, priv, kbook, "/ipns/"+string(id), []byte("bad data"), ts.Add(time.Hour), ErrBadRecord)
82 - testValidatorCase(t, priv, kbook, "/ipns/"+"bad key", nil, ts.Add(time.Hour), ErrKeyFormat)
83 - testValidatorCase(t, priv, emptyKbook, "/ipns/"+string(id), nil, ts.Add(time.Hour), ErrPublicKeyNotFound)
84 - testValidatorCase(t, priv2, kbook, "/ipns/"+string(id2), nil, ts.Add(time.Hour), ErrPublicKeyNotFound)
85 - testValidatorCase(t, priv2, kbook, "/ipns/"+string(id), nil, ts.Add(time.Hour), ErrSignature)
86 - testValidatorCase(t, priv, kbook, "//"+string(id), nil, ts.Add(time.Hour), ErrInvalidPath)
87 - testValidatorCase(t, priv, kbook, "/wrong/"+string(id), nil, ts.Add(time.Hour), ErrInvalidPath)
88 -}
89 -
90 -func mustMarshal(t *testing.T, entry *pb.IpnsEntry) []byte {
91 - t.Helper()
92 - data, err := proto.Marshal(entry)
93 - if err != nil {
94 - t.Fatal(err)
95 - }
96 - return data
97 -}
98 -
99 -func TestEmbeddedPubKeyValidate(t *testing.T) {
100 - goodeol := time.Now().Add(time.Hour)
101 - kbook := pstore.NewPeerstore()
102 -
103 - pth := path.Path("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG")
104 -
105 - priv, _, _, ipnsk := genKeys(t)
106 -
107 - entry, err := CreateRoutingEntryData(priv, pth, 1, goodeol)
108 - if err != nil {
109 - t.Fatal(err)
110 - }
111 -
112 - testValidatorCase(t, priv, kbook, ipnsk, mustMarshal(t, entry), goodeol, ErrPublicKeyNotFound)
113 -
114 - pubkb, err := priv.GetPublic().Bytes()
115 - if err != nil {
116 - t.Fatal(err)
117 - }
118 -
119 - entry.PubKey = pubkb
120 - testValidatorCase(t, priv, kbook, ipnsk, mustMarshal(t, entry), goodeol, nil)
121 -
122 - entry.PubKey = []byte("probably not a public key")
123 - testValidatorCaseMatchFunc(t, priv, kbook, ipnsk, mustMarshal(t, entry), goodeol, func(t *testing.T, err error) {
124 - if !strings.Contains(err.Error(), "unmarshaling pubkey in record:") {
125 - t.Fatal("expected pubkey unmarshaling to fail")
126 - }
127 - })
128 -
129 - opriv, _, _, _ := genKeys(t)
130 - wrongkeydata, err := opriv.GetPublic().Bytes()
131 - if err != nil {
132 - t.Fatal(err)
133 - }
134 -
135 - entry.PubKey = wrongkeydata
136 - testValidatorCase(t, priv, kbook, ipnsk, mustMarshal(t, entry), goodeol, ErrPublicKeyMismatch)
137 -}
138 -
139 -func TestPeerIDPubKeyValidate(t *testing.T) {
140 - goodeol := time.Now().Add(time.Hour)
141 - kbook := pstore.NewPeerstore()
142 -
143 - pth := path.Path("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG")
144 -
145 - sk, pk, err := ci.GenerateEd25519Key(rand.New(rand.NewSource(42)))
146 - if err != nil {
147 - t.Fatal(err)
148 - }
149 -
150 - pid, err := peer.IDFromPublicKey(pk)
151 - if err != nil {
152 - t.Fatal(err)
153 - }
154 -
155 - ipnsk := "/ipns/" + string(pid)
156 -
157 - entry, err := CreateRoutingEntryData(sk, pth, 1, goodeol)
158 - if err != nil {
159 - t.Fatal(err)
160 - }
161 -
162 - dataNoKey, err := proto.Marshal(entry)
163 - if err != nil {
164 - t.Fatal(err)
165 - }
166 -
167 - testValidatorCase(t, sk, kbook, ipnsk, dataNoKey, goodeol, nil)
168 -}
169 -
25 func TestResolverValidation(t *testing.T) {
26 ctx := context.Background()
27 rid := testutil.RandIdentityOrFatal(t)
@@ -179,8 +34,8 @@ func TestResolverValidation(t *testing.T) {
34 // Create entry with expiry in one hour
35 priv, id, _, ipnsDHTPath := genKeys(t)
36 ts := time.Now()
182 - p := path.Path("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG")
183 - entry, err := CreateRoutingEntryData(priv, p, 1, ts.Add(time.Hour))
37 + p := []byte("/ipfs/QmfM2r8seH2GiRaC4esTjeraXEachRt8ZsSeGaWTPLyMoG")
38 + entry, err := ipns.Create(priv, p, 1, ts.Add(time.Hour))
39 if err != nil {
40 t.Fatal(err)
41 }
@@ -202,12 +57,12 @@ func TestResolverValidation(t *testing.T) {
57 if err != nil {
58 t.Fatal(err)
59 }
205 - if resp != p {
60 + if resp != path.Path(p) {
61 t.Fatalf("Mismatch between published path %s and resolved path %s", p, resp)
62 }
63
64 // Create expired entry
210 - expiredEntry, err := CreateRoutingEntryData(priv, p, 1, ts.Add(-1*time.Hour))
65 + expiredEntry, err := ipns.Create(priv, p, 1, ts.Add(-1*time.Hour))
66 if err != nil {
67 t.Fatal(err)
68 }
@@ -248,7 +103,7 @@ func TestResolverValidation(t *testing.T) {
103
104 // Publish entry without making public key available in peer store
105 priv3, id3, pubkDHTPath3, ipnsDHTPath3 := genKeys(t)
251 - entry3, err := CreateRoutingEntryData(priv3, p, 1, ts.Add(time.Hour))
106 + entry3, err := ipns.Create(priv3, p, 1, ts.Add(time.Hour))
107 if err != nil {
108 t.Fatal(err)
109 }
@@ -292,9 +147,8 @@ func genKeys(t *testing.T) (ci.PrivKey, peer.ID, string, string) {
147 if err != nil {
148 t.Fatal(err)
149 }
295 - pubkDHTPath, ipnsDHTPath := IpnsKeysForID(pid)
150
297 - return priv, pid, pubkDHTPath, ipnsDHTPath
151 + return priv, pid, PkKeyForID(pid), ipns.RecordKey(pid)
152 }
153
154 type mockValueStore struct {
@@ -307,7 +161,7 @@ func newMockValueStore(id testutil.Identity, dstore ds.Datastore, kbook pstore.K
161 serv := mockrouting.NewServer()
162 r := serv.ClientWithDatastore(context.Background(), id, dstore)
163 return &mockValueStore{r, kbook, record.NamespacedValidator{
310 - "ipns": IpnsValidator{kbook},
164 + "ipns": ipns.Validator{KeyBook: kbook},
165 "pk": record.PublicKeyValidator{},
166 }}
167 }
namesys/ipns_select_test.go deleted
-127
@@ -1,127 +0,0 @@
1 -package namesys
2 -
3 -import (
4 - "fmt"
5 - "math/rand"
6 - "testing"
7 - "time"
8 -
9 - pb "github.com/ipfs/go-ipfs/namesys/pb"
10 - path "github.com/ipfs/go-ipfs/path"
11 -
12 - u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util"
13 - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto"
14 - ci "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto"
15 -)
16 -
17 -func shuffle(a []*pb.IpnsEntry) {
18 - for n := 0; n < 5; n++ {
19 - for i, _ := range a {
20 - j := rand.Intn(len(a))
21 - a[i], a[j] = a[j], a[i]
22 - }
23 - }
24 -}
25 -
26 -func AssertSelected(r *pb.IpnsEntry, from ...*pb.IpnsEntry) error {
27 - shuffle(from)
28 - var vals [][]byte
29 - for _, r := range from {
30 - data, err := proto.Marshal(r)
31 - if err != nil {
32 - return err
33 - }
34 - vals = append(vals, data)
35 - }
36 -
37 - i, err := selectRecord(from, vals)
38 - if err != nil {
39 - return err
40 - }
41 -
42 - if from[i] != r {
43 - return fmt.Errorf("selected incorrect record %d", i)
44 - }
45 -
46 - return nil
47 -}
48 -
49 -func TestOrdering(t *testing.T) {
50 - // select timestamp so selection is deterministic
51 - ts := time.Unix(1000000, 0)
52 -
53 - // generate a key for signing the records
54 - r := u.NewSeededRand(15) // generate deterministic keypair
55 - priv, _, err := ci.GenerateKeyPairWithReader(ci.RSA, 1024, r)
56 - if err != nil {
57 - t.Fatal(err)
58 - }
59 -
60 - e1, err := CreateRoutingEntryData(priv, path.Path("foo"), 1, ts.Add(time.Hour))
61 - if err != nil {
62 - t.Fatal(err)
63 - }
64 -
65 - e2, err := CreateRoutingEntryData(priv, path.Path("bar"), 2, ts.Add(time.Hour))
66 - if err != nil {
67 - t.Fatal(err)
68 - }
69 -
70 - e3, err := CreateRoutingEntryData(priv, path.Path("baz"), 3, ts.Add(time.Hour))
71 - if err != nil {
72 - t.Fatal(err)
73 - }
74 -
75 - e4, err := CreateRoutingEntryData(priv, path.Path("cat"), 3, ts.Add(time.Hour*2))
76 - if err != nil {
77 - t.Fatal(err)
78 - }
79 -
80 - e5, err := CreateRoutingEntryData(priv, path.Path("dog"), 4, ts.Add(time.Hour*3))
81 - if err != nil {
82 - t.Fatal(err)
83 - }
84 -
85 - e6, err := CreateRoutingEntryData(priv, path.Path("fish"), 4, ts.Add(time.Hour*3))
86 - if err != nil {
87 - t.Fatal(err)
88 - }
89 -
90 - // e1 is the only record, i hope it gets this right
91 - err = AssertSelected(e1, e1)
92 - if err != nil {
93 - t.Fatal(err)
94 - }
95 -
96 - // e2 has the highest sequence number
97 - err = AssertSelected(e2, e1, e2)
98 - if err != nil {
99 - t.Fatal(err)
100 - }
101 -
102 - // e3 has the highest sequence number
103 - err = AssertSelected(e3, e1, e2, e3)
104 - if err != nil {
105 - t.Fatal(err)
106 - }
107 -
108 - // e4 has a higher timeout
109 - err = AssertSelected(e4, e1, e2, e3, e4)
110 - if err != nil {
111 - t.Fatal(err)
112 - }
113 -
114 - // e5 has the highest sequence number
115 - err = AssertSelected(e5, e1, e2, e3, e4, e5)
116 - if err != nil {
117 - t.Fatal(err)
118 - }
119 -
120 - // e6 should be selected as its signauture will win in the comparison
121 - err = AssertSelected(e6, e1, e2, e3, e4, e5, e6)
122 - if err != nil {
123 - t.Fatal(err)
124 - }
125 -
126 - _ = []interface{}{e1, e2, e3, e4, e5, e6}
127 -}
namesys/pb/Rules.mk deleted
-8
@@ -1,8 +0,0 @@
1 -include mk/header.mk
2 -
3 -PB_$(d) = $(wildcard $(d)/*.proto)
4 -TGTS_$(d) = $(PB_$(d):.proto=.pb.go)
5 -
6 -#DEPS_GO += $(TGTS_$(d))
7 -
8 -include mk/footer.mk
namesys/pb/namesys.pb.go deleted
-127
@@ -1,127 +0,0 @@
1 -// Code generated by protoc-gen-gogo.
2 -// source: namesys/pb/namesys.proto
3 -// DO NOT EDIT!
4 -
5 -/*
6 -Package namesys_pb is a generated protocol buffer package.
7 -
8 -It is generated from these files:
9 - namesys/pb/namesys.proto
10 -
11 -It has these top-level messages:
12 - IpnsEntry
13 -*/
14 -package namesys_pb
15 -
16 -import proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto"
17 -import fmt "fmt"
18 -import math "math"
19 -
20 -// Reference imports to suppress errors if they are not otherwise used.
21 -var _ = proto.Marshal
22 -var _ = fmt.Errorf
23 -var _ = math.Inf
24 -
25 -type IpnsEntry_ValidityType int32
26 -
27 -const (
28 - // setting an EOL says "this record is valid until..."
29 - IpnsEntry_EOL IpnsEntry_ValidityType = 0
30 -)
31 -
32 -var IpnsEntry_ValidityType_name = map[int32]string{
33 - 0: "EOL",
34 -}
35 -var IpnsEntry_ValidityType_value = map[string]int32{
36 - "EOL": 0,
37 -}
38 -
39 -func (x IpnsEntry_ValidityType) Enum() *IpnsEntry_ValidityType {
40 - p := new(IpnsEntry_ValidityType)
41 - *p = x
42 - return p
43 -}
44 -func (x IpnsEntry_ValidityType) String() string {
45 - return proto.EnumName(IpnsEntry_ValidityType_name, int32(x))
46 -}
47 -func (x *IpnsEntry_ValidityType) UnmarshalJSON(data []byte) error {
48 - value, err := proto.UnmarshalJSONEnum(IpnsEntry_ValidityType_value, data, "IpnsEntry_ValidityType")
49 - if err != nil {
50 - return err
51 - }
52 - *x = IpnsEntry_ValidityType(value)
53 - return nil
54 -}
55 -
56 -type IpnsEntry struct {
57 - Value []byte `protobuf:"bytes,1,req,name=value" json:"value,omitempty"`
58 - Signature []byte `protobuf:"bytes,2,req,name=signature" json:"signature,omitempty"`
59 - ValidityType *IpnsEntry_ValidityType `protobuf:"varint,3,opt,name=validityType,enum=namesys.pb.IpnsEntry_ValidityType" json:"validityType,omitempty"`
60 - Validity []byte `protobuf:"bytes,4,opt,name=validity" json:"validity,omitempty"`
61 - Sequence *uint64 `protobuf:"varint,5,opt,name=sequence" json:"sequence,omitempty"`
62 - Ttl *uint64 `protobuf:"varint,6,opt,name=ttl" json:"ttl,omitempty"`
63 - // in order for nodes to properly validate a record upon receipt, they need the public
64 - // key associated with it. For old RSA keys, its easiest if we just send this as part of
65 - // the record itself. For newer ed25519 keys, the public key can be embedded in the
66 - // peerID, making this field unnecessary.
67 - PubKey []byte `protobuf:"bytes,7,opt,name=pubKey" json:"pubKey,omitempty"`
68 - XXX_unrecognized []byte `json:"-"`
69 -}
70 -
71 -func (m *IpnsEntry) Reset() { *m = IpnsEntry{} }
72 -func (m *IpnsEntry) String() string { return proto.CompactTextString(m) }
73 -func (*IpnsEntry) ProtoMessage() {}
74 -
75 -func (m *IpnsEntry) GetValue() []byte {
76 - if m != nil {
77 - return m.Value
78 - }
79 - return nil
80 -}
81 -
82 -func (m *IpnsEntry) GetSignature() []byte {
83 - if m != nil {
84 - return m.Signature
85 - }
86 - return nil
87 -}
88 -
89 -func (m *IpnsEntry) GetValidityType() IpnsEntry_ValidityType {
90 - if m != nil && m.ValidityType != nil {
91 - return *m.ValidityType
92 - }
93 - return IpnsEntry_EOL
94 -}
95 -
96 -func (m *IpnsEntry) GetValidity() []byte {
97 - if m != nil {
98 - return m.Validity
99 - }
100 - return nil
101 -}
102 -
103 -func (m *IpnsEntry) GetSequence() uint64 {
104 - if m != nil && m.Sequence != nil {
105 - return *m.Sequence
106 - }
107 - return 0
108 -}
109 -
110 -func (m *IpnsEntry) GetTtl() uint64 {
111 - if m != nil && m.Ttl != nil {
112 - return *m.Ttl
113 - }
114 - return 0
115 -}
116 -
117 -func (m *IpnsEntry) GetPubKey() []byte {
118 - if m != nil {
119 - return m.PubKey
120 - }
121 - return nil
122 -}
123 -
124 -func init() {
125 - proto.RegisterType((*IpnsEntry)(nil), "namesys.pb.IpnsEntry")
126 - proto.RegisterEnum("namesys.pb.IpnsEntry_ValidityType", IpnsEntry_ValidityType_name, IpnsEntry_ValidityType_value)
127 -}
namesys/pb/namesys.proto deleted
-23
@@ -1,23 +0,0 @@
1 -package namesys.pb;
2 -
3 -message IpnsEntry {
4 - enum ValidityType {
5 - // setting an EOL says "this record is valid until..."
6 - EOL = 0;
7 - }
8 - required bytes value = 1;
9 - required bytes signature = 2;
10 -
11 - optional ValidityType validityType = 3;
12 - optional bytes validity = 4;
13 -
14 - optional uint64 sequence = 5;
15 -
16 - optional uint64 ttl = 6;
17 -
18 - // in order for nodes to properly validate a record upon receipt, they need the public
19 - // key associated with it. For old RSA keys, its easiest if we just send this as part of
20 - // the record itself. For newer ed25519 keys, the public key can be embedded in the
21 - // peerID, making this field unnecessary.
22 - optional bytes pubKey = 7;
23 -}
namesys/publisher.go
+16 -56
@@ -1,19 +1,18 @@
1 package namesys
2
3 import (
4 - "bytes"
4 "context"
5 "fmt"
6 "strings"
7 "sync"
8 "time"
9
11 - pb "github.com/ipfs/go-ipfs/namesys/pb"
10 path "github.com/ipfs/go-ipfs/path"
11 pin "github.com/ipfs/go-ipfs/pin"
12 ft "github.com/ipfs/go-ipfs/unixfs"
13
16 - u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util"
14 + ipns "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns"
15 + pb "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns/pb"
16 routing "gx/ipfs/QmUV9hDAAyjeGbxbXkJ2sYqZ6dTd1DXJ2REhYEkRm178Tg/go-libp2p-routing"
17 peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer"
18 proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto"
@@ -131,7 +130,7 @@ func (p *IpnsPublisher) GetPublished(ctx context.Context, id peer.ID, checkRouti
130 if !checkRouting {
131 return nil, nil
132 }
134 - _, ipnskey := IpnsKeysForID(id)
133 + ipnskey := ipns.RecordKey(id)
134 value, err = p.routing.GetValue(ctx, ipnskey)
135 if err != nil {
136 // 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
174 }
175
176 // Create record
178 - entry, err := CreateRoutingEntryData(k, value, seqno, eol)
177 + entry, err := ipns.Create(k, []byte(value), seqno, eol)
178 if err != nil {
179 return nil, err
180 }
@@ -229,40 +228,29 @@ func PutRecordToRouting(ctx context.Context, r routing.ValueStore, k ci.PubKey,
228
229 errs := make(chan error, 2) // At most two errors (IPNS, and public key)
230
232 - id, err := peer.IDFromPublicKey(k)
233 - if err != nil {
231 + if err := ipns.EmbedPublicKey(k, entry); err != nil {
232 return err
233 }
234
237 - // Attempt to extract the public key from the ID
238 - extractedPublicKey, err := id.ExtractPublicKey()
235 + id, err := peer.IDFromPublicKey(k)
236 if err != nil {
237 return err
238 }
239
243 - // if we can't derive the public key from the peerID, embed the entire pubkey in
244 - // the record to make the verifiers job easier
245 - if extractedPublicKey == nil {
246 - pubkeyBytes, err := k.Bytes()
247 - if err != nil {
248 - return err
249 - }
250 -
251 - entry.PubKey = pubkeyBytes
252 - }
253 -
254 - namekey, ipnskey := IpnsKeysForID(id)
255 -
240 go func() {
257 - errs <- PublishEntry(ctx, r, ipnskey, entry)
241 + errs <- PublishEntry(ctx, r, ipns.RecordKey(id), entry)
242 }()
243
244 // Publish the public key if a public key cannot be extracted from the ID
245 // TODO: once v0.4.16 is widespread enough, we can stop doing this
246 // and at that point we can even deprecate the /pk/ namespace in the dht
263 - if extractedPublicKey == nil {
247 + //
248 + // NOTE: This check actually checks if the public key has been embedded
249 + // in the IPNS entry. This check is sufficient because we embed the
250 + // public key in the IPNS entry if it can't be extracted from the ID.
251 + if entry.PubKey != nil {
252 go func() {
265 - errs <- PublishPublicKey(ctx, r, namekey, k)
253 + errs <- PublishPublicKey(ctx, r, PkKeyForID(id), k)
254 }()
255
256 if err := waitOnErrChan(ctx, errs); err != nil {
@@ -309,32 +297,6 @@ func PublishEntry(ctx context.Context, r routing.ValueStore, ipnskey string, rec
297 return r.PutValue(timectx, ipnskey, data)
298 }
299
312 -func CreateRoutingEntryData(pk ci.PrivKey, val path.Path, seq uint64, eol time.Time) (*pb.IpnsEntry, error) {
313 - entry := new(pb.IpnsEntry)
314 -
315 - entry.Value = []byte(val)
316 - typ := pb.IpnsEntry_EOL
317 - entry.ValidityType = &typ
318 - entry.Sequence = proto.Uint64(seq)
319 - entry.Validity = []byte(u.FormatRFC3339(eol))
320 -
321 - sig, err := pk.Sign(ipnsEntryDataForSig(entry))
322 - if err != nil {
323 - return nil, err
324 - }
325 - entry.Signature = sig
326 - return entry, nil
327 -}
328 -
329 -func ipnsEntryDataForSig(e *pb.IpnsEntry) []byte {
330 - return bytes.Join([][]byte{
331 - e.Value,
332 - e.Validity,
333 - []byte(fmt.Sprint(e.GetValidityType())),
334 - },
335 - []byte{})
336 -}
337 -
300 // InitializeKeyspace sets the ipns record for the given key to
301 // point to an empty directory.
302 // TODO: this doesnt feel like it belongs here
@@ -356,9 +318,7 @@ func InitializeKeyspace(ctx context.Context, pub Publisher, pins pin.Pinner, key
318 return pub.Publish(ctx, key, path.FromCid(emptyDir.Cid()))
319 }
320
359 -func IpnsKeysForID(id peer.ID) (name, ipns string) {
360 - namekey := "/pk/" + string(id)
361 - ipnskey := "/ipns/" + string(id)
362 -
363 - return namekey, ipnskey
321 +// PkKeyForID returns the public key routing key for the given peer ID.
322 +func PkKeyForID(id peer.ID) string {
323 + return "/pk/" + string(id)
324 }
namesys/publisher_test.go
+4 -5
@@ -6,10 +6,9 @@ import (
6 "testing"
7 "time"
8
9 - path "github.com/ipfs/go-ipfs/path"
10 -
9 dshelp "gx/ipfs/QmNP2u7bofwUQptHQGPfabGWtTCbxhNLSZKqbf1uzsup9V/go-ipfs-ds-help"
10 testutil "gx/ipfs/QmPdxCaVp4jZ9RbxqZADvKH6kiCR5jHvdR5f2ycjAY6T2a/go-testutil"
11 + ipns "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns"
12 ma "gx/ipfs/QmUxSEGbv2nmYNnfXi7839wwQqTN3kwQeUxe8dTjZWZs7J/go-multiaddr"
13 peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer"
14 mockrouting "gx/ipfs/Qmb1N7zdjG2FexpzWNj8T289u9QnQLEiSsTRadDGQxX32D/go-ipfs-routing/mock"
@@ -55,7 +54,7 @@ func testNamekeyPublisher(t *testing.T, keyType int, expectedErr error, expected
54 }
55
56 // Value
58 - value := path.Path("ipfs/TESTING")
57 + value := []byte("ipfs/TESTING")
58
59 // Seqnum
60 seqnum := uint64(0)
@@ -75,7 +74,7 @@ func testNamekeyPublisher(t *testing.T, keyType int, expectedErr error, expected
74 serv := mockrouting.NewServer()
75 r := serv.ClientWithDatastore(context.Background(), &identity{p}, dstore)
76
78 - entry, err := CreateRoutingEntryData(privKey, value, seqnum, eol)
77 + entry, err := ipns.Create(privKey, value, seqnum, eol)
78 if err != nil {
79 t.Fatal(err)
80 }
@@ -86,7 +85,7 @@ func testNamekeyPublisher(t *testing.T, keyType int, expectedErr error, expected
85 }
86
87 // Check for namekey existence in value store
89 - namekey, _ := IpnsKeysForID(id)
88 + namekey := PkKeyForID(id)
89 _, err = r.GetValue(ctx, namekey)
90 if err != expectedErr {
91 t.Fatal(err)
namesys/republisher/repub.go
+1 -1
@@ -7,9 +7,9 @@ import (
7
8 keystore "github.com/ipfs/go-ipfs/keystore"
9 namesys "github.com/ipfs/go-ipfs/namesys"
10 - pb "github.com/ipfs/go-ipfs/namesys/pb"
10 path "github.com/ipfs/go-ipfs/path"
11
12 + pb "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns/pb"
13 goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess"
14 gpctx "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess/context"
15 peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer"
namesys/resolve_test.go
+3 -2
@@ -9,6 +9,7 @@ import (
9 path "github.com/ipfs/go-ipfs/path"
10
11 testutil "gx/ipfs/QmPdxCaVp4jZ9RbxqZADvKH6kiCR5jHvdR5f2ycjAY6T2a/go-testutil"
12 + ipns "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns"
13 peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer"
14 mockrouting "gx/ipfs/Qmb1N7zdjG2FexpzWNj8T289u9QnQLEiSsTRadDGQxX32D/go-ipfs-routing/mock"
15 ds "gx/ipfs/QmeiCcJfDW1GJnWUArudsv5rQsihpi4oyddPhdqo3CfX6i/go-datastore"
@@ -71,7 +72,7 @@ func TestPrexistingExpiredRecord(t *testing.T) {
72 h := path.FromString("/ipfs/QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN")
73 eol := time.Now().Add(time.Hour * -1)
74
74 - entry, err := CreateRoutingEntryData(privk, h, 0, eol)
75 + entry, err := ipns.Create(privk, []byte(h), 0, eol)
76 if err != nil {
77 t.Fatal(err)
78 }
@@ -112,7 +113,7 @@ func TestPrexistingRecord(t *testing.T) {
113 // Make a good record and put it in the datastore
114 h := path.FromString("/ipfs/QmZULkCELmmk5XNfCgTnCyFgAVxBRBXyDHGGMVoLFLiXEN")
115 eol := time.Now().Add(time.Hour)
115 - entry, err := CreateRoutingEntryData(privk, h, 0, eol)
116 + entry, err := ipns.Create(privk, []byte(h), 0, eol)
117 if err != nil {
118 t.Fatal(err)
119 }
namesys/routing.go
+10 -15
@@ -6,11 +6,11 @@ import (
6 "time"
7
8 opts "github.com/ipfs/go-ipfs/namesys/opts"
9 - pb "github.com/ipfs/go-ipfs/namesys/pb"
9 path "github.com/ipfs/go-ipfs/path"
10
12 - u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util"
11 mh "gx/ipfs/QmPnFwZ2JXKnXgMw8CdBPxn7FWh6LLdjUjxV1fKHuJnkr8/go-multihash"
12 + ipns "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns"
13 + pb "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns/pb"
14 routing "gx/ipfs/QmUV9hDAAyjeGbxbXkJ2sYqZ6dTd1DXJ2REhYEkRm178Tg/go-libp2p-routing"
15 peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer"
16 proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto"
@@ -82,7 +82,7 @@ func (r *IpnsResolver) resolveOnce(ctx context.Context, name string, options *op
82 // Use the routing system to get the name.
83 // Note that the DHT will call the ipns validator when retrieving
84 // the value, which in turn verifies the ipns record signature
85 - _, ipnsKey := IpnsKeysForID(pid)
85 + ipnsKey := ipns.RecordKey(pid)
86 val, err := r.routing.GetValue(ctx, ipnsKey, dht.Quorum(int(options.DhtRecordCount)))
87 if err != nil {
88 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
114 if entry.Ttl != nil {
115 ttl = time.Duration(*entry.Ttl)
116 }
117 - if eol, ok := checkEOL(entry); ok {
117 + switch eol, err := ipns.GetEOL(entry); err {
118 + case ipns.ErrUnrecognizedValidity:
119 + // No EOL.
120 + case nil:
121 ttEol := eol.Sub(time.Now())
122 if ttEol < 0 {
123 // It *was* valid when we first resolved it.
@@ -122,18 +125,10 @@ func (r *IpnsResolver) resolveOnce(ctx context.Context, name string, options *op
125 } else if ttEol < ttl {
126 ttl = ttEol
127 }
128 + default:
129 + log.Errorf("encountered error when parsing EOL: %s", err)
130 + return "", 0, err
131 }
132
133 return p, ttl, nil
134 }
129 -
130 -func checkEOL(e *pb.IpnsEntry) (time.Time, bool) {
131 - if e.GetValidityType() == pb.IpnsEntry_EOL {
132 - eol, err := u.ParseRFC3339(string(e.GetValidity()))
133 - if err != nil {
134 - return time.Time{}, false
135 - }
136 - return eol, true
137 - }
138 - return time.Time{}, false
139 -}
namesys/validator.go deleted
-179
@@ -1,179 +0,0 @@
1 -package namesys
2 -
3 -import (
4 - "bytes"
5 - "errors"
6 - "fmt"
7 - "time"
8 -
9 - pb "github.com/ipfs/go-ipfs/namesys/pb"
10 - peer "gx/ipfs/QmVf8hTAsLLFtn4WPCRNdnaF2Eag2qTBS6uR8AiHPZARXy/go-libp2p-peer"
11 - pstore "gx/ipfs/QmZhsmorLpD9kmQ4ynbAu4vbKv2goMUnXazwGA4gnWHDjB/go-libp2p-peerstore"
12 - ic "gx/ipfs/Qme1knMqwt1hKZbc1BmQFmnm9f36nyQGwXxPGVpVJ9rMK5/go-libp2p-crypto"
13 -
14 - record "gx/ipfs/QmPWjVzxHeJdrjp4Jr2R2sPxBrMbBgGPWQtKwCKHHCBF7x/go-libp2p-record"
15 - u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util"
16 - proto "gx/ipfs/QmZ4Qi3GaRbjcx28Sme5eMH7RQjGkt8wHxt2a65oLaeFEV/gogo-protobuf/proto"
17 -)
18 -
19 -// ErrExpiredRecord should be returned when an ipns record is
20 -// invalid due to being too old
21 -var ErrExpiredRecord = errors.New("expired record")
22 -
23 -// ErrUnrecognizedValidity is returned when an IpnsRecord has an
24 -// unknown validity type.
25 -var ErrUnrecognizedValidity = errors.New("unrecognized validity type")
26 -
27 -// ErrInvalidPath should be returned when an ipns record path
28 -// is not in a valid format
29 -var ErrInvalidPath = errors.New("record path invalid")
30 -
31 -// ErrSignature should be returned when an ipns record fails
32 -// signature verification
33 -var ErrSignature = errors.New("record signature verification failed")
34 -
35 -// ErrBadRecord should be returned when an ipns record cannot be unmarshalled
36 -var ErrBadRecord = errors.New("record could not be unmarshalled")
37 -
38 -// ErrKeyFormat should be returned when an ipns record key is
39 -// incorrectly formatted (not a peer ID)
40 -var ErrKeyFormat = errors.New("record key could not be parsed into peer ID")
41 -
42 -// ErrPublicKeyNotFound should be returned when the public key
43 -// corresponding to the ipns record path cannot be retrieved
44 -// from the peer store
45 -var ErrPublicKeyNotFound = errors.New("public key not found in peer store")
46 -
47 -var ErrPublicKeyMismatch = errors.New("public key in record did not match expected pubkey")
48 -
49 -type IpnsValidator struct {
50 - KeyBook pstore.KeyBook
51 -}
52 -
53 -func (v IpnsValidator) Validate(key string, value []byte) error {
54 - ns, pidString, err := record.SplitKey(key)
55 - if err != nil || ns != "ipns" {
56 - return ErrInvalidPath
57 - }
58 -
59 - // Parse the value into an IpnsEntry
60 - entry := new(pb.IpnsEntry)
61 - err = proto.Unmarshal(value, entry)
62 - if err != nil {
63 - return ErrBadRecord
64 - }
65 -
66 - // Get the public key defined by the ipns path
67 - pid, err := peer.IDFromString(pidString)
68 - if err != nil {
69 - log.Debugf("failed to parse ipns record key %s into peer ID", pidString)
70 - return ErrKeyFormat
71 - }
72 -
73 - pubk, err := v.getPublicKey(pid, entry)
74 - if err != nil {
75 - return err
76 - }
77 -
78 - // Check the ipns record signature with the public key
79 - if ok, err := pubk.Verify(ipnsEntryDataForSig(entry), entry.GetSignature()); err != nil || !ok {
80 - log.Debugf("failed to verify signature for ipns record %s", pidString)
81 - return ErrSignature
82 - }
83 -
84 - // Check that record has not expired
85 - switch entry.GetValidityType() {
86 - case pb.IpnsEntry_EOL:
87 - t, err := u.ParseRFC3339(string(entry.GetValidity()))
88 - if err != nil {
89 - log.Debugf("failed parsing time for ipns record EOL in record %s", pidString)
90 - return err
91 - }
92 - if time.Now().After(t) {
93 - return ErrExpiredRecord
94 - }
95 - default:
96 - return ErrUnrecognizedValidity
97 - }
98 - return nil
99 -}
100 -
101 -func (v IpnsValidator) getPublicKey(pid peer.ID, entry *pb.IpnsEntry) (ic.PubKey, error) {
102 - if entry.PubKey != nil {
103 - pk, err := ic.UnmarshalPublicKey(entry.PubKey)
104 - if err != nil {
105 - log.Debugf("public key in ipns record failed to parse: ", err)
106 - return nil, fmt.Errorf("unmarshaling pubkey in record: %s", err)
107 - }
108 -
109 - expPid, err := peer.IDFromPublicKey(pk)
110 - if err != nil {
111 - return nil, fmt.Errorf("could not regenerate peerID from pubkey: %s", err)
112 - }
113 -
114 - if pid != expPid {
115 - return nil, ErrPublicKeyMismatch
116 - }
117 -
118 - return pk, nil
119 - }
120 -
121 - pubk := v.KeyBook.PubKey(pid)
122 - if pubk == nil {
123 - log.Debugf("public key with hash %s not found in peer store", pid)
124 - return nil, ErrPublicKeyNotFound
125 - }
126 - return pubk, nil
127 -}
128 -
129 -// IpnsSelectorFunc selects the best record by checking which has the highest
130 -// sequence number and latest EOL
131 -func (v IpnsValidator) Select(k string, vals [][]byte) (int, error) {
132 - var recs []*pb.IpnsEntry
133 - for _, v := range vals {
134 - e := new(pb.IpnsEntry)
135 - err := proto.Unmarshal(v, e)
136 - if err == nil {
137 - recs = append(recs, e)
138 - } else {
139 - recs = append(recs, nil)
140 - }
141 - }
142 -
143 - return selectRecord(recs, vals)
144 -}
145 -
146 -func selectRecord(recs []*pb.IpnsEntry, vals [][]byte) (int, error) {
147 - var bestSeq uint64
148 - besti := -1
149 -
150 - for i, r := range recs {
151 - if r == nil || r.GetSequence() < bestSeq {
152 - continue
153 - }
154 - rt, err := u.ParseRFC3339(string(r.GetValidity()))
155 - if err != nil {
156 - log.Errorf("failed to parse ipns record EOL %s", r.GetValidity())
157 - continue
158 - }
159 -
160 - if besti == -1 || r.GetSequence() > bestSeq {
161 - bestSeq = r.GetSequence()
162 - besti = i
163 - } else if r.GetSequence() == bestSeq {
164 - bestt, _ := u.ParseRFC3339(string(recs[besti].GetValidity()))
165 - if rt.After(bestt) {
166 - besti = i
167 - } else if rt == bestt {
168 - if bytes.Compare(vals[i], vals[besti]) > 0 {
169 - besti = i
170 - }
171 - }
172 - }
173 - }
174 - if besti == -1 {
175 - return 0, errors.New("no usable records in given set")
176 - }
177 -
178 - return besti, nil
179 -}
package.json
+6
@@ -539,6 +539,12 @@
539 "hash": "QmfNjggF4Pt6erqg3NDafD3MdvDHk1qqCVr8pL5hnPucS8",
540 "name": "fsnotify",
541 "version": "0.1.1"
542 + },
543 + {
544 + "author": "stebalien",
545 + "hash": "QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt",
546 + "name": "go-ipns",
547 + "version": "0.1.1"
548 }
549 ],
550 "gxVersion": "0.10.0",