diff --git a/Rules.mk b/Rules.mk index 864002d49..de1dc58a5 100644 --- a/Rules.mk +++ b/Rules.mk @@ -47,9 +47,6 @@ ifneq ($(filter coverage% clean distclean,$(MAKECMDGOALS)),) include $(dir)/Rules.mk endif -dir := namesys/pb -include $(dir)/Rules.mk - dir := unixfs/pb include $(dir)/Rules.mk diff --git a/core/commands/dht_test.go b/core/commands/dht_test.go index 0ad7b0274..0969c6a21 100644 --- a/core/commands/dht_test.go +++ b/core/commands/dht_test.go @@ -4,12 +4,15 @@ import ( "testing" "github.com/ipfs/go-ipfs/namesys" + tu "gx/ipfs/QmPdxCaVp4jZ9RbxqZADvKH6kiCR5jHvdR5f2ycjAY6T2a/go-testutil" + ipns "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns" ) func TestKeyTranslation(t *testing.T) { pid := tu.RandPeerIDFatal(t) - a, b := namesys.IpnsKeysForID(pid) + pkname := namesys.PkKeyForID(pid) + ipnsname := ipns.RecordKey(pid) pkk, err := escapeDhtKey("/pk/" + pid.Pretty()) if err != nil { @@ -21,11 +24,11 @@ func TestKeyTranslation(t *testing.T) { t.Fatal(err) } - if pkk != a { + if pkk != pkname { t.Fatal("keys didnt match!") } - if ipnsk != b { + if ipnsk != ipnsname { t.Fatal("keys didnt match!") } } diff --git a/core/core.go b/core/core.go index db7cc7bb4..c267aad52 100644 --- a/core/core.go +++ b/core/core.go @@ -42,6 +42,7 @@ import ( circuit "gx/ipfs/QmPavh4h3Edx5cv8GJQPC35AQAws7faXmzEZyru7k9b9Mn/go-libp2p-circuit" u "gx/ipfs/QmPdKqUcHGFdeSpvjVoaTRPPstGif9GBZb5Q56RVw9o69A/go-ipfs-util" p2phost "gx/ipfs/QmQQGtcp6nVUrQjNsnU53YWV1q8fK1Kd9S7FEkYbRZzxry/go-libp2p-host" + ipns "gx/ipfs/QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt/go-ipns" floodsub "gx/ipfs/QmRFEBGcNjtWPupwHA7zGHeGVLuUyE4ZRFi2MgtrPM6pfb/go-libp2p-floodsub" pnet "gx/ipfs/QmRGvSwDpN4eunxgDNfmQhayZ6Z9F5a2v31V2D7y77osLg/go-libp2p-pnet" goprocess "gx/ipfs/QmSF8fPo3jgVBAy8fpdjjYqgG87dkJgUprRBHRd2tmfgpP/goprocess" @@ -459,7 +460,7 @@ func (n *IpfsNode) startOnlineServicesWithHost(ctx context.Context, host p2phost validator := record.NamespacedValidator{ "pk": record.PublicKeyValidator{}, - "ipns": namesys.IpnsValidator{KeyBook: host.Peerstore()}, + "ipns": ipns.Validator{KeyBook: host.Peerstore()}, } // setup routing service 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/Rules.mk b/namesys/pb/Rules.mk deleted file mode 100644 index 505f70e75..000000000 --- a/namesys/pb/Rules.mk +++ /dev/null @@ -1,8 +0,0 @@ -include mk/header.mk - -PB_$(d) = $(wildcard $(d)/*.proto) -TGTS_$(d) = $(PB_$(d):.proto=.pb.go) - -#DEPS_GO += $(TGTS_$(d)) - -include mk/footer.mk 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) + if err := ipns.EmbedPublicKey(k, entry); err != nil { + return err + } + id, err := peer.IDFromPublicKey(k) if err != nil { return err } - // Attempt to extract the public key from the ID - extractedPublicKey, err := id.ExtractPublicKey() - 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 -} diff --git a/package.json b/package.json index 084f08964..cd1f431b6 100644 --- a/package.json +++ b/package.json @@ -539,6 +539,12 @@ "hash": "QmfNjggF4Pt6erqg3NDafD3MdvDHk1qqCVr8pL5hnPucS8", "name": "fsnotify", "version": "0.1.1" + }, + { + "author": "stebalien", + "hash": "QmRAPFFaF7nrezCZQaLihyp2qbAXqSJU5WpvSpwroMv1Xt", + "name": "go-ipns", + "version": "0.1.1" } ], "gxVersion": "0.10.0",