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 }