Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -79,9 +79,9 @@ class AssetTransfer extends Contract {
const asset = {
ID: id,
Color: color,
Size: size,
Size: Number(size),
Owner: owner,
AppraisedValue: appraisedValue,
AppraisedValue: Number(appraisedValue),
};
// we insert data in alphabetic order using 'json-stringify-deterministic' and 'sort-keys-recursive'
await ctx.stub.putState(id, Buffer.from(stringify(sortKeysRecursive(asset))));
Expand Down
2 changes: 1 addition & 1 deletion asset-transfer-events/application-gateway-go/connect.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const (
func newGrpcConnection() *grpc.ClientConn {
certificatePEM, err := os.ReadFile(tlsCertPath)
if err != nil {
panic(fmt.Errorf("failed to read TLS certifcate file: %w", err))
panic(fmt.Errorf("failed to read TLS certificate file: %w", err))
}

certificate, err := identity.CertificateFromPEM(certificatePEM)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
Copyright 2022 IBM All Rights Reserved.
Copyright 2024 IBM All Rights Reserved.

SPDX-License-Identifier: Apache-2.0
*/
Expand Down Expand Up @@ -77,8 +77,8 @@ func newIdentity(certDirectoryPath, mspId string) *identity.X509Identity {
}

// newSign creates a function that generates a digital signature from a message digest using a private key.
func newSign(keyDirectoryPash string) identity.Sign {
privateKeyPEM, err := readFirstFile(keyDirectoryPash)
func newSign(keyDirectoryPath string) identity.Sign {
privateKeyPEM, err := readFirstFile(keyDirectoryPath)
if err != nil {
panic(fmt.Errorf("failed to read private key file: %w", err))
}
Expand Down
13 changes: 12 additions & 1 deletion ci/scripts/run-test-network-off-chain.sh
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,22 @@ print "Initializing Typescript off-chain data application"
pushd ../off_chain_data/application-typescript
rm -f checkpoint.json store.log
npm install
print "Running the output app"
print "Running the Typescript app"
SIMULATED_FAILURE_COUNT=1 npm start getAllAssets transact getAllAssets listen
SIMULATED_FAILURE_COUNT=1 npm start listen
popd

# Run off-chain data Go application
export CHAINCODE_NAME=go_off_chain_data
deployChaincode
print "Initializing Go off-chain data application"
pushd ../off_chain_data/application-go
rm -f checkpoint.json store.log
print "Running the Go app"
SIMULATED_FAILURE_COUNT=1 go run . getAllAssets transact getAllAssets listen
SIMULATED_FAILURE_COUNT=1 go run . listen
popd

# Run off-chain data Java application
#createNetwork
export CHAINCODE_NAME=off_chain_data
Expand Down
21 changes: 19 additions & 2 deletions off_chain_data/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,16 +19,19 @@ The client application provides several "commands" that can be invoked using the
- **getAllAssets**: Retrieve the current details of all assets recorded on the ledger. See:
- TypeScript: [application-typescript/src/getAllAssets.ts](application-typescript/src/getAllAssets.ts)
- Java: [application-java/app/src/main/java/GetAllAssets.java](application-java/app/src/main/java/GetAllAssets.java)
- Go: [application-go/getAllAssets.go](application-go/getAllAssets.go)
- **listen**: Listen for block events, and use them to replicate ledger updates in an off-chain data store. See:
- TypeScript: [application-typescript/src/listen.ts](application-typescript/src/listen.ts)
- Java: [application-java/app/src/main/java/Listen.java](application-java/app/src/main/java/Listen.java)
- Go: [application-go/listen.go](application-go/listen.go)
- **transact**: Submit a set of transactions to create, modify and delete assets. See:
- TypeScript: [application-typescript/src/transact.ts](application-typescript/src/transact.ts)
- Java: [application-java/app/src/main/java/Transact.java](application-java/app/src/main/java/Transact.java)
- Go: [application-go/transact.go](application-go/transact.go)

To keep the sample code concise, the **listen** command writes ledger updates to an output file named `store.log` in the current working directory (which for the Java sample is the `application-java/app` directory). A real implementation could write ledger updates directly to an off-chain data store of choice. You can inspect the information captured in this file as you run the sample.

Note that the **listen** command is is restartable and will resume event listening after the last successfully processed block / transaction. This is achieved using a checkpointer to persist the current listening position. Checkpoint state is persisted to a file named `checkpoint.json` in the current working directory. If no checkpoint state is present, event listening begins from the start of the ledger (block number zero).
Note that the **listen** command is restartable and will resume event listening after the last successfully processed block / transaction. This is achieved using a checkpointer to persist the current listening position. Checkpoint state is persisted to a file named `checkpoint.json` in the current working directory. If no checkpoint state is present, event listening begins from the start of the ledger (block number zero).

### Smart Contract

Expand Down Expand Up @@ -65,6 +68,10 @@ The Fabric test network is used to deploy and run this sample. Follow these step
npm install
npm start transact listen

# To run the Go sample application
cd application-go
go run . transact listen

# To run the Java sample application
cd application-java
./gradlew run --quiet --args='transact listen'
Expand All @@ -79,6 +86,10 @@ The Fabric test network is used to deploy and run this sample. Follow these step
cd application-typescript
npm --silent start getAllAssets

# To run the Go sample application
cd application-go
go run . getAllAssets

# To run the Java sample application
cd application-java
./gradlew run --quiet --args=getAllAssets
Expand All @@ -93,6 +104,12 @@ The Fabric test network is used to deploy and run this sample. Follow these step
SIMULATED_FAILURE_COUNT=5 npm start listen
npm start listen

# To run the Go sample application
cd application-go
go run . transact
SIMULATED_FAILURE_COUNT=5 go run . listen
go run . listen

# To run the Java sample application
cd application-java
./gradlew run --quiet --args=transact
Expand All @@ -112,4 +129,4 @@ When you are finished, you can bring down the test network (from the `test-netwo

```
./network.sh down
```
```
66 changes: 66 additions & 0 deletions off_chain_data/application-go/app.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
package main

import (
"errors"
"fmt"
"os"
"strings"

"google.golang.org/grpc"
)

type command func(grpc.ClientConnInterface) error

var allCommands = map[string]command{
"getAllAssets": getAllAssets,
"transact": transact,
"listen": listen,
}

func main() {
commands := os.Args[1:]
if len(commands) == 0 {
printUsage()
panic(errors.New("missing command"))
}

for _, name := range commands {
if _, exists := allCommands[name]; !exists {
printUsage()
panic(fmt.Errorf("unknown command: %s", name))
}
fmt.Println("command:", name)
}

client := newGrpcConnection()
defer client.Close()

for _, name := range commands {
command := allCommands[name]

if err := command(client); err != nil {
if errors.Is(err, errExpected) {
fmt.Println(err)
return
}

panic(err)
}
}
}

func printUsage() {
fmt.Println("Arguments: <command1> [<command2> ...]")
fmt.Println("Available commands:", availableCommands())
}

func availableCommands() string {
result := make([]string, len(allCommands))
i := 0
for command := range allCommands {
result[i] = command
i++
}

return strings.Join(result, ", ")
}
128 changes: 128 additions & 0 deletions off_chain_data/application-go/connect.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
package main

import (
"crypto/x509"
"fmt"
"os"
"path"
"time"

"github.com/hyperledger/fabric-gateway/pkg/client"
"github.com/hyperledger/fabric-gateway/pkg/hash"
"github.com/hyperledger/fabric-gateway/pkg/identity"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials"
)

const peerName = "peer0.org1.example.com"

var (
channelName = envOrDefault("CHANNEL_NAME", "mychannel")
chaincodeName = envOrDefault("CHAINCODE_NAME", "basic")
mspID = envOrDefault("MSP_ID", "Org1MSP")

// Path to crypto materials.
cryptoPath = envOrDefault("CRYPTO_PATH", "../../test-network/organizations/peerOrganizations/org1.example.com")

// Path to user private key directory.
keyDirectoryPath = envOrDefault("KEY_DIRECTORY_PATH", cryptoPath+"/users/[email protected]/msp/keystore")

// Path to user certificate.
certPath = envOrDefault("CERT_PATH", cryptoPath+"/users/[email protected]/msp/signcerts/cert.pem")

// Path to peer tls certificate.
tlsCertPath = envOrDefault("TLS_CERT_PATH", cryptoPath+"/peers/peer0.org1.example.com/tls/ca.crt")

// Gateway peer endpoint.
peerEndpoint = envOrDefault("PEER_ENDPOINT", "dns:///localhost:7051")

// Gateway peer SSL host name override.
peerHostAlias = envOrDefault("PEER_HOST_ALIAS", peerName)
)

func newGrpcConnection() *grpc.ClientConn {
certificatePEM, err := os.ReadFile(tlsCertPath)
if err != nil {
panic(fmt.Errorf("failed to read TLS certificate file: %w", err))
}

certificate, err := identity.CertificateFromPEM(certificatePEM)
if err != nil {
panic(err)
}

certPool := x509.NewCertPool()
certPool.AddCert(certificate)
transportCredentials := credentials.NewClientTLSFromCert(certPool, peerHostAlias)

connection, err := grpc.NewClient(peerEndpoint, grpc.WithTransportCredentials(transportCredentials))
if err != nil {
panic(fmt.Errorf("failed to create gRPC connection: %w", err))
}

return connection
}

func newConnectOptions(clientConnection grpc.ClientConnInterface) (identity.Identity, []client.ConnectOption) {
return newIdentity(), []client.ConnectOption{
client.WithSign(newSign()),
client.WithHash(hash.SHA256),
client.WithClientConnection(clientConnection),
client.WithEvaluateTimeout(5 * time.Second),
client.WithEndorseTimeout(15 * time.Second),
client.WithSubmitTimeout(5 * time.Second),
client.WithCommitStatusTimeout(1 * time.Minute),
}
}

func newIdentity() *identity.X509Identity {
certificatePEM, err := os.ReadFile(certPath)
if err != nil {
panic(fmt.Errorf("failed to read certificate file: %w", err))
}

certificate, err := identity.CertificateFromPEM(certificatePEM)
if err != nil {
panic(err)
}

id, err := identity.NewX509Identity(mspID, certificate)
if err != nil {
panic(err)
}

return id
}

func newSign() identity.Sign {
privateKeyPEM, err := readFirstFile(keyDirectoryPath)
if err != nil {
panic(fmt.Errorf("failed to read private key file: %w", err))
}

privateKey, err := identity.PrivateKeyFromPEM(privateKeyPEM)
if err != nil {
panic(err)
}

sign, err := identity.NewPrivateKeySign(privateKey)
if err != nil {
panic(err)
}

return sign
}

func readFirstFile(dirPath string) ([]byte, error) {
dir, err := os.Open(dirPath)
if err != nil {
return nil, err
}

fileNames, err := dir.Readdirnames(1)
if err != nil {
return nil, err
}

return os.ReadFile(path.Join(dirPath, fileNames[0]))
}
76 changes: 76 additions & 0 deletions off_chain_data/application-go/contract/contract.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package contract

import (
"encoding/json"
"strconv"

"github.com/hyperledger/fabric-gateway/pkg/client"
)

type AssetTransferBasic struct {
contract *client.Contract
}

func NewAssetTransferBasic(contract *client.Contract) *AssetTransferBasic {
return &AssetTransferBasic{contract}
}

func (atb *AssetTransferBasic) CreateAsset(anAsset Asset) error {
if _, err := atb.contract.Submit(
"CreateAsset",
client.WithArguments(
anAsset.ID,
anAsset.Color,
strconv.FormatUint(anAsset.Size, 10),
anAsset.Owner,
strconv.FormatUint(anAsset.AppraisedValue, 10),
)); err != nil {
return err
}
return nil
}

func (atb *AssetTransferBasic) TransferAsset(id, newOwner string) (string, error) {
result, err := atb.contract.Submit(
"TransferAsset",
client.WithArguments(
id,
newOwner,
),
)
if err != nil {
return "", err
}

return string(result), nil
}

func (atb *AssetTransferBasic) DeleteAsset(id string) error {
if _, err := atb.contract.Submit(
"DeleteAsset",
client.WithArguments(
id,
),
); err != nil {
return err
}
return nil
}

func (atb *AssetTransferBasic) GetAllAssets() ([]Asset, error) {
assetsRaw, err := atb.contract.Evaluate("GetAllAssets")
if err != nil {
return nil, err
}

if len(assetsRaw) == 0 {
return nil, nil
}

assets := []Asset{}
if err := json.Unmarshal(assetsRaw, &assets); err != nil {
return nil, err
}

return assets, nil
}
Loading
Loading