diff --git a/asset-transfer-events/application-gateway-go/connect.go b/asset-transfer-events/application-gateway-go/connect.go index 7b21d8b1..bc707109 100755 --- a/asset-transfer-events/application-gateway-go/connect.go +++ b/asset-transfer-events/application-gateway-go/connect.go @@ -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) diff --git a/off_chain_data/application-go/app.go b/off_chain_data/application-go/app.go index 2a8339f1..fa49de02 100644 --- a/off_chain_data/application-go/app.go +++ b/off_chain_data/application-go/app.go @@ -1,9 +1,3 @@ -/* - * Copyright 2024 IBM All Rights Reserved. - * - * SPDX-License-Identifier: Apache-2.0 - */ - package main import ( @@ -15,7 +9,9 @@ import ( "google.golang.org/grpc" ) -var allCommands = map[string]func(*grpc.ClientConn){ +type command func(grpc.ClientConnInterface) error + +var allCommands = map[string]command{ "getAllAssets": getAllAssets, "transact": transact, "listen": listen, @@ -41,7 +37,10 @@ func main() { for _, name := range commands { command := allCommands[name] - command(client) + + if err := command(client); err != nil { + panic(err) + } } } diff --git a/off_chain_data/application-go/connect.go b/off_chain_data/application-go/connect.go index ab979075..d4566132 100644 --- a/off_chain_data/application-go/connect.go +++ b/off_chain_data/application-go/connect.go @@ -1,9 +1,3 @@ -/* - * Copyright 2024 IBM All Rights Reserved. - * - * SPDX-License-Identifier: Apache-2.0 - */ - package main import ( @@ -69,7 +63,7 @@ func newGrpcConnection() *grpc.ClientConn { return connection } -func newConnectOptions(clientConnection *grpc.ClientConn) (identity.Identity, []client.ConnectOption) { +func newConnectOptions(clientConnection grpc.ClientConnInterface) (identity.Identity, []client.ConnectOption) { return newIdentity(), []client.ConnectOption{ client.WithSign(newSign()), client.WithHash(hash.SHA256), diff --git a/off_chain_data/application-go/contract/contract.go b/off_chain_data/application-go/contract/contract.go index c63ae10d..433343d2 100644 --- a/off_chain_data/application-go/contract/contract.go +++ b/off_chain_data/application-go/contract/contract.go @@ -1,12 +1,7 @@ -/* - * Copyright 2024 IBM All Rights Reserved. - * - * SPDX-License-Identifier: Apache-2.0 - */ package contract import ( - "fmt" + "encoding/json" "strconv" "github.com/hyperledger/fabric-gateway/pkg/client" @@ -30,7 +25,7 @@ func (atb *AssetTransferBasic) CreateAsset(anAsset Asset) error { anAsset.Owner, strconv.FormatUint(anAsset.AppraisedValue, 10), )); err != nil { - return fmt.Errorf("in CreateAsset: %w", err) + return err } return nil } @@ -44,7 +39,7 @@ func (atb *AssetTransferBasic) TransferAsset(id, newOwner string) (string, error ), ) if err != nil { - return "", fmt.Errorf("in TransferAsset: %w", err) + return "", err } return string(result), nil @@ -57,15 +52,25 @@ func (atb *AssetTransferBasic) DeleteAsset(id string) error { id, ), ); err != nil { - return fmt.Errorf("in DeleteAsset: %w", err) + return err } return nil } -func (atb *AssetTransferBasic) GetAllAssets() ([]byte, error) { - result, err := atb.contract.Evaluate("GetAllAssets") +func (atb *AssetTransferBasic) GetAllAssets() ([]Asset, error) { + assetsRaw, err := atb.contract.Evaluate("GetAllAssets") if err != nil { - return nil, fmt.Errorf("in GetAllAssets: %w", err) + return nil, err } - return result, nil + + if len(assetsRaw) == 0 { + return []Asset{}, nil + } + + var assets []Asset + if err := json.Unmarshal(assetsRaw, &assets); err != nil { + return nil, err + } + + return assets, nil } diff --git a/off_chain_data/application-go/contract/model.go b/off_chain_data/application-go/contract/model.go index e159475d..a3dece53 100644 --- a/off_chain_data/application-go/contract/model.go +++ b/off_chain_data/application-go/contract/model.go @@ -1,8 +1,3 @@ -/* - * Copyright 2024 IBM All Rights Reserved. - * - * SPDX-License-Identifier: Apache-2.0 - */ package contract type Asset struct { diff --git a/off_chain_data/application-go/getAllAssets.go b/off_chain_data/application-go/getAllAssets.go index 10e2d545..4668a886 100644 --- a/off_chain_data/application-go/getAllAssets.go +++ b/off_chain_data/application-go/getAllAssets.go @@ -1,13 +1,6 @@ -/* - * Copyright 2024 IBM All Rights Reserved. - * - * SPDX-License-Identifier: Apache-2.0 - */ - package main import ( - "bytes" "encoding/json" "fmt" atb "offchaindata/contract" @@ -16,11 +9,11 @@ import ( "google.golang.org/grpc" ) -func getAllAssets(clientConnection *grpc.ClientConn) { +func getAllAssets(clientConnection grpc.ClientConnInterface) error { id, options := newConnectOptions(clientConnection) gateway, err := client.Connect(id, options...) if err != nil { - panic(err) + return err } defer gateway.Close() @@ -28,21 +21,15 @@ func getAllAssets(clientConnection *grpc.ClientConn) { smartContract := atb.NewAssetTransferBasic(contract) assets, err := smartContract.GetAllAssets() if err != nil { - panic(err) + return err } - if len(assets) == 0 { - fmt.Println("no assets") - return + assetsJSONformatted, err := json.MarshalIndent(assets, "", " ") + if err != nil { + return err } - fmt.Println(formatJSON(assets)) -} - -func formatJSON(data []byte) string { - var result bytes.Buffer - if err := json.Indent(&result, data, "", " "); err != nil { - panic(fmt.Errorf("failed to parse JSON: %w", err)) - } - return result.String() + fmt.Println(assetsJSONformatted) + + return nil } diff --git a/off_chain_data/application-go/listen.go b/off_chain_data/application-go/listen.go index 05192bed..81ef6830 100644 --- a/off_chain_data/application-go/listen.go +++ b/off_chain_data/application-go/listen.go @@ -14,11 +14,11 @@ import ( "google.golang.org/grpc" ) -func listen(clientConnection *grpc.ClientConn) { +func listen(clientConnection grpc.ClientConnInterface) error { id, options := newConnectOptions(clientConnection) gateway, err := client.Connect(id, options...) if err != nil { - panic(err) + return err } defer func() { gateway.Close() @@ -28,7 +28,7 @@ func listen(clientConnection *grpc.ClientConn) { checkpointFile := envOrDefault("CHECKPOINT_FILE", "checkpoint.json") checkpointer, err := client.NewFileCheckpointer(checkpointFile) if err != nil { - panic(err) + return err } defer func() { checkpointer.Close() @@ -57,7 +57,7 @@ func listen(clientConnection *grpc.ClientConn) { client.WithCheckpoint(checkpointer), ) if err != nil { - panic(err) + return err } for blockProto := range blocks { @@ -69,12 +69,12 @@ func listen(clientConnection *grpc.ClientConn) { } if err := aBlockProcessor.process(); err != nil { - fmt.Println("\033[31m[ERROR]\033[0m", err) - return + return err } } fmt.Println("\nShutting down listener gracefully...") + return nil } type blockProcessor struct { @@ -85,39 +85,37 @@ type blockProcessor struct { } func (b *blockProcessor) process() error { - funcName := "Process" - fmt.Println("\nReceived block", b.parsedBlock.Number()) validTransactions, err := b.validTransactions() if err != nil { - return fmt.Errorf("in %s: %w", funcName, err) + return err } for _, validTransaction := range validTransactions { - aTransaction := transactionProcessor{ + txProcessor := transactionProcessor{ b.parsedBlock.Number(), validTransaction, - // TODO use pointer to parent and get blockNumber, store and channelName from parent + // TODO use reference to parent and get blockNumber, store and channelName from parent b.writeToStore, b.channelName, } - if err := aTransaction.process(); err != nil { - return fmt.Errorf("in %s: %w", funcName, err) + if err := txProcessor.process(); err != nil { + return err } channelHeader, err := validTransaction.ChannelHeader() if err != nil { - return fmt.Errorf("in %s: %w", funcName, err) + return err } transactionID := channelHeader.GetTxId() if err := b.checkpointer.CheckpointTransaction(b.parsedBlock.Number(), transactionID); err != nil { - return fmt.Errorf("in %s: %w", funcName, err) + return err } } if err := b.checkpointer.CheckpointBlock(b.parsedBlock.Number()); err != nil { - return fmt.Errorf("in %s: %w", funcName, err) + return err } return nil @@ -127,7 +125,7 @@ func (b *blockProcessor) validTransactions() ([]*parser.Transaction, error) { result := []*parser.Transaction{} newTransactions, err := b.getNewTransactions() if err != nil { - return nil, fmt.Errorf("in validTransactions: %w", err) + return nil, err } for _, transaction := range newTransactions { @@ -139,11 +137,9 @@ func (b *blockProcessor) validTransactions() ([]*parser.Transaction, error) { } func (b *blockProcessor) getNewTransactions() ([]*parser.Transaction, error) { - funcName := "getNewTransactions" - transactions, err := b.parsedBlock.Transactions() if err != nil { - return nil, fmt.Errorf("in %s: %w", funcName, err) + return nil, err } lastTransactionID := b.checkpointer.TransactionID() @@ -155,24 +151,22 @@ func (b *blockProcessor) getNewTransactions() ([]*parser.Transaction, error) { // Ignore transactions up to the last processed transaction ID lastProcessedIndex, err := b.findLastProcessedIndex() if err != nil { - return nil, fmt.Errorf("in %s: %w", funcName, err) + return nil, err } return transactions[lastProcessedIndex+1:], nil } func (b *blockProcessor) findLastProcessedIndex() (int, error) { - funcName := "findLastProcessedIndex" - transactions, err := b.parsedBlock.Transactions() if err != nil { - return 0, fmt.Errorf("in %s: %w", funcName, err) + return 0, err } blockTransactionIDs := []string{} for _, transaction := range transactions { channelHeader, err := transaction.ChannelHeader() if err != nil { - return 0, fmt.Errorf("in %s: %w", funcName, err) + return 0, err } blockTransactionIDs = append(blockTransactionIDs, channelHeader.GetTxId()) } @@ -204,17 +198,15 @@ type transactionProcessor struct { } func (t *transactionProcessor) process() error { - funcName := "process" - channelHeader, err := t.transaction.ChannelHeader() if err != nil { - return fmt.Errorf("in %s: %w", funcName, err) + return err } transactionID := channelHeader.GetTxId() writes, err := t.writes() if err != nil { - return fmt.Errorf("in %s: %w", funcName, err) + return err } if len(writes) == 0 { @@ -229,25 +221,24 @@ func (t *transactionProcessor) process() error { TransactionID: transactionID, Writes: writes, }); err != nil { - return fmt.Errorf("in %s: %w", funcName, err) + return err } return nil } func (t *transactionProcessor) writes() ([]write, error) { - funcName := "writes" // TODO this entire code should live in the parser and just return the kvWrite which // we then map to write and return channelHeader, err := t.transaction.ChannelHeader() if err != nil { - return nil, fmt.Errorf("in %s: %w", funcName, err) + return nil, err } t.channelName = channelHeader.GetChannelId() nsReadWriteSets, err := t.transaction.NamespaceReadWriteSets() if err != nil { - return nil, fmt.Errorf("in %s: %w", funcName, err) + return nil, err } nonSystemCCReadWriteSets := []*parser.NamespaceReadWriteSet{} @@ -263,7 +254,7 @@ func (t *transactionProcessor) writes() ([]write, error) { kvReadWriteSet, err := readWriteSet.ReadWriteSet() if err != nil { - return nil, fmt.Errorf("in %s: %w", funcName, err) + return nil, err } for _, kvWrite := range kvReadWriteSet.GetWrites() { diff --git a/off_chain_data/application-go/parser/block.go b/off_chain_data/application-go/parser/block.go index 8a05e9e7..7d0458e3 100644 --- a/off_chain_data/application-go/parser/block.go +++ b/off_chain_data/application-go/parser/block.go @@ -1,19 +1,21 @@ package parser import ( - "fmt" + "sync" "github.com/hyperledger/fabric-protos-go-apiv2/common" "google.golang.org/protobuf/proto" ) type Block struct { - block *common.Block - cachedTransactions []*Transaction + block *common.Block + transactions func() ([]*Transaction, error) } func ParseBlock(block *common.Block) *Block { - return &Block{block, nil} + result := &Block{block, nil} + result.transactions = sync.OnceValues(result.unmarshalTransactions) + return result } func (b *Block) Number() uint64 { @@ -21,37 +23,34 @@ func (b *Block) Number() uint64 { } func (b *Block) Transactions() ([]*Transaction, error) { - if b.cachedTransactions != nil { - return b.cachedTransactions, nil - } + return b.transactions() +} - funcName := "Transactions" - envelopes, err := b.unmarshalEnvelopesFromBlockData() +func (b *Block) unmarshalTransactions() ([]*Transaction, error) { + envelopes, err := b.unmarshalEnvelopes() if err != nil { - return nil, fmt.Errorf("in %s: %w", funcName, err) + return nil, err } commonPayloads, err := b.unmarshalPayloadsFrom(envelopes) if err != nil { - return nil, fmt.Errorf("in %s: %w", funcName, err) + return nil, err } payloads, err := b.parse(commonPayloads) if err != nil { - return nil, fmt.Errorf("in %s: %w", funcName, err) + return nil, err } - b.cachedTransactions = b.createTransactionsFrom(payloads) - - return b.cachedTransactions, nil + return b.createTransactionsFrom(payloads), nil } -func (b *Block) unmarshalEnvelopesFromBlockData() ([]*common.Envelope, error) { +func (b *Block) unmarshalEnvelopes() ([]*common.Envelope, error) { result := []*common.Envelope{} for _, blockData := range b.block.GetData().GetData() { envelope := &common.Envelope{} if err := proto.Unmarshal(blockData, envelope); err != nil { - return nil, fmt.Errorf("in unmarshalEnvelopesFromBlockData: %w", err) + return nil, err } result = append(result, envelope) } @@ -63,7 +62,7 @@ func (*Block) unmarshalPayloadsFrom(envelopes []*common.Envelope) ([]*common.Pay for _, envelope := range envelopes { commonPayload := &common.Payload{} if err := proto.Unmarshal(envelope.GetPayload(), commonPayload); err != nil { - return nil, fmt.Errorf("in unmarshalPayloadsFrom: %w", err) + return nil, err } result = append(result, commonPayload) } @@ -71,8 +70,6 @@ func (*Block) unmarshalPayloadsFrom(envelopes []*common.Envelope) ([]*common.Pay } func (b *Block) parse(commonPayloads []*common.Payload) ([]*payload, error) { - funcName := "parse" - validationCodes := b.block.GetMetadata().GetMetadata()[common.BlockMetadataIndex_TRANSACTIONS_FILTER] result := []*payload{} @@ -82,7 +79,7 @@ func (b *Block) parse(commonPayloads []*common.Payload) ([]*payload, error) { payload := parsePayload(commonPayload, int32(statusCode)) is, err := payload.isEndorserTransaction() if err != nil { - return nil, fmt.Errorf("in %s: %w", funcName, err) + return nil, err } if is { result = append(result, payload) diff --git a/off_chain_data/application-go/parser/block_test.go b/off_chain_data/application-go/parser/block_test.go index 3581b35e..2b3ca504 100644 --- a/off_chain_data/application-go/parser/block_test.go +++ b/off_chain_data/application-go/parser/block_test.go @@ -10,7 +10,6 @@ import ( "github.com/hyperledger/fabric-protos-go-apiv2/ledger/rwset/kvrwset" "github.com/hyperledger/fabric-protos-go-apiv2/peer" "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/reflect/protoreflect" ) func Test_GetReadWriteSetsFromEndorserTransaction(t *testing.T) { @@ -128,7 +127,7 @@ func nsReadWriteSetFake() (*rwset.NsReadWriteSet, string, atb.Asset) { return result, expectedNamespace, expectedAsset } -func protoMarshalOrPanic(v protoreflect.ProtoMessage) []byte { +func protoMarshalOrPanic(v proto.Message) []byte { result, err := proto.Marshal(v) if err != nil { panic(err) diff --git a/off_chain_data/application-go/parser/endorserTransaction.go b/off_chain_data/application-go/parser/endorserTransaction.go index 9df89f1c..cdedef80 100644 --- a/off_chain_data/application-go/parser/endorserTransaction.go +++ b/off_chain_data/application-go/parser/endorserTransaction.go @@ -1,7 +1,7 @@ package parser import ( - "fmt" + "sync" "github.com/hyperledger/fabric-protos-go-apiv2/ledger/rwset" "github.com/hyperledger/fabric-protos-go-apiv2/peer" @@ -9,49 +9,43 @@ import ( ) type endorserTransaction struct { - transaction *peer.Transaction - cachedReadWriteSets []*readWriteSet + transaction *peer.Transaction + readWriteSets func() ([]*readWriteSet, error) } func parseEndorserTransaction(transaction *peer.Transaction) *endorserTransaction { - return &endorserTransaction{transaction, nil} + result := &endorserTransaction{transaction, nil} + result.readWriteSets = sync.OnceValues(result.unmarshalReadWriteSets) + return result } -func (p *endorserTransaction) readWriteSets() ([]*readWriteSet, error) { - funcName := "readWriteSets" - - if p.cachedReadWriteSets != nil { - return p.cachedReadWriteSets, nil - } - +func (p *endorserTransaction) unmarshalReadWriteSets() ([]*readWriteSet, error) { chaincodeActionPayloads, err := p.unmarshalChaincodeActionPayloads() if err != nil { - return nil, fmt.Errorf("in %s: %w", funcName, err) + return nil, err } chaincodeEndorsedActions, err := p.extractChaincodeEndorsedActionsFrom(chaincodeActionPayloads) if err != nil { - return nil, fmt.Errorf("in %s: %w", funcName, err) + return nil, err } proposalResponsePayloads, err := p.unmarshalProposalResponsePayloadsFrom(chaincodeEndorsedActions) if err != nil { - return nil, fmt.Errorf("in %s: %w", funcName, err) + return nil, err } chaincodeActions, err := p.unmarshalChaincodeActionsFrom(proposalResponsePayloads) if err != nil { - return nil, fmt.Errorf("in %s: %w", funcName, err) + return nil, err } txReadWriteSets, err := p.unmarshalTxReadWriteSetsFrom(chaincodeActions) if err != nil { - return nil, fmt.Errorf("in %s: %w", funcName, err) + return nil, err } - p.cachedReadWriteSets = p.parseReadWriteSets(txReadWriteSets) - - return p.cachedReadWriteSets, nil + return p.parseReadWriteSets(txReadWriteSets), nil } func (p *endorserTransaction) unmarshalChaincodeActionPayloads() ([]*peer.ChaincodeActionPayload, error) { @@ -59,7 +53,7 @@ func (p *endorserTransaction) unmarshalChaincodeActionPayloads() ([]*peer.Chainc for _, transactionAction := range p.transaction.GetActions() { chaincodeActionPayload := &peer.ChaincodeActionPayload{} if err := proto.Unmarshal(transactionAction.GetPayload(), chaincodeActionPayload); err != nil { - return nil, fmt.Errorf("in unmarshalChaincodeActionPayloads: %w", err) + return nil, err } result = append(result, chaincodeActionPayload) @@ -80,7 +74,7 @@ func (*endorserTransaction) unmarshalProposalResponsePayloadsFrom(chaincodeEndor for _, endorsedAction := range chaincodeEndorsedActions { proposalResponsePayload := &peer.ProposalResponsePayload{} if err := proto.Unmarshal(endorsedAction.GetProposalResponsePayload(), proposalResponsePayload); err != nil { - return nil, fmt.Errorf("in unmarshalProposalResponsePayloadsFrom: %w", err) + return nil, err } result = append(result, proposalResponsePayload) } @@ -92,7 +86,7 @@ func (*endorserTransaction) unmarshalChaincodeActionsFrom(proposalResponsePayloa for _, proposalResponsePayload := range proposalResponsePayloads { chaincodeAction := &peer.ChaincodeAction{} if err := proto.Unmarshal(proposalResponsePayload.GetExtension(), chaincodeAction); err != nil { - return nil, fmt.Errorf("in unmarshalChaincodeActionsFrom: %w", err) + return nil, err } result = append(result, chaincodeAction) } @@ -104,7 +98,7 @@ func (*endorserTransaction) unmarshalTxReadWriteSetsFrom(chaincodeActions []*pee for _, chaincodeAction := range chaincodeActions { txReadWriteSet := &rwset.TxReadWriteSet{} if err := proto.Unmarshal(chaincodeAction.GetResults(), txReadWriteSet); err != nil { - return nil, fmt.Errorf("in unmarshalTxReadWriteSetsFrom: %w", err) + return nil, err } result = append(result, txReadWriteSet) } diff --git a/off_chain_data/application-go/parser/namespaceReadWriteSet.go b/off_chain_data/application-go/parser/namespaceReadWriteSet.go index a1f0541f..ce108d4a 100644 --- a/off_chain_data/application-go/parser/namespaceReadWriteSet.go +++ b/off_chain_data/application-go/parser/namespaceReadWriteSet.go @@ -1,7 +1,7 @@ package parser import ( - "fmt" + "sync" "github.com/hyperledger/fabric-protos-go-apiv2/ledger/rwset" "github.com/hyperledger/fabric-protos-go-apiv2/ledger/rwset/kvrwset" @@ -9,12 +9,14 @@ import ( ) type NamespaceReadWriteSet struct { - nsReadWriteSet *rwset.NsReadWriteSet - cachedReadWriteSet *kvrwset.KVRWSet + nsReadWriteSet *rwset.NsReadWriteSet + readWriteSet func() (*kvrwset.KVRWSet, error) } func parseNamespaceReadWriteSet(nsRwSet *rwset.NsReadWriteSet) *NamespaceReadWriteSet { - return &NamespaceReadWriteSet{nsRwSet, nil} + result := &NamespaceReadWriteSet{nsRwSet, nil} + result.readWriteSet = sync.OnceValues(result.unmarshalReadWriteSet) + return result } func (p *NamespaceReadWriteSet) Namespace() string { @@ -22,14 +24,14 @@ func (p *NamespaceReadWriteSet) Namespace() string { } func (p *NamespaceReadWriteSet) ReadWriteSet() (*kvrwset.KVRWSet, error) { - if p.cachedReadWriteSet != nil { - return p.cachedReadWriteSet, nil - } - - p.cachedReadWriteSet = &kvrwset.KVRWSet{} - if err := proto.Unmarshal(p.nsReadWriteSet.GetRwset(), p.cachedReadWriteSet); err != nil { - return nil, fmt.Errorf("in ReadWriteSet: %w", err) - } - - return p.cachedReadWriteSet, nil + return p.readWriteSet() +} + +func (p *NamespaceReadWriteSet) unmarshalReadWriteSet() (*kvrwset.KVRWSet, error) { + result := &kvrwset.KVRWSet{} + if err := proto.Unmarshal(p.nsReadWriteSet.GetRwset(), result); err != nil { + return nil, err + } + + return result, nil } diff --git a/off_chain_data/application-go/parser/payload.go b/off_chain_data/application-go/parser/payload.go index 77c58eda..a989a296 100644 --- a/off_chain_data/application-go/parser/payload.go +++ b/off_chain_data/application-go/parser/payload.go @@ -2,6 +2,7 @@ package parser import ( "fmt" + "sync" "github.com/hyperledger/fabric-protos-go-apiv2/common" "github.com/hyperledger/fabric-protos-go-apiv2/peer" @@ -9,46 +10,42 @@ import ( ) type payload struct { - commonPayload *common.Payload - statusCode int32 - cachedChannelHeader *common.ChannelHeader + commonPayload *common.Payload + statusCode int32 + channelHeader func() (*common.ChannelHeader, error) } func parsePayload(commonPayload *common.Payload, statusCode int32) *payload { - return &payload{commonPayload, statusCode, nil} + result := &payload{commonPayload, statusCode, nil} + result.channelHeader = sync.OnceValues(result.unmarshalChannelHeader) + return result } -func (p *payload) channelHeader() (*common.ChannelHeader, error) { - if p.cachedChannelHeader != nil { - return p.cachedChannelHeader, nil +func (p *payload) unmarshalChannelHeader() (*common.ChannelHeader, error) { + result := &common.ChannelHeader{} + if err := proto.Unmarshal(p.commonPayload.GetHeader().GetChannelHeader(), result); err != nil { + return nil, err } - p.cachedChannelHeader = &common.ChannelHeader{} - if err := proto.Unmarshal(p.commonPayload.GetHeader().GetChannelHeader(), p.cachedChannelHeader); err != nil { - return nil, fmt.Errorf("in channelHeader: %w", err) - } - - return p.cachedChannelHeader, nil + return result, nil } func (p *payload) endorserTransaction() (*endorserTransaction, error) { - funcName := "endorserTransaction" - is, err := p.isEndorserTransaction() if err != nil { - return nil, fmt.Errorf("in %s: %w", funcName, err) + return nil, err } if !is { channelHeader, err := p.channelHeader() if err != nil { - return nil, fmt.Errorf("in %s: %w", funcName, err) + return nil, err } return nil, fmt.Errorf("unexpected payload type: %d", channelHeader.GetType()) } result := &peer.Transaction{} if err := proto.Unmarshal(p.commonPayload.GetData(), result); err != nil { - return nil, fmt.Errorf("in %s: %w", funcName, err) + return nil, err } return parseEndorserTransaction(result), nil @@ -57,7 +54,7 @@ func (p *payload) endorserTransaction() (*endorserTransaction, error) { func (p *payload) isEndorserTransaction() (bool, error) { channelHeader, err := p.channelHeader() if err != nil { - return false, fmt.Errorf("in isEndorserTransaction: %w", err) + return false, err } return channelHeader.GetType() == int32(common.HeaderType_ENDORSER_TRANSACTION), nil diff --git a/off_chain_data/application-go/parser/transaction.go b/off_chain_data/application-go/parser/transaction.go index 7bead914..ea120fd7 100644 --- a/off_chain_data/application-go/parser/transaction.go +++ b/off_chain_data/application-go/parser/transaction.go @@ -1,8 +1,6 @@ package parser import ( - "fmt" - "github.com/hyperledger/fabric-protos-go-apiv2/common" ) @@ -19,16 +17,14 @@ func (t *Transaction) ChannelHeader() (*common.ChannelHeader, error) { } func (t *Transaction) NamespaceReadWriteSets() ([]*NamespaceReadWriteSet, error) { - funcName := "NamespaceReadWriteSets" - endorserTransaction, err := t.payload.endorserTransaction() if err != nil { - return nil, fmt.Errorf("in %s: %w", funcName, err) + return nil, err } txReadWriteSets, err := endorserTransaction.readWriteSets() if err != nil { - return nil, fmt.Errorf("in %s: %w", funcName, err) + return nil, err } result := []*NamespaceReadWriteSet{} diff --git a/off_chain_data/application-go/store.go b/off_chain_data/application-go/store.go index 8cae9fed..ccc90e5d 100644 --- a/off_chain_data/application-go/store.go +++ b/off_chain_data/application-go/store.go @@ -4,7 +4,6 @@ import ( "encoding/json" "errors" "fmt" - "math" "os" "strconv" "strings" @@ -41,17 +40,15 @@ type write struct { // Apply writes for a given transaction to off-chain data store, ideally in a single operation for fault tolerance. // This implementation just writes to a file. func applyWritesToOffChainStore(data ledgerUpdate) error { - funcName := "applyWritesToOffChainStore" - if err := simulateFailureIfRequired(); err != nil { - return fmt.Errorf("in %s: %w", funcName, err) + return err } writes := []string{} for _, write := range data.Writes { marshaled, err := json.Marshal(write) if err != nil { - return fmt.Errorf("in %s: %w", funcName, err) + return err } writes = append(writes, string(marshaled)) @@ -59,16 +56,16 @@ func applyWritesToOffChainStore(data ledgerUpdate) error { f, err := os.OpenFile(storeFile, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) if err != nil { - return fmt.Errorf("in %s: %w", funcName, err) + return err } if _, err := f.Write([]byte(strings.Join(writes, "\n") + "\n")); err != nil { f.Close() - return fmt.Errorf("in %s: %w", funcName, err) + return err } if err := f.Close(); err != nil { - return fmt.Errorf("in %s: %w", funcName, err) + return err } return nil @@ -87,13 +84,8 @@ func simulateFailureIfRequired() error { func getSimulatedFailureCount() uint { valueAsString := envOrDefault("SIMULATED_FAILURE_COUNT", "0") - valueAsFloat, err := strconv.ParseFloat(valueAsString, 64) + result, err := strconv.ParseUint(valueAsString, 10, 0) if err != nil { - panic(err) - } - - result := math.Floor(valueAsFloat) - if valueAsFloat < 0 { panic(fmt.Errorf("invalid SIMULATED_FAILURE_COUNT value: %s", valueAsString)) } diff --git a/off_chain_data/application-go/transact.go b/off_chain_data/application-go/transact.go index 112ed51c..46afafdc 100644 --- a/off_chain_data/application-go/transact.go +++ b/off_chain_data/application-go/transact.go @@ -1,9 +1,9 @@ package main import ( - "crypto/rand" + "context" "fmt" - "math/big" + "math/rand/v2" "sync" atb "offchaindata/contract" @@ -15,11 +15,11 @@ import ( var owners = []string{"alice", "bob", "charlie"} -func transact(clientConnection *grpc.ClientConn) { +func transact(clientConnection grpc.ClientConnInterface) error { id, options := newConnectOptions(clientConnection) gateway, err := client.Connect(id, options...) if err != nil { - panic((err)) + return err } defer func() { gateway.Close() @@ -30,7 +30,11 @@ func transact(clientConnection *grpc.ClientConn) { smartContract := atb.NewAssetTransferBasic(contract) app := newTransactApp(smartContract) - app.run() + if err := app.run(); err != nil { + return err + } + + return nil } type transactApp struct { @@ -42,90 +46,94 @@ func newTransactApp(smartContract *atb.AssetTransferBasic) *transactApp { return &transactApp{smartContract, 10} } -func (t *transactApp) run() { +func (t *transactApp) run() error { + ctx, cancel := context.WithCancelCause(context.Background()) + defer cancel(nil) + var wg sync.WaitGroup - for i := 0; i < t.batchSize; i++ { + for range t.batchSize { wg.Add(1) go func() { defer wg.Done() - if err := t.transact(); err != nil { - fmt.Println("\033[31m[ERROR]\033[0m", err) + select { + case <-ctx.Done(): return + default: + if err := t.transact(); err != nil { + cancel(err) + return + } } }() } wg.Wait() + + if err := context.Cause(ctx); err != nil { + return err + } + + return nil } func (t *transactApp) transact() error { - funcName := "transact" - - anAsset := newAsset() - - err := t.smartContract.CreateAsset(anAsset) + anAsset, err := newAsset() if err != nil { - return fmt.Errorf("in %s: %w", funcName, err) + return err + } + + if err := t.smartContract.CreateAsset(anAsset); err != nil { + return err } fmt.Println("Created asset", anAsset.ID) // Transfer randomly 1 in 2 assets to a new owner. - if randomInt(2) == 0 { + if rand.N(2) == 0 { newOwner := differentElement(owners, anAsset.Owner) oldOwner, err := t.smartContract.TransferAsset(anAsset.ID, newOwner) if err != nil { - return fmt.Errorf("in %s: %w", funcName, err) + return err } fmt.Printf("Transferred asset %s from %s to %s\n", anAsset.ID, oldOwner, newOwner) } // Delete randomly 1 in 4 created assets. - if randomInt(4) == 0 { - err := t.smartContract.DeleteAsset(anAsset.ID) - if err != nil { - return fmt.Errorf("in %s: %w", funcName, err) + if rand.N(4) == 0 { + if err := t.smartContract.DeleteAsset(anAsset.ID); err != nil { + return err } fmt.Println("Deleted asset", anAsset.ID) } + return nil } -func newAsset() atb.Asset { +func newAsset() (atb.Asset, error) { id, err := uuid.NewRandom() if err != nil { - panic(err) + return atb.Asset{}, err } return atb.Asset{ ID: id.String(), Color: randomElement([]string{"red", "green", "blue"}), - Size: uint64(randomInt(10) + 1), + Size: uint64(rand.N(10) + 1), Owner: randomElement(owners), - AppraisedValue: uint64(randomInt(1000) + 1), - } + AppraisedValue: uint64(rand.N(1000) + 1), + }, nil } // Pick a random element from an array. func randomElement(values []string) string { - result := values[randomInt(len(values))] + result := values[rand.N(len(values))] return result } -// Generate a random integer in the range 0 to max - 1. -func randomInt(max int) int { - result, err := rand.Int(rand.Reader, big.NewInt(int64(max))) - if err != nil { - panic(err) - } - - return int(result.Int64()) -} - // Pick a random element from an array, excluding the current value. func differentElement(values []string, currentValue string) string { - candidateValues := []string{} + var candidateValues []string for _, v := range values { if v != currentValue { candidateValues = append(candidateValues, v)