mirror of
https://github.com/ipfs/kubo.git
synced 2025-08-06 19:44:01 +08:00
131 lines
3.2 KiB
Go
131 lines
3.2 KiB
Go
package republisher
|
|
|
|
import (
|
|
"errors"
|
|
"sync"
|
|
"time"
|
|
|
|
key "github.com/ipfs/go-ipfs/blocks/key"
|
|
namesys "github.com/ipfs/go-ipfs/namesys"
|
|
pb "github.com/ipfs/go-ipfs/namesys/pb"
|
|
peer "github.com/ipfs/go-ipfs/p2p/peer"
|
|
path "github.com/ipfs/go-ipfs/path"
|
|
"github.com/ipfs/go-ipfs/routing"
|
|
dhtpb "github.com/ipfs/go-ipfs/routing/dht/pb"
|
|
|
|
proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
|
|
ds "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore"
|
|
goprocess "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess"
|
|
gpctx "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/jbenet/goprocess/context"
|
|
context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
|
|
logging "github.com/ipfs/go-ipfs/vendor/QmQg1J6vikuXF9oDvm4wpdeAUvvkVEKW1EYDw9HhTMnP2b/go-log"
|
|
)
|
|
|
|
var errNoEntry = errors.New("no previous entry")
|
|
|
|
var log = logging.Logger("ipns-repub")
|
|
|
|
var DefaultRebroadcastInterval = time.Hour * 4
|
|
|
|
const DefaultRecordLifetime = time.Hour * 24
|
|
|
|
type Republisher struct {
|
|
r routing.IpfsRouting
|
|
ds ds.Datastore
|
|
ps peer.Peerstore
|
|
|
|
Interval time.Duration
|
|
|
|
// how long records that are republished should be valid for
|
|
RecordLifetime time.Duration
|
|
|
|
entrylock sync.Mutex
|
|
entries map[peer.ID]struct{}
|
|
}
|
|
|
|
func NewRepublisher(r routing.IpfsRouting, ds ds.Datastore, ps peer.Peerstore) *Republisher {
|
|
return &Republisher{
|
|
r: r,
|
|
ps: ps,
|
|
ds: ds,
|
|
entries: make(map[peer.ID]struct{}),
|
|
Interval: DefaultRebroadcastInterval,
|
|
RecordLifetime: DefaultRecordLifetime,
|
|
}
|
|
}
|
|
|
|
func (rp *Republisher) AddName(id peer.ID) {
|
|
rp.entrylock.Lock()
|
|
defer rp.entrylock.Unlock()
|
|
rp.entries[id] = struct{}{}
|
|
}
|
|
|
|
func (rp *Republisher) Run(proc goprocess.Process) {
|
|
tick := time.NewTicker(rp.Interval)
|
|
defer tick.Stop()
|
|
|
|
for {
|
|
select {
|
|
case <-tick.C:
|
|
err := rp.republishEntries(proc)
|
|
if err != nil {
|
|
log.Error("Republisher failed to republish: ", err)
|
|
}
|
|
case <-proc.Closing():
|
|
return
|
|
}
|
|
}
|
|
}
|
|
|
|
func (rp *Republisher) republishEntries(p goprocess.Process) error {
|
|
ctx, cancel := context.WithCancel(gpctx.OnClosingContext(p))
|
|
defer cancel()
|
|
|
|
for id, _ := range rp.entries {
|
|
log.Debugf("republishing ipns entry for %s", id)
|
|
priv := rp.ps.PrivKey(id)
|
|
|
|
// Look for it locally only
|
|
_, ipnskey := namesys.IpnsKeysForID(id)
|
|
p, seq, err := rp.getLastVal(ipnskey)
|
|
if err != nil {
|
|
if err == errNoEntry {
|
|
continue
|
|
}
|
|
return err
|
|
}
|
|
|
|
// update record with same sequence number
|
|
eol := time.Now().Add(rp.RecordLifetime)
|
|
err = namesys.PutRecordToRouting(ctx, priv, p, seq, eol, rp.r, id)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (rp *Republisher) getLastVal(k key.Key) (path.Path, uint64, error) {
|
|
ival, err := rp.ds.Get(k.DsKey())
|
|
if err != nil {
|
|
// not found means we dont have a previously published entry
|
|
return "", 0, errNoEntry
|
|
}
|
|
|
|
val := ival.([]byte)
|
|
dhtrec := new(dhtpb.Record)
|
|
err = proto.Unmarshal(val, dhtrec)
|
|
if err != nil {
|
|
return "", 0, err
|
|
}
|
|
|
|
// extract published data from record
|
|
e := new(pb.IpnsEntry)
|
|
err = proto.Unmarshal(dhtrec.GetValue(), e)
|
|
if err != nil {
|
|
return "", 0, err
|
|
}
|
|
return path.Path(e.Value), e.GetSequence(), nil
|
|
}
|