Encapsulate block parser in a package

Created parser, contract and utils packages and extracted each piece of
functionality into its own files. Removed "Get" prefix from methods and
changed return values from interfaces to structs.

Signed-off-by: Stanislav Jakuschevskij <stas@two-giants.com>
This commit is contained in:
Stanislav Jakuschevskij 2024-12-07 15:20:07 +01:00
parent 1eaf609711
commit 2c43e03591
No known key found for this signature in database
GPG key ID: 78195D2E6998E2EB
15 changed files with 581 additions and 543 deletions

View file

@ -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",
)
}

View file

@ -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
}

View file

@ -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),
}
}

View file

@ -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
}

View file

@ -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))
}

View file

@ -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
)

View file

@ -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=

View file

@ -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",
)
}

View file

@ -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,

View file

@ -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()
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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
}

View file

@ -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)
}
}

View file

@ -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))
}