github.com/decred/politeia@v1.4.0/politeiawww/cmd/politeiaverify/record.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  
    12  	"github.com/decred/politeia/politeiad/backend"
    13  	rcv1 "github.com/decred/politeia/politeiawww/api/records/v1"
    14  	"github.com/decred/politeia/politeiawww/client"
    15  )
    16  
    17  // recordBundle represents the record bundle that is available for download
    18  // in politeiagui.
    19  type recordBundle struct {
    20  	Record          rcv1.Record `json:"record"`
    21  	ServerPublicKey string      `json:"serverpublickey"`
    22  }
    23  
    24  // verifyRecordBundle takes the file path of a record bundle file and verifies
    25  // that the record bundle has been accepted by politeia and that all user
    26  // signatures are correct.
    27  func verifyRecordBundle(fp string) error {
    28  	// Decode record bundle
    29  	b, err := os.ReadFile(fp)
    30  	if err != nil {
    31  		return err
    32  	}
    33  	var rb recordBundle
    34  	err = json.Unmarshal(b, &rb)
    35  	if err != nil {
    36  		return fmt.Errorf("could not unmarshal record bundle: %v", err)
    37  	}
    38  
    39  	// Verify censorship record
    40  	fmt.Printf("Server public key: %v\n", rb.ServerPublicKey)
    41  	fmt.Printf("Censorship record\n")
    42  	fmt.Printf("  Token      : %v\n", rb.Record.CensorshipRecord.Token)
    43  	fmt.Printf("  Merkle root: %v\n", rb.Record.CensorshipRecord.Merkle)
    44  	fmt.Printf("  Signature  : %v\n", rb.Record.CensorshipRecord.Signature)
    45  
    46  	err = client.RecordVerify(rb.Record, rb.ServerPublicKey)
    47  	if err != nil {
    48  		return fmt.Errorf("could not verify record: %v", err)
    49  	}
    50  
    51  	fmt.Printf("Censorship record verified!\n")
    52  	fmt.Printf("\n")
    53  
    54  	// Verify user metadata
    55  	um, err := client.UserMetadataDecode(rb.Record.Metadata)
    56  	if err != nil {
    57  		return err
    58  	}
    59  
    60  	fmt.Printf("Author metadata\n")
    61  	fmt.Printf("  ID        : %v\n", um.UserID)
    62  	fmt.Printf("  Public key: %v\n", um.PublicKey)
    63  	fmt.Printf("  Signature : %v\n", um.Signature)
    64  
    65  	err = client.UserMetadataVerify(*um, rb.Record.CensorshipRecord.Merkle)
    66  	if err != nil {
    67  		return err
    68  	}
    69  
    70  	fmt.Printf("Author signature verified!\n")
    71  	fmt.Printf("\n")
    72  
    73  	// Verify status change signatures
    74  	schanges, err := client.StatusChangesDecode(rb.Record.Metadata)
    75  	if err != nil {
    76  		return err
    77  	}
    78  	if len(schanges) == 0 {
    79  		// No status changes have occurred. We're done.
    80  		return nil
    81  	}
    82  
    83  	for _, v := range schanges {
    84  		fmt.Printf("Status change: %v\n", rcv1.RecordStatuses[v.Status])
    85  		if v.Reason != "" {
    86  			fmt.Printf("  Reason     : %v\n", v.Reason)
    87  		}
    88  		fmt.Printf("  Public key : %v\n", v.PublicKey)
    89  		fmt.Printf("  Signature  : %v\n", v.Signature)
    90  	}
    91  
    92  	err = client.StatusChangesVerify(schanges)
    93  	if err != nil {
    94  		return err
    95  	}
    96  
    97  	fmt.Printf("Status change signatures verified!\n")
    98  
    99  	return nil
   100  }
   101  
   102  // verifyRecordTimestamps takes the filepath of record timestamps and verifies
   103  // the validity of all timestamps included in the records v1 TimestampsReply.
   104  func verifyRecordTimestamps(fp string) error {
   105  	// Decode timestamps reply
   106  	b, err := os.ReadFile(fp)
   107  	if err != nil {
   108  		return err
   109  	}
   110  	var tr rcv1.TimestampsReply
   111  	err = json.Unmarshal(b, &tr)
   112  	if err != nil {
   113  		return fmt.Errorf("could not unmarshal record timestamps: %v", err)
   114  	}
   115  	if tr.RecordMetadata.TxID == "" {
   116  		return fmt.Errorf("data not anchored yet")
   117  	}
   118  
   119  	// Pull the token out of the record metadata
   120  	var rm backend.RecordMetadata
   121  	err = json.Unmarshal([]byte(tr.RecordMetadata.Data), &rm)
   122  	if err != nil {
   123  		return fmt.Errorf("could not unmarshal record metadata: %v", err)
   124  	}
   125  
   126  	fmt.Printf("Token      : %v\n", rm.Token)
   127  	fmt.Printf("Merkle root: %v\n", tr.RecordMetadata.MerkleRoot)
   128  	fmt.Printf("DCR tx     : %v\n", tr.RecordMetadata.TxID)
   129  	fmt.Printf("Record contents\n")
   130  	fmt.Printf("  Metadata\n")
   131  	for mdID, streams := range tr.Metadata {
   132  		for streamID := range streams {
   133  			fmt.Printf("    %v %v\n", mdID, streamID)
   134  		}
   135  	}
   136  	fmt.Printf("  Files\n")
   137  	for fn := range tr.Files {
   138  		fmt.Printf("    %v\n", fn)
   139  	}
   140  
   141  	// Verify timestamps
   142  	err = client.RecordTimestampsVerify(tr)
   143  	if err != nil {
   144  		return err
   145  	}
   146  
   147  	fmt.Printf("Record timestamps verified!\n")
   148  	fmt.Printf("The merkle root can be found in the OP_RETURN of the DCR tx.\n")
   149  
   150  	return nil
   151  }