github.com/leonlxy/hyperledger@v1.0.0-alpha.0.20170427033203-34922035d248/protos/utils/proputils.go (about) 1 /* 2 Copyright IBM Corp. 2016 All Rights Reserved. 3 4 Licensed under the Apache License, Version 2.0 (the "License"); 5 you may not use this file except in compliance with the License. 6 You may obtain a copy of the License at 7 8 http://www.apache.org/licenses/LICENSE-2.0 9 10 Unless required by applicable law or agreed to in writing, software 11 distributed under the License is distributed on an "AS IS" BASIS, 12 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 See the License for the specific language governing permissions and 14 limitations under the License. 15 */ 16 17 package utils 18 19 import ( 20 "errors" 21 "fmt" 22 23 "encoding/binary" 24 25 "encoding/hex" 26 27 "github.com/golang/protobuf/proto" 28 "github.com/hyperledger/fabric/bccsp" 29 "github.com/hyperledger/fabric/bccsp/factory" 30 "github.com/hyperledger/fabric/common/crypto" 31 "github.com/hyperledger/fabric/common/util" 32 "github.com/hyperledger/fabric/core/chaincode/platforms" 33 "github.com/hyperledger/fabric/protos/common" 34 "github.com/hyperledger/fabric/protos/peer" 35 ) 36 37 // GetChaincodeInvocationSpec get the ChaincodeInvocationSpec from the proposal 38 func GetChaincodeInvocationSpec(prop *peer.Proposal) (*peer.ChaincodeInvocationSpec, error) { 39 txhdr := &common.Header{} 40 err := proto.Unmarshal(prop.Header, txhdr) 41 if err != nil { 42 return nil, err 43 } 44 ccPropPayload := &peer.ChaincodeProposalPayload{} 45 err = proto.Unmarshal(prop.Payload, ccPropPayload) 46 if err != nil { 47 return nil, err 48 } 49 cis := &peer.ChaincodeInvocationSpec{} 50 err = proto.Unmarshal(ccPropPayload.Input, cis) 51 if err != nil { 52 return nil, err 53 } 54 return cis, nil 55 } 56 57 // GetChaincodeProposalContext returns creator and transient 58 func GetChaincodeProposalContext(prop *peer.Proposal) ([]byte, map[string][]byte, error) { 59 if prop == nil { 60 return nil, nil, fmt.Errorf("Proposal is nil") 61 } 62 if len(prop.Header) == 0 { 63 return nil, nil, fmt.Errorf("Proposal's header is nil") 64 } 65 if len(prop.Payload) == 0 { 66 return nil, nil, fmt.Errorf("Proposal's payload is nil") 67 } 68 69 //// get back the header 70 hdr, err := GetHeader(prop.Header) 71 if err != nil { 72 return nil, nil, fmt.Errorf("Could not extract the header from the proposal: %s", err) 73 } 74 if hdr == nil { 75 return nil, nil, fmt.Errorf("Unmarshalled header is nil") 76 } 77 78 chdr, err := UnmarshalChannelHeader(hdr.ChannelHeader) 79 if err != nil { 80 return nil, nil, fmt.Errorf("Could not extract the channel header from the proposal: %s", err) 81 } 82 83 if common.HeaderType(chdr.Type) != common.HeaderType_ENDORSER_TRANSACTION && 84 common.HeaderType(chdr.Type) != common.HeaderType_CONFIG { 85 return nil, nil, fmt.Errorf("Invalid proposal type expected ENDORSER_TRANSACTION or CONFIG. Was: %d", chdr.Type) 86 } 87 88 shdr, err := GetSignatureHeader(hdr.SignatureHeader) 89 if err != nil { 90 return nil, nil, fmt.Errorf("Could not extract the signature header from the proposal: %s", err) 91 } 92 93 ccPropPayload := &peer.ChaincodeProposalPayload{} 94 err = proto.Unmarshal(prop.Payload, ccPropPayload) 95 if err != nil { 96 return nil, nil, err 97 } 98 99 return shdr.Creator, ccPropPayload.TransientMap, nil 100 } 101 102 // GetHeader Get Header from bytes 103 func GetHeader(bytes []byte) (*common.Header, error) { 104 hdr := &common.Header{} 105 err := proto.Unmarshal(bytes, hdr) 106 if err != nil { 107 return nil, err 108 } 109 110 return hdr, nil 111 } 112 113 // GetNonce returns the nonce used in Proposal 114 func GetNonce(prop *peer.Proposal) ([]byte, error) { 115 // get back the header 116 hdr, err := GetHeader(prop.Header) 117 if err != nil { 118 return nil, fmt.Errorf("Could not extract the header from the proposal: %s", err) 119 } 120 121 chdr, err := UnmarshalChannelHeader(hdr.ChannelHeader) 122 if err != nil { 123 return nil, fmt.Errorf("Could not extract the channel header from the proposal: %s", err) 124 } 125 shdr, err := GetSignatureHeader(hdr.SignatureHeader) 126 if err != nil { 127 return nil, fmt.Errorf("Could not extract the signature header from the proposal: %s", err) 128 } 129 130 if common.HeaderType(chdr.Type) != common.HeaderType_ENDORSER_TRANSACTION && 131 common.HeaderType(chdr.Type) != common.HeaderType_CONFIG { 132 return nil, fmt.Errorf("Invalid proposal type expected ENDORSER_TRANSACTION or CONFIG. Was: %d", chdr.Type) 133 } 134 135 if hdr.SignatureHeader == nil { 136 return nil, errors.New("Invalid signature header. It must be different from nil.") 137 } 138 139 ccPropPayload := &peer.ChaincodeProposalPayload{} 140 err = proto.Unmarshal(prop.Payload, ccPropPayload) 141 if err != nil { 142 return nil, err 143 } 144 145 return shdr.Nonce, nil 146 } 147 148 // GetChaincodeHeaderExtension get chaincode header extension given header 149 func GetChaincodeHeaderExtension(hdr *common.Header) (*peer.ChaincodeHeaderExtension, error) { 150 chdr, err := UnmarshalChannelHeader(hdr.ChannelHeader) 151 if err != nil { 152 return nil, err 153 } 154 155 chaincodeHdrExt := &peer.ChaincodeHeaderExtension{} 156 err = proto.Unmarshal(chdr.Extension, chaincodeHdrExt) 157 if err != nil { 158 return nil, err 159 } 160 161 return chaincodeHdrExt, nil 162 } 163 164 // GetProposalResponse given proposal in bytes 165 func GetProposalResponse(prBytes []byte) (*peer.ProposalResponse, error) { 166 proposalResponse := &peer.ProposalResponse{} 167 err := proto.Unmarshal(prBytes, proposalResponse) 168 if err != nil { 169 return nil, err 170 } 171 172 return proposalResponse, nil 173 } 174 175 // GetChaincodeDeploymentSpec returns a ChaincodeDeploymentSpec given args 176 func GetChaincodeDeploymentSpec(code []byte) (*peer.ChaincodeDeploymentSpec, error) { 177 cds := &peer.ChaincodeDeploymentSpec{} 178 err := proto.Unmarshal(code, cds) 179 if err != nil { 180 return nil, err 181 } 182 183 // FAB-2122: Validate the CDS according to platform specific requirements 184 platform, err := platforms.Find(cds.ChaincodeSpec.Type) 185 if err != nil { 186 return nil, err 187 } 188 189 err = platform.ValidateDeploymentSpec(cds) 190 if err != nil { 191 return nil, err 192 } 193 194 return cds, nil 195 } 196 197 // GetChaincodeAction gets the ChaincodeAction given chaicnode action bytes 198 func GetChaincodeAction(caBytes []byte) (*peer.ChaincodeAction, error) { 199 chaincodeAction := &peer.ChaincodeAction{} 200 err := proto.Unmarshal(caBytes, chaincodeAction) 201 if err != nil { 202 return nil, err 203 } 204 205 return chaincodeAction, nil 206 } 207 208 // GetResponse gets the Response given response bytes 209 func GetResponse(resBytes []byte) (*peer.Response, error) { 210 response := &peer.Response{} 211 err := proto.Unmarshal(resBytes, response) 212 if err != nil { 213 return nil, err 214 } 215 216 return response, nil 217 } 218 219 // GetChaincodeEvents gets the ChaincodeEvents given chaicnode event bytes 220 func GetChaincodeEvents(eBytes []byte) (*peer.ChaincodeEvent, error) { 221 chaincodeEvent := &peer.ChaincodeEvent{} 222 err := proto.Unmarshal(eBytes, chaincodeEvent) 223 if err != nil { 224 return nil, err 225 } 226 227 return chaincodeEvent, nil 228 } 229 230 // GetProposalResponsePayload gets the proposal response payload 231 func GetProposalResponsePayload(prpBytes []byte) (*peer.ProposalResponsePayload, error) { 232 prp := &peer.ProposalResponsePayload{} 233 err := proto.Unmarshal(prpBytes, prp) 234 if err != nil { 235 return nil, err 236 } 237 238 return prp, nil 239 } 240 241 // GetProposal returns a Proposal message from its bytes 242 func GetProposal(propBytes []byte) (*peer.Proposal, error) { 243 prop := &peer.Proposal{} 244 err := proto.Unmarshal(propBytes, prop) 245 if err != nil { 246 return nil, err 247 } 248 249 return prop, nil 250 } 251 252 // GetPayload Get Payload from Envelope message 253 func GetPayload(e *common.Envelope) (*common.Payload, error) { 254 payload := &common.Payload{} 255 err := proto.Unmarshal(e.Payload, payload) 256 if err != nil { 257 return nil, err 258 } 259 260 return payload, nil 261 } 262 263 // GetTransaction Get Transaction from bytes 264 func GetTransaction(txBytes []byte) (*peer.Transaction, error) { 265 tx := &peer.Transaction{} 266 err := proto.Unmarshal(txBytes, tx) 267 if err != nil { 268 return nil, err 269 } 270 271 return tx, nil 272 } 273 274 // GetChaincodeActionPayload Get ChaincodeActionPayload from bytes 275 func GetChaincodeActionPayload(capBytes []byte) (*peer.ChaincodeActionPayload, error) { 276 cap := &peer.ChaincodeActionPayload{} 277 err := proto.Unmarshal(capBytes, cap) 278 if err != nil { 279 return nil, err 280 } 281 282 return cap, nil 283 } 284 285 // GetChaincodeProposalPayload Get ChaincodeProposalPayload from bytes 286 func GetChaincodeProposalPayload(bytes []byte) (*peer.ChaincodeProposalPayload, error) { 287 cpp := &peer.ChaincodeProposalPayload{} 288 err := proto.Unmarshal(bytes, cpp) 289 if err != nil { 290 return nil, err 291 } 292 293 return cpp, nil 294 } 295 296 // GetSignatureHeader Get SignatureHeader from bytes 297 func GetSignatureHeader(bytes []byte) (*common.SignatureHeader, error) { 298 sh := &common.SignatureHeader{} 299 err := proto.Unmarshal(bytes, sh) 300 if err != nil { 301 return nil, err 302 } 303 304 return sh, nil 305 } 306 307 // GetSignaturePolicyEnvelope returns a SignaturePolicyEnvelope from bytes 308 func GetSignaturePolicyEnvelope(bytes []byte) (*common.SignaturePolicyEnvelope, error) { 309 p := &common.SignaturePolicyEnvelope{} 310 err := proto.Unmarshal(bytes, p) 311 if err != nil { 312 return nil, err 313 } 314 315 return p, nil 316 } 317 318 // CreateChaincodeProposal creates a proposal from given input. 319 // It returns the proposal and the transaction id associated to the proposal 320 func CreateChaincodeProposal(typ common.HeaderType, chainID string, cis *peer.ChaincodeInvocationSpec, creator []byte) (*peer.Proposal, string, error) { 321 return CreateChaincodeProposalWithTransient(typ, chainID, cis, creator, nil) 322 } 323 324 // CreateChaincodeProposalWithTransient creates a proposal from given input 325 // It returns the proposal and the transaction id associated to the proposal 326 func CreateChaincodeProposalWithTransient(typ common.HeaderType, chainID string, cis *peer.ChaincodeInvocationSpec, creator []byte, transientMap map[string][]byte) (*peer.Proposal, string, error) { 327 // generate a random nonce 328 nonce, err := crypto.GetRandomNonce() 329 if err != nil { 330 return nil, "", err 331 } 332 333 // compute txid 334 txid, err := ComputeProposalTxID(nonce, creator) 335 if err != nil { 336 return nil, "", err 337 } 338 339 return CreateChaincodeProposalWithTxIDNonceAndTransient(txid, typ, chainID, cis, nonce, creator, transientMap) 340 } 341 342 // CreateChaincodeProposalWithTxIDNonceAndTransient creates a proposal from given input 343 func CreateChaincodeProposalWithTxIDNonceAndTransient(txid string, typ common.HeaderType, chainID string, cis *peer.ChaincodeInvocationSpec, nonce, creator []byte, transientMap map[string][]byte) (*peer.Proposal, string, error) { 344 ccHdrExt := &peer.ChaincodeHeaderExtension{ChaincodeId: cis.ChaincodeSpec.ChaincodeId} 345 ccHdrExtBytes, err := proto.Marshal(ccHdrExt) 346 if err != nil { 347 return nil, "", err 348 } 349 350 cisBytes, err := proto.Marshal(cis) 351 if err != nil { 352 return nil, "", err 353 } 354 355 ccPropPayload := &peer.ChaincodeProposalPayload{Input: cisBytes, TransientMap: transientMap} 356 ccPropPayloadBytes, err := proto.Marshal(ccPropPayload) 357 if err != nil { 358 return nil, "", err 359 } 360 361 // TODO: epoch is now set to zero. This must be changed once we 362 // get a more appropriate mechanism to handle it in. 363 var epoch uint64 = 0 364 365 timestamp := util.CreateUtcTimestamp() 366 367 hdr := &common.Header{ChannelHeader: MarshalOrPanic(&common.ChannelHeader{ 368 Type: int32(typ), 369 TxId: txid, 370 Timestamp: timestamp, 371 ChannelId: chainID, 372 Extension: ccHdrExtBytes, 373 Epoch: epoch}), 374 SignatureHeader: MarshalOrPanic(&common.SignatureHeader{Nonce: nonce, Creator: creator})} 375 376 hdrBytes, err := proto.Marshal(hdr) 377 if err != nil { 378 return nil, "", err 379 } 380 381 return &peer.Proposal{Header: hdrBytes, Payload: ccPropPayloadBytes}, txid, nil 382 } 383 384 // GetBytesProposalResponsePayload gets proposal response payload 385 func GetBytesProposalResponsePayload(hash []byte, response *peer.Response, result []byte, event []byte, ccid *peer.ChaincodeID) ([]byte, error) { 386 cAct := &peer.ChaincodeAction{Events: event, Results: result, Response: response, ChaincodeId: ccid} 387 cActBytes, err := proto.Marshal(cAct) 388 if err != nil { 389 return nil, err 390 } 391 392 prp := &peer.ProposalResponsePayload{Extension: cActBytes, ProposalHash: hash} 393 prpBytes, err := proto.Marshal(prp) 394 if err != nil { 395 return nil, err 396 } 397 398 return prpBytes, nil 399 } 400 401 // GetBytesChaincodeProposalPayload gets the chaincode proposal payload 402 func GetBytesChaincodeProposalPayload(cpp *peer.ChaincodeProposalPayload) ([]byte, error) { 403 cppBytes, err := proto.Marshal(cpp) 404 if err != nil { 405 return nil, err 406 } 407 408 return cppBytes, nil 409 } 410 411 // GetBytesResponse gets the bytes of Response 412 func GetBytesResponse(res *peer.Response) ([]byte, error) { 413 resBytes, err := proto.Marshal(res) 414 if err != nil { 415 return nil, err 416 } 417 418 return resBytes, nil 419 } 420 421 // GetBytesChaincodeEvent gets the bytes of ChaincodeEvent 422 func GetBytesChaincodeEvent(event *peer.ChaincodeEvent) ([]byte, error) { 423 eventBytes, err := proto.Marshal(event) 424 if err != nil { 425 return nil, err 426 } 427 428 return eventBytes, nil 429 } 430 431 // GetBytesChaincodeActionPayload get the bytes of ChaincodeActionPayload from the message 432 func GetBytesChaincodeActionPayload(cap *peer.ChaincodeActionPayload) ([]byte, error) { 433 capBytes, err := proto.Marshal(cap) 434 if err != nil { 435 return nil, err 436 } 437 438 return capBytes, nil 439 } 440 441 // GetBytesProposalResponse gets propoal bytes response 442 func GetBytesProposalResponse(pr *peer.ProposalResponse) ([]byte, error) { 443 respBytes, err := proto.Marshal(pr) 444 if err != nil { 445 return nil, err 446 } 447 448 return respBytes, nil 449 } 450 451 // GetBytesProposal returns the bytes of a proposal message 452 func GetBytesProposal(prop *peer.Proposal) ([]byte, error) { 453 propBytes, err := proto.Marshal(prop) 454 if err != nil { 455 return nil, err 456 } 457 458 return propBytes, nil 459 } 460 461 // GetBytesHeader get the bytes of Header from the message 462 func GetBytesHeader(hdr *common.Header) ([]byte, error) { 463 bytes, err := proto.Marshal(hdr) 464 if err != nil { 465 return nil, err 466 } 467 468 return bytes, nil 469 } 470 471 // GetBytesSignatureHeader get the bytes of SignatureHeader from the message 472 func GetBytesSignatureHeader(hdr *common.SignatureHeader) ([]byte, error) { 473 bytes, err := proto.Marshal(hdr) 474 if err != nil { 475 return nil, err 476 } 477 478 return bytes, nil 479 } 480 481 // GetBytesTransaction get the bytes of Transaction from the message 482 func GetBytesTransaction(tx *peer.Transaction) ([]byte, error) { 483 bytes, err := proto.Marshal(tx) 484 if err != nil { 485 return nil, err 486 } 487 488 return bytes, nil 489 } 490 491 // GetBytesPayload get the bytes of Payload from the message 492 func GetBytesPayload(payl *common.Payload) ([]byte, error) { 493 bytes, err := proto.Marshal(payl) 494 if err != nil { 495 return nil, err 496 } 497 498 return bytes, nil 499 } 500 501 // GetBytesEnvelope get the bytes of Envelope from the message 502 func GetBytesEnvelope(env *common.Envelope) ([]byte, error) { 503 bytes, err := proto.Marshal(env) 504 if err != nil { 505 return nil, err 506 } 507 508 return bytes, nil 509 } 510 511 // GetActionFromEnvelope extracts a ChaincodeAction message from a serialized Envelope 512 func GetActionFromEnvelope(envBytes []byte) (*peer.ChaincodeAction, error) { 513 env, err := GetEnvelopeFromBlock(envBytes) 514 if err != nil { 515 return nil, err 516 } 517 518 payl, err := GetPayload(env) 519 if err != nil { 520 return nil, err 521 } 522 523 tx, err := GetTransaction(payl.Data) 524 if err != nil { 525 return nil, err 526 } 527 528 if len(tx.Actions) == 0 { 529 return nil, fmt.Errorf("At least one TransactionAction is required") 530 } 531 532 _, respPayload, err := GetPayloads(tx.Actions[0]) 533 return respPayload, err 534 } 535 536 // CreateProposalFromCIS returns a proposal given a serialized identity and a ChaincodeInvocationSpec 537 func CreateProposalFromCIS(typ common.HeaderType, chainID string, cis *peer.ChaincodeInvocationSpec, creator []byte) (*peer.Proposal, string, error) { 538 return CreateChaincodeProposal(typ, chainID, cis, creator) 539 } 540 541 // CreateInstallProposalFromCDS returns a install proposal given a serialized identity and a ChaincodeDeploymentSpec 542 func CreateInstallProposalFromCDS(ccpack proto.Message, creator []byte) (*peer.Proposal, string, error) { 543 return createProposalFromCDS("", ccpack, creator, nil, nil, nil, "install") 544 } 545 546 // CreateDeployProposalFromCDS returns a deploy proposal given a serialized identity and a ChaincodeDeploymentSpec 547 func CreateDeployProposalFromCDS(chainID string, cds *peer.ChaincodeDeploymentSpec, creator []byte, policy []byte, escc []byte, vscc []byte) (*peer.Proposal, string, error) { 548 return createProposalFromCDS(chainID, cds, creator, policy, escc, vscc, "deploy") 549 } 550 551 // CreateUpgradeProposalFromCDS returns a upgrade proposal given a serialized identity and a ChaincodeDeploymentSpec 552 func CreateUpgradeProposalFromCDS(chainID string, cds *peer.ChaincodeDeploymentSpec, creator []byte, policy []byte, escc []byte, vscc []byte) (*peer.Proposal, string, error) { 553 return createProposalFromCDS(chainID, cds, creator, policy, escc, vscc, "upgrade") 554 } 555 556 // createProposalFromCDS returns a deploy or upgrade proposal given a serialized identity and a ChaincodeDeploymentSpec 557 func createProposalFromCDS(chainID string, msg proto.Message, creator []byte, policy []byte, escc []byte, vscc []byte, propType string) (*peer.Proposal, string, error) { 558 //in the new mode, cds will be nil, "deploy" and "upgrade" are instantiates. 559 var ccinp *peer.ChaincodeInput 560 var b []byte 561 var err error 562 if msg != nil { 563 b, err = proto.Marshal(msg) 564 if err != nil { 565 return nil, "", err 566 } 567 } 568 switch propType { 569 case "deploy": 570 fallthrough 571 case "upgrade": 572 cds, ok := msg.(*peer.ChaincodeDeploymentSpec) 573 if !ok || cds == nil { 574 return nil, "", fmt.Errorf("invalid message for creating lifecycle chaincode proposal from") 575 } 576 ccinp = &peer.ChaincodeInput{Args: [][]byte{[]byte(propType), []byte(chainID), b, policy, escc, vscc}} 577 case "install": 578 ccinp = &peer.ChaincodeInput{Args: [][]byte{[]byte(propType), b}} 579 } 580 581 //wrap the deployment in an invocation spec to lscc... 582 lsccSpec := &peer.ChaincodeInvocationSpec{ 583 ChaincodeSpec: &peer.ChaincodeSpec{ 584 Type: peer.ChaincodeSpec_GOLANG, 585 ChaincodeId: &peer.ChaincodeID{Name: "lscc"}, 586 Input: ccinp}} 587 588 //...and get the proposal for it 589 return CreateProposalFromCIS(common.HeaderType_ENDORSER_TRANSACTION, chainID, lsccSpec, creator) 590 } 591 592 // ComputeProposalTxID computes TxID as the Hash computed 593 // over the concatenation of nonce and creator. 594 func ComputeProposalTxID(nonce, creator []byte) (string, error) { 595 // TODO: Get the Hash function to be used from 596 // channel configuration 597 digest, err := factory.GetDefault().Hash( 598 append(nonce, creator...), 599 &bccsp.SHA256Opts{}) 600 if err != nil { 601 return "", err 602 } 603 return hex.EncodeToString(digest), nil 604 } 605 606 // CheckProposalTxID checks that txid is equal to the Hash computed 607 // over the concatenation of nonce and creator. 608 func CheckProposalTxID(txid string, nonce, creator []byte) error { 609 computedTxID, err := ComputeProposalTxID(nonce, creator) 610 if err != nil { 611 return fmt.Errorf("Failed computing target TXID for comparison [%s]", err) 612 } 613 614 if txid != computedTxID { 615 return fmt.Errorf("Transaction is not valid. Got [%s], expected [%s]", txid, computedTxID) 616 } 617 618 return nil 619 } 620 621 // ComputeProposalBinding computes the binding of a proposal 622 func ComputeProposalBinding(proposal *peer.Proposal) ([]byte, error) { 623 if proposal == nil { 624 return nil, fmt.Errorf("Porposal is nil") 625 } 626 if len(proposal.Header) == 0 { 627 return nil, fmt.Errorf("Proposal's Header is nil") 628 } 629 630 h, err := GetHeader(proposal.Header) 631 if err != nil { 632 return nil, err 633 } 634 635 chdr, err := UnmarshalChannelHeader(h.ChannelHeader) 636 if err != nil { 637 return nil, err 638 } 639 shdr, err := GetSignatureHeader(h.SignatureHeader) 640 if err != nil { 641 return nil, err 642 } 643 644 return computeProposalBindingInternal(shdr.Nonce, shdr.Creator, chdr.Epoch) 645 } 646 647 func computeProposalBindingInternal(nonce, creator []byte, epoch uint64) ([]byte, error) { 648 epochBytes := make([]byte, 8) 649 binary.LittleEndian.PutUint64(epochBytes, epoch) 650 651 // TODO: add to genesis block the hash function used for the binding computation. 652 digest, err := factory.GetDefault().Hash( 653 append(append(nonce, creator...), epochBytes...), 654 &bccsp.SHA256Opts{}) 655 if err != nil { 656 return nil, err 657 } 658 return digest, nil 659 }