github.com/decred/politeia@v1.4.0/politeiad/cmd/legacypoliteia/convert.go (about) 1 // Copyright (c) 2022 The Decred developers 2 // Use of this source code is governed by an ISC 3 // license that can be found in the LICENSE file. 4 5 package main 6 7 import ( 8 "encoding/base64" 9 "encoding/hex" 10 "encoding/json" 11 "fmt" 12 "net/http" 13 "strconv" 14 15 backend "github.com/decred/politeia/politeiad/backendv2" 16 "github.com/decred/politeia/politeiad/cmd/legacypoliteia/gitbe" 17 "github.com/decred/politeia/politeiad/plugins/comments" 18 "github.com/decred/politeia/politeiad/plugins/pi" 19 "github.com/decred/politeia/politeiad/plugins/ticketvote" 20 "github.com/decred/politeia/politeiad/plugins/usermd" 21 "github.com/decred/politeia/util" 22 ) 23 24 // convert.go contains the conversion functions for converting git backend 25 // types into tstore backend and plugin types. 26 27 func convertRecordMetadata(r gitbe.RecordMetadata, version uint32) backend.RecordMetadata { 28 return backend.RecordMetadata{ 29 Token: r.Token, 30 Version: version, // Parsed from git path 31 Iteration: uint32(r.Iteration), 32 State: backend.StateVetted, 33 Status: convertMDStatus(r.Status), 34 Timestamp: r.Timestamp, 35 Merkle: r.Merkle, 36 } 37 } 38 39 func convertMDStatus(s gitbe.MDStatusT) backend.StatusT { 40 switch s { 41 case gitbe.MDStatusInvalid: 42 return backend.StatusInvalid 43 case gitbe.MDStatusUnvetted: 44 return backend.StatusUnreviewed 45 case gitbe.MDStatusVetted: 46 return backend.StatusPublic 47 case gitbe.MDStatusCensored: 48 return backend.StatusCensored 49 case gitbe.MDStatusIterationUnvetted: 50 return backend.StatusUnreviewed 51 case gitbe.MDStatusArchived: 52 return backend.StatusArchived 53 default: 54 panic(fmt.Sprintf("invalid md status %v", s)) 55 } 56 } 57 58 func convertFile(payload []byte, fileName string) backend.File { 59 return backend.File{ 60 Name: fileName, // Parsed from git path 61 MIME: http.DetectContentType(payload), 62 Digest: hex.EncodeToString(util.Digest(payload)), 63 Payload: base64.StdEncoding.EncodeToString(payload), 64 } 65 } 66 67 func convertProposalMetadata(name string) pi.ProposalMetadata { 68 return pi.ProposalMetadata{ 69 Name: name, // Parsed from index file 70 Amount: 0, 71 StartDate: 0, 72 EndDate: 0, 73 Domain: "", 74 LegacyToken: "", // Populated by the import command 75 } 76 } 77 78 func convertVoteMetadata(pm gitbe.ProposalMetadata) ticketvote.VoteMetadata { 79 return ticketvote.VoteMetadata{ 80 LinkBy: pm.LinkBy, 81 LinkTo: pm.LinkTo, 82 } 83 } 84 85 func convertUserMetadata(pg gitbe.ProposalGeneralV2, userID string) usermd.UserMetadata { 86 return usermd.UserMetadata{ 87 UserID: userID, // Retrieved from politeia API using public key 88 PublicKey: pg.PublicKey, 89 Signature: pg.Signature, 90 } 91 } 92 93 func convertStatusChange(sc gitbe.RecordStatusChangeV2, token string, version uint32) usermd.StatusChangeMetadata { 94 return usermd.StatusChangeMetadata{ 95 Token: token, // Parsed from git path 96 Version: version, // Parsed from git path 97 Status: uint32(convertRecordStatus(sc.NewStatus)), 98 Reason: sc.StatusChangeMessage, 99 PublicKey: sc.AdminPubKey, 100 // Some signatures may be empty since the signature 101 // field is only present on the v2 gitbe status 102 // change struct. 103 Signature: sc.Signature, 104 Timestamp: sc.Timestamp, 105 } 106 } 107 108 func convertRecordStatus(r gitbe.RecordStatusT) backend.StatusT { 109 switch r { 110 case gitbe.RecordStatusNotReviewed: 111 return backend.StatusUnreviewed 112 case gitbe.RecordStatusCensored: 113 return backend.StatusCensored 114 case gitbe.RecordStatusPublic: 115 return backend.StatusPublic 116 case gitbe.RecordStatusUnreviewedChanges: 117 return backend.StatusUnreviewed 118 case gitbe.RecordStatusArchived: 119 return backend.StatusArchived 120 } 121 panic(fmt.Sprintf("invalid status %v", r)) 122 } 123 124 func convertCommentAdd(c gitbe.Comment, userID string) comments.CommentAdd { 125 parentID, err := strconv.ParseUint(c.ParentID, 10, 64) 126 if err != nil { 127 panic(err) 128 } 129 commentID, err := strconv.ParseUint(c.CommentID, 10, 64) 130 if err != nil { 131 panic(err) 132 } 133 return comments.CommentAdd{ 134 UserID: userID, // Retrieved from the politeia API by public key 135 State: comments.RecordStateVetted, 136 Token: c.Token, 137 ParentID: uint32(parentID), 138 Comment: c.Comment, 139 PublicKey: c.PublicKey, 140 Signature: c.Signature, 141 CommentID: uint32(commentID), 142 Version: 1, // Edits were not allowed on legacy comments 143 Timestamp: c.Timestamp, 144 Receipt: c.Receipt, 145 ExtraData: "", // Intentionally omitted 146 ExtraDataHint: "", // Intentionally omitted 147 } 148 } 149 150 func convertCommentDel(cc gitbe.CensorComment, parentID uint32, userID string) comments.CommentDel { 151 commentID, err := strconv.ParseUint(cc.CommentID, 10, 64) 152 if err != nil { 153 panic(err) 154 } 155 return comments.CommentDel{ 156 Token: cc.Token, 157 State: comments.RecordStateVetted, 158 CommentID: uint32(commentID), 159 Reason: cc.Reason, 160 PublicKey: cc.PublicKey, 161 Signature: cc.Signature, 162 ParentID: parentID, // Taken from the parent comment 163 UserID: userID, // Retrieved from the politeia API by public key 164 Timestamp: cc.Timestamp, 165 Receipt: cc.Receipt, 166 } 167 } 168 169 func convertCommentVote(lc gitbe.LikeComment, userID string) comments.CommentVote { 170 commentID, err := strconv.ParseUint(lc.CommentID, 10, 64) 171 if err != nil { 172 panic(err) 173 } 174 var vote comments.VoteT 175 switch { 176 case lc.Action == "1": 177 vote = comments.VoteUpvote 178 case lc.Action == "-1": 179 vote = comments.VoteDownvote 180 default: 181 panic("invalid comment vote code") 182 } 183 return comments.CommentVote{ 184 UserID: userID, // Retrieved from the politeia API by public key 185 State: comments.RecordStateVetted, 186 Token: lc.Token, 187 CommentID: uint32(commentID), 188 Vote: vote, 189 PublicKey: lc.PublicKey, 190 Signature: lc.Signature, 191 Timestamp: lc.Timestamp, 192 Receipt: lc.Receipt, 193 } 194 } 195 196 func convertVoteDetails(startVoteJSON []byte, svr gitbe.StartVoteReply, version uint32, voteMD *ticketvote.VoteMetadata) ticketvote.VoteDetails { 197 // The start vote structure has a v1 and v2. 198 // The fields that we need are pulled out of 199 // the specific structure. 200 var ( 201 token string 202 proposalVersion uint32 203 voteType ticketvote.VoteT 204 mask uint64 205 duration uint32 206 quorum uint32 207 pass uint32 208 options []ticketvote.VoteOption 209 publicKey string 210 ) 211 structVersion, err := decodeVersion(startVoteJSON) 212 if err != nil { 213 panic(err) 214 } 215 switch structVersion { 216 case 1: 217 // Decode the start vote 218 var sv gitbe.StartVoteV1 219 err = json.Unmarshal(startVoteJSON, &sv) 220 if err != nil { 221 panic(err) 222 } 223 224 // Pull the fields that we need 225 token = sv.Vote.Token 226 proposalVersion = version 227 voteType = ticketvote.VoteTypeStandard 228 mask = sv.Vote.Mask 229 duration = sv.Vote.Duration 230 quorum = sv.Vote.QuorumPercentage 231 pass = sv.Vote.PassPercentage 232 options = convertVoteOptions(sv.Vote.Options) 233 publicKey = sv.PublicKey 234 235 case 2: 236 // Decode the start vote 237 var sv gitbe.StartVoteV2 238 err = json.Unmarshal(startVoteJSON, &sv) 239 if err != nil { 240 panic(err) 241 } 242 243 // Sanity check proposal version. The version in the start vote 244 // should be the same version from the proposal directory path. 245 if version != sv.Vote.ProposalVersion { 246 panic(fmt.Sprintf("start vote version mismatch: %v %v", 247 version, sv.Vote.ProposalVersion)) 248 } 249 250 // Pull the fields that we need 251 token = sv.Vote.Token 252 proposalVersion = version 253 voteType = convertVoteType(sv.Vote.Type) 254 mask = sv.Vote.Mask 255 duration = sv.Vote.Duration 256 quorum = sv.Vote.QuorumPercentage 257 pass = sv.Vote.PassPercentage 258 options = convertVoteOptions(sv.Vote.Options) 259 publicKey = sv.PublicKey 260 261 default: 262 panic(fmt.Sprintf("invalid start vote version '%v'", structVersion)) 263 } 264 265 // Populate parent if it's a RFP submission 266 var parent string 267 if voteMD != nil { 268 parent = voteMD.LinkTo 269 } 270 271 startHeight, err := strconv.ParseUint(svr.StartBlockHeight, 10, 32) 272 if err != nil { 273 panic(err) 274 } 275 endHeight, err := strconv.ParseUint(svr.EndHeight, 10, 32) 276 if err != nil { 277 panic(err) 278 } 279 280 // The Signature and Receipt have been omitted because the 281 // message being signed changes between the git backend and 282 // the tstore backend. Transferring the old signature would 283 // result in invalid signature errors. 284 return ticketvote.VoteDetails{ 285 Params: ticketvote.VoteParams{ 286 Token: token, 287 Version: proposalVersion, // Parsed from git path 288 Type: voteType, 289 Mask: mask, 290 Duration: duration, 291 QuorumPercentage: quorum, 292 PassPercentage: pass, 293 Options: options, 294 Parent: parent, 295 }, 296 PublicKey: publicKey, 297 Signature: "", // Intentionally omitted 298 Receipt: "", // Intentionally omitted 299 StartBlockHeight: uint32(startHeight), 300 StartBlockHash: svr.StartBlockHash, 301 EndBlockHeight: uint32(endHeight), 302 EligibleTickets: svr.EligibleTickets, 303 } 304 } 305 306 func convertVoteOptions(options []gitbe.VoteOption) []ticketvote.VoteOption { 307 opts := make([]ticketvote.VoteOption, 0, len(options)) 308 for _, v := range options { 309 opts = append(opts, ticketvote.VoteOption{ 310 ID: v.Id, 311 Description: v.Description, 312 Bit: v.Bits, 313 }) 314 } 315 return opts 316 } 317 318 func convertVoteType(t gitbe.VoteT) ticketvote.VoteT { 319 switch t { 320 case gitbe.VoteTypeStandard: 321 return ticketvote.VoteTypeStandard 322 case gitbe.VoteTypeRunoff: 323 return ticketvote.VoteTypeRunoff 324 } 325 panic(fmt.Sprintf("invalid vote type %v", t)) 326 } 327 328 func convertCastVoteDetails(cvj gitbe.CastVoteJournal, address string, timestamp int64) ticketvote.CastVoteDetails { 329 return ticketvote.CastVoteDetails{ 330 Token: cvj.CastVote.Token, 331 Ticket: cvj.CastVote.Ticket, 332 VoteBit: cvj.CastVote.VoteBit, 333 Signature: cvj.CastVote.Signature, 334 Address: address, // Retrieved from dcrdata 335 Receipt: cvj.Receipt, 336 // Timestamp will be the timestamp of the git commit that 337 // added the vote to the ballots journal. The exact 338 // timestamp of when the vote was cast does not exist. 339 Timestamp: timestamp, 340 } 341 } 342 343 // decodeVersion returns the version field from the provided JSON payload. This 344 // function should only be used when the payload contains a single struct with 345 // a "version" field. 346 func decodeVersion(payload []byte) (uint, error) { 347 data := make(map[string]interface{}, 32) 348 err := json.Unmarshal(payload, &data) 349 if err != nil { 350 return 0, err 351 } 352 version := uint(data["version"].(float64)) 353 if version == 0 { 354 return 0, fmt.Errorf("version not found") 355 } 356 return version, nil 357 }