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 }