2

I tried to write a chaincode such that when it's executed in a peer instance, it uploads data to google cloud storage bucket. The file I'll be uploading is actually stored as small file chunks in a folder, so that different peers upload different chunks to the GCS bucket. I'm using the fabcar blueprint to develop this chaincode, and test-network script files to execute the chaincode. The function I used to upload data is working well when I executed locally, but when I tried to use in the chaincode, it's showing

Error: endorsement failure during invoke. response: status:500 message:"error in simulation: failed to execute transaction 49a9b96088ff2f32906a6b6c9ba1f4ac0a530779bf8d506b176fcdfb8818afe2: error sending: chaincode stream terminated"

(What I'm doing might sound crazy, but I'm new to this hyperledger fabric)

Below is the code sample I'm executing (I think it's the problem with uploadGCS or InitLedger function)(FYI: chaincode execution runs InitLedger function only, which ofcourse uses uploadGCS function)

package main

import (
    "fmt"
    "os"
    "io"
    "log"
    "strings"
    "encoding/json"
    "encoding/hex"
    "github.com/hyperledger/fabric-contract-api-go/contractapi"
    "path/filepath"
    "strconv"
    "crypto/sha256"
    "time"
    "context"
    "cloud.google.com/go/storage"
    "google.golang.org/api/option"
    "golang.org/x/oauth2/google"
)

type SmartContract struct {
    contractapi.Contract
}

type Data struct {
    Owner  string `json:"owner"`
    File string `json:"file"`
    FileChunkNumber string `json:"filechunknumber"`
    SHA256 string `json:"sha256"`
}

func uploadGCS(owner, filechunklocation, uploadlocation string) error {
    ct := context.Background()
    creds, err := google.FindDefaultCredentials(ct, storage.ScopeReadOnly)
    if err != nil {
            log.Fatal("GoT an err %s", err)
    }

    client, err := storage.NewClient(ct, option.WithCredentials(creds))
    if err != nil {
        return fmt.Errorf("storage.NewClient: %v", err)
    }
    defer client.Close()

    // Open local file.
    f, err := os.Open(filechunklocation)
    if err != nil {
        return fmt.Errorf("os.Open: %v", err)
    }
    defer f.Close()

    ct, cancel := context.WithTimeout(ct, time.Second*50)
    defer cancel()

    // Upload an object with storage.Writer.
    wc := client.Bucket("btp2016bcs0015-cloud-storage").Object(uploadlocation).NewWriter(ct)
    if _, err = io.Copy(wc, f); err != nil {
        return fmt.Errorf("io.Copy: %v", err)
    }
    if err := wc.Close(); err != nil {
        return fmt.Errorf("Writer.Close: %v", err)
    }
    return nil
}

func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error {
    filelocation := "/home/busyfriend/go/src/github.com/hyperledger/fabric-samples/test-network/samplefile---pdf"
    data := []Data{
        Data{Owner: "ID126859", File: "samplefile.pdf", FileChunkNumber: "1", SHA256: "eb73a20d61c1fb294b0eba4d35568d10c8ddbfe2544a3cacc959d640077673f5"},
        Data{Owner: "ID126859", File: "samplefile.pdf", FileChunkNumber: "2", SHA256: "92dd8ea8aa0da4a48a2cb45ae38f70f17526b6b50ef80c44367a56de6ec9abf9"},
        Data{Owner: "ID126859", File: "samplefile.pdf", FileChunkNumber: "3", SHA256: "b97027d261d01f86d1e514a52886add096ddc4e66d15d01e53516dd9d5cfb20b"},
        Data{Owner: "ID126859", File: "samplefile.pdf", FileChunkNumber: "4", SHA256: "377582f5e62dc3b34e40741f2d70d8f37a029856f75cbe68a6659328258e23a3"},
        Data{Owner: "ID126859", File: "samplefile.pdf", FileChunkNumber: "5", SHA256: "afb6c6d112d446ac07d78b13957bb440105038411095032de444bf08e3bbdba8"},
        Data{Owner: "ID126859", File: "samplefile.pdf", FileChunkNumber: "6", SHA256: "e43b885c2bfb47130c54fa70528fb2a91d9d1af1417a0f7c5a4c22d8f16efb01"},
    }

    for i := range data {
        _, dir := filepath.Split(filelocation)
        dir_1 := strings.Split(dir,"---")
        filechunk := dir_1[0]+"_"+ data[i].FileChunkNumber
        filechunklocation := filepath.Join(filelocation, filechunk)
        uploadlocation :=  data[i].Owner + "/" + dir + "/" + filechunk

        err := uploadGCS(data[i].Owner, filechunklocation, uploadlocation)

        if err != nil {
            return fmt.Errorf("Got an error %s", err.Error())
        }
    }

    for i, putdata := range data {
        dataAsBytes, _ := json.Marshal(putdata)
        err := ctx.GetStub().PutState("DATA"+strconv.Itoa(i), dataAsBytes)

        if err != nil {
            return fmt.Errorf("Failed to put to world state. %s", err.Error())
        }
    }

    return nil
}

// Uploads new data to the world state with given details
func (s *SmartContract) uploadData(ctx contractapi.TransactionContextInterface, dataID string, owner string, filelocation string, filechunknumber string) error {
    //Uploads the filechunk to the cloud storage
    _, dir := filepath.Split(filelocation)
    dir_1 := strings.Split(dir,"---")
    filechunk := dir_1[0]+"_"+ filechunknumber
    filechunklocation := filepath.Join(filelocation, filechunk)
    uploadlocation :=  owner + "/" + dir + "/" + filechunk
    err := uploadGCS(owner, filechunklocation, uploadlocation)
    if err != nil {
        fmt.Println(err.Error())
        return err
    }

    //Creates SHA256 hash of the file chunk
    f, err := os.Open(filechunklocation)
    if err != nil {
        log.Fatal(err)
    }
    defer f.Close()
    h := sha256.New()
    if _, err := io.Copy(h, f); err != nil {
        log.Fatal(err)
    }

    data := Data{
        Owner: owner,
        File: dir_1[0]+"."+dir_1[1],
        FileChunkNumber: filechunknumber,
        SHA256: hex.EncodeToString(h.Sum(nil)),
    }

    dataAsBytes, _ := json.Marshal(data)

    return ctx.GetStub().PutState(dataID, dataAsBytes)
}


func main() {

    chaincode, err := contractapi.NewChaincode(new(SmartContract))

    if err != nil {
        fmt.Printf("Error create cloud chaincode: %s", err.Error())
        return
    }

    if err := chaincode.Start(); err != nil {
        fmt.Printf("Error starting cloud chaincode: %s", err.Error())
    }
}

This is something I got after executing this chaincode terminal result

SSK
  • 3,444
  • 6
  • 32
  • 59
Sai Madhav
  • 43
  • 4

1 Answers1

1

Can you check chaincode containers' logs? You can find extra containers created which have your chaincode's name and version in it. If you want to see the logs generated by chaincode, you need to look into those containers (with docker logs <container>).

In your code, you are logging some errors with Fatal. Instead of log & continue approach, it'd be better to return errors and fail.

Please note that, according to endorsement policy (i.e. AND(Org1.member, Org2.member)) one invoke request can be executed on multiple peers. All those executions has to return same result and put/get exactly same data to/from ledger. This is a design to ensure trust between different organizations having same chaincode running. (i.e. it'd fail if your different peers convert same object to string with different attributes order before putting to ledger.)

My opinion if to keep I/O operation separate then just put hashes to ledger. Your approach has some in-common with off-chain data concept. Please have a look into it before reconsideration.

hsnkhrmn
  • 961
  • 7
  • 20
  • It worked!! actually new containers are being created while deploying chaincode. So I need to put my creds and necessary files in that container and it worked ```client, err := storage.NewClient(ct, option.WithCredentials(creds))``` ```f, err := os.Open(filechunklocation)```. Yeah, my ideology of this concept is completely wrong @hsnkhrmn, I'll change – Sai Madhav Jun 16 '20 at 05:42