mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-19 08:15:08 +00:00
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:
parent
1eaf609711
commit
2c43e03591
15 changed files with 581 additions and 543 deletions
|
|
@ -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",
|
||||
)
|
||||
}
|
||||
|
|
@ -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
|
||||
}
|
||||
45
off_chain_data/application-go/contract/asset.go
Normal file
45
off_chain_data/application-go/contract/asset.go
Normal 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),
|
||||
}
|
||||
}
|
||||
68
off_chain_data/application-go/contract/contract.go
Normal file
68
off_chain_data/application-go/contract/contract.go
Normal 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
|
||||
}
|
||||
|
|
@ -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))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -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=
|
||||
|
|
|
|||
87
off_chain_data/application-go/parser/blockParser.go
Normal file
87
off_chain_data/application-go/parser/blockParser.go
Normal 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",
|
||||
)
|
||||
}
|
||||
|
|
@ -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,
|
||||
16
off_chain_data/application-go/parser/identity.go
Normal file
16
off_chain_data/application-go/parser/identity.go
Normal 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()
|
||||
}
|
||||
82
off_chain_data/application-go/parser/payload.go
Normal file
82
off_chain_data/application-go/parser/payload.go
Normal 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
|
||||
}
|
||||
65
off_chain_data/application-go/parser/readWriteSet.go
Normal file
65
off_chain_data/application-go/parser/readWriteSet.go
Normal 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
|
||||
}
|
||||
169
off_chain_data/application-go/parser/transaction.go
Normal file
169
off_chain_data/application-go/parser/transaction.go
Normal 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
|
||||
}
|
||||
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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))
|
||||
}
|
||||
Loading…
Reference in a new issue