mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-17 15:35:09 +00:00
refactor the commands
Signed-off-by: nXtCyberNet <rohantech2005@gmail.com>
This commit is contained in:
parent
d42cc27a98
commit
cf7aa047c4
17 changed files with 447 additions and 325 deletions
13
.github/workflows/test-fsat.yaml
vendored
13
.github/workflows/test-fsat.yaml
vendored
|
|
@ -31,6 +31,19 @@ jobs:
|
||||||
- run: just test-appdev
|
- run: just test-appdev
|
||||||
working-directory: full-stack-asset-transfer-guide
|
working-directory: full-stack-asset-transfer-guide
|
||||||
|
|
||||||
|
appdev-go:
|
||||||
|
runs-on: ${{ github.repository == 'hyperledger/fabric-samples' && 'fabric-ubuntu-22.04' || 'ubuntu-22.04' }}
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v6
|
||||||
|
- name: Set up Go
|
||||||
|
uses: actions/setup-go@v6
|
||||||
|
with:
|
||||||
|
go-version-file: full-stack-asset-transfer-guide/applications/trader-go/go.mod
|
||||||
|
- name: Set up Full Stack Runtime
|
||||||
|
uses: ./.github/actions/fsat-setup
|
||||||
|
- run: just test-appdev-go
|
||||||
|
working-directory: full-stack-asset-transfer-guide
|
||||||
|
|
||||||
chaincode:
|
chaincode:
|
||||||
runs-on: ${{ github.repository == 'hyperledger/fabric-samples' && 'fabric-ubuntu-22.04' || 'ubuntu-22.04' }}
|
runs-on: ${{ github.repository == 'hyperledger/fabric-samples' && 'fabric-ubuntu-22.04' || 'ubuntu-22.04' }}
|
||||||
steps:
|
steps:
|
||||||
|
|
|
||||||
|
|
@ -1,10 +1,10 @@
|
||||||
package main
|
package commands
|
||||||
|
|
||||||
import "github.com/hyperledger/fabric-gateway/pkg/client"
|
import "github.com/hyperledger/fabric-gateway/pkg/client"
|
||||||
|
|
||||||
type Command func(gw *client.Gateway, args []string) error
|
type Command func(gw *client.Gateway, args []string) error
|
||||||
|
|
||||||
var commands = map[string]Command{
|
var Commands = map[string]Command{
|
||||||
"create": cmdCreate,
|
"create": cmdCreate,
|
||||||
"delete": cmdDelete,
|
"delete": cmdDelete,
|
||||||
"getAllAssets": cmdGetAllAssets,
|
"getAllAssets": cmdGetAllAssets,
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import "os"
|
||||||
|
|
||||||
|
func channelName() string {
|
||||||
|
if v := os.Getenv("CHANNEL_NAME"); v != "" {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return "mychannel"
|
||||||
|
}
|
||||||
|
|
||||||
|
func chaincodeName() string {
|
||||||
|
if v := os.Getenv("CHAINCODE_NAME"); v != "" {
|
||||||
|
return v
|
||||||
|
}
|
||||||
|
return "asset-transfer"
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package main
|
package commands
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
@ -0,0 +1,27 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hyperledger/fabric-gateway/pkg/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// cmdCreate creates a new asset on the ledger.
|
||||||
|
// Arguments: <assetId> <ownerName> <color>
|
||||||
|
func cmdCreate(gw *client.Gateway, args []string) error {
|
||||||
|
if len(args) < 3 {
|
||||||
|
return fmt.Errorf("arguments: <assetId> <ownerName> <color>")
|
||||||
|
}
|
||||||
|
|
||||||
|
network := gw.GetNetwork(channelName())
|
||||||
|
contract := network.GetContract(chaincodeName())
|
||||||
|
|
||||||
|
smartContract := NewAssetTransfer(contract)
|
||||||
|
return smartContract.CreateAsset(Asset{
|
||||||
|
ID: args[0],
|
||||||
|
Owner: args[1],
|
||||||
|
Color: args[2],
|
||||||
|
Size: 1,
|
||||||
|
AppraisedValue: 1,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hyperledger/fabric-gateway/pkg/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// cmdDelete deletes an asset from the ledger.
|
||||||
|
// Arguments: <assetId>
|
||||||
|
func cmdDelete(gw *client.Gateway, args []string) error {
|
||||||
|
if len(args) < 1 {
|
||||||
|
return fmt.Errorf("arguments: <assetId>")
|
||||||
|
}
|
||||||
|
|
||||||
|
network := gw.GetNetwork(channelName())
|
||||||
|
contract := network.GetContract(chaincodeName())
|
||||||
|
|
||||||
|
smartContract := NewAssetTransfer(contract)
|
||||||
|
return smartContract.DeleteAsset(args[0])
|
||||||
|
}
|
||||||
|
|
@ -1,10 +1,4 @@
|
||||||
/*
|
package commands
|
||||||
* Copyright IBM Corp. All Rights Reserved.
|
|
||||||
*
|
|
||||||
* SPDX-License-Identifier: Apache-2.0
|
|
||||||
*/
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
// ExpectedError represents a known, expected application error that should be
|
// ExpectedError represents a known, expected application error that should be
|
||||||
// displayed normally rather than treated as an unexpected failure.
|
// displayed normally rather than treated as an unexpected failure.
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/hyperledger/fabric-gateway/pkg/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// cmdGetAllAssets queries and prints all assets currently on the ledger.
|
||||||
|
func cmdGetAllAssets(gw *client.Gateway, _ []string) error {
|
||||||
|
network := gw.GetNetwork(channelName())
|
||||||
|
contract := network.GetContract(chaincodeName())
|
||||||
|
|
||||||
|
smartContract := NewAssetTransfer(contract)
|
||||||
|
assets, err := smartContract.GetAllAssets()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.MarshalIndent(assets, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to marshal assets: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, line := range strings.Split(string(data), "\n") {
|
||||||
|
fmt.Println(line)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,81 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
|
"github.com/hyperledger/fabric-gateway/pkg/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
const startBlock = uint64(0)
|
||||||
|
|
||||||
|
func cmdListen(gw *client.Gateway, _ []string) error {
|
||||||
|
network := gw.GetNetwork(channelName())
|
||||||
|
|
||||||
|
checkpointFile := os.Getenv("CHECKPOINT_FILE")
|
||||||
|
if checkpointFile == "" {
|
||||||
|
checkpointFile = "checkpoint.json"
|
||||||
|
}
|
||||||
|
|
||||||
|
simulatedFailureCount, err := getSimulatedFailureCount()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
checkpointer, err := client.NewFileCheckpointer(checkpointFile)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create checkpointer: %w", err)
|
||||||
|
}
|
||||||
|
defer checkpointer.Close()
|
||||||
|
|
||||||
|
displayBlock := checkpointer.BlockNumber()
|
||||||
|
if displayBlock == 0 {
|
||||||
|
displayBlock = startBlock
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println("Starting event listening from block", displayBlock)
|
||||||
|
fmt.Println("Last processed transaction ID within block:", checkpointer.TransactionID())
|
||||||
|
if simulatedFailureCount > 0 {
|
||||||
|
fmt.Println("Simulating a write failure every", simulatedFailureCount, "transactions")
|
||||||
|
}
|
||||||
|
|
||||||
|
ctx, cancel := context.WithCancel(context.Background())
|
||||||
|
defer cancel()
|
||||||
|
|
||||||
|
events, err := network.ChaincodeEvents(ctx, chaincodeName(),
|
||||||
|
client.WithStartBlock(startBlock),
|
||||||
|
client.WithCheckpoint(checkpointer),
|
||||||
|
)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to start chaincode event subscription: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
eventCount := 0
|
||||||
|
for event := range events {
|
||||||
|
if simulatedFailureCount > 0 {
|
||||||
|
eventCount++
|
||||||
|
if eventCount >= simulatedFailureCount {
|
||||||
|
eventCount = 0
|
||||||
|
return &ExpectedError{Message: "Simulated write failure"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fmt.Printf("Chaincode event: BlockNumber=%d TxID=%s Name=%s Payload=%s\n",
|
||||||
|
event.BlockNumber, event.TransactionID, event.EventName, string(event.Payload))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func getSimulatedFailureCount() (int, error) {
|
||||||
|
value := os.Getenv("SIMULATED_FAILURE_COUNT")
|
||||||
|
if value == "" {
|
||||||
|
return 0, nil
|
||||||
|
}
|
||||||
|
count, err := strconv.Atoi(value)
|
||||||
|
if err != nil || count < 0 {
|
||||||
|
return 0, fmt.Errorf("invalid SIMULATED_FAILURE_COUNT value: %s", value)
|
||||||
|
}
|
||||||
|
return count, nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hyperledger/fabric-gateway/pkg/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// cmdRead reads and prints a single asset from the ledger.
|
||||||
|
// Arguments: <assetId>
|
||||||
|
func cmdRead(gw *client.Gateway, args []string) error {
|
||||||
|
if len(args) < 1 {
|
||||||
|
return fmt.Errorf("arguments: <assetId>")
|
||||||
|
}
|
||||||
|
|
||||||
|
network := gw.GetNetwork(channelName())
|
||||||
|
contract := network.GetContract(chaincodeName())
|
||||||
|
|
||||||
|
smartContract := NewAssetTransfer(contract)
|
||||||
|
asset, err := smartContract.ReadAsset(args[0])
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
data, err := json.MarshalIndent(asset, "", " ")
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to marshal asset: %w", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
fmt.Println(string(data))
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,126 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rand"
|
||||||
|
"encoding/hex"
|
||||||
|
"fmt"
|
||||||
|
"math/big"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
|
||||||
|
"github.com/hyperledger/fabric-gateway/pkg/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
colors = []string{"red", "green", "blue"}
|
||||||
|
maxInitialSize = 10
|
||||||
|
maxInitialVal = 1000
|
||||||
|
)
|
||||||
|
|
||||||
|
// cmdTransact runs a batch of concurrent create/update/delete transactions to demonstrate
|
||||||
|
func cmdTransact(gw *client.Gateway, _ []string) error {
|
||||||
|
network := gw.GetNetwork(channelName())
|
||||||
|
contract := network.GetContract(chaincodeName())
|
||||||
|
|
||||||
|
smartContract := NewAssetTransfer(contract)
|
||||||
|
app := &transactApp{smartContract: smartContract, batchSize: 6}
|
||||||
|
return app.run()
|
||||||
|
}
|
||||||
|
|
||||||
|
type transactApp struct {
|
||||||
|
smartContract *AssetTransfer
|
||||||
|
batchSize int
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *transactApp) run() error {
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
errCh := make(chan error, a.batchSize)
|
||||||
|
|
||||||
|
for i := 0; i < a.batchSize; i++ {
|
||||||
|
wg.Add(1)
|
||||||
|
go func() {
|
||||||
|
defer wg.Done()
|
||||||
|
if err := a.transact(); err != nil {
|
||||||
|
errCh <- err
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
|
||||||
|
wg.Wait()
|
||||||
|
close(errCh)
|
||||||
|
|
||||||
|
var failures []string
|
||||||
|
for err := range errCh {
|
||||||
|
failures = append(failures, err.Error())
|
||||||
|
}
|
||||||
|
if len(failures) > 0 {
|
||||||
|
return fmt.Errorf("%d failures:\n- %s", len(failures), strings.Join(failures, "\n- "))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *transactApp) transact() error {
|
||||||
|
asset := a.newAsset()
|
||||||
|
|
||||||
|
if err := a.smartContract.CreateAsset(asset); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("Created asset %s\n", asset.ID)
|
||||||
|
|
||||||
|
if randomInt(2) == 0 {
|
||||||
|
oldColor := asset.Color
|
||||||
|
asset.Color = differentElement(colors, oldColor)
|
||||||
|
if err := a.smartContract.UpdateAsset(asset); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("Updated color of asset %s from %s to %s\n", asset.ID, oldColor, asset.Color)
|
||||||
|
}
|
||||||
|
|
||||||
|
if randomInt(4) == 0 {
|
||||||
|
if err := a.smartContract.DeleteAsset(asset.ID); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
fmt.Printf("Deleted asset %s\n", asset.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *transactApp) newAsset() Asset {
|
||||||
|
return Asset{
|
||||||
|
ID: randomHexString(8),
|
||||||
|
Color: randomElement(colors),
|
||||||
|
Size: randomInt(maxInitialSize) + 1,
|
||||||
|
AppraisedValue: float64(randomInt(maxInitialVal) + 1),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func randomHexString(length int) string {
|
||||||
|
b := make([]byte, (length+1)/2)
|
||||||
|
if _, err := rand.Read(b); err != nil {
|
||||||
|
panic(fmt.Sprintf("failed to generate random bytes: %v", err))
|
||||||
|
}
|
||||||
|
return hex.EncodeToString(b)[:length]
|
||||||
|
}
|
||||||
|
|
||||||
|
func randomInt(max int) int {
|
||||||
|
n, err := rand.Int(rand.Reader, big.NewInt(int64(max)))
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("failed to generate random int: %v", err))
|
||||||
|
}
|
||||||
|
return int(n.Int64())
|
||||||
|
}
|
||||||
|
|
||||||
|
func randomElement(values []string) string {
|
||||||
|
return values[randomInt(len(values))]
|
||||||
|
}
|
||||||
|
|
||||||
|
func differentElement(values []string, currentValue string) string {
|
||||||
|
var candidates []string
|
||||||
|
for _, v := range values {
|
||||||
|
if v != currentValue {
|
||||||
|
candidates = append(candidates, v)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return randomElement(candidates)
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
package commands
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hyperledger/fabric-gateway/pkg/client"
|
||||||
|
)
|
||||||
|
|
||||||
|
// cmdTransfer transfers ownership of an asset to a new owner in a different organisation.
|
||||||
|
// Arguments: <assetId> <ownerName> <ownerMspId>
|
||||||
|
func cmdTransfer(gw *client.Gateway, args []string) error {
|
||||||
|
if len(args) < 3 {
|
||||||
|
return fmt.Errorf("arguments: <assetId> <ownerName> <ownerMspId>")
|
||||||
|
}
|
||||||
|
|
||||||
|
network := gw.GetNetwork(channelName())
|
||||||
|
contract := network.GetContract(chaincodeName())
|
||||||
|
|
||||||
|
smartContract := NewAssetTransfer(contract)
|
||||||
|
return smartContract.TransferAsset(args[0], args[1], args[2])
|
||||||
|
}
|
||||||
|
|
@ -1,296 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"crypto/rand"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/json"
|
|
||||||
"fmt"
|
|
||||||
"math/big"
|
|
||||||
"os"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"github.com/hyperledger/fabric-gateway/pkg/client"
|
|
||||||
)
|
|
||||||
|
|
||||||
// cmdCreate creates a new asset on the ledger.
|
|
||||||
// Arguments: <assetId> <ownerName> <color>
|
|
||||||
func cmdCreate(gw *client.Gateway, args []string) error {
|
|
||||||
if len(args) < 3 {
|
|
||||||
return fmt.Errorf("arguments: <assetId> <ownerName> <color>")
|
|
||||||
}
|
|
||||||
|
|
||||||
network := gw.GetNetwork(channelName())
|
|
||||||
contract := network.GetContract(chaincodeName())
|
|
||||||
|
|
||||||
smartContract := NewAssetTransfer(contract)
|
|
||||||
return smartContract.CreateAsset(Asset{
|
|
||||||
ID: args[0],
|
|
||||||
Owner: args[1],
|
|
||||||
Color: args[2],
|
|
||||||
Size: 1,
|
|
||||||
AppraisedValue: 1,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
// cmdDelete deletes an asset from the ledger.
|
|
||||||
// Arguments: <assetId>
|
|
||||||
func cmdDelete(gw *client.Gateway, args []string) error {
|
|
||||||
if len(args) < 1 {
|
|
||||||
return fmt.Errorf("arguments: <assetId>")
|
|
||||||
}
|
|
||||||
|
|
||||||
network := gw.GetNetwork(channelName())
|
|
||||||
contract := network.GetContract(chaincodeName())
|
|
||||||
|
|
||||||
smartContract := NewAssetTransfer(contract)
|
|
||||||
return smartContract.DeleteAsset(args[0])
|
|
||||||
}
|
|
||||||
|
|
||||||
// cmdGetAllAssets queries and prints all assets currently on the ledger.
|
|
||||||
func cmdGetAllAssets(gw *client.Gateway, _ []string) error {
|
|
||||||
network := gw.GetNetwork(channelName())
|
|
||||||
contract := network.GetContract(chaincodeName())
|
|
||||||
|
|
||||||
smartContract := NewAssetTransfer(contract)
|
|
||||||
assets, err := smartContract.GetAllAssets()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := json.MarshalIndent(assets, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to marshal assets: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, line := range strings.Split(string(data), "\n") {
|
|
||||||
fmt.Println(line)
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
const startBlock = uint64(0)
|
|
||||||
|
|
||||||
func cmdListen(gw *client.Gateway, _ []string) error {
|
|
||||||
network := gw.GetNetwork(channelName())
|
|
||||||
|
|
||||||
checkpointFile := os.Getenv("CHECKPOINT_FILE")
|
|
||||||
if checkpointFile == "" {
|
|
||||||
checkpointFile = "checkpoint.json"
|
|
||||||
}
|
|
||||||
|
|
||||||
simulatedFailureCount, err := getSimulatedFailureCount()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
checkpointer, err := client.NewFileCheckpointer(checkpointFile)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to create checkpointer: %w", err)
|
|
||||||
}
|
|
||||||
defer checkpointer.Close()
|
|
||||||
|
|
||||||
displayBlock := checkpointer.BlockNumber()
|
|
||||||
if displayBlock == 0 {
|
|
||||||
displayBlock = startBlock
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println("Starting event listening from block", displayBlock)
|
|
||||||
fmt.Println("Last processed transaction ID within block:", checkpointer.TransactionID())
|
|
||||||
if simulatedFailureCount > 0 {
|
|
||||||
fmt.Println("Simulating a write failure every", simulatedFailureCount, "transactions")
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx, cancel := context.WithCancel(context.Background())
|
|
||||||
defer cancel()
|
|
||||||
|
|
||||||
events, err := network.ChaincodeEvents(ctx, chaincodeName(),
|
|
||||||
client.WithStartBlock(startBlock),
|
|
||||||
client.WithCheckpoint(checkpointer),
|
|
||||||
)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to start chaincode event subscription: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
eventCount := 0
|
|
||||||
for event := range events {
|
|
||||||
if simulatedFailureCount > 0 {
|
|
||||||
eventCount++
|
|
||||||
if eventCount >= simulatedFailureCount {
|
|
||||||
eventCount = 0
|
|
||||||
return &ExpectedError{Message: "Simulated write failure"}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fmt.Printf("Chaincode event: BlockNumber=%d TxID=%s Name=%s Payload=%s\n",
|
|
||||||
event.BlockNumber, event.TransactionID, event.EventName, string(event.Payload))
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func getSimulatedFailureCount() (int, error) {
|
|
||||||
value := os.Getenv("SIMULATED_FAILURE_COUNT")
|
|
||||||
if value == "" {
|
|
||||||
return 0, nil
|
|
||||||
}
|
|
||||||
count, err := strconv.Atoi(value)
|
|
||||||
if err != nil || count < 0 {
|
|
||||||
return 0, fmt.Errorf("invalid SIMULATED_FAILURE_COUNT value: %s", value)
|
|
||||||
}
|
|
||||||
return count, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// cmdRead reads and prints a single asset from the ledger.
|
|
||||||
// Arguments: <assetId>
|
|
||||||
func cmdRead(gw *client.Gateway, args []string) error {
|
|
||||||
if len(args) < 1 {
|
|
||||||
return fmt.Errorf("arguments: <assetId>")
|
|
||||||
}
|
|
||||||
|
|
||||||
network := gw.GetNetwork(channelName())
|
|
||||||
contract := network.GetContract(chaincodeName())
|
|
||||||
|
|
||||||
smartContract := NewAssetTransfer(contract)
|
|
||||||
asset, err := smartContract.ReadAsset(args[0])
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
data, err := json.MarshalIndent(asset, "", " ")
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("failed to marshal asset: %w", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fmt.Println(string(data))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
colors = []string{"red", "green", "blue"}
|
|
||||||
maxInitialSize = 10
|
|
||||||
maxInitialVal = 1000
|
|
||||||
)
|
|
||||||
|
|
||||||
// cmdTransact runs a batch of concurrent create/update/delete transactions to demonstrate
|
|
||||||
func cmdTransact(gw *client.Gateway, _ []string) error {
|
|
||||||
network := gw.GetNetwork(channelName())
|
|
||||||
contract := network.GetContract(chaincodeName())
|
|
||||||
|
|
||||||
smartContract := NewAssetTransfer(contract)
|
|
||||||
app := &transactApp{smartContract: smartContract, batchSize: 6}
|
|
||||||
return app.run()
|
|
||||||
}
|
|
||||||
|
|
||||||
type transactApp struct {
|
|
||||||
smartContract *AssetTransfer
|
|
||||||
batchSize int
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *transactApp) run() error {
|
|
||||||
var wg sync.WaitGroup
|
|
||||||
errCh := make(chan error, a.batchSize)
|
|
||||||
|
|
||||||
for i := 0; i < a.batchSize; i++ {
|
|
||||||
wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
defer wg.Done()
|
|
||||||
if err := a.transact(); err != nil {
|
|
||||||
errCh <- err
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
}
|
|
||||||
|
|
||||||
wg.Wait()
|
|
||||||
close(errCh)
|
|
||||||
|
|
||||||
var failures []string
|
|
||||||
for err := range errCh {
|
|
||||||
failures = append(failures, err.Error())
|
|
||||||
}
|
|
||||||
if len(failures) > 0 {
|
|
||||||
return fmt.Errorf("%d failures:\n- %s", len(failures), strings.Join(failures, "\n- "))
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *transactApp) transact() error {
|
|
||||||
asset := a.newAsset()
|
|
||||||
|
|
||||||
if err := a.smartContract.CreateAsset(asset); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Printf("Created asset %s\n", asset.ID)
|
|
||||||
|
|
||||||
if randomInt(2) == 0 {
|
|
||||||
oldColor := asset.Color
|
|
||||||
asset.Color = differentElement(colors, oldColor)
|
|
||||||
if err := a.smartContract.UpdateAsset(asset); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Printf("Updated color of asset %s from %s to %s\n", asset.ID, oldColor, asset.Color)
|
|
||||||
}
|
|
||||||
|
|
||||||
if randomInt(4) == 0 {
|
|
||||||
if err := a.smartContract.DeleteAsset(asset.ID); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
fmt.Printf("Deleted asset %s\n", asset.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (a *transactApp) newAsset() Asset {
|
|
||||||
return Asset{
|
|
||||||
ID: randomHexString(8),
|
|
||||||
Color: randomElement(colors),
|
|
||||||
Size: randomInt(maxInitialSize) + 1,
|
|
||||||
AppraisedValue: float64(randomInt(maxInitialVal) + 1),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func randomHexString(length int) string {
|
|
||||||
b := make([]byte, (length+1)/2)
|
|
||||||
if _, err := rand.Read(b); err != nil {
|
|
||||||
panic(fmt.Sprintf("failed to generate random bytes: %v", err))
|
|
||||||
}
|
|
||||||
return hex.EncodeToString(b)[:length]
|
|
||||||
}
|
|
||||||
|
|
||||||
func randomInt(max int) int {
|
|
||||||
n, err := rand.Int(rand.Reader, big.NewInt(int64(max)))
|
|
||||||
if err != nil {
|
|
||||||
panic(fmt.Sprintf("failed to generate random int: %v", err))
|
|
||||||
}
|
|
||||||
return int(n.Int64())
|
|
||||||
}
|
|
||||||
|
|
||||||
func randomElement(values []string) string {
|
|
||||||
return values[randomInt(len(values))]
|
|
||||||
}
|
|
||||||
|
|
||||||
func differentElement(values []string, currentValue string) string {
|
|
||||||
var candidates []string
|
|
||||||
for _, v := range values {
|
|
||||||
if v != currentValue {
|
|
||||||
candidates = append(candidates, v)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return randomElement(candidates)
|
|
||||||
}
|
|
||||||
|
|
||||||
// cmdTransfer transfers ownership of an asset to a new owner in a different organisation.
|
|
||||||
// Arguments: <assetId> <ownerName> <ownerMspId>
|
|
||||||
func cmdTransfer(gw *client.Gateway, args []string) error {
|
|
||||||
if len(args) < 3 {
|
|
||||||
return fmt.Errorf("arguments: <assetId> <ownerName> <ownerMspId>")
|
|
||||||
}
|
|
||||||
|
|
||||||
network := gw.GetNetwork(channelName())
|
|
||||||
contract := network.GetContract(chaincodeName())
|
|
||||||
|
|
||||||
smartContract := NewAssetTransfer(contract)
|
|
||||||
return smartContract.TransferAsset(args[0], args[1], args[2])
|
|
||||||
}
|
|
||||||
|
|
@ -25,20 +25,6 @@ func tlsCertPath() string {
|
||||||
return os.Getenv("TLS_CERT")
|
return os.Getenv("TLS_CERT")
|
||||||
}
|
}
|
||||||
|
|
||||||
func channelName() string {
|
|
||||||
if v := os.Getenv("CHANNEL_NAME"); v != "" {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
return "mychannel"
|
|
||||||
}
|
|
||||||
|
|
||||||
func chaincodeName() string {
|
|
||||||
if v := os.Getenv("CHAINCODE_NAME"); v != "" {
|
|
||||||
return v
|
|
||||||
}
|
|
||||||
return "asset-transfer"
|
|
||||||
}
|
|
||||||
|
|
||||||
func hostAlias() string {
|
func hostAlias() string {
|
||||||
return os.Getenv("HOST_ALIAS")
|
return os.Getenv("HOST_ALIAS")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -5,11 +5,13 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"sort"
|
"sort"
|
||||||
|
|
||||||
|
"trader-go/commands"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
if err := run(); err != nil {
|
if err := run(); err != nil {
|
||||||
var expectedErr *ExpectedError
|
var expectedErr *commands.ExpectedError
|
||||||
if errors.As(err, &expectedErr) {
|
if errors.As(err, &expectedErr) {
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -29,7 +31,7 @@ func run() error {
|
||||||
commandName := args[0]
|
commandName := args[0]
|
||||||
commandArgs := args[1:]
|
commandArgs := args[1:]
|
||||||
|
|
||||||
command, ok := commands[commandName]
|
command, ok := commands.Commands[commandName]
|
||||||
if !ok {
|
if !ok {
|
||||||
printUsage()
|
printUsage()
|
||||||
return fmt.Errorf("unknown command: %s", commandName)
|
return fmt.Errorf("unknown command: %s", commandName)
|
||||||
|
|
@ -51,8 +53,8 @@ func run() error {
|
||||||
}
|
}
|
||||||
|
|
||||||
func printUsage() {
|
func printUsage() {
|
||||||
names := make([]string, 0, len(commands))
|
names := make([]string, 0, len(commands.Commands))
|
||||||
for name := range commands {
|
for name := range commands.Commands {
|
||||||
names = append(names, name)
|
names = append(names, name)
|
||||||
}
|
}
|
||||||
sort.Strings(names)
|
sort.Strings(names)
|
||||||
|
|
|
||||||
|
|
@ -101,7 +101,7 @@ operator-crds: check-kube
|
||||||
###############################################################################
|
###############################################################################
|
||||||
|
|
||||||
# Run e2e tests of all scenarios
|
# Run e2e tests of all scenarios
|
||||||
test: test-chaincode test-appdev test-cloud # test-ansible
|
test: test-chaincode test-appdev test-appdev-go test-cloud # test-ansible
|
||||||
|
|
||||||
# Run an e2e test of the SmartContractDev scenario
|
# Run an e2e test of the SmartContractDev scenario
|
||||||
test-chaincode:
|
test-chaincode:
|
||||||
|
|
@ -111,6 +111,10 @@ test-chaincode:
|
||||||
test-appdev:
|
test-appdev:
|
||||||
tests/10-appdev-e2e.sh
|
tests/10-appdev-e2e.sh
|
||||||
|
|
||||||
|
# Run an e2e test of the ApplicationDev Go scenario
|
||||||
|
test-appdev-go:
|
||||||
|
tests/10-appdev-e2e-go.sh
|
||||||
|
|
||||||
# Run an e2e test of the CloudNative scenario
|
# Run an e2e test of the CloudNative scenario
|
||||||
test-cloud:
|
test-cloud:
|
||||||
tests/20-cloud-e2e.sh
|
tests/20-cloud-e2e.sh
|
||||||
|
|
|
||||||
62
full-stack-asset-transfer-guide/tests/10-appdev-e2e-go.sh
Executable file
62
full-stack-asset-transfer-guide/tests/10-appdev-e2e-go.sh
Executable file
|
|
@ -0,0 +1,62 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
|
#
|
||||||
|
# Copyright contributors to the Hyperledgendary Full Stack Asset Transfer project
|
||||||
|
#
|
||||||
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
|
#
|
||||||
|
|
||||||
|
set -v -eou pipefail
|
||||||
|
|
||||||
|
# All tests run in the workshop root folder
|
||||||
|
cd "$(dirname "$0")"/..
|
||||||
|
|
||||||
|
export WORKSHOP_PATH="${PWD}"
|
||||||
|
export PATH="${WORKSHOP_PATH}/bin:${PATH}"
|
||||||
|
export FABRIC_CFG_PATH="${WORKSHOP_PATH}/config"
|
||||||
|
|
||||||
|
"${WORKSHOP_PATH}/check.sh"
|
||||||
|
|
||||||
|
CHAINCODE_PID=
|
||||||
|
|
||||||
|
function exitHook() {
|
||||||
|
|
||||||
|
# shut down the chaincode container/process
|
||||||
|
[ -n "${CHAINCODE_PID}" ] && kill "${CHAINCODE_PID}"
|
||||||
|
|
||||||
|
# Shut down microfab
|
||||||
|
docker kill microfab &> /dev/null
|
||||||
|
|
||||||
|
# Delete the network configuration and crypto material
|
||||||
|
rm -rf "${WORKSHOP_PATH}"/_cfg
|
||||||
|
}
|
||||||
|
|
||||||
|
trap exitHook SIGINT SIGTERM EXIT
|
||||||
|
|
||||||
|
just microfab
|
||||||
|
|
||||||
|
source "${WORKSHOP_PATH}/_cfg/uf/org1admin.env"
|
||||||
|
just debugcc
|
||||||
|
|
||||||
|
source "${WORKSHOP_PATH}/_cfg/uf/org1admin.env"
|
||||||
|
cd "${WORKSHOP_PATH}/contracts/asset-transfer-typescript"
|
||||||
|
npm install
|
||||||
|
npm run build
|
||||||
|
node_modules/.bin/fabric-chaincode-node server --chaincode-address="${CHAINCODE_SERVER_ADDRESS}" --chaincode-id="${CHAINCODE_ID}" &
|
||||||
|
CHAINCODE_PID=$!
|
||||||
|
|
||||||
|
sleep 5
|
||||||
|
|
||||||
|
cd "${WORKSHOP_PATH}/applications/trader-go"
|
||||||
|
export ENDPOINT=org1peer-api.127-0-0-1.nip.io:8080
|
||||||
|
export MSP_ID=org1MSP
|
||||||
|
export CERTIFICATE=../../_cfg/uf/_msp/org1/org1admin/msp/signcerts/cert.pem
|
||||||
|
export PRIVATE_KEY=../../_cfg/uf/_msp/org1/org1admin/msp/keystore/cert_sk
|
||||||
|
go build -o trader
|
||||||
|
./trader getAllAssets
|
||||||
|
./trader transact
|
||||||
|
./trader getAllAssets
|
||||||
|
./trader create banana bananaman yellow
|
||||||
|
./trader read banana
|
||||||
|
./trader delete banana
|
||||||
|
SIMULATED_FAILURE_COUNT=2 ./trader listen
|
||||||
Loading…
Reference in a new issue