github.com/keltia/go-ipfs@v0.3.8-0.20150909044612-210793031c63/namesys/publisher.go (about)

     1  package namesys
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"fmt"
     7  	"time"
     8  
     9  	proto "github.com/ipfs/go-ipfs/Godeps/_workspace/src/github.com/gogo/protobuf/proto"
    10  	context "github.com/ipfs/go-ipfs/Godeps/_workspace/src/golang.org/x/net/context"
    11  
    12  	key "github.com/ipfs/go-ipfs/blocks/key"
    13  	dag "github.com/ipfs/go-ipfs/merkledag"
    14  	pb "github.com/ipfs/go-ipfs/namesys/pb"
    15  	ci "github.com/ipfs/go-ipfs/p2p/crypto"
    16  	path "github.com/ipfs/go-ipfs/path"
    17  	pin "github.com/ipfs/go-ipfs/pin"
    18  	routing "github.com/ipfs/go-ipfs/routing"
    19  	record "github.com/ipfs/go-ipfs/routing/record"
    20  	ft "github.com/ipfs/go-ipfs/unixfs"
    21  	u "github.com/ipfs/go-ipfs/util"
    22  )
    23  
    24  // ErrExpiredRecord should be returned when an ipns record is
    25  // invalid due to being too old
    26  var ErrExpiredRecord = errors.New("expired record")
    27  
    28  // ErrUnrecognizedValidity is returned when an IpnsRecord has an
    29  // unknown validity type.
    30  var ErrUnrecognizedValidity = errors.New("unrecognized validity type")
    31  
    32  // ipnsPublisher is capable of publishing and resolving names to the IPFS
    33  // routing system.
    34  type ipnsPublisher struct {
    35  	routing routing.IpfsRouting
    36  }
    37  
    38  // NewRoutingPublisher constructs a publisher for the IPFS Routing name system.
    39  func NewRoutingPublisher(route routing.IpfsRouting) Publisher {
    40  	return &ipnsPublisher{routing: route}
    41  }
    42  
    43  // Publish implements Publisher. Accepts a keypair and a value,
    44  // and publishes it out to the routing system
    45  func (p *ipnsPublisher) Publish(ctx context.Context, k ci.PrivKey, value path.Path) error {
    46  	log.Debugf("Publish %s", value)
    47  
    48  	data, err := createRoutingEntryData(k, value)
    49  	if err != nil {
    50  		return err
    51  	}
    52  	pubkey := k.GetPublic()
    53  	pkbytes, err := pubkey.Bytes()
    54  	if err != nil {
    55  		return err
    56  	}
    57  
    58  	nameb := u.Hash(pkbytes)
    59  	namekey := key.Key("/pk/" + string(nameb))
    60  
    61  	log.Debugf("Storing pubkey at: %s", namekey)
    62  	// Store associated public key
    63  	timectx, cancel := context.WithDeadline(ctx, time.Now().Add(time.Second*10))
    64  	defer cancel()
    65  	err = p.routing.PutValue(timectx, namekey, pkbytes)
    66  	if err != nil {
    67  		return err
    68  	}
    69  
    70  	ipnskey := key.Key("/ipns/" + string(nameb))
    71  
    72  	log.Debugf("Storing ipns entry at: %s", ipnskey)
    73  	// Store ipns entry at "/ipns/"+b58(h(pubkey))
    74  	timectx, cancel = context.WithDeadline(ctx, time.Now().Add(time.Second*10))
    75  	defer cancel()
    76  	if err := p.routing.PutValue(timectx, ipnskey, data); err != nil {
    77  		return err
    78  	}
    79  
    80  	return nil
    81  }
    82  
    83  func createRoutingEntryData(pk ci.PrivKey, val path.Path) ([]byte, error) {
    84  	entry := new(pb.IpnsEntry)
    85  
    86  	entry.Value = []byte(val)
    87  	typ := pb.IpnsEntry_EOL
    88  	entry.ValidityType = &typ
    89  	entry.Validity = []byte(u.FormatRFC3339(time.Now().Add(time.Hour * 24)))
    90  
    91  	sig, err := pk.Sign(ipnsEntryDataForSig(entry))
    92  	if err != nil {
    93  		return nil, err
    94  	}
    95  	entry.Signature = sig
    96  	return proto.Marshal(entry)
    97  }
    98  
    99  func ipnsEntryDataForSig(e *pb.IpnsEntry) []byte {
   100  	return bytes.Join([][]byte{
   101  		e.Value,
   102  		e.Validity,
   103  		[]byte(fmt.Sprint(e.GetValidityType())),
   104  	},
   105  		[]byte{})
   106  }
   107  
   108  var IpnsRecordValidator = &record.ValidChecker{
   109  	Func: ValidateIpnsRecord,
   110  	Sign: true,
   111  }
   112  
   113  // ValidateIpnsRecord implements ValidatorFunc and verifies that the
   114  // given 'val' is an IpnsEntry and that that entry is valid.
   115  func ValidateIpnsRecord(k key.Key, val []byte) error {
   116  	entry := new(pb.IpnsEntry)
   117  	err := proto.Unmarshal(val, entry)
   118  	if err != nil {
   119  		return err
   120  	}
   121  	switch entry.GetValidityType() {
   122  	case pb.IpnsEntry_EOL:
   123  		t, err := u.ParseRFC3339(string(entry.GetValidity()))
   124  		if err != nil {
   125  			log.Debug("Failed parsing time for ipns record EOL")
   126  			return err
   127  		}
   128  		if time.Now().After(t) {
   129  			return ErrExpiredRecord
   130  		}
   131  	default:
   132  		return ErrUnrecognizedValidity
   133  	}
   134  	return nil
   135  }
   136  
   137  // InitializeKeyspace sets the ipns record for the given key to
   138  // point to an empty directory.
   139  // TODO: this doesnt feel like it belongs here
   140  func InitializeKeyspace(ctx context.Context, ds dag.DAGService, pub Publisher, pins pin.Pinner, key ci.PrivKey) error {
   141  	emptyDir := &dag.Node{Data: ft.FolderPBData()}
   142  	nodek, err := ds.Add(emptyDir)
   143  	if err != nil {
   144  		return err
   145  	}
   146  
   147  	// pin recursively because this might already be pinned
   148  	// and doing a direct pin would throw an error in that case
   149  	err = pins.Pin(ctx, emptyDir, true)
   150  	if err != nil {
   151  		return err
   152  	}
   153  
   154  	err = pins.Flush()
   155  	if err != nil {
   156  		return err
   157  	}
   158  
   159  	err = pub.Publish(ctx, key, path.FromKey(nodek))
   160  	if err != nil {
   161  		return err
   162  	}
   163  
   164  	return nil
   165  }