1// Copyright 2015 Keybase, Inc. All rights reserved. Use of 2// this source code is governed by the included BSD license. 3 4package libkb 5 6import ( 7 keybase1 "github.com/keybase/client/go/protocol/keybase1" 8 jsonw "github.com/keybase/go-jsonw" 9) 10 11// RemoteProofLinks holds a set of RemoteProofChainLinks, 12// organized by service. 13type RemoteProofLinks struct { 14 Contextified 15 links map[string][]ProofLinkWithState 16} 17 18// ProofLinkWithState contains a RemoteProofChainLink and the 19// proof state. In addition, it satisfies the TrackIdComponent interface. 20type ProofLinkWithState struct { 21 link RemoteProofChainLink 22 state keybase1.ProofState 23} 24 25// NewRemoteProofLinks creates a new empty collection of proof 26// links. 27func NewRemoteProofLinks(g *GlobalContext) *RemoteProofLinks { 28 return &RemoteProofLinks{ 29 Contextified: NewContextified(g), 30 links: make(map[string][]ProofLinkWithState), 31 } 32} 33 34// Insert adds a link to the collection of proof links. 35func (r *RemoteProofLinks) Insert(link RemoteProofChainLink, err ProofError) { 36 key := link.TableKey() 37 if len(key) == 0 { 38 return 39 } 40 r.links[key] = append(r.links[key], ProofLinkWithState{link: link, state: ProofErrorToState(err)}) 41} 42 43// ForService returns all the active proof links for a service. 44func (r *RemoteProofLinks) ForService(st ServiceType) []RemoteProofChainLink { 45 var links []RemoteProofChainLink 46 for _, linkWithState := range r.links[st.Key()] { 47 if linkWithState.link.LastWriterWins() { 48 // Clear the array if it's a last-writer wins service. 49 // (like many social networks) 50 links = nil 51 } 52 if linkWithState.link.IsRevoked() { 53 continue 54 } 55 links = append(links, linkWithState.link) 56 } 57 return links 58} 59 60// Active returns all the active proof links, deduplicating any and 61// honoring the LastWriterWins option. 62func (r *RemoteProofLinks) Active() []RemoteProofChainLink { 63 a := r.active() 64 links := make([]RemoteProofChainLink, len(a)) 65 for i, b := range a { 66 links[i] = b.link 67 } 68 return links 69} 70 71// TrackingStatement generates the remote proofs portions of the 72// tracking statement from the active proofs. 73func (r *RemoteProofLinks) TrackingStatement() *jsonw.Wrapper { 74 var proofs []*jsonw.Wrapper 75 for _, x := range r.active() { 76 d, err := x.link.ToTrackingStatement(x.state) 77 if err != nil { 78 r.G().Log.Warning("Problem with a proof: %s", err) 79 continue 80 } 81 if d != nil { 82 proofs = append(proofs, d) 83 } 84 } 85 86 res := jsonw.NewArray(len(proofs)) 87 for i, proof := range proofs { 88 _ = res.SetIndex(i, proof) 89 } 90 return res 91} 92 93// TrackSet creates a new TrackSet with all the active proofs. 94func (r *RemoteProofLinks) TrackSet() *TrackSet { 95 ret := NewTrackSet() 96 for _, ap := range r.active() { 97 ret.Add(ap) 98 } 99 return ret 100} 101 102// AddProofsToSet adds the active proofs to an existing ProofSet, if they're one of the 103// given OkStates. If okStates is nil, then we check only against keybase1.ProofState_OK. 104func (r *RemoteProofLinks) AddProofsToSet(existing *ProofSet, okStates []keybase1.ProofState) { 105 if okStates == nil { 106 okStates = []keybase1.ProofState{keybase1.ProofState_OK} 107 } 108 isOkState := func(s1 keybase1.ProofState) bool { 109 for _, s2 := range okStates { 110 if s1 == s2 { 111 return true 112 } 113 } 114 return false 115 } 116 for _, a := range r.active() { 117 if !isOkState(a.state) { 118 continue 119 } 120 AddToProofSetNoChecks(a.link, existing) 121 } 122} 123 124func RemoteProofChainLinkToProof(r RemoteProofChainLink) Proof { 125 k, v := r.ToKeyValuePair() 126 return Proof{Key: k, Value: v} 127} 128 129func AddToProofSetNoChecks(r RemoteProofChainLink, ps *ProofSet) { 130 ps.Add(RemoteProofChainLinkToProof(r)) 131} 132 133func (r *RemoteProofLinks) active() []ProofLinkWithState { 134 var links []ProofLinkWithState 135 seen := make(map[string]bool) 136 137 // Loop over all types of services 138 for _, list := range r.links { 139 140 // Loop over all proofs for that type, from most recent, 141 // to oldest. 142 for i := len(list) - 1; i >= 0; i-- { 143 both := list[i] 144 link := both.link 145 id := CanonicalProofName(link) 146 147 if !link.IsRevoked() && !seen[id] { 148 links = append(links, both) 149 } 150 151 // We only want to use the last proof in the list 152 // if we have several (like for dns://chriscoyne.com) 153 seen[id] = true 154 155 // Things like Twitter, Github, etc, are last-writer wins. 156 // Things like dns/https can have multiples 157 if link.LastWriterWins() { 158 break 159 } 160 } 161 } 162 return links 163} 164 165// TrackIdComponent interface functions: 166 167func (p ProofLinkWithState) GetProofState() keybase1.ProofState { 168 return p.state 169} 170 171func (p ProofLinkWithState) LastWriterWins() bool { 172 return p.link.LastWriterWins() 173} 174 175func (p ProofLinkWithState) ToIDString() string { 176 return p.link.ToIDString() 177} 178 179func (p ProofLinkWithState) ToKeyValuePair() (string, string) { 180 return p.link.ToKeyValuePair() 181} 182 183func (p ProofLinkWithState) GetProofType() keybase1.ProofType { return p.link.GetProofType() } 184 185func (r *RemoteProofLinks) toServiceSummary() (ret UserServiceSummary) { 186 ret = make(UserServiceSummary, len(r.links)) 187 activeProofs := r.Active() 188 for _, proof := range activeProofs { 189 key, val := proof.ToKeyValuePair() 190 ret[key] = val 191 } 192 return ret 193} 194