namesys
NameSystem interface
// Namesys represents a cohesive name publishing and resolving system.
//
// Publishing a name is the process of establishing a mapping, a key-value
// pair, according to naming rules and databases.
//
// Resolving a name is the process of looking up the value associated with the
// key (name).
type NameSystem interface {
Resolver
Publisher
}
namesys支援多種域名系統的解析,現支援ipns,dns和proquint。
// mpns (a multi-protocol NameSystem) implements generic IPFS naming.
//
// Uses several Resolvers:
// (a) IPFS routing naming: SFS-like PKI names.
// (b) dns domains: resolves using links in DNS TXT records
// (c) proquints: interprets string as the raw byte data.
//
// It can only publish to: (a) IPFS routing naming.
//
type mpns struct {
dnsResolver, proquintResolver, ipnsResolver resolver
ipnsPublisher Publisher
staticMap map[string]path.Path
cache *lru.Cache
}
Publish
Publisher interface
// Publisher is an object capable of publishing particular names.
type Publisher interface {
// Publish establishes a name-value mapping.
// TODO make this not PrivKey specific.
Publish(ctx context.Context, name ci.PrivKey, value path.Path) error
// TODO: to be replaced by a more generic 'PublishWithValidity' type
// call once the records spec is implemented
PublishWithEOL(ctx context.Context, name ci.PrivKey, value path.Path, eol time.Time) error
}
IpnsPublisher
通過routing調用dht的put_value,将ipns記錄publish。
// IpnsPublisher is capable of publishing and resolving names to the IPFS
// routing system.
type IpnsPublisher struct {
routing routing.ValueStore
ds ds.Datastore
// Used to ensure we assign IPNS records *sequential* sequence numbers.
mu sync.Mutex
}
func (p *IpnsPublisher) updateRecord(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) (*pb.IpnsEntry, error) {
id, err := peer.IDFromPrivateKey(k)
if err != nil {
return nil, err
}
p.mu.Lock()
defer p.mu.Unlock()
// get previous records sequence number
rec, err := p.GetPublished(ctx, id, true)
if err != nil {
return nil, err
}
seqno := rec.GetSequence() // returns 0 if rec is nil
if rec != nil && value != path.Path(rec.GetValue()) {
// Don't bother incrementing the sequence number unless the
// value changes.
seqno++
}
// Create record
entry, err := ipns.Create(k, []byte(value), seqno, eol)
if err != nil {
return nil, err
}
// Set the TTL
// TODO: Make this less hacky.
ttl, ok := checkCtxTTL(ctx)
if ok {
entry.Ttl = proto.Uint64(uint64(ttl.Nanoseconds()))
}
data, err := proto.Marshal(entry)
if err != nil {
return nil, err
}
// Put the new record.
key := IpnsDsKey(id)
if err := p.ds.Put(key, data); err != nil {
return nil, err
}
if err := p.ds.Sync(key); err != nil {
return nil, err
}
return entry, nil
}
// PublishWithEOL is a temporary stand in for the ipns records implementation
// see here for more details: https://github.com/ipfs/specs/tree/master/records
func (p *IpnsPublisher) PublishWithEOL(ctx context.Context, k ci.PrivKey, value path.Path, eol time.Time) error {
record, err := p.updateRecord(ctx, k, value, eol)
if err != nil {
return err
}
return PutRecordToRouting(ctx, p.routing, k.GetPublic(), record)
}
Republish
每條ipns記錄的有效期預設為24h,預設每4h啟動1次Republish
// DefaultRebroadcastInterval is the default interval at which we rebroadcast IPNS records
var DefaultRebroadcastInterval = time.Hour * 4
// DefaultRecordLifetime is the default lifetime for IPNS records
const DefaultRecordLifetime = time.Hour * 24
type Republisher struct {
ns namesys.Publisher
ds ds.Datastore
self ic.PrivKey
ks keystore.Keystore
Interval time.Duration
// how long records that are republished should be valid for
RecordLifetime time.Duration
}
// NewRepublisher creates a new Republisher
func NewRepublisher(ns namesys.Publisher, ds ds.Datastore, self ic.PrivKey, ks keystore.Keystore) *Republisher {
return &Republisher{
ns: ns,
ds: ds,
self: self,
ks: ks,
Interval: DefaultRebroadcastInterval,
RecordLifetime: DefaultRecordLifetime,
}
}
func (rp *Republisher) Run(proc goprocess.Process) {
timer := time.NewTimer(InitialRebroadcastDelay)
defer timer.Stop()
if rp.Interval < InitialRebroadcastDelay {
timer.Reset(rp.Interval)
}
for {
select {
case <-timer.C:
timer.Reset(rp.Interval)
err := rp.republishEntries(proc)
if err != nil {
log.Info("republisher failed to republish: ", err)
if FailureRetryInterval < rp.Interval {
timer.Reset(FailureRetryInterval)
}
}
case <-proc.Closing():
return
}
}
}
Resolve
Resolver interface
// Resolver is an object capable of resolving names.
type Resolver interface {
// Resolve performs a recursive lookup, returning the dereferenced
// path. For example, if ipfs.io has a DNS TXT record pointing to
// /ipns/QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
// and there is a DHT IPNS entry for
// QmatmE9msSfkKxoffpHwNLNKgwZG8eT9Bud6YoPab52vpy
// -> /ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj
// then
// Resolve(ctx, "/ipns/ipfs.io")
// will resolve both names, returning
// /ipfs/Qmcqtw8FfrVSBaRmbWwHxt3AuySBhJLcvmFYi3Lbc4xnwj
//
// There is a default depth-limit to avoid infinite recursion. Most
// users will be fine with this default limit, but if you need to
// adjust the limit you can specify it as an option.
Resolve(ctx context.Context, name string, options ...opts.ResolveOpt) (value path.Path, err error)
// ResolveAsync performs recursive name lookup, like Resolve, but it returns
// entries as they are discovered in the DHT. Each returned result is guaranteed
// to be "better" (which usually means newer) than the previous one.
ResolveAsync(ctx context.Context, name string, options ...opts.ResolveOpt) <-chan Result
}
IpnsResolver
通過routing調用dht的get_value,擷取到ipns記錄,再将其解析。
// IpnsResolver implements NSResolver for the main IPFS SFS-like naming
type IpnsResolver struct {
routing routing.ValueStore
}