Some changes at namesys/routing
This commit is contained in:
parent
b914745b47
commit
5b8678b48c
3 changed files with 375 additions and 12 deletions
|
@ -10,7 +10,7 @@
|
||||||
struct cacheEntry {
|
struct cacheEntry {
|
||||||
char *key;
|
char *key;
|
||||||
char *value;
|
char *value;
|
||||||
struct stime eol;
|
struct timespec eol;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct routingResolver {
|
struct routingResolver {
|
||||||
|
@ -29,7 +29,8 @@
|
||||||
// ipfs_namesys_routing_resolve_once implements resolver. Uses the IPFS
|
// ipfs_namesys_routing_resolve_once implements resolver. Uses the IPFS
|
||||||
// routing system to resolve SFS-like names.
|
// routing system to resolve SFS-like names.
|
||||||
int ipfs_namesys_routing_resolve_once (char **path, char *name, int depth, char *prefix, struct namesys_pb *pb);
|
int ipfs_namesys_routing_resolve_once (char **path, char *name, int depth, char *prefix, struct namesys_pb *pb);
|
||||||
int ipfs_namesys_routing_check_EOL (struct stime *st, struct namesys_pb *pb);
|
int ipfs_namesys_routing_check_EOL (struct timespec *ts, struct namesys_pb *pb);
|
||||||
|
|
||||||
int ipfs_namesys_routing_get_value (char*, char*);
|
int ipfs_namesys_routing_get_value (char*, char*);
|
||||||
int ipfs_namesys_routing_getpublic_key (char*, struct MultiHash*);
|
int ipfs_namesys_routing_getpublic_key (char*, struct MultiHash*);
|
||||||
#endif // IPNS_NAMESYS_ROUTING_H
|
#endif // IPNS_NAMESYS_ROUTING_H
|
||||||
|
|
362
namesys/publisher.c
Normal file
362
namesys/publisher.c
Normal file
|
@ -0,0 +1,362 @@
|
||||||
|
var PublishPutValTimeout = time.Minute
|
||||||
|
|
||||||
|
// ipnsPublisher is capable of publishing and resolving names to the IPFS
|
||||||
|
// routing system.
|
||||||
|
type ipnsPublisher struct {
|
||||||
|
routing routing.ValueStore
|
||||||
|
ds ds.Datastore
|
||||||
|
}
|
||||||
|
|
||||||
|
// NewRoutingPublisher constructs a publisher for the IPFS Routing name system.
|
||||||
|
func NewRoutingPublisher(route routing.ValueStore, ds ds.Datastore) *ipnsPublisher {
|
||||||
|
if ds == nil {
|
||||||
|
panic("nil datastore")
|
||||||
|
}
|
||||||
|
return &ipnsPublisher{routing: route, ds: ds}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Publish implements Publisher. Accepts a keypair and a value,
|
||||||
|
// and publishes it out to the routing system
|
||||||
|
func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Path) error {
|
||||||
|
log.Debugf("Publish %s", value)
|
||||||
|
return p.PublishWithEOL(ctx, k, value, time.Now().Add(time.Hour*24))
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublishWithEOL is a temporary stand in for the ipns records implementation
|
||||||
|
// see here for more details: https://github.com/ipfs/specs/tree/master/records
|
||||||
|
func (p *ipnsPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) error {
|
||||||
|
|
||||||
|
id, err := peer.IDFromPrivateKey(k)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
_, ipnskey := IpnsKeysForID(id)
|
||||||
|
|
||||||
|
// get previous records sequence number
|
||||||
|
seqnum, err := p.getPreviousSeqNo(ctx, ipnskey)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// increment it
|
||||||
|
seqnum++
|
||||||
|
|
||||||
|
return PutRecordToRouting(ctx, k, value, seqnum, eol, p.routing, id)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (p *ipnsPublisher) getPreviousSeqNo(ctx context.Context, ipnskey string) (uint64, error) {
|
||||||
|
prevrec, err := p.ds.Get(dshelp.NewKeyFromBinary(ipnskey))
|
||||||
|
if err != nil && err != ds.ErrNotFound {
|
||||||
|
// None found, lets start at zero!
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
var val []byte
|
||||||
|
if err == nil {
|
||||||
|
prbytes, ok := prevrec.([]byte)
|
||||||
|
if !ok {
|
||||||
|
return 0, fmt.Errorf("unexpected type returned from datastore: %#v", prevrec)
|
||||||
|
}
|
||||||
|
dhtrec := new(dhtpb.Record)
|
||||||
|
err := proto.Unmarshal(prbytes, dhtrec)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
val = dhtrec.GetValue()
|
||||||
|
} else {
|
||||||
|
// try and check the dht for a record
|
||||||
|
ctx, cancel := context.WithTimeout(ctx, time.Second*30)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
rv, err := p.routing.GetValue(ctx, ipnskey)
|
||||||
|
if err != nil {
|
||||||
|
// no such record found, start at zero!
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
val = rv
|
||||||
|
}
|
||||||
|
|
||||||
|
e := new(pb.IpnsEntry)
|
||||||
|
err = proto.Unmarshal(val, e)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return e.GetSequence(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// setting the TTL on published records is an experimental feature.
|
||||||
|
// as such, i'm using the context to wire it through to avoid changing too
|
||||||
|
// much code along the way.
|
||||||
|
func checkCtxTTL(ctx context.Context) (time.Duration, bool) {
|
||||||
|
v := ctx.Value("ipns-publish-ttl")
|
||||||
|
if v == nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
d, ok := v.(time.Duration)
|
||||||
|
return d, ok
|
||||||
|
}
|
||||||
|
|
||||||
|
func PutRecordToRouting(ctx context.Context, k ci.PrivKey, value path.Path, seqnum uint64, eol time.Time, r routing.ValueStore, id peer.ID) error {
|
||||||
|
ctx, cancel := context.WithCancel(ctx)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
namekey, ipnskey := IpnsKeysForID(id)
|
||||||
|
entry, err := CreateRoutingEntryData(k, value, seqnum, eol)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
ttl, ok := checkCtxTTL(ctx)
|
||||||
|
if ok {
|
||||||
|
entry.Ttl = proto.Uint64(uint64(ttl.Nanoseconds()))
|
||||||
|
}
|
||||||
|
|
||||||
|
errs := make(chan error, 2)
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
errs <- PublishEntry(ctx, r, ipnskey, entry)
|
||||||
|
}()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
errs <- PublishPublicKey(ctx, r, namekey, k.GetPublic())
|
||||||
|
}()
|
||||||
|
|
||||||
|
err = waitOnErrChan(ctx, errs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = waitOnErrChan(ctx, errs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func waitOnErrChan(ctx context.Context, errs chan error) error {
|
||||||
|
select {
|
||||||
|
case err := <-errs:
|
||||||
|
return err
|
||||||
|
case <-ctx.Done():
|
||||||
|
return ctx.Err()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func PublishPublicKey(ctx context.Context, r routing.ValueStore, k string, pubk ci.PubKey) error {
|
||||||
|
log.Debugf("Storing pubkey at: %s", k)
|
||||||
|
pkbytes, err := pubk.Bytes()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Store associated public key
|
||||||
|
timectx, cancel := context.WithTimeout(ctx, PublishPutValTimeout)
|
||||||
|
defer cancel()
|
||||||
|
err = r.PutValue(timectx, k, pkbytes)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func PublishEntry(ctx context.Context, r routing.ValueStore, ipnskey string, rec *pb.IpnsEntry) error {
|
||||||
|
timectx, cancel := context.WithTimeout(ctx, PublishPutValTimeout)
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
data, err := proto.Marshal(rec)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Debugf("Storing ipns entry at: %s", ipnskey)
|
||||||
|
// Store ipns entry at "/ipns/"+b58(h(pubkey))
|
||||||
|
if err := r.PutValue(timectx, ipnskey, data); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
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{})
|
||||||
|
}
|
||||||
|
|
||||||
|
var IpnsRecordValidator = &record.ValidChecker{
|
||||||
|
Func: ValidateIpnsRecord,
|
||||||
|
Sign: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
func IpnsSelectorFunc(k string, vals [][]byte) (int, error) {
|
||||||
|
var recs []*pb.IpnsEntry
|
||||||
|
for _, v := range vals {
|
||||||
|
e := new(pb.IpnsEntry)
|
||||||
|
err := proto.Unmarshal(v, e)
|
||||||
|
if err == nil {
|
||||||
|
recs = append(recs, e)
|
||||||
|
} else {
|
||||||
|
recs = append(recs, nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return selectRecord(recs, vals)
|
||||||
|
}
|
||||||
|
|
||||||
|
func selectRecord(recs []*pb.IpnsEntry, vals [][]byte) (int, error) {
|
||||||
|
var best_seq uint64
|
||||||
|
best_i := -1
|
||||||
|
|
||||||
|
for i, r := range recs {
|
||||||
|
if r == nil || r.GetSequence() < best_seq {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if best_i == -1 || r.GetSequence() > best_seq {
|
||||||
|
best_seq = r.GetSequence()
|
||||||
|
best_i = i
|
||||||
|
} else if r.GetSequence() == best_seq {
|
||||||
|
rt, err := u.ParseRFC3339(string(r.GetValidity()))
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
bestt, err := u.ParseRFC3339(string(recs[best_i].GetValidity()))
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if rt.After(bestt) {
|
||||||
|
best_i = i
|
||||||
|
} else if rt == bestt {
|
||||||
|
if bytes.Compare(vals[i], vals[best_i]) > 0 {
|
||||||
|
best_i = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if best_i == -1 {
|
||||||
|
return 0, errors.New("no usable records in given set")
|
||||||
|
}
|
||||||
|
|
||||||
|
return best_i, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// ValidateIpnsRecord implements ValidatorFunc and verifies that the
|
||||||
|
// given 'val' is an IpnsEntry and that that entry is valid.
|
||||||
|
int ValidateIpnsRecord (char *k, char *val)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
|
||||||
|
}
|
||||||
|
func ValidateIpnsRecord(k string, val []byte) error {
|
||||||
|
entry := new(pb.IpnsEntry)
|
||||||
|
err := proto.Unmarshal(val, entry)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
switch entry.GetValidityType() {
|
||||||
|
case pb.IpnsEntry_EOL:
|
||||||
|
t, err := u.ParseRFC3339(string(entry.GetValidity()))
|
||||||
|
if err != nil {
|
||||||
|
log.Debug("failed parsing time for ipns record EOL")
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if time.Now().After(t) {
|
||||||
|
return ErrExpiredRecord
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return ErrUnrecognizedValidity
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitializeKeyspace sets the ipns record for the given key to
|
||||||
|
// point to an empty directory.
|
||||||
|
// TODO: this doesnt feel like it belongs here
|
||||||
|
int InitializeKeyspace (DAGService ds, Publisher pub, Pinner pins, ciPrivKey key)
|
||||||
|
{
|
||||||
|
int err;
|
||||||
|
Node emptyDir;
|
||||||
|
Cid nodek;
|
||||||
|
|
||||||
|
err = ipfs_merkledag_add(ds, nodek, emptyDir);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
// pin recursively because this might already be pinned
|
||||||
|
// and doing a direct pin would throw an error in that case
|
||||||
|
err = ipfs_pins_pin(emptyDir, TRUE);
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ipfs_pins_flush();
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
err = ipfs_pub_publish(key, PathFromCid(nodek));
|
||||||
|
if (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int IpnsKeysForID (char **namekey, char **ipnskey, char *id)
|
||||||
|
{
|
||||||
|
char namekey_prefix[] = "/pk/";
|
||||||
|
char ipnskey_prefix[] = "/ipns/";
|
||||||
|
int i, n;
|
||||||
|
|
||||||
|
n = sizeof(namekey_prefix) + strlen(id);
|
||||||
|
*namekey = malloc(n);
|
||||||
|
if (!*namekey) {
|
||||||
|
return ErrAllocFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
*ipnskey = malloc(i);
|
||||||
|
if (!*ipnskey) {
|
||||||
|
free (*namekey);
|
||||||
|
*namekey = NULL;
|
||||||
|
return ErrAllocFailed;
|
||||||
|
}
|
||||||
|
|
||||||
|
namekey[--n] = '\0';
|
||||||
|
strncpy (*namekey, namekey_prefix, n);
|
||||||
|
strncat (*namekey, id, n - strlen (namekey));
|
||||||
|
|
||||||
|
ipnskey[--i] = '\0';
|
||||||
|
strncpy (*ipnskey, ipnskey_prefix, i);
|
||||||
|
strncat (*ipnskey, id, i - strlen (ipnskey));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
|
@ -13,15 +13,15 @@ char* ipfs_routing_cache_get (char *key, struct ipns_entry *ientry)
|
||||||
{
|
{
|
||||||
int i;
|
int i;
|
||||||
struct routingResolver *cache;
|
struct routingResolver *cache;
|
||||||
struct stime now;
|
struct timespec now;
|
||||||
|
|
||||||
if (key && ientry) {
|
if (key && ientry) {
|
||||||
cache = ientry->cache;
|
cache = ientry->cache;
|
||||||
if (cache) {
|
if (cache) {
|
||||||
get_gmttime (&now);
|
timespec_get (&now);
|
||||||
for (i = 0 ; i < cache->next ; i++) {
|
for (i = 0 ; i < cache->next ; i++) {
|
||||||
if (((now.t < cache->data[i]->eol.t ||
|
if (((now.tv_sec < cache->data[i]->eol.tv_sec ||
|
||||||
(now.t == cache->data[i]->eol.t && now.ts.tv_nsec < cache->data[i]->eol.ts.tv_nsec))) &&
|
(now.tv_sec == cache->data[i]->eol.tv_sec && now.tv_nsec < cache->data[i]->eol.tv_nsec))) &&
|
||||||
strcmp(cache->data[i]->key, key) == 0) {
|
strcmp(cache->data[i]->key, key) == 0) {
|
||||||
return cache->data[i]->value;
|
return cache->data[i]->value;
|
||||||
}
|
}
|
||||||
|
@ -43,8 +43,8 @@ void ipfs_routing_cache_set (char *key, char *value, struct ipns_entry *ientry)
|
||||||
if (n) {
|
if (n) {
|
||||||
n->key = key;
|
n->key = key;
|
||||||
n->value = value;
|
n->value = value;
|
||||||
get_gmttime (&n->eol); // now
|
timespec_get (&n->eol); // now
|
||||||
n->eol.t += DefaultResolverCacheTTL; // sum TTL seconds to time seconds.
|
n->eol.tv_sec += DefaultResolverCacheTTL; // sum TTL seconds to time seconds.
|
||||||
cache->data[cache->next++] = n;
|
cache->data[cache->next++] = n;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -152,7 +152,7 @@ int ipfs_namesys_routing_resolve_once (char **path, char *name, int depth, char
|
||||||
}
|
}
|
||||||
|
|
||||||
// check sig with pk
|
// check sig with pk
|
||||||
err = libp2p_crypto_verify (ipfs_ipns_entry_data_for_sig(pb->IpnsEntry), ipfs_ipns_entry_get_signature(pb->IpnsEntry), &ok);
|
err = libp2p_crypto_verify (ipns_entry_data_for_sig(pb->IpnsEntry), pb->IpnsEntry->signature, &ok);
|
||||||
if (err || !ok) {
|
if (err || !ok) {
|
||||||
char buf[500];
|
char buf[500];
|
||||||
snprintf(buf, sizeof(buf), Err[ErrInvalidSignatureFmt], pubkey);
|
snprintf(buf, sizeof(buf), Err[ErrInvalidSignatureFmt], pubkey);
|
||||||
|
@ -198,12 +198,12 @@ int ipfs_namesys_routing_resolve_once (char **path, char *name, int depth, char
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int ipfs_namesys_routing_check_EOL (struct stime *st, struct namesys_pb *pb)
|
int ipfs_namesys_routing_check_EOL (struct timespec *ts, struct namesys_pb *pb)
|
||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (ipfs_namesys_pb_get_validity_type (pb->IpnsEntry) == IpnsEntry_EOL) {
|
if (*(pb->IpnsEntry->validityType) == IpnsEntry_EOL) {
|
||||||
err = ipfs_util_time_parse_RFC3339 (st, ipfs_namesys_pb_get_validity (pb->IpnsEntry));
|
err = ipfs_util_time_parse_RFC3339 (ts, pb->IpnsEntry->validity);
|
||||||
if (!err) {
|
if (!err) {
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue