mirror of
https://github.com/ipfs/kubo.git
synced 2026-02-21 10:27:46 +08:00
extract ipns record logic to go-ipns
License: MIT Signed-off-by: Steven Allen <steven@stebalien.com>
This commit is contained in:
parent
d643c045d2
commit
dafe495376
3
Rules.mk
3
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
|
||||
|
||||
|
||||
@ -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!")
|
||||
}
|
||||
}
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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{},
|
||||
}}
|
||||
}
|
||||
@ -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}
|
||||
}
|
||||
@ -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
|
||||
@ -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)
|
||||
}
|
||||
@ -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;
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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"
|
||||
|
||||
@ -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)
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
|
||||
@ -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
|
||||
}
|
||||
@ -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",
|
||||
|
||||
Loading…
Reference in New Issue
Block a user