github.com/vchain-us/vcn@v0.9.11-0.20210921212052-a2484d23c0b3/pkg/api/verify.go (about)

     1  /*
     2   * Copyright (c) 2018-2020 vChain, Inc. All Rights Reserved.
     3   * This software is released under GPL3.
     4   * The full license information can be found under:
     5   * https://www.gnu.org/licenses/gpl-3.0.en.html
     6   *
     7   */
     8  
     9  package api
    10  
    11  import (
    12  	"crypto/sha256"
    13  	"encoding/json"
    14  	"fmt"
    15  
    16  	"github.com/vchain-us/vcn/internal/errors"
    17  	"github.com/vchain-us/vcn/internal/logs"
    18  
    19  	"math/big"
    20  	"os"
    21  	"strings"
    22  	"time"
    23  
    24  	"github.com/ethereum/go-ethereum/common"
    25  	"github.com/ethereum/go-ethereum/ethclient"
    26  	"github.com/sirupsen/logrus"
    27  	"github.com/vchain-us/vcn/internal/blockchain"
    28  	"github.com/vchain-us/vcn/pkg/meta"
    29  )
    30  
    31  // BlockchainVerification represents the notarized data onto the blockchain.
    32  type BlockchainVerification struct {
    33  	Owner     common.Address `json:"owner" yaml:"owner"`
    34  	Level     meta.Level     `json:"level" yaml:"level"`
    35  	Status    meta.Status    `json:"status" yaml:"status"`
    36  	Timestamp time.Time      `json:"timestamp" yaml:"timestamp"`
    37  }
    38  
    39  // Trusted returns true if v.Status is meta.StatusTrusted
    40  func (v *BlockchainVerification) Trusted() bool {
    41  	return v != nil && v.Status == meta.StatusTrusted
    42  }
    43  
    44  // Unknown returns true if v is nil or v.Status is meta.StatusUnknown
    45  func (v *BlockchainVerification) Unknown() bool {
    46  	return v == nil || v.Status == meta.StatusUnknown
    47  }
    48  
    49  func (v *BlockchainVerification) toMap() map[string]interface{} {
    50  	if v == nil {
    51  		return nil
    52  	}
    53  	return map[string]interface{}{
    54  		"owner":     v.SignerID(),
    55  		"level":     v.Level,
    56  		"status":    v.Status,
    57  		"timestamp": v.Date(),
    58  	}
    59  }
    60  
    61  func (v *BlockchainVerification) fromUnmarshaler(unmarshal func(interface{}) error) error {
    62  	if v == nil {
    63  		v = &BlockchainVerification{}
    64  	}
    65  	data := struct {
    66  		Owner     string
    67  		Level     int64
    68  		Status    int64
    69  		Timestamp string
    70  	}{}
    71  
    72  	if err := unmarshal(&data); err != nil {
    73  		return err
    74  	}
    75  
    76  	if data.Owner != "" {
    77  		v.Owner = common.HexToAddress(data.Owner)
    78  	}
    79  	v.Level = meta.Level(data.Level)
    80  	v.Status = meta.Status(data.Status)
    81  	if data.Timestamp != "" {
    82  		v.Timestamp.UnmarshalText([]byte(data.Timestamp))
    83  	}
    84  	return nil
    85  }
    86  
    87  // MarshalJSON implements the json.Marshaler interface.
    88  func (v *BlockchainVerification) MarshalJSON() ([]byte, error) {
    89  	return json.Marshal(v.toMap())
    90  }
    91  
    92  // UnmarshalJSON implements the json.Unmarshaler interface.
    93  func (v *BlockchainVerification) UnmarshalJSON(b []byte) error {
    94  	return v.fromUnmarshaler(func(value interface{}) error {
    95  		return json.Unmarshal(b, value)
    96  	})
    97  }
    98  
    99  // MarshalYAML implements the yaml.Marshaler interface.
   100  func (v *BlockchainVerification) MarshalYAML() (interface{}, error) {
   101  	return v.toMap(), nil
   102  }
   103  
   104  // UnmarshalYAML implements the yaml.Unmarshaler interface.
   105  func (v *BlockchainVerification) UnmarshalYAML(unmarshal func(interface{}) error) error {
   106  	return v.fromUnmarshaler(unmarshal)
   107  }
   108  
   109  // MetaHash returns the SHA256 digest of BlockchainVerification's data.
   110  // The returned value uniquely identify a single notarization.
   111  func (v *BlockchainVerification) MetaHash() string {
   112  	if v == nil {
   113  		return ""
   114  	}
   115  	metadata := fmt.Sprintf("%s-%d-%d-%d",
   116  		v.Owner.Hex(),
   117  		int64(v.Level),
   118  		int64(v.Status),
   119  		int64(v.Timestamp.Unix()))
   120  	metadataHashAsBytes := sha256.Sum256([]byte(metadata))
   121  	metahash := fmt.Sprintf("%x", metadataHashAsBytes)
   122  	logger().WithFields(logrus.Fields{
   123  		"metadata": metadata,
   124  		"metahash": metahash,
   125  	}).Trace("Generated metahash")
   126  	return metahash
   127  }
   128  
   129  // SignerID returns the public address derived from owner's public key (v.Owner), if any, otherwise an empty string.
   130  func (v *BlockchainVerification) SignerID() string {
   131  	if v != nil && v.Owner != common.BigToAddress(big.NewInt(0)) {
   132  		return strings.ToLower(v.Owner.Hex())
   133  	}
   134  	return ""
   135  }
   136  
   137  // Date returns a RFC3339 formatted string of verification time (v.Timestamp), if any, otherwise an empty string.
   138  func (v *BlockchainVerification) Date() string {
   139  	if v != nil {
   140  		ut := v.Timestamp.UTC()
   141  		if ut.Unix() > 0 {
   142  			return ut.Format(time.RFC3339)
   143  		}
   144  	}
   145  	return ""
   146  }
   147  
   148  func callVerifyFunc(f func(*blockchain.AssetsRelay) (common.Address, *big.Int, *big.Int, *big.Int, error)) (*BlockchainVerification, error) {
   149  	client, err := ethclient.Dial(meta.MainNet())
   150  	if err != nil {
   151  		return nil, err
   152  	}
   153  	contractAddress := common.HexToAddress(meta.AssetsRelayContractAddress())
   154  	instance, err := blockchain.NewAssetsRelay(contractAddress, client)
   155  	if err != nil {
   156  		return nil, err
   157  	}
   158  	address, level, status, timestamp, err := f(instance)
   159  	if err != nil {
   160  		return nil, err
   161  	}
   162  	if meta.Status(status.Int64()) != meta.StatusUnknown && address != common.BigToAddress(big.NewInt(0)) {
   163  		verification := &BlockchainVerification{
   164  			Owner:     address,
   165  			Level:     meta.Level(level.Int64()),
   166  			Status:    meta.Status(status.Int64()),
   167  			Timestamp: time.Unix(timestamp.Int64(), 0),
   168  		}
   169  		logger().
   170  			WithField("verification", verification).
   171  			Trace("Blockchain verification found")
   172  		return verification, nil
   173  	}
   174  
   175  	logger().Trace("No blockchain verification found")
   176  	return &BlockchainVerification{
   177  		Status: meta.StatusUnknown,
   178  	}, nil
   179  }
   180  
   181  // Verify returns the most recent *BlockchainVerification with highest level available for the given hash.
   182  func Verify(hash string) (*BlockchainVerification, error) {
   183  	logger().WithFields(logrus.Fields{
   184  		"hash": hash,
   185  	}).Trace("Verify")
   186  
   187  	return callVerifyFunc(func(instance *blockchain.AssetsRelay) (common.Address, *big.Int, *big.Int, *big.Int, error) {
   188  		return instance.Verify(nil, hash)
   189  	})
   190  }
   191  
   192  // VerifyMatchingSignerIDWithFallback returns *BlockchainVerification for the hash matching a given SignerID,
   193  // if any, otherwise it returns the same result of Verify().
   194  func VerifyMatchingSignerIDWithFallback(hash string, signerID string) (*BlockchainVerification, error) {
   195  	logger().WithFields(logrus.Fields{
   196  		"hash":     hash,
   197  		"signerID": signerID,
   198  	}).Trace("VerifyMatchingSignerIDWithFallback")
   199  
   200  	address := common.HexToAddress(signerID)
   201  
   202  	return callVerifyFunc(func(instance *blockchain.AssetsRelay) (common.Address, *big.Int, *big.Int, *big.Int, error) {
   203  		return instance.VerifyAgainstPublisherWithFallback(nil, hash, address)
   204  	})
   205  }
   206  
   207  // VerifyMatchingSignerID returns *BlockchainVerification for hash matching a given SignerID.
   208  func VerifyMatchingSignerID(hash string, signerID string) (*BlockchainVerification, error) {
   209  	return VerifyMatchingSignerIDs(hash, []string{signerID})
   210  }
   211  
   212  // VerifyMatchingSignerIDs returns *BlockchainVerification for hash
   213  // matching at least one of signerIDs.
   214  func VerifyMatchingSignerIDs(hash string, signerIDs []string) (*BlockchainVerification, error) {
   215  	logger().WithFields(logrus.Fields{
   216  		"hash":      hash,
   217  		"signerIDs": signerIDs,
   218  	}).Trace("VerifyMatchingSignerIDs")
   219  
   220  	addresses := make([]common.Address, len(signerIDs))
   221  	for i, s := range signerIDs {
   222  		addresses[i] = common.HexToAddress(s)
   223  	}
   224  
   225  	return callVerifyFunc(func(instance *blockchain.AssetsRelay) (common.Address, *big.Int, *big.Int, *big.Int, error) {
   226  		return instance.VerifyAgainstPublishers(nil, hash, addresses)
   227  	})
   228  }
   229  
   230  // PublicCNLCVerify allow connection and verification on CNLC ledger with a single call using environment variables.
   231  // LcLedger parameter is used when a cross-ledger key is provided in order to specify the ledger on which future operations will be directed. Empty string is accepted.
   232  // signerID parameter is used to filter result on a specific signer ID. If empty value is provided is used the current logged signerID value.
   233  func LcVerifyEnv(hash, lcLedger, signerID string) (a *LcArtifact, err error) {
   234  	lcHost := os.Getenv(meta.VcnLcHost)
   235  	lcPort := os.Getenv(meta.VcnLcPort)
   236  	lcCert := os.Getenv(meta.VcnLcCert)
   237  	lcSkipTlsVerify := os.Getenv(meta.VcnLcSkipTlsVerify)
   238  	lcNoTls := os.Getenv(meta.VcnLcNoTls)
   239  	return PublicCNLCVerify(hash, lcLedger, signerID, lcHost, lcPort, lcCert, lcSkipTlsVerify == "true", lcNoTls == "true")
   240  }
   241  
   242  // PublicCNLCVerify allow connection and verification on CNLC ledger with a single call.
   243  // LcLedger parameter is used when a cross-ledger key is provided in order to specify the ledger on which future operations will be directed. Empty string is accepted
   244  // signerID parameter is used to filter result on a specific signer ID. If empty value is provided is used the current logged signerID value.
   245  func PublicCNLCVerify(hash, lcLedger, signerID, lcHost, lcPort, lcCert string, lcSkipTlsVerify, lcNoTls bool) (a *LcArtifact, err error) {
   246  	logger().WithFields(logrus.Fields{
   247  		"hash": hash,
   248  	}).Trace("LcVerify")
   249  
   250  	apiKey := os.Getenv(meta.VcnLcApiKey)
   251  	if apiKey == "" {
   252  		logs.LOG.Trace("Lc api key provided (environment)")
   253  		return nil, errors.ErrNoLcApiKeyEnv
   254  	}
   255  
   256  	client, err := NewLcClient(apiKey, lcLedger, lcHost, lcPort, lcCert, lcSkipTlsVerify, lcNoTls, nil)
   257  	if err != nil {
   258  		return nil, err
   259  	}
   260  
   261  	lcUser := &LcUser{Client: client}
   262  
   263  	err = lcUser.Client.Connect()
   264  	if err != nil {
   265  		return nil, err
   266  	}
   267  
   268  	if hash != "" {
   269  		a, _, err = lcUser.LoadArtifact(
   270  			hash,
   271  			signerID,
   272  			"",
   273  			0,
   274  			map[string][]string{meta.VcnLCCmdHeaderName: {meta.VcnLCVerifyCmdHeaderValue}})
   275  		if err != nil {
   276  			return nil, err
   277  		}
   278  	}
   279  
   280  	return a, nil
   281  }