diff --git a/off_chain_data/application-go/blockParser.go b/off_chain_data/application-go/blockParser.go deleted file mode 100644 index 55f895fa..00000000 --- a/off_chain_data/application-go/blockParser.go +++ /dev/null @@ -1,397 +0,0 @@ -package main - -import ( - "fmt" - - "github.com/hyperledger/fabric-gateway/pkg/identity" - "github.com/hyperledger/fabric-protos-go-apiv2/common" - "github.com/hyperledger/fabric-protos-go-apiv2/ledger/rwset" - "github.com/hyperledger/fabric-protos-go-apiv2/ledger/rwset/kvrwset" - "github.com/hyperledger/fabric-protos-go-apiv2/msp" - "github.com/hyperledger/fabric-protos-go-apiv2/peer" - "google.golang.org/protobuf/proto" -) - -type Block interface { - GetNumber() uint64 - GetTransactions() []Transaction - ToProto() *common.Block -} - -type ParsedBlock struct { - block *common.Block - validationCodes []byte - transactions []Transaction -} - -func NewParsedBlock(block *common.Block) Block { - validationCodes := getTransactionValidationCodes(block) - - return &ParsedBlock{block, validationCodes, nil} -} - -func (pb *ParsedBlock) GetNumber() uint64 { - header := assertDefined(pb.block.GetHeader(), "missing block header") - return header.GetNumber() -} - -// TODO: needs cache, getPayloads, parsePayload -func (pb *ParsedBlock) GetTransactions() []Transaction { - return nil -} - -func (pb *ParsedBlock) ToProto() *common.Block { - return nil -} - -// Implements identity.Identity Interface -type IdentityImpl struct { - creator *msp.SerializedIdentity -} - -func (i *IdentityImpl) MspID() string { - return i.creator.GetMspid() -} - -func (i *IdentityImpl) Credentials() []byte { - return i.creator.GetIdBytes() -} - -type Transaction interface { - GetChannelHeader() *common.ChannelHeader - GetCreator() identity.Identity - GetValidationCode() int32 - IsValid() bool - GetNamespaceReadWriteSets() []NamespaceReadWriteSet - ToProto() *common.Payload -} - -type TransactionImpl struct { - payload Payload -} - -func NewTransaction(payload Payload) Transaction { - return &TransactionImpl{payload} -} - -func (t *TransactionImpl) GetChannelHeader() *common.ChannelHeader { - return t.payload.GetChannelHeader() -} - -func (t *TransactionImpl) GetCreator() identity.Identity { - creator := &msp.SerializedIdentity{} - if err := proto.Unmarshal(t.payload.GetSignatureHeader().GetCreator(), creator); err != nil { - panic(err) - } - - return &IdentityImpl{creator} -} - -func (t *TransactionImpl) GetNamespaceReadWriteSets() []NamespaceReadWriteSet { - result := []NamespaceReadWriteSet{} - for _, readWriteSet := range t.payload.GetEndorserTransaction().GetReadWriteSets() { - result = append(result, readWriteSet.GetNamespaceReadWriteSets()...) - } - - return result -} - -func (t *TransactionImpl) GetValidationCode() int32 { - return t.payload.GetTransactionValidationCode() -} - -func (t *TransactionImpl) IsValid() bool { - return t.payload.IsValid() -} - -func (t *TransactionImpl) ToProto() *common.Payload { - return t.payload.ToProto() -} - -type EndorserTransaction interface { - GetReadWriteSets() []ReadWriteSet - ToProto() *peer.Transaction -} - -type ParsedEndorserTransaction struct { - transaction *peer.Transaction -} - -func NewParsedEndorserTransaction(transaction *peer.Transaction) EndorserTransaction { - return &ParsedEndorserTransaction{transaction} -} - -// TODO add cache -func (p *ParsedEndorserTransaction) GetReadWriteSets() []ReadWriteSet { - chaincodeActionPayloads := p.getChaincodeActionPayloads() - - chaincodeEndorsedActions := p.getChaincodeEndorsedActions(chaincodeActionPayloads) - - proposalResponsePayloads := p.getProposalResponsePayloads(chaincodeEndorsedActions) - - chaincodeActions := p.getChaincodeActions(proposalResponsePayloads) - - txReadWriteSets := p.getTxReadWriteSets(chaincodeActions) - - parsedReadWriteSets := p.parseReadWriteSets(txReadWriteSets) - - return parsedReadWriteSets -} - -func (p *ParsedEndorserTransaction) getChaincodeActionPayloads() []*peer.ChaincodeActionPayload { - result := []*peer.ChaincodeActionPayload{} - for _, transactionAction := range p.transaction.GetActions() { - chaincodeActionPayload := &peer.ChaincodeActionPayload{} - if err := proto.Unmarshal(transactionAction.GetPayload(), chaincodeActionPayload); err != nil { - panic(err) - } - - result = append(result, chaincodeActionPayload) - } - return result -} - -func (*ParsedEndorserTransaction) getChaincodeEndorsedActions(chaincodeActionPayloads []*peer.ChaincodeActionPayload) []*peer.ChaincodeEndorsedAction { - result := []*peer.ChaincodeEndorsedAction{} - for _, payload := range chaincodeActionPayloads { - result = append( - result, - assertDefined( - payload.GetAction(), - "missing chaincode endorsed action", - ), - ) - } - return result -} - -func (*ParsedEndorserTransaction) getProposalResponsePayloads(chaincodeEndorsedActions []*peer.ChaincodeEndorsedAction) []*peer.ProposalResponsePayload { - result := []*peer.ProposalResponsePayload{} - for _, endorsedAction := range chaincodeEndorsedActions { - proposalResponsePayload := &peer.ProposalResponsePayload{} - if err := proto.Unmarshal(endorsedAction.GetProposalResponsePayload(), proposalResponsePayload); err != nil { - panic(err) - } - result = append(result, proposalResponsePayload) - } - return result -} - -func (*ParsedEndorserTransaction) getChaincodeActions(proposalResponsePayloads []*peer.ProposalResponsePayload) []*peer.ChaincodeAction { - result := []*peer.ChaincodeAction{} - for _, proposalResponsePayload := range proposalResponsePayloads { - chaincodeAction := &peer.ChaincodeAction{} - if err := proto.Unmarshal(proposalResponsePayload.GetExtension(), chaincodeAction); err != nil { - panic(err) - } - result = append(result, chaincodeAction) - } - return result -} - -func (*ParsedEndorserTransaction) getTxReadWriteSets(chaincodeActions []*peer.ChaincodeAction) []*rwset.TxReadWriteSet { - result := []*rwset.TxReadWriteSet{} - for _, chaincodeAction := range chaincodeActions { - txReadWriteSet := &rwset.TxReadWriteSet{} - if err := proto.Unmarshal(chaincodeAction.GetResults(), txReadWriteSet); err != nil { - continue - } - result = append(result, txReadWriteSet) - } - return result -} - -func (*ParsedEndorserTransaction) parseReadWriteSets(txReadWriteSets []*rwset.TxReadWriteSet) []ReadWriteSet { - result := []ReadWriteSet{} - for _, txReadWriteSet := range txReadWriteSets { - parsedReadWriteSet := NewParsedReadWriteSet(txReadWriteSet) - result = append(result, parsedReadWriteSet) - } - return result -} - -func (p *ParsedEndorserTransaction) ToProto() *peer.Transaction { - return p.transaction -} - -type ReadWriteSet interface { - GetNamespaceReadWriteSets() []NamespaceReadWriteSet - ToProto() *rwset.TxReadWriteSet -} - -type ParsedReadWriteSet struct { - readWriteSet *rwset.TxReadWriteSet -} - -func NewParsedReadWriteSet(rwSet *rwset.TxReadWriteSet) ReadWriteSet { - return &ParsedReadWriteSet{rwSet} -} - -func (p *ParsedReadWriteSet) GetNamespaceReadWriteSets() []NamespaceReadWriteSet { - result := []NamespaceReadWriteSet{} - for _, nsReadWriteSet := range p.readWriteSet.GetNsRwset() { - parsedNamespaceReadWriteSet := NewParsedNamespaceReadWriteSet(nsReadWriteSet) - result = append(result, parsedNamespaceReadWriteSet) - } - return result -} - -func (p *ParsedReadWriteSet) ToProto() *rwset.TxReadWriteSet { - return p.readWriteSet -} - -type NamespaceReadWriteSet interface { - GetNamespace() string - GetReadWriteSet() *kvrwset.KVRWSet - ToProto() *rwset.NsReadWriteSet -} - -type ParsedNamespaceReadWriteSet struct { - nsReadWriteSet *rwset.NsReadWriteSet -} - -func NewParsedNamespaceReadWriteSet(nsRwSet *rwset.NsReadWriteSet) NamespaceReadWriteSet { - return &ParsedNamespaceReadWriteSet{nsRwSet} -} - -func (p *ParsedNamespaceReadWriteSet) GetNamespace() string { - return p.nsReadWriteSet.GetNamespace() -} - -// TODO add cache -func (p *ParsedNamespaceReadWriteSet) GetReadWriteSet() *kvrwset.KVRWSet { - result := kvrwset.KVRWSet{} - if err := proto.Unmarshal(p.nsReadWriteSet.GetRwset(), &result); err != nil { - panic(err) - } - - return &result -} - -func (p *ParsedNamespaceReadWriteSet) ToProto() *rwset.NsReadWriteSet { - return p.nsReadWriteSet -} - -func (pb *ParsedBlock) payloads() []*common.Payload { - var payloads []*common.Payload - - for _, envelopeBytes := range pb.block.GetData().GetData() { - envelope := &common.Envelope{} - if err := proto.Unmarshal(envelopeBytes, envelope); err != nil { - panic(err) - } - - payload := &common.Payload{} - if err := proto.Unmarshal(envelope.Payload, payload); err != nil { - panic(err) - } - - payloads = append(payloads, payload) - } - - return payloads -} - -// TODO not sure about this -func (pb *ParsedBlock) statusCode(txIndex int) peer.TxValidationCode { - blockMetadata := assertDefined( - pb.block.GetMetadata(), - "missing block metadata", - ) - - metadata := blockMetadata.GetMetadata() - if int(common.BlockMetadataIndex_TRANSACTIONS_FILTER) >= len(metadata) { - return peer.TxValidationCode_INVALID_OTHER_REASON - } - - statusCodes := metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] - if txIndex >= len(statusCodes) { - return peer.TxValidationCode_INVALID_OTHER_REASON - } - - return peer.TxValidationCode(statusCodes[txIndex]) -} - -type Payload interface { - GetChannelHeader() *common.ChannelHeader - GetEndorserTransaction() EndorserTransaction - GetSignatureHeader() *common.SignatureHeader - GetTransactionValidationCode() int32 - IsEndorserTransaction() bool - IsValid() bool - ToProto() *common.Payload -} - -type PayloadImpl struct { - payload *common.Payload - statusCode int32 -} - -func NewPayloadImpl(payload *common.Payload, statusCode int32) Payload { - return &PayloadImpl{payload, statusCode} -} - -func (p *PayloadImpl) GetChannelHeader() *common.ChannelHeader { - header := assertDefined(p.payload.GetHeader(), "missing payload header") - - // TODO add cache, return cachedChannelHeader like in blockParser.ts:77 - result := &common.ChannelHeader{} - if err := proto.Unmarshal(header.GetChannelHeader(), result); err != nil { - panic(err) - } - - return result -} - -func (p *PayloadImpl) GetEndorserTransaction() EndorserTransaction { - if !p.IsEndorserTransaction() { - panic(fmt.Errorf("unexpected payload type: %d", p.GetChannelHeader().GetType())) - } - - result := &peer.Transaction{} - if err := proto.Unmarshal(p.payload.GetData(), result); err != nil { - panic(err) - } - - return NewParsedEndorserTransaction(result) -} - -func (p *PayloadImpl) GetSignatureHeader() *common.SignatureHeader { - header := assertDefined(p.payload.GetHeader(), "missing payload header") - - // TODO add cache, return cachedSignatureHeader like in blockParser.ts:77 - result := &common.SignatureHeader{} - if err := proto.Unmarshal(header.GetSignatureHeader(), result); err != nil { - panic(err) - } - - return result -} - -func (p *PayloadImpl) GetTransactionValidationCode() int32 { - return p.statusCode -} - -func (p *PayloadImpl) IsEndorserTransaction() bool { - return p.GetChannelHeader().GetType() == int32(common.HeaderType_ENDORSER_TRANSACTION) -} - -func (p *PayloadImpl) IsValid() bool { - return p.statusCode == int32(peer.TxValidationCode_VALID) -} - -func (p *PayloadImpl) ToProto() *common.Payload { - return p.payload -} - -func getTransactionValidationCodes(block *common.Block) []byte { - metadata := assertDefined( - block.GetMetadata(), - "missing block metadata", - ) - - return assertDefined( - metadata.GetMetadata()[common.BlockMetadataIndex_TRANSACTIONS_FILTER], - "missing transaction validation code", - ) -} diff --git a/off_chain_data/application-go/contract.go b/off_chain_data/application-go/contract.go deleted file mode 100644 index 2eb5b360..00000000 --- a/off_chain_data/application-go/contract.go +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright 2024 IBM All Rights Reserved. - * - * SPDX-License-Identifier: Apache-2.0 - */ - -package main - -import ( - "strconv" - - "github.com/google/uuid" - "github.com/hyperledger/fabric-gateway/pkg/client" -) - -type Asset struct { - ID string - Color string - Size uint64 - Owner string - AppraisedValue uint64 -} - -func NewAsset() Asset { - id, err := uuid.NewRandom() - if err != nil { - panic(err) - } - - return Asset{ - ID: id.String(), - Color: randomElement(colors), - Size: uint64(randomInt(maxInitialSize) + 1), - Owner: randomElement(owners), - AppraisedValue: uint64(randomInt(maxInitialValue) + 1), - } -} - -type assetTransferBasic struct { - contract *client.Contract -} - -func newAssetTransferBasic(contract *client.Contract) *assetTransferBasic { - return &assetTransferBasic{contract} -} - -func (atb *assetTransferBasic) createAsset(anAsset Asset) { - 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 { - panic(err) - } -} - -func (atb *assetTransferBasic) transferAsset(id, newOwner string) string { - result, err := atb.contract.Submit( - "TransferAsset", - client.WithArguments( - id, - newOwner, - ), - ) - if err != nil { - panic(err) - } - - return string(result) -} - -func (atb *assetTransferBasic) deleteAsset(id string) { - if _, err := atb.contract.Submit( - "DeleteAsset", - client.WithArguments( - id, - ), - ); err != nil { - panic(err) - } -} - -func (atb *assetTransferBasic) getAllAssets() []byte { - result, err := atb.contract.Evaluate("GetAllAssets") - if err != nil { - panic(err) - } - return result -} diff --git a/off_chain_data/application-go/contract/asset.go b/off_chain_data/application-go/contract/asset.go new file mode 100644 index 00000000..70c4e688 --- /dev/null +++ b/off_chain_data/application-go/contract/asset.go @@ -0,0 +1,45 @@ +/* + * Copyright 2024 IBM All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package contract + +import ( + "offChainData/utils" + + "github.com/google/uuid" +) + +var ( + colors = []string{"red", "green", "blue"} + Owners = []string{"alice", "bob", "charlie"} +) + +const ( + maxInitialValue = 1000 + maxInitialSize = 10 +) + +type Asset struct { + ID string `json:"ID"` + Color string `json:"Color"` + Size uint64 `json:"Size"` + Owner string `json:"Owner"` + AppraisedValue uint64 `json:"AppraisedValue"` +} + +func NewAsset() Asset { + id, err := uuid.NewRandom() + if err != nil { + panic(err) + } + + return Asset{ + ID: id.String(), + Color: utils.RandomElement(colors), + Size: uint64(utils.RandomInt(maxInitialSize) + 1), + Owner: utils.RandomElement(Owners), + AppraisedValue: uint64(utils.RandomInt(maxInitialValue) + 1), + } +} diff --git a/off_chain_data/application-go/contract/contract.go b/off_chain_data/application-go/contract/contract.go new file mode 100644 index 00000000..e672620d --- /dev/null +++ b/off_chain_data/application-go/contract/contract.go @@ -0,0 +1,68 @@ +/* + * Copyright 2024 IBM All Rights Reserved. + * + * SPDX-License-Identifier: Apache-2.0 + */ +package contract + +import ( + "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) { + 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 { + panic(err) + } +} + +func (atb *AssetTransferBasic) TransferAsset(id, newOwner string) string { + result, err := atb.contract.Submit( + "TransferAsset", + client.WithArguments( + id, + newOwner, + ), + ) + if err != nil { + panic(err) + } + + return string(result) +} + +func (atb *AssetTransferBasic) DeleteAsset(id string) { + if _, err := atb.contract.Submit( + "DeleteAsset", + client.WithArguments( + id, + ), + ); err != nil { + panic(err) + } +} + +func (atb *AssetTransferBasic) GetAllAssets() []byte { + result, err := atb.contract.Evaluate("GetAllAssets") + if err != nil { + panic(err) + } + return result +} diff --git a/off_chain_data/application-go/getAllAssets.go b/off_chain_data/application-go/getAllAssets.go index 9c6643da..a6aff5db 100644 --- a/off_chain_data/application-go/getAllAssets.go +++ b/off_chain_data/application-go/getAllAssets.go @@ -10,6 +10,7 @@ import ( "bytes" "encoding/json" "fmt" + atb "offChainData/contract" "github.com/hyperledger/fabric-gateway/pkg/client" "google.golang.org/grpc" @@ -24,8 +25,8 @@ func getAllAssets(clientConnection *grpc.ClientConn) { defer gateway.Close() contract := gateway.GetNetwork(channelName).GetContract(chaincodeName) - smartContract := newAssetTransferBasic(contract) - assets := smartContract.getAllAssets() + smartContract := atb.NewAssetTransferBasic(contract) + assets := smartContract.GetAllAssets() fmt.Printf("%s\n", formatJSON(assets)) } diff --git a/off_chain_data/application-go/go.mod b/off_chain_data/application-go/go.mod index d73c77b2..83adb07d 100644 --- a/off_chain_data/application-go/go.mod +++ b/off_chain_data/application-go/go.mod @@ -1,14 +1,12 @@ module offChainData -go 1.22.7 - -toolchain go1.23.0 +go 1.22.0 require ( github.com/google/uuid v1.6.0 github.com/hyperledger/fabric-gateway v1.7.0 github.com/hyperledger/fabric-protos-go-apiv2 v0.3.4 - google.golang.org/grpc v1.68.0 + google.golang.org/grpc v1.68.0-dev google.golang.org/protobuf v1.35.2 ) diff --git a/off_chain_data/application-go/go.sum b/off_chain_data/application-go/go.sum index ba6230a4..e3d2874e 100644 --- a/off_chain_data/application-go/go.sum +++ b/off_chain_data/application-go/go.sum @@ -1,7 +1,5 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= @@ -28,8 +26,8 @@ golang.org/x/text v0.20.0 h1:gK/Kv2otX8gz+wn7Rmb3vT96ZwuoxnQlY+HlJVj7Qug= golang.org/x/text v0.20.0/go.mod h1:D4IsuqiFMhST5bX19pQ9ikHC2GsaKyk/oF+pn3ducp4= google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697 h1:LWZqQOEjDyONlF1H6afSWpAL/znlREo2tHfLoe+8LMA= google.golang.org/genproto/googleapis/rpc v0.0.0-20241118233622-e639e219e697/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= -google.golang.org/grpc v1.68.0 h1:aHQeeJbo8zAkAa3pRzrVjZlbz6uSfeOXlJNQM0RAbz0= -google.golang.org/grpc v1.68.0/go.mod h1:fmSPC5AsjSBCK54MyHRx48kpOti1/jRfOlwEWywNjWA= +google.golang.org/grpc v1.68.0-dev h1:Qao/m2HpklhJt2QbpdRutxyNfRuwM8nGPpmi2UkuEHw= +google.golang.org/grpc v1.68.0-dev/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= diff --git a/off_chain_data/application-go/parser/blockParser.go b/off_chain_data/application-go/parser/blockParser.go new file mode 100644 index 00000000..c12f3b25 --- /dev/null +++ b/off_chain_data/application-go/parser/blockParser.go @@ -0,0 +1,87 @@ +package parser + +import ( + "offChainData/utils" + + "github.com/hyperledger/fabric-protos-go-apiv2/common" + "github.com/hyperledger/fabric-protos-go-apiv2/peer" + "google.golang.org/protobuf/proto" +) + +type Block struct { + block *common.Block + validationCodes []byte + transactions []Transaction +} + +func ParseBlock(block *common.Block) Block { + validationCodes := extractTransactionValidationCodes(block) + + return Block{block, validationCodes, nil} +} + +func (b *Block) Number() uint64 { + header := utils.AssertDefined(b.block.GetHeader(), "missing block header") + return header.GetNumber() +} + +// TODO: needs cache, getPayloads, parsePayload +func (b *Block) Transactions() []Transaction { + return nil +} + +func (b *Block) ToProto() *common.Block { + return nil +} + +func (b *Block) payloads() []*common.Payload { + var payloads []*common.Payload + + for _, envelopeBytes := range b.block.GetData().GetData() { + envelope := &common.Envelope{} + if err := proto.Unmarshal(envelopeBytes, envelope); err != nil { + panic(err) + } + + payload := &common.Payload{} + if err := proto.Unmarshal(envelope.Payload, payload); err != nil { + panic(err) + } + + payloads = append(payloads, payload) + } + + return payloads +} + +// TODO not sure about this +func (pb *Block) statusCode(txIndex int) peer.TxValidationCode { + blockMetadata := utils.AssertDefined( + pb.block.GetMetadata(), + "missing block metadata", + ) + + metadata := blockMetadata.GetMetadata() + if int(common.BlockMetadataIndex_TRANSACTIONS_FILTER) >= len(metadata) { + return peer.TxValidationCode_INVALID_OTHER_REASON + } + + statusCodes := metadata[common.BlockMetadataIndex_TRANSACTIONS_FILTER] + if txIndex >= len(statusCodes) { + return peer.TxValidationCode_INVALID_OTHER_REASON + } + + return peer.TxValidationCode(statusCodes[txIndex]) +} + +func extractTransactionValidationCodes(block *common.Block) []byte { + metadata := utils.AssertDefined( + block.GetMetadata(), + "missing block metadata", + ) + + return utils.AssertDefined( + metadata.GetMetadata()[common.BlockMetadataIndex_TRANSACTIONS_FILTER], + "missing transaction validation code", + ) +} diff --git a/off_chain_data/application-go/blockParser_test.go b/off_chain_data/application-go/parser/blockParser_test.go similarity index 75% rename from off_chain_data/application-go/blockParser_test.go rename to off_chain_data/application-go/parser/blockParser_test.go index a90bdc71..0432f720 100644 --- a/off_chain_data/application-go/blockParser_test.go +++ b/off_chain_data/application-go/parser/blockParser_test.go @@ -1,10 +1,11 @@ -package main_test +package parser_test import ( "encoding/json" "testing" - ocd "offChainData" + atb "offChainData/contract" + "offChainData/parser" "github.com/hyperledger/fabric-protos-go-apiv2/ledger/rwset" "github.com/hyperledger/fabric-protos-go-apiv2/ledger/rwset/kvrwset" @@ -34,25 +35,30 @@ func TestGetReadWriteSetsFromEndorserTransaction(t *testing.T) { }, } - parsedEndorserTransaction := ocd.NewParsedEndorserTransaction(transaction) - if len(parsedEndorserTransaction.GetReadWriteSets()) != 1 { - t.Fatal("expected 1 ReadWriteSet, got", len(parsedEndorserTransaction.GetReadWriteSets())) + parsedEndorserTransaction := parser.ParseEndorserTransaction(transaction) + if len(parsedEndorserTransaction.ReadWriteSets()) != 1 { + t.Fatal("expected 1 ReadWriteSet, got", len(parsedEndorserTransaction.ReadWriteSets())) } assertReadWriteSet( - parsedEndorserTransaction.GetReadWriteSets()[0].GetNamespaceReadWriteSets()[0], + parsedEndorserTransaction.ReadWriteSets()[0].NamespaceReadWriteSets()[0], expectedNamespace, expectedAsset, t, ) } -func assertReadWriteSet(parsedNsRwSet ocd.NamespaceReadWriteSet, expectedNamespace string, expectedAsset ocd.Asset, t *testing.T) { - if parsedNsRwSet.GetNamespace() != expectedNamespace { - t.Errorf("expected namespace %s, got %s", expectedNamespace, parsedNsRwSet.GetNamespace()) +func assertReadWriteSet( + parsedNsRwSet parser.NamespaceReadWriteSet, + expectedNamespace string, + expectedAsset atb.Asset, + t *testing.T, +) { + if parsedNsRwSet.Namespace() != expectedNamespace { + t.Errorf("expected namespace %s, got %s", expectedNamespace, parsedNsRwSet.Namespace()) } - actualKVRWSet := parsedNsRwSet.GetReadWriteSet() + actualKVRWSet := parsedNsRwSet.ReadWriteSet() if len(actualKVRWSet.Writes) != 1 { t.Fatal("expected 1 write, got", len(actualKVRWSet.Writes)) } @@ -74,16 +80,16 @@ func TestReadWriteSetWrapping(t *testing.T) { NsRwset: []*rwset.NsReadWriteSet{nsReadWriteSetFake}, } - parsedRwSet := ocd.NewParsedReadWriteSet(txReadWriteSetFake) - if len(parsedRwSet.GetNamespaceReadWriteSets()) != 1 { - t.Fatalf("Expected 1 NamespaceReadWriteSet, got %d", len(parsedRwSet.GetNamespaceReadWriteSets())) + parsedRwSet := parser.ParseReadWriteSet(txReadWriteSetFake) + if len(parsedRwSet.NamespaceReadWriteSets()) != 1 { + t.Fatalf("Expected 1 NamespaceReadWriteSet, got %d", len(parsedRwSet.NamespaceReadWriteSets())) } } func TestNamespaceReadWriteSetParsing(t *testing.T) { nsReadWriteSetFake, expectedNamespace, expectedAsset := nsReadWriteSetFake() - parsedNsRwSet := ocd.NewParsedNamespaceReadWriteSet(nsReadWriteSetFake) + parsedNsRwSet := parser.ParseNamespaceReadWriteSet(nsReadWriteSetFake) assertReadWriteSet( parsedNsRwSet, expectedNamespace, @@ -92,9 +98,9 @@ func TestNamespaceReadWriteSetParsing(t *testing.T) { ) } -func nsReadWriteSetFake() (*rwset.NsReadWriteSet, string, ocd.Asset) { +func nsReadWriteSetFake() (*rwset.NsReadWriteSet, string, atb.Asset) { expectedNamespace := "basic" - expectedAsset := ocd.NewAsset() + expectedAsset := atb.NewAsset() result := &rwset.NsReadWriteSet{ Namespace: expectedNamespace, diff --git a/off_chain_data/application-go/parser/identity.go b/off_chain_data/application-go/parser/identity.go new file mode 100644 index 00000000..85c9ecb7 --- /dev/null +++ b/off_chain_data/application-go/parser/identity.go @@ -0,0 +1,16 @@ +package parser + +import "github.com/hyperledger/fabric-protos-go-apiv2/msp" + +// Implements identity.Identity Interface +type identityImpl struct { + creator *msp.SerializedIdentity +} + +func (i *identityImpl) MspID() string { + return i.creator.GetMspid() +} + +func (i *identityImpl) Credentials() []byte { + return i.creator.GetIdBytes() +} diff --git a/off_chain_data/application-go/parser/payload.go b/off_chain_data/application-go/parser/payload.go new file mode 100644 index 00000000..a6f95246 --- /dev/null +++ b/off_chain_data/application-go/parser/payload.go @@ -0,0 +1,82 @@ +package parser + +import ( + "fmt" + "offChainData/utils" + + "github.com/hyperledger/fabric-protos-go-apiv2/common" + "github.com/hyperledger/fabric-protos-go-apiv2/peer" + "google.golang.org/protobuf/proto" +) + +type Payload interface { + ChannelHeader() *common.ChannelHeader + EndorserTransaction() EndorserTransaction + SignatureHeader() *common.SignatureHeader + TransactionValidationCode() int32 + IsEndorserTransaction() bool + IsValid() bool + ToProto() *common.Payload +} + +type PayloadImpl struct { + payload *common.Payload + statusCode int32 +} + +func ParsePayload(payload *common.Payload, statusCode int32) *PayloadImpl { + return &PayloadImpl{payload, statusCode} +} + +func (p *PayloadImpl) ChannelHeader() *common.ChannelHeader { + header := utils.AssertDefined(p.payload.GetHeader(), "missing payload header") + + // TODO add cache, return cachedChannelHeader like in blockParser.ts:77 + result := &common.ChannelHeader{} + if err := proto.Unmarshal(header.GetChannelHeader(), result); err != nil { + panic(err) + } + + return result +} + +func (p *PayloadImpl) EndorserTransaction() EndorserTransaction { + if !p.IsEndorserTransaction() { + panic(fmt.Errorf("unexpected payload type: %d", p.ChannelHeader().GetType())) + } + + result := &peer.Transaction{} + if err := proto.Unmarshal(p.payload.GetData(), result); err != nil { + panic(err) + } + + return ParseEndorserTransaction(result) +} + +func (p *PayloadImpl) SignatureHeader() *common.SignatureHeader { + header := utils.AssertDefined(p.payload.GetHeader(), "missing payload header") + + // TODO add cache, return cachedSignatureHeader like in blockParser.ts:77 + result := &common.SignatureHeader{} + if err := proto.Unmarshal(header.GetSignatureHeader(), result); err != nil { + panic(err) + } + + return result +} + +func (p *PayloadImpl) TransactionValidationCode() int32 { + return p.statusCode +} + +func (p *PayloadImpl) IsEndorserTransaction() bool { + return p.ChannelHeader().GetType() == int32(common.HeaderType_ENDORSER_TRANSACTION) +} + +func (p *PayloadImpl) IsValid() bool { + return p.statusCode == int32(peer.TxValidationCode_VALID) +} + +func (p *PayloadImpl) ToProto() *common.Payload { + return p.payload +} diff --git a/off_chain_data/application-go/parser/readWriteSet.go b/off_chain_data/application-go/parser/readWriteSet.go new file mode 100644 index 00000000..9bb25ac0 --- /dev/null +++ b/off_chain_data/application-go/parser/readWriteSet.go @@ -0,0 +1,65 @@ +package parser + +import ( + "github.com/hyperledger/fabric-protos-go-apiv2/ledger/rwset" + "github.com/hyperledger/fabric-protos-go-apiv2/ledger/rwset/kvrwset" + "google.golang.org/protobuf/proto" +) + +type ReadWriteSet interface { + NamespaceReadWriteSets() []NamespaceReadWriteSet + ToProto() *rwset.TxReadWriteSet +} + +type ReadWriteSetImpl struct { + readWriteSet *rwset.TxReadWriteSet +} + +func ParseReadWriteSet(rwSet *rwset.TxReadWriteSet) *ReadWriteSetImpl { + return &ReadWriteSetImpl{rwSet} +} + +func (p *ReadWriteSetImpl) NamespaceReadWriteSets() []NamespaceReadWriteSet { + result := []NamespaceReadWriteSet{} + for _, nsReadWriteSet := range p.readWriteSet.GetNsRwset() { + parsedNamespaceReadWriteSet := ParseNamespaceReadWriteSet(nsReadWriteSet) + result = append(result, parsedNamespaceReadWriteSet) + } + return result +} + +func (p *ReadWriteSetImpl) ToProto() *rwset.TxReadWriteSet { + return p.readWriteSet +} + +type NamespaceReadWriteSet interface { + Namespace() string + ReadWriteSet() *kvrwset.KVRWSet + ToProto() *rwset.NsReadWriteSet +} + +type NamespaceReadWriteSetImpl struct { + nsReadWriteSet *rwset.NsReadWriteSet +} + +func ParseNamespaceReadWriteSet(nsRwSet *rwset.NsReadWriteSet) *NamespaceReadWriteSetImpl { + return &NamespaceReadWriteSetImpl{nsRwSet} +} + +func (p *NamespaceReadWriteSetImpl) Namespace() string { + return p.nsReadWriteSet.GetNamespace() +} + +// TODO add cache +func (p *NamespaceReadWriteSetImpl) ReadWriteSet() *kvrwset.KVRWSet { + result := kvrwset.KVRWSet{} + if err := proto.Unmarshal(p.nsReadWriteSet.GetRwset(), &result); err != nil { + panic(err) + } + + return &result +} + +func (p *NamespaceReadWriteSetImpl) ToProto() *rwset.NsReadWriteSet { + return p.nsReadWriteSet +} diff --git a/off_chain_data/application-go/parser/transaction.go b/off_chain_data/application-go/parser/transaction.go new file mode 100644 index 00000000..1ba31697 --- /dev/null +++ b/off_chain_data/application-go/parser/transaction.go @@ -0,0 +1,169 @@ +package parser + +import ( + "offChainData/utils" + + "github.com/hyperledger/fabric-gateway/pkg/identity" + "github.com/hyperledger/fabric-protos-go-apiv2/common" + "github.com/hyperledger/fabric-protos-go-apiv2/ledger/rwset" + "github.com/hyperledger/fabric-protos-go-apiv2/msp" + "github.com/hyperledger/fabric-protos-go-apiv2/peer" + "google.golang.org/protobuf/proto" +) + +type Transaction interface { + ChannelHeader() *common.ChannelHeader + Creator() identity.Identity + ValidationCode() int32 + IsValid() bool + NamespaceReadWriteSets() []NamespaceReadWriteSet + ToProto() *common.Payload +} + +type TransactionImpl struct { + payload Payload +} + +func NewTransactionImpl(payload Payload) *TransactionImpl { + return &TransactionImpl{payload} +} + +func (t *TransactionImpl) ChannelHeader() *common.ChannelHeader { + return t.payload.ChannelHeader() +} + +func (t *TransactionImpl) Creator() identity.Identity { + creator := &msp.SerializedIdentity{} + if err := proto.Unmarshal(t.payload.SignatureHeader().GetCreator(), creator); err != nil { + panic(err) + } + + return &identityImpl{creator} +} + +func (t *TransactionImpl) NamespaceReadWriteSets() []NamespaceReadWriteSet { + result := []NamespaceReadWriteSet{} + for _, readWriteSet := range t.payload.EndorserTransaction().ReadWriteSets() { + result = append(result, readWriteSet.NamespaceReadWriteSets()...) + } + + return result +} + +func (t *TransactionImpl) ValidationCode() int32 { + return t.payload.TransactionValidationCode() +} + +func (t *TransactionImpl) IsValid() bool { + return t.payload.IsValid() +} + +func (t *TransactionImpl) ToProto() *common.Payload { + return t.payload.ToProto() +} + +type EndorserTransaction interface { + ReadWriteSets() []ReadWriteSet + ToProto() *peer.Transaction +} + +type EndorserTransactionImpl struct { + transaction *peer.Transaction +} + +func ParseEndorserTransaction(transaction *peer.Transaction) *EndorserTransactionImpl { + return &EndorserTransactionImpl{transaction} +} + +// TODO add cache +func (p *EndorserTransactionImpl) ReadWriteSets() []ReadWriteSet { + chaincodeActionPayloads := p.unmarshalChaincodeActionPayloads() + + chaincodeEndorsedActions := p.extractChaincodeEndorsedActionsFrom(chaincodeActionPayloads) + + proposalResponsePayloads := p.unmarshalProposalResponsePayloadsFrom(chaincodeEndorsedActions) + + chaincodeActions := p.unmarshalChaincodeActionsFrom(proposalResponsePayloads) + + txReadWriteSets := p.unmarshalTxReadWriteSetsFrom(chaincodeActions) + + parsedReadWriteSets := p.parseReadWriteSets(txReadWriteSets) + + return parsedReadWriteSets +} + +func (p *EndorserTransactionImpl) unmarshalChaincodeActionPayloads() []*peer.ChaincodeActionPayload { + result := []*peer.ChaincodeActionPayload{} + for _, transactionAction := range p.transaction.GetActions() { + chaincodeActionPayload := &peer.ChaincodeActionPayload{} + if err := proto.Unmarshal(transactionAction.GetPayload(), chaincodeActionPayload); err != nil { + panic(err) + } + + result = append(result, chaincodeActionPayload) + } + return result +} + +func (*EndorserTransactionImpl) extractChaincodeEndorsedActionsFrom(chaincodeActionPayloads []*peer.ChaincodeActionPayload) []*peer.ChaincodeEndorsedAction { + result := []*peer.ChaincodeEndorsedAction{} + for _, payload := range chaincodeActionPayloads { + result = append( + result, + utils.AssertDefined( + payload.GetAction(), + "missing chaincode endorsed action", + ), + ) + } + return result +} + +func (*EndorserTransactionImpl) unmarshalProposalResponsePayloadsFrom(chaincodeEndorsedActions []*peer.ChaincodeEndorsedAction) []*peer.ProposalResponsePayload { + result := []*peer.ProposalResponsePayload{} + for _, endorsedAction := range chaincodeEndorsedActions { + proposalResponsePayload := &peer.ProposalResponsePayload{} + if err := proto.Unmarshal(endorsedAction.GetProposalResponsePayload(), proposalResponsePayload); err != nil { + panic(err) + } + result = append(result, proposalResponsePayload) + } + return result +} + +func (*EndorserTransactionImpl) unmarshalChaincodeActionsFrom(proposalResponsePayloads []*peer.ProposalResponsePayload) []*peer.ChaincodeAction { + result := []*peer.ChaincodeAction{} + for _, proposalResponsePayload := range proposalResponsePayloads { + chaincodeAction := &peer.ChaincodeAction{} + if err := proto.Unmarshal(proposalResponsePayload.GetExtension(), chaincodeAction); err != nil { + panic(err) + } + result = append(result, chaincodeAction) + } + return result +} + +func (*EndorserTransactionImpl) unmarshalTxReadWriteSetsFrom(chaincodeActions []*peer.ChaincodeAction) []*rwset.TxReadWriteSet { + result := []*rwset.TxReadWriteSet{} + for _, chaincodeAction := range chaincodeActions { + txReadWriteSet := &rwset.TxReadWriteSet{} + if err := proto.Unmarshal(chaincodeAction.GetResults(), txReadWriteSet); err != nil { + continue + } + result = append(result, txReadWriteSet) + } + return result +} + +func (*EndorserTransactionImpl) parseReadWriteSets(txReadWriteSets []*rwset.TxReadWriteSet) []ReadWriteSet { + result := []ReadWriteSet{} + for _, txReadWriteSet := range txReadWriteSets { + parsedReadWriteSet := ParseReadWriteSet(txReadWriteSet) + result = append(result, parsedReadWriteSet) + } + return result +} + +func (p *EndorserTransactionImpl) ToProto() *peer.Transaction { + return p.transaction +} diff --git a/off_chain_data/application-go/transact.go b/off_chain_data/application-go/transact.go index b872b836..93ca3398 100644 --- a/off_chain_data/application-go/transact.go +++ b/off_chain_data/application-go/transact.go @@ -3,6 +3,9 @@ package main import ( "fmt" + atb "offChainData/contract" + "offChainData/utils" + "github.com/hyperledger/fabric-gateway/pkg/client" "google.golang.org/grpc" ) @@ -17,30 +20,20 @@ func transact(clientConnection *grpc.ClientConn) { contract := gateway.GetNetwork(channelName).GetContract(chaincodeName) - smartContract := newAssetTransferBasic(contract) + smartContract := atb.NewAssetTransferBasic(contract) app := newTransactApp(smartContract) app.run() } type transactApp struct { - smartContract *assetTransferBasic + smartContract *atb.AssetTransferBasic batchSize uint } -func newTransactApp(smartContract *assetTransferBasic) *transactApp { +func newTransactApp(smartContract *atb.AssetTransferBasic) *transactApp { return &transactApp{smartContract, 10} } -var ( - colors = []string{"red", "green", "blue"} - owners = []string{"alice", "bob", "charlie"} -) - -const ( - maxInitialValue = 1000 - maxInitialSize = 10 -) - func (t *transactApp) run() { for i := 0; i < int(t.batchSize); i++ { go t.transact() @@ -48,21 +41,21 @@ func (t *transactApp) run() { } func (t *transactApp) transact() { - anAsset := NewAsset() + anAsset := atb.NewAsset() - t.smartContract.createAsset(anAsset) + t.smartContract.CreateAsset(anAsset) fmt.Printf("\nCreated asset %s\n", anAsset.ID) // Transfer randomly 1 in 2 assets to a new owner. - if randomInt(2) == 0 { - newOwner := differentElement(owners, anAsset.Owner) - oldOwner := t.smartContract.transferAsset(anAsset.ID, newOwner) + if utils.RandomInt(2) == 0 { + newOwner := utils.DifferentElement(atb.Owners, anAsset.Owner) + oldOwner := t.smartContract.TransferAsset(anAsset.ID, newOwner) 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 { - t.smartContract.deleteAsset(anAsset.ID) + if utils.RandomInt(4) == 0 { + t.smartContract.DeleteAsset(anAsset.ID) fmt.Printf("Deleted asset %s\n", anAsset.ID) } } diff --git a/off_chain_data/application-go/utils.go b/off_chain_data/application-go/utils/utils.go similarity index 59% rename from off_chain_data/application-go/utils.go rename to off_chain_data/application-go/utils/utils.go index c0c77af2..315ff4d6 100644 --- a/off_chain_data/application-go/utils.go +++ b/off_chain_data/application-go/utils/utils.go @@ -1,4 +1,4 @@ -package main +package utils import ( "crypto/rand" @@ -6,12 +6,12 @@ import ( "math/big" ) -func randomElement(values []string) string { - result := values[randomInt(len(values))] +func RandomElement(values []string) string { + result := values[RandomInt(len(values))] return result } -func randomInt(max int) int { +func RandomInt(max int) int { result, err := rand.Int(rand.Reader, big.NewInt(int64(max))) if err != nil { panic(err) @@ -20,17 +20,17 @@ func randomInt(max int) int { return int(result.Int64()) } -func differentElement(values []string, currentValue string) string { +func DifferentElement(values []string, currentValue string) string { candidateValues := []string{} for _, v := range values { if v != currentValue { candidateValues = append(candidateValues, v) } } - return randomElement(candidateValues) + return RandomElement(candidateValues) } -func assertDefined[T any](value T, message string) T { +func AssertDefined[T any](value T, message string) T { if any(value) == any(nil) { panic(errors.New(message)) }