github.com/iotexproject/iotex-core@v1.14.1-rc1/endorsement/endorsement.go (about)

     1  // Copyright (c) 2019 IoTeX Foundation
     2  // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
     3  // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
     4  // This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
     5  
     6  package endorsement
     7  
     8  import (
     9  	"time"
    10  
    11  	"github.com/iotexproject/go-pkgs/crypto"
    12  	"github.com/iotexproject/go-pkgs/hash"
    13  	"github.com/iotexproject/iotex-proto/golang/iotextypes"
    14  	"google.golang.org/protobuf/types/known/timestamppb"
    15  
    16  	"github.com/iotexproject/iotex-core/pkg/util/byteutil"
    17  )
    18  
    19  type (
    20  	// Document defines a signable docuement
    21  	Document interface {
    22  		Hash() ([]byte, error)
    23  	}
    24  
    25  	// Endorsement defines an endorsement with timestamp
    26  	Endorsement struct {
    27  		ts        time.Time
    28  		endorser  crypto.PublicKey
    29  		signature []byte
    30  	}
    31  
    32  	// EndorsedDocument is an signed document
    33  	EndorsedDocument interface {
    34  		Document() Document
    35  		Endorsement() *Endorsement
    36  	}
    37  )
    38  
    39  func hashDocWithTime(doc Document, ts time.Time) ([]byte, error) {
    40  	h, err := doc.Hash()
    41  	if err != nil {
    42  		return nil, err
    43  	}
    44  	h = append(h, byteutil.Uint64ToBytes(uint64(ts.Unix()))...)
    45  	h256 := hash.Hash256b(append(h, byteutil.Uint32ToBytes(uint32(ts.Nanosecond()))...))
    46  
    47  	return h256[:], nil
    48  }
    49  
    50  // NewEndorsement creates a new Endorsement
    51  func NewEndorsement(
    52  	ts time.Time,
    53  	endorserPubKey crypto.PublicKey,
    54  	sig []byte,
    55  ) *Endorsement {
    56  	cs := make([]byte, len(sig))
    57  	copy(cs, sig)
    58  	return &Endorsement{
    59  		ts:        ts.UTC(),
    60  		endorser:  endorserPubKey,
    61  		signature: cs,
    62  	}
    63  }
    64  
    65  // Endorse endorses a document
    66  func Endorse(
    67  	signer crypto.PrivateKey,
    68  	doc Document,
    69  	ts time.Time,
    70  ) (*Endorsement, error) {
    71  	hash, err := hashDocWithTime(doc, ts)
    72  	if err != nil {
    73  		return nil, err
    74  	}
    75  	sig, err := signer.Sign(hash)
    76  	if err != nil {
    77  		return nil, err
    78  	}
    79  	return NewEndorsement(ts, signer.PublicKey(), sig), nil
    80  }
    81  
    82  // VerifyEndorsedDocument checks an endorsed document
    83  func VerifyEndorsedDocument(endorsedDoc EndorsedDocument) bool {
    84  	return VerifyEndorsement(endorsedDoc.Document(), endorsedDoc.Endorsement())
    85  }
    86  
    87  // VerifyEndorsement checks the signature in an endorsement against a document
    88  func VerifyEndorsement(doc Document, en *Endorsement) bool {
    89  	hash, err := hashDocWithTime(doc, en.Timestamp())
    90  	if err != nil {
    91  		return false
    92  	}
    93  
    94  	return en.Endorser().Verify(hash, en.Signature())
    95  }
    96  
    97  // Timestamp returns the signature time
    98  func (en *Endorsement) Timestamp() time.Time {
    99  	return en.ts
   100  }
   101  
   102  // Endorser returns the endorser's public key
   103  func (en *Endorsement) Endorser() crypto.PublicKey {
   104  	return en.endorser
   105  }
   106  
   107  // Signature returns the signature of this endorsement
   108  func (en *Endorsement) Signature() []byte {
   109  	signature := make([]byte, len(en.signature))
   110  	copy(signature, en.signature)
   111  
   112  	return signature
   113  }
   114  
   115  // Proto converts an endorsement to protobuf message
   116  func (en *Endorsement) Proto() (*iotextypes.Endorsement, error) {
   117  	ts := timestamppb.New(en.ts)
   118  	return &iotextypes.Endorsement{
   119  		Timestamp: ts,
   120  		Endorser:  en.endorser.Bytes(),
   121  		Signature: en.Signature(),
   122  	}, nil
   123  }
   124  
   125  // LoadProto converts a protobuf message to endorsement
   126  func (en *Endorsement) LoadProto(ePb *iotextypes.Endorsement) (err error) {
   127  	if err = ePb.Timestamp.CheckValid(); err != nil {
   128  		return err
   129  	}
   130  	en.ts = ePb.Timestamp.AsTime()
   131  	eb := make([]byte, len(ePb.Endorser))
   132  	copy(eb, ePb.Endorser)
   133  	if en.endorser, err = crypto.BytesToPublicKey(eb); err != nil {
   134  		return err
   135  	}
   136  	en.signature = make([]byte, len(ePb.Signature))
   137  	copy(en.signature, ePb.Signature)
   138  
   139  	return nil
   140  }