github.com/decred/politeia@v1.4.0/politeiawww/cmd/politeiaverify/comments.go (about)

     1  // Copyright (c) 2020-2021 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/json"
     9  	"fmt"
    10  	"os"
    11  	"strconv"
    12  	"strings"
    13  
    14  	cmv1 "github.com/decred/politeia/politeiawww/api/comments/v1"
    15  	"github.com/decred/politeia/politeiawww/client"
    16  )
    17  
    18  // commentsBundle represents the comments bundle that is available for download
    19  // in politeiagui.
    20  type commentsBundle struct {
    21  	Comments        []cmv1.Comment `json:"comments"`
    22  	ServerPublicKey string         `json:"serverpublickey"`
    23  }
    24  
    25  // verifyCommentsBundle takes the filepath of a comments bundle and verifies
    26  // the contents of the file. This includes verifying the signature and receipt
    27  // of each comment in the bundle. If the comment has been deleted, the original
    28  // comment signature will not exist but the deletion signature and receipt are
    29  // verified instead.
    30  func verifyCommentsBundle(fp string) error {
    31  	// Decode comments bundle
    32  	b, err := os.ReadFile(fp)
    33  	if err != nil {
    34  		return err
    35  	}
    36  	var cb commentsBundle
    37  	err = json.Unmarshal(b, &cb)
    38  	if err != nil {
    39  		return fmt.Errorf("could not unmarshal comments bundle: %v", err)
    40  	}
    41  	if len(cb.Comments) == 0 {
    42  		fmt.Printf("No comments found\n")
    43  		return nil
    44  	}
    45  
    46  	// Verify comment signatures and receipts
    47  	var (
    48  		comments int
    49  		dels     int
    50  	)
    51  	for _, v := range cb.Comments {
    52  		err := client.CommentVerify(v, cb.ServerPublicKey)
    53  		if err != nil {
    54  			return err
    55  		}
    56  		if v.Deleted {
    57  			dels++
    58  			continue
    59  		}
    60  		comments++
    61  	}
    62  
    63  	fmt.Printf("Record token    : %v\n", cb.Comments[0].Token)
    64  	fmt.Printf("Comments        : %v\n", comments)
    65  	fmt.Printf("Deleted comments: %v\n", dels)
    66  	fmt.Printf("All signatures and receipts verified!\n")
    67  
    68  	return nil
    69  }
    70  
    71  // verifyCommentTimestamps takes the filepath of a comment timestamps file and
    72  // verifies the validity of all timestamps included in the comments v1
    73  // TimestampsReply.
    74  func verifyCommentTimestamps(fp string) error {
    75  	// Decode timestamps reply
    76  	b, err := os.ReadFile(fp)
    77  	if err != nil {
    78  		return err
    79  	}
    80  	var tr cmv1.TimestampsReply
    81  	err = json.Unmarshal(b, &tr)
    82  	if err != nil {
    83  		return err
    84  	}
    85  	if len(tr.Comments) == 0 {
    86  		return fmt.Errorf("no comments found")
    87  	}
    88  
    89  	fmt.Printf("Total comments: %v\n", len(tr.Comments))
    90  
    91  	// Verify timestamps
    92  	notTimestamped, err := client.CommentTimestampsVerify(tr)
    93  	if err != nil {
    94  		return err
    95  	}
    96  
    97  	// Print the IDs of the comments that have not been timestamped yet
    98  	if len(notTimestamped) > 0 {
    99  		// Write all comment IDs to a string
   100  		builder := strings.Builder{}
   101  		for i, cid := range notTimestamped {
   102  			s := strconv.FormatUint(uint64(cid), 10)
   103  			if i == len(notTimestamped)-1 {
   104  				// This is the last comment ID. Don't include a comma.
   105  				builder.WriteString(s)
   106  				break
   107  			}
   108  			builder.WriteString(fmt.Sprintf("%v, ", s))
   109  		}
   110  
   111  		// Print results
   112  		fmt.Printf("Comments not yet timestamped: %v\n", builder.String())
   113  	}
   114  
   115  	fmt.Printf("All timestamps verified!\n")
   116  
   117  	return nil
   118  
   119  }