diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..17976610 --- /dev/null +++ b/Makefile @@ -0,0 +1,3 @@ +test: + @echo "go test -mod vendor ./..." + @go test -mod vendor ./... \ No newline at end of file diff --git a/README.md b/README.md index dcfb95b8..4c52c139 100644 --- a/README.md +++ b/README.md @@ -32,8 +32,9 @@ and [others](docs/chaincode-examples.md) ### CCKit features -* [Centralized chaincode invocation handling](router) with methods routing and middleware capabilities -* [Chaincode state modelling](state) using [protocol buffers](examples/cpaper_extended) / [golang struct to json marshalling](examples/cars), with [private data support](examples/private_cars) +* [Chaincode method router](router) with invocation handlers and middleware capabilities +* [Chaincode state modeling](state) using [protocol buffers](examples/cpaper_extended) / [golang struct to json marshalling](examples/cars), with [private data support](examples/private_cars) +* Designing chaincode in [gGRPC service notation](gateway) with code generation of chaincode SDK, gRPC and REST-API * [MockStub testing](testing), allowing to immediately receive test results * [Data encryption](extensions/encryption) on application level * Chaincode method [access control](extensions/owner) diff --git a/examples/cpaper_asservice/Makefile b/examples/cpaper_asservice/Makefile index fc787143..5c2c3d0f 100644 --- a/examples/cpaper_asservice/Makefile +++ b/examples/cpaper_asservice/Makefile @@ -15,6 +15,7 @@ generate: -I=../../vendor \ -I=../../third_party/googleapis \ --go_out=plugins=grpc:./service/ \ + --cc-gateway_out=logtostderr=true:./service/ \ --grpc-gateway_out=logtostderr=true:./service/ \ --swagger_out=logtostderr=true:./service/ \ ./service/service.proto \ No newline at end of file diff --git a/examples/cpaper_asservice/chaincode.go b/examples/cpaper_asservice/chaincode.go index e7138100..406df61d 100644 --- a/examples/cpaper_asservice/chaincode.go +++ b/examples/cpaper_asservice/chaincode.go @@ -16,7 +16,7 @@ import ( m "github.com/s7techlab/cckit/state/mapping" ) -func Router(name string) (*router.Group, error) { +func CCRouter(name string) (*router.Group, error) { r := router.New(name) // Store on the ledger the information about chaincode instantiation r.Init(owner.InvokeSetFromCreator) @@ -29,7 +29,7 @@ func Router(name string) (*router.Group, error) { } func NewCC() (*router.Chaincode, error) { - r, err := Router(`CommercialPaper`) + r, err := CCRouter(`CommercialPaper`) if err != nil { return nil, err } @@ -38,7 +38,7 @@ func NewCC() (*router.Chaincode, error) { } func NewCCEncrypted() (*router.Chaincode, error) { - r, err := Router(`CommercialPaperEncrypted`) + r, err := CCRouter(`CommercialPaperEncrypted`) if err != nil { return nil, err } diff --git a/examples/cpaper_asservice/chaincode_test.go b/examples/cpaper_asservice/chaincode_test.go index f818ddfc..59ddf5b7 100644 --- a/examples/cpaper_asservice/chaincode_test.go +++ b/examples/cpaper_asservice/chaincode_test.go @@ -7,6 +7,7 @@ import ( "time" "github.com/golang/protobuf/ptypes" + "github.com/golang/protobuf/ptypes/empty" "github.com/hyperledger/fabric/protos/peer" . "github.com/onsi/ginkgo" . "github.com/onsi/gomega" @@ -118,7 +119,7 @@ var _ = Describe(`CommercialPaper`, func() { }) It("Allow issuer to get a list of commercial papers", func() { - queryResponse := cc.Query(s.CPaperChaincode_List) + queryResponse := cc.Query(s.CPaperChaincode_List, &empty.Empty{}) papers := expectcc.PayloadIs(queryResponse, &schema.CommercialPaperList{}).(*schema.CommercialPaperList) @@ -194,7 +195,7 @@ var _ = Describe(`CommercialPaper`, func() { })) // Validate there are 0 Commercial Papers in the world state - queryResponse := cc.Query(s.CPaperChaincode_List) + queryResponse := cc.Query(s.CPaperChaincode_List, &empty.Empty{}) papers := expectcc.PayloadIs(queryResponse, &schema.CommercialPaperList{}).(*schema.CommercialPaperList) Expect(len(papers.Items)).To(BeNumerically("==", 0)) diff --git a/examples/cpaper_asservice/schema/schema.pb.go b/examples/cpaper_asservice/schema/schema.pb.go index e6f61964..f8c1f666 100644 --- a/examples/cpaper_asservice/schema/schema.pb.go +++ b/examples/cpaper_asservice/schema/schema.pb.go @@ -1,28 +1,15 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // source: schema.proto -/* -Package schema is a generated protocol buffer package. - -It is generated from these files: - schema.proto - -It has these top-level messages: - CommercialPaper - CommercialPaperId - ExternalId - CommercialPaperList - IssueCommercialPaper - BuyCommercialPaper - RedeemCommercialPaper -*/ package schema -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" -import google_protobuf "github.com/golang/protobuf/ptypes/timestamp" -import _ "github.com/mwitkow/go-proto-validators" +import ( + fmt "fmt" + proto "github.com/golang/protobuf/proto" + timestamp "github.com/golang/protobuf/ptypes/timestamp" + _ "github.com/mwitkow/go-proto-validators" + math "math" +) // Reference imports to suppress errors if they are not otherwise used. var _ = proto.Marshal @@ -33,7 +20,7 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package type CommercialPaper_State int32 @@ -48,6 +35,7 @@ var CommercialPaper_State_name = map[int32]string{ 1: "TRADING", 2: "REDEEMED", } + var CommercialPaper_State_value = map[string]int32{ "ISSUED": 0, "TRADING": 1, @@ -57,26 +45,52 @@ var CommercialPaper_State_value = map[string]int32{ func (x CommercialPaper_State) String() string { return proto.EnumName(CommercialPaper_State_name, int32(x)) } -func (CommercialPaper_State) EnumDescriptor() ([]byte, []int) { return fileDescriptor0, []int{0, 0} } + +func (CommercialPaper_State) EnumDescriptor() ([]byte, []int) { + return fileDescriptor_1c5fb4d8cc22d66a, []int{0, 0} +} // Commercial Paper state entry type CommercialPaper struct { // Issuer and Paper number comprises composite primary key of Commercial paper entry - Issuer string `protobuf:"bytes,1,opt,name=issuer" json:"issuer,omitempty"` - PaperNumber string `protobuf:"bytes,2,opt,name=paper_number,json=paperNumber" json:"paper_number,omitempty"` - Owner string `protobuf:"bytes,3,opt,name=owner" json:"owner,omitempty"` - IssueDate *google_protobuf.Timestamp `protobuf:"bytes,4,opt,name=issue_date,json=issueDate" json:"issue_date,omitempty"` - MaturityDate *google_protobuf.Timestamp `protobuf:"bytes,5,opt,name=maturity_date,json=maturityDate" json:"maturity_date,omitempty"` - FaceValue int32 `protobuf:"varint,6,opt,name=face_value,json=faceValue" json:"face_value,omitempty"` - State CommercialPaper_State `protobuf:"varint,7,opt,name=state,enum=schema.CommercialPaper_State" json:"state,omitempty"` + Issuer string `protobuf:"bytes,1,opt,name=issuer,proto3" json:"issuer,omitempty"` + PaperNumber string `protobuf:"bytes,2,opt,name=paper_number,json=paperNumber,proto3" json:"paper_number,omitempty"` + Owner string `protobuf:"bytes,3,opt,name=owner,proto3" json:"owner,omitempty"` + IssueDate *timestamp.Timestamp `protobuf:"bytes,4,opt,name=issue_date,json=issueDate,proto3" json:"issue_date,omitempty"` + MaturityDate *timestamp.Timestamp `protobuf:"bytes,5,opt,name=maturity_date,json=maturityDate,proto3" json:"maturity_date,omitempty"` + FaceValue int32 `protobuf:"varint,6,opt,name=face_value,json=faceValue,proto3" json:"face_value,omitempty"` + State CommercialPaper_State `protobuf:"varint,7,opt,name=state,proto3,enum=schema.CommercialPaper_State" json:"state,omitempty"` // Additional unique field for entry - ExternalId string `protobuf:"bytes,8,opt,name=external_id,json=externalId" json:"external_id,omitempty"` + ExternalId string `protobuf:"bytes,8,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CommercialPaper) Reset() { *m = CommercialPaper{} } +func (m *CommercialPaper) String() string { return proto.CompactTextString(m) } +func (*CommercialPaper) ProtoMessage() {} +func (*CommercialPaper) Descriptor() ([]byte, []int) { + return fileDescriptor_1c5fb4d8cc22d66a, []int{0} +} + +func (m *CommercialPaper) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CommercialPaper.Unmarshal(m, b) +} +func (m *CommercialPaper) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CommercialPaper.Marshal(b, m, deterministic) +} +func (m *CommercialPaper) XXX_Merge(src proto.Message) { + xxx_messageInfo_CommercialPaper.Merge(m, src) +} +func (m *CommercialPaper) XXX_Size() int { + return xxx_messageInfo_CommercialPaper.Size(m) +} +func (m *CommercialPaper) XXX_DiscardUnknown() { + xxx_messageInfo_CommercialPaper.DiscardUnknown(m) } -func (m *CommercialPaper) Reset() { *m = CommercialPaper{} } -func (m *CommercialPaper) String() string { return proto.CompactTextString(m) } -func (*CommercialPaper) ProtoMessage() {} -func (*CommercialPaper) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{0} } +var xxx_messageInfo_CommercialPaper proto.InternalMessageInfo func (m *CommercialPaper) GetIssuer() string { if m != nil { @@ -99,14 +113,14 @@ func (m *CommercialPaper) GetOwner() string { return "" } -func (m *CommercialPaper) GetIssueDate() *google_protobuf.Timestamp { +func (m *CommercialPaper) GetIssueDate() *timestamp.Timestamp { if m != nil { return m.IssueDate } return nil } -func (m *CommercialPaper) GetMaturityDate() *google_protobuf.Timestamp { +func (m *CommercialPaper) GetMaturityDate() *timestamp.Timestamp { if m != nil { return m.MaturityDate } @@ -136,14 +150,37 @@ func (m *CommercialPaper) GetExternalId() string { // CommercialPaperId identifier part type CommercialPaperId struct { - Issuer string `protobuf:"bytes,1,opt,name=issuer" json:"issuer,omitempty"` - PaperNumber string `protobuf:"bytes,2,opt,name=paper_number,json=paperNumber" json:"paper_number,omitempty"` + Issuer string `protobuf:"bytes,1,opt,name=issuer,proto3" json:"issuer,omitempty"` + PaperNumber string `protobuf:"bytes,2,opt,name=paper_number,json=paperNumber,proto3" json:"paper_number,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *CommercialPaperId) Reset() { *m = CommercialPaperId{} } -func (m *CommercialPaperId) String() string { return proto.CompactTextString(m) } -func (*CommercialPaperId) ProtoMessage() {} -func (*CommercialPaperId) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{1} } +func (m *CommercialPaperId) Reset() { *m = CommercialPaperId{} } +func (m *CommercialPaperId) String() string { return proto.CompactTextString(m) } +func (*CommercialPaperId) ProtoMessage() {} +func (*CommercialPaperId) Descriptor() ([]byte, []int) { + return fileDescriptor_1c5fb4d8cc22d66a, []int{1} +} + +func (m *CommercialPaperId) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CommercialPaperId.Unmarshal(m, b) +} +func (m *CommercialPaperId) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CommercialPaperId.Marshal(b, m, deterministic) +} +func (m *CommercialPaperId) XXX_Merge(src proto.Message) { + xxx_messageInfo_CommercialPaperId.Merge(m, src) +} +func (m *CommercialPaperId) XXX_Size() int { + return xxx_messageInfo_CommercialPaperId.Size(m) +} +func (m *CommercialPaperId) XXX_DiscardUnknown() { + xxx_messageInfo_CommercialPaperId.DiscardUnknown(m) +} + +var xxx_messageInfo_CommercialPaperId proto.InternalMessageInfo func (m *CommercialPaperId) GetIssuer() string { if m != nil { @@ -161,13 +198,36 @@ func (m *CommercialPaperId) GetPaperNumber() string { // ExternalId type ExternalId struct { - Id string `protobuf:"bytes,1,opt,name=id" json:"id,omitempty"` + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *ExternalId) Reset() { *m = ExternalId{} } -func (m *ExternalId) String() string { return proto.CompactTextString(m) } -func (*ExternalId) ProtoMessage() {} -func (*ExternalId) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{2} } +func (m *ExternalId) Reset() { *m = ExternalId{} } +func (m *ExternalId) String() string { return proto.CompactTextString(m) } +func (*ExternalId) ProtoMessage() {} +func (*ExternalId) Descriptor() ([]byte, []int) { + return fileDescriptor_1c5fb4d8cc22d66a, []int{2} +} + +func (m *ExternalId) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_ExternalId.Unmarshal(m, b) +} +func (m *ExternalId) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_ExternalId.Marshal(b, m, deterministic) +} +func (m *ExternalId) XXX_Merge(src proto.Message) { + xxx_messageInfo_ExternalId.Merge(m, src) +} +func (m *ExternalId) XXX_Size() int { + return xxx_messageInfo_ExternalId.Size(m) +} +func (m *ExternalId) XXX_DiscardUnknown() { + xxx_messageInfo_ExternalId.DiscardUnknown(m) +} + +var xxx_messageInfo_ExternalId proto.InternalMessageInfo func (m *ExternalId) GetId() string { if m != nil { @@ -178,13 +238,36 @@ func (m *ExternalId) GetId() string { // Container for returning multiple entities type CommercialPaperList struct { - Items []*CommercialPaper `protobuf:"bytes,1,rep,name=items" json:"items,omitempty"` + Items []*CommercialPaper `protobuf:"bytes,1,rep,name=items,proto3" json:"items,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *CommercialPaperList) Reset() { *m = CommercialPaperList{} } +func (m *CommercialPaperList) String() string { return proto.CompactTextString(m) } +func (*CommercialPaperList) ProtoMessage() {} +func (*CommercialPaperList) Descriptor() ([]byte, []int) { + return fileDescriptor_1c5fb4d8cc22d66a, []int{3} +} + +func (m *CommercialPaperList) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_CommercialPaperList.Unmarshal(m, b) +} +func (m *CommercialPaperList) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_CommercialPaperList.Marshal(b, m, deterministic) +} +func (m *CommercialPaperList) XXX_Merge(src proto.Message) { + xxx_messageInfo_CommercialPaperList.Merge(m, src) +} +func (m *CommercialPaperList) XXX_Size() int { + return xxx_messageInfo_CommercialPaperList.Size(m) +} +func (m *CommercialPaperList) XXX_DiscardUnknown() { + xxx_messageInfo_CommercialPaperList.DiscardUnknown(m) } -func (m *CommercialPaperList) Reset() { *m = CommercialPaperList{} } -func (m *CommercialPaperList) String() string { return proto.CompactTextString(m) } -func (*CommercialPaperList) ProtoMessage() {} -func (*CommercialPaperList) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{3} } +var xxx_messageInfo_CommercialPaperList proto.InternalMessageInfo func (m *CommercialPaperList) GetItems() []*CommercialPaper { if m != nil { @@ -195,19 +278,42 @@ func (m *CommercialPaperList) GetItems() []*CommercialPaper { // IssueCommercialPaper event type IssueCommercialPaper struct { - Issuer string `protobuf:"bytes,1,opt,name=issuer" json:"issuer,omitempty"` - PaperNumber string `protobuf:"bytes,2,opt,name=paper_number,json=paperNumber" json:"paper_number,omitempty"` - IssueDate *google_protobuf.Timestamp `protobuf:"bytes,3,opt,name=issue_date,json=issueDate" json:"issue_date,omitempty"` - MaturityDate *google_protobuf.Timestamp `protobuf:"bytes,4,opt,name=maturity_date,json=maturityDate" json:"maturity_date,omitempty"` - FaceValue int32 `protobuf:"varint,5,opt,name=face_value,json=faceValue" json:"face_value,omitempty"` + Issuer string `protobuf:"bytes,1,opt,name=issuer,proto3" json:"issuer,omitempty"` + PaperNumber string `protobuf:"bytes,2,opt,name=paper_number,json=paperNumber,proto3" json:"paper_number,omitempty"` + IssueDate *timestamp.Timestamp `protobuf:"bytes,3,opt,name=issue_date,json=issueDate,proto3" json:"issue_date,omitempty"` + MaturityDate *timestamp.Timestamp `protobuf:"bytes,4,opt,name=maturity_date,json=maturityDate,proto3" json:"maturity_date,omitempty"` + FaceValue int32 `protobuf:"varint,5,opt,name=face_value,json=faceValue,proto3" json:"face_value,omitempty"` // external_id - once more uniq id of state entry - ExternalId string `protobuf:"bytes,6,opt,name=external_id,json=externalId" json:"external_id,omitempty"` + ExternalId string `protobuf:"bytes,6,opt,name=external_id,json=externalId,proto3" json:"external_id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` +} + +func (m *IssueCommercialPaper) Reset() { *m = IssueCommercialPaper{} } +func (m *IssueCommercialPaper) String() string { return proto.CompactTextString(m) } +func (*IssueCommercialPaper) ProtoMessage() {} +func (*IssueCommercialPaper) Descriptor() ([]byte, []int) { + return fileDescriptor_1c5fb4d8cc22d66a, []int{4} +} + +func (m *IssueCommercialPaper) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_IssueCommercialPaper.Unmarshal(m, b) +} +func (m *IssueCommercialPaper) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_IssueCommercialPaper.Marshal(b, m, deterministic) +} +func (m *IssueCommercialPaper) XXX_Merge(src proto.Message) { + xxx_messageInfo_IssueCommercialPaper.Merge(m, src) +} +func (m *IssueCommercialPaper) XXX_Size() int { + return xxx_messageInfo_IssueCommercialPaper.Size(m) +} +func (m *IssueCommercialPaper) XXX_DiscardUnknown() { + xxx_messageInfo_IssueCommercialPaper.DiscardUnknown(m) } -func (m *IssueCommercialPaper) Reset() { *m = IssueCommercialPaper{} } -func (m *IssueCommercialPaper) String() string { return proto.CompactTextString(m) } -func (*IssueCommercialPaper) ProtoMessage() {} -func (*IssueCommercialPaper) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{4} } +var xxx_messageInfo_IssueCommercialPaper proto.InternalMessageInfo func (m *IssueCommercialPaper) GetIssuer() string { if m != nil { @@ -223,14 +329,14 @@ func (m *IssueCommercialPaper) GetPaperNumber() string { return "" } -func (m *IssueCommercialPaper) GetIssueDate() *google_protobuf.Timestamp { +func (m *IssueCommercialPaper) GetIssueDate() *timestamp.Timestamp { if m != nil { return m.IssueDate } return nil } -func (m *IssueCommercialPaper) GetMaturityDate() *google_protobuf.Timestamp { +func (m *IssueCommercialPaper) GetMaturityDate() *timestamp.Timestamp { if m != nil { return m.MaturityDate } @@ -253,18 +359,41 @@ func (m *IssueCommercialPaper) GetExternalId() string { // BuyCommercialPaper event type BuyCommercialPaper struct { - Issuer string `protobuf:"bytes,1,opt,name=issuer" json:"issuer,omitempty"` - PaperNumber string `protobuf:"bytes,2,opt,name=paper_number,json=paperNumber" json:"paper_number,omitempty"` - CurrentOwner string `protobuf:"bytes,3,opt,name=current_owner,json=currentOwner" json:"current_owner,omitempty"` - NewOwner string `protobuf:"bytes,4,opt,name=new_owner,json=newOwner" json:"new_owner,omitempty"` - Price int32 `protobuf:"varint,5,opt,name=price" json:"price,omitempty"` - PurchaseDate *google_protobuf.Timestamp `protobuf:"bytes,6,opt,name=purchase_date,json=purchaseDate" json:"purchase_date,omitempty"` + Issuer string `protobuf:"bytes,1,opt,name=issuer,proto3" json:"issuer,omitempty"` + PaperNumber string `protobuf:"bytes,2,opt,name=paper_number,json=paperNumber,proto3" json:"paper_number,omitempty"` + CurrentOwner string `protobuf:"bytes,3,opt,name=current_owner,json=currentOwner,proto3" json:"current_owner,omitempty"` + NewOwner string `protobuf:"bytes,4,opt,name=new_owner,json=newOwner,proto3" json:"new_owner,omitempty"` + Price int32 `protobuf:"varint,5,opt,name=price,proto3" json:"price,omitempty"` + PurchaseDate *timestamp.Timestamp `protobuf:"bytes,6,opt,name=purchase_date,json=purchaseDate,proto3" json:"purchase_date,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *BuyCommercialPaper) Reset() { *m = BuyCommercialPaper{} } -func (m *BuyCommercialPaper) String() string { return proto.CompactTextString(m) } -func (*BuyCommercialPaper) ProtoMessage() {} -func (*BuyCommercialPaper) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{5} } +func (m *BuyCommercialPaper) Reset() { *m = BuyCommercialPaper{} } +func (m *BuyCommercialPaper) String() string { return proto.CompactTextString(m) } +func (*BuyCommercialPaper) ProtoMessage() {} +func (*BuyCommercialPaper) Descriptor() ([]byte, []int) { + return fileDescriptor_1c5fb4d8cc22d66a, []int{5} +} + +func (m *BuyCommercialPaper) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_BuyCommercialPaper.Unmarshal(m, b) +} +func (m *BuyCommercialPaper) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_BuyCommercialPaper.Marshal(b, m, deterministic) +} +func (m *BuyCommercialPaper) XXX_Merge(src proto.Message) { + xxx_messageInfo_BuyCommercialPaper.Merge(m, src) +} +func (m *BuyCommercialPaper) XXX_Size() int { + return xxx_messageInfo_BuyCommercialPaper.Size(m) +} +func (m *BuyCommercialPaper) XXX_DiscardUnknown() { + xxx_messageInfo_BuyCommercialPaper.DiscardUnknown(m) +} + +var xxx_messageInfo_BuyCommercialPaper proto.InternalMessageInfo func (m *BuyCommercialPaper) GetIssuer() string { if m != nil { @@ -301,7 +430,7 @@ func (m *BuyCommercialPaper) GetPrice() int32 { return 0 } -func (m *BuyCommercialPaper) GetPurchaseDate() *google_protobuf.Timestamp { +func (m *BuyCommercialPaper) GetPurchaseDate() *timestamp.Timestamp { if m != nil { return m.PurchaseDate } @@ -310,16 +439,39 @@ func (m *BuyCommercialPaper) GetPurchaseDate() *google_protobuf.Timestamp { // RedeemCommercialPaper event type RedeemCommercialPaper struct { - Issuer string `protobuf:"bytes,1,opt,name=issuer" json:"issuer,omitempty"` - PaperNumber string `protobuf:"bytes,2,opt,name=paper_number,json=paperNumber" json:"paper_number,omitempty"` - RedeemingOwner string `protobuf:"bytes,3,opt,name=redeeming_owner,json=redeemingOwner" json:"redeeming_owner,omitempty"` - RedeemDate *google_protobuf.Timestamp `protobuf:"bytes,4,opt,name=redeem_date,json=redeemDate" json:"redeem_date,omitempty"` + Issuer string `protobuf:"bytes,1,opt,name=issuer,proto3" json:"issuer,omitempty"` + PaperNumber string `protobuf:"bytes,2,opt,name=paper_number,json=paperNumber,proto3" json:"paper_number,omitempty"` + RedeemingOwner string `protobuf:"bytes,3,opt,name=redeeming_owner,json=redeemingOwner,proto3" json:"redeeming_owner,omitempty"` + RedeemDate *timestamp.Timestamp `protobuf:"bytes,4,opt,name=redeem_date,json=redeemDate,proto3" json:"redeem_date,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } -func (m *RedeemCommercialPaper) Reset() { *m = RedeemCommercialPaper{} } -func (m *RedeemCommercialPaper) String() string { return proto.CompactTextString(m) } -func (*RedeemCommercialPaper) ProtoMessage() {} -func (*RedeemCommercialPaper) Descriptor() ([]byte, []int) { return fileDescriptor0, []int{6} } +func (m *RedeemCommercialPaper) Reset() { *m = RedeemCommercialPaper{} } +func (m *RedeemCommercialPaper) String() string { return proto.CompactTextString(m) } +func (*RedeemCommercialPaper) ProtoMessage() {} +func (*RedeemCommercialPaper) Descriptor() ([]byte, []int) { + return fileDescriptor_1c5fb4d8cc22d66a, []int{6} +} + +func (m *RedeemCommercialPaper) XXX_Unmarshal(b []byte) error { + return xxx_messageInfo_RedeemCommercialPaper.Unmarshal(m, b) +} +func (m *RedeemCommercialPaper) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + return xxx_messageInfo_RedeemCommercialPaper.Marshal(b, m, deterministic) +} +func (m *RedeemCommercialPaper) XXX_Merge(src proto.Message) { + xxx_messageInfo_RedeemCommercialPaper.Merge(m, src) +} +func (m *RedeemCommercialPaper) XXX_Size() int { + return xxx_messageInfo_RedeemCommercialPaper.Size(m) +} +func (m *RedeemCommercialPaper) XXX_DiscardUnknown() { + xxx_messageInfo_RedeemCommercialPaper.DiscardUnknown(m) +} + +var xxx_messageInfo_RedeemCommercialPaper proto.InternalMessageInfo func (m *RedeemCommercialPaper) GetIssuer() string { if m != nil { @@ -342,7 +494,7 @@ func (m *RedeemCommercialPaper) GetRedeemingOwner() string { return "" } -func (m *RedeemCommercialPaper) GetRedeemDate() *google_protobuf.Timestamp { +func (m *RedeemCommercialPaper) GetRedeemDate() *timestamp.Timestamp { if m != nil { return m.RedeemDate } @@ -350,6 +502,7 @@ func (m *RedeemCommercialPaper) GetRedeemDate() *google_protobuf.Timestamp { } func init() { + proto.RegisterEnum("schema.CommercialPaper_State", CommercialPaper_State_name, CommercialPaper_State_value) proto.RegisterType((*CommercialPaper)(nil), "schema.CommercialPaper") proto.RegisterType((*CommercialPaperId)(nil), "schema.CommercialPaperId") proto.RegisterType((*ExternalId)(nil), "schema.ExternalId") @@ -357,12 +510,11 @@ func init() { proto.RegisterType((*IssueCommercialPaper)(nil), "schema.IssueCommercialPaper") proto.RegisterType((*BuyCommercialPaper)(nil), "schema.BuyCommercialPaper") proto.RegisterType((*RedeemCommercialPaper)(nil), "schema.RedeemCommercialPaper") - proto.RegisterEnum("schema.CommercialPaper_State", CommercialPaper_State_name, CommercialPaper_State_value) } -func init() { proto.RegisterFile("schema.proto", fileDescriptor0) } +func init() { proto.RegisterFile("schema.proto", fileDescriptor_1c5fb4d8cc22d66a) } -var fileDescriptor0 = []byte{ +var fileDescriptor_1c5fb4d8cc22d66a = []byte{ // 597 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xb4, 0x53, 0x5d, 0x6b, 0xd4, 0x40, 0x14, 0x6d, 0xb2, 0x4d, 0xda, 0xbd, 0xbb, 0x6d, 0xd7, 0xb1, 0x6a, 0x28, 0xad, 0x8d, 0x11, 0x71, diff --git a/examples/cpaper_asservice/schema/schema.proto b/examples/cpaper_asservice/schema/schema.proto index dd83e992..05c5e71a 100644 --- a/examples/cpaper_asservice/schema/schema.proto +++ b/examples/cpaper_asservice/schema/schema.proto @@ -2,7 +2,6 @@ syntax = "proto3"; package schema; - import "google/protobuf/timestamp.proto"; import "github.com/mwitkow/go-proto-validators/validator.proto"; diff --git a/examples/cpaper_asservice/service/service.pb.cc.go b/examples/cpaper_asservice/service/service.pb.cc.go index cb0e5269..68ce676e 100644 --- a/examples/cpaper_asservice/service/service.pb.cc.go +++ b/examples/cpaper_asservice/service/service.pb.cc.go @@ -1,108 +1,145 @@ +// Code generated by protoc-gen-cc-gateway. DO NOT EDIT. +// source: service.proto + +/* +Package service contains + * chaincode interface definition + * chaincode gateway definition + * chaincode service to cckit router registration func +*/ package service -// todo: be autogenerated - import ( - "context" + context "context" "github.com/golang/protobuf/ptypes/empty" - "github.com/hyperledger/fabric/msp" "github.com/s7techlab/cckit/examples/cpaper_asservice/schema" - "github.com/s7techlab/cckit/gateway" - "github.com/s7techlab/cckit/gateway/service" - "github.com/s7techlab/cckit/router" - "github.com/s7techlab/cckit/router/param/defparam" + cckit_gateway "github.com/s7techlab/cckit/gateway" + cckit_ccservice "github.com/s7techlab/cckit/gateway/service" + cckit_router "github.com/s7techlab/cckit/router" + cckit_defparam "github.com/s7techlab/cckit/router/param/defparam" ) -// chaincode methods interface -type CPaperChaincode interface { - List(router.Context, *empty.Empty) (*schema.CommercialPaperList, error) - Get(router.Context, *schema.CommercialPaperId) (*schema.CommercialPaper, error) - GetByExternalId(router.Context, *schema.ExternalId) (*schema.CommercialPaper, error) - Issue(router.Context, *schema.IssueCommercialPaper) (*schema.CommercialPaper, error) - Buy(router.Context, *schema.BuyCommercialPaper) (*schema.CommercialPaper, error) - Redeem(router.Context, *schema.RedeemCommercialPaper) (*schema.CommercialPaper, error) - Delete(router.Context, *schema.CommercialPaperId) (*schema.CommercialPaper, error) -} - -// chaincode method names without router group namespace +// CPaperChaincode method names const ( - CPaperChaincode_List = `List` - CPaperChaincode_Get = `Get` - CPaperChaincode_GetByExternalId = `GetByExternalId` - CPaperChaincode_Issue = `Issue` - CPaperChaincode_Buy = `Buy` - CPaperChaincode_Redeem = `Redeem` - CPaperChaincode_Delete = `Delete` + CPaperChaincode_List = "List" + + CPaperChaincode_Get = "Get" + + CPaperChaincode_GetByExternalId = "GetByExternalId" + + CPaperChaincode_Issue = "Issue" + + CPaperChaincode_Buy = "Buy" + + CPaperChaincode_Redeem = "Redeem" + + CPaperChaincode_Delete = "Delete" ) +// CPaperChaincode chaincode methods interface +type CPaperChaincode interface { + List(cckit_router.Context, *empty.Empty) (*schema.CommercialPaperList, error) + + Get(cckit_router.Context, *schema.CommercialPaperId) (*schema.CommercialPaper, error) + + GetByExternalId(cckit_router.Context, *schema.ExternalId) (*schema.CommercialPaper, error) + + Issue(cckit_router.Context, *schema.IssueCommercialPaper) (*schema.CommercialPaper, error) + + Buy(cckit_router.Context, *schema.BuyCommercialPaper) (*schema.CommercialPaper, error) + + Redeem(cckit_router.Context, *schema.RedeemCommercialPaper) (*schema.CommercialPaper, error) + + Delete(cckit_router.Context, *schema.CommercialPaperId) (*schema.CommercialPaper, error) +} + // RegisterCPaperChaincode registers service methods as chaincode router handlers -func RegisterCPaperChaincode(r *router.Group, cc CPaperChaincode) error { +func RegisterCPaperChaincode(r *cckit_router.Group, cc CPaperChaincode) error { r.Query(CPaperChaincode_List, - func(ctx router.Context) (interface{}, error) { - return cc.List(ctx, nil) - }) + func(ctx cckit_router.Context) (interface{}, error) { + return cc.List(ctx, ctx.Param().(*empty.Empty)) + }, + cckit_defparam.Proto(&empty.Empty{})) r.Query(CPaperChaincode_Get, - func(ctx router.Context) (interface{}, error) { + func(ctx cckit_router.Context) (interface{}, error) { return cc.Get(ctx, ctx.Param().(*schema.CommercialPaperId)) }, - defparam.Proto(&schema.CommercialPaperId{})) + cckit_defparam.Proto(&schema.CommercialPaperId{})) - r.Invoke(CPaperChaincode_GetByExternalId, - func(ctx router.Context) (interface{}, error) { + r.Query(CPaperChaincode_GetByExternalId, + func(ctx cckit_router.Context) (interface{}, error) { return cc.GetByExternalId(ctx, ctx.Param().(*schema.ExternalId)) }, - defparam.Proto(&schema.ExternalId{})) + cckit_defparam.Proto(&schema.ExternalId{})) r.Invoke(CPaperChaincode_Issue, - func(ctx router.Context) (interface{}, error) { + func(ctx cckit_router.Context) (interface{}, error) { return cc.Issue(ctx, ctx.Param().(*schema.IssueCommercialPaper)) }, - defparam.Proto(&schema.IssueCommercialPaper{})) + cckit_defparam.Proto(&schema.IssueCommercialPaper{})) r.Invoke(CPaperChaincode_Buy, - func(ctx router.Context) (interface{}, error) { + func(ctx cckit_router.Context) (interface{}, error) { return cc.Buy(ctx, ctx.Param().(*schema.BuyCommercialPaper)) }, - defparam.Proto(&schema.BuyCommercialPaper{})) + cckit_defparam.Proto(&schema.BuyCommercialPaper{})) r.Invoke(CPaperChaincode_Redeem, - func(ctx router.Context) (interface{}, error) { + func(ctx cckit_router.Context) (interface{}, error) { return cc.Redeem(ctx, ctx.Param().(*schema.RedeemCommercialPaper)) }, - defparam.Proto(&schema.RedeemCommercialPaper{})) + cckit_defparam.Proto(&schema.RedeemCommercialPaper{})) r.Invoke(CPaperChaincode_Delete, - func(ctx router.Context) (interface{}, error) { + func(ctx cckit_router.Context) (interface{}, error) { return cc.Delete(ctx, ctx.Param().(*schema.CommercialPaperId)) }, - defparam.Proto(&schema.CommercialPaperId{})) + cckit_defparam.Proto(&schema.CommercialPaperId{})) return nil } -func NewCPaperGateway(ccService service.Chaincode, channel, chaincode string, signer msp.SigningIdentity) *CPaperGateway { - return &CPaperGateway{Gateway: gateway.NewChaincode(ccService, channel, chaincode, gateway.WithDefaultSigner(signer))} +// NewCPaperGateway creates gateway to access chaincode method via chaincode service +func NewCPaperGateway(ccService cckit_ccservice.Chaincode, channel, chaincode string, opts ...cckit_gateway.Opt) *CPaperGateway { + return &CPaperGateway{Gateway: cckit_gateway.NewChaincode(ccService, channel, chaincode, opts...)} +} + +// gateway implementation +// gateway can be used as kind of SDK, GRPC or REST server ( via grpc-gateway or clay ) +type CPaperGateway struct { + Gateway cckit_gateway.Chaincode } -func CPaperApiDef(service *CPaperGateway) gateway.ServiceDef { - return gateway.ServiceDef{ +// ApiDef returns service definition +func (c *CPaperGateway) ApiDef() cckit_gateway.ServiceDef { + return cckit_gateway.ServiceDef{ Desc: &_CPaper_serviceDesc, - Service: service, + Service: c, HandlerFromEndpointRegister: RegisterCPaperHandlerFromEndpoint, } } -// gateway implementation -// gateway can be used as kind of SDK, GRPC or REST server ( via grpc-gateway or clay ) -type CPaperGateway struct { - Gateway gateway.Chaincode +// Events returns events subscription +func (c *CPaperGateway) Events(ctx context.Context) (cckit_gateway.ChaincodeEventSub, error) { + return c.Gateway.Events(ctx) +} + +type ValidatorInterface interface { + Validate() error } func (c *CPaperGateway) List(ctx context.Context, in *empty.Empty) (*schema.CommercialPaperList, error) { - if res, err := c.Gateway.Query(ctx, CPaperChaincode_List, []interface{}{}, &schema.CommercialPaperList{}); err != nil { + var inMsg interface{} = in + if v, ok := inMsg.(ValidatorInterface); ok { + if err := v.Validate(); err != nil { + return nil, err + } + } + + if res, err := c.Gateway.Query(ctx, CPaperChaincode_List, []interface{}{in}, &schema.CommercialPaperList{}); err != nil { return nil, err } else { return res.(*schema.CommercialPaperList), nil @@ -110,6 +147,13 @@ func (c *CPaperGateway) List(ctx context.Context, in *empty.Empty) (*schema.Comm } func (c *CPaperGateway) Get(ctx context.Context, in *schema.CommercialPaperId) (*schema.CommercialPaper, error) { + var inMsg interface{} = in + if v, ok := inMsg.(ValidatorInterface); ok { + if err := v.Validate(); err != nil { + return nil, err + } + } + if res, err := c.Gateway.Query(ctx, CPaperChaincode_Get, []interface{}{in}, &schema.CommercialPaper{}); err != nil { return nil, err } else { @@ -118,6 +162,13 @@ func (c *CPaperGateway) Get(ctx context.Context, in *schema.CommercialPaperId) ( } func (c *CPaperGateway) GetByExternalId(ctx context.Context, in *schema.ExternalId) (*schema.CommercialPaper, error) { + var inMsg interface{} = in + if v, ok := inMsg.(ValidatorInterface); ok { + if err := v.Validate(); err != nil { + return nil, err + } + } + if res, err := c.Gateway.Query(ctx, CPaperChaincode_GetByExternalId, []interface{}{in}, &schema.CommercialPaper{}); err != nil { return nil, err } else { @@ -126,9 +177,14 @@ func (c *CPaperGateway) GetByExternalId(ctx context.Context, in *schema.External } func (c *CPaperGateway) Issue(ctx context.Context, in *schema.IssueCommercialPaper) (*schema.CommercialPaper, error) { - if err := in.Validate(); err != nil { - return nil, err - } else if res, err := c.Gateway.Invoke(ctx, CPaperChaincode_Issue, []interface{}{in}, &schema.CommercialPaper{}); err != nil { + var inMsg interface{} = in + if v, ok := inMsg.(ValidatorInterface); ok { + if err := v.Validate(); err != nil { + return nil, err + } + } + + if res, err := c.Gateway.Invoke(ctx, CPaperChaincode_Issue, []interface{}{in}, &schema.CommercialPaper{}); err != nil { return nil, err } else { return res.(*schema.CommercialPaper), nil @@ -136,9 +192,14 @@ func (c *CPaperGateway) Issue(ctx context.Context, in *schema.IssueCommercialPap } func (c *CPaperGateway) Buy(ctx context.Context, in *schema.BuyCommercialPaper) (*schema.CommercialPaper, error) { - if err := in.Validate(); err != nil { - return nil, err - } else if res, err := c.Gateway.Invoke(ctx, CPaperChaincode_Buy, []interface{}{in}, &schema.CommercialPaper{}); err != nil { + var inMsg interface{} = in + if v, ok := inMsg.(ValidatorInterface); ok { + if err := v.Validate(); err != nil { + return nil, err + } + } + + if res, err := c.Gateway.Invoke(ctx, CPaperChaincode_Buy, []interface{}{in}, &schema.CommercialPaper{}); err != nil { return nil, err } else { return res.(*schema.CommercialPaper), nil @@ -146,9 +207,14 @@ func (c *CPaperGateway) Buy(ctx context.Context, in *schema.BuyCommercialPaper) } func (c *CPaperGateway) Redeem(ctx context.Context, in *schema.RedeemCommercialPaper) (*schema.CommercialPaper, error) { - if err := in.Validate(); err != nil { - return nil, err - } else if res, err := c.Gateway.Invoke(ctx, CPaperChaincode_Redeem, []interface{}{in}, &schema.CommercialPaper{}); err != nil { + var inMsg interface{} = in + if v, ok := inMsg.(ValidatorInterface); ok { + if err := v.Validate(); err != nil { + return nil, err + } + } + + if res, err := c.Gateway.Invoke(ctx, CPaperChaincode_Redeem, []interface{}{in}, &schema.CommercialPaper{}); err != nil { return nil, err } else { return res.(*schema.CommercialPaper), nil @@ -156,9 +222,14 @@ func (c *CPaperGateway) Redeem(ctx context.Context, in *schema.RedeemCommercialP } func (c *CPaperGateway) Delete(ctx context.Context, in *schema.CommercialPaperId) (*schema.CommercialPaper, error) { - if err := in.Validate(); err != nil { - return nil, err - } else if res, err := c.Gateway.Invoke(ctx, CPaperChaincode_Delete, []interface{}{in}, &schema.CommercialPaper{}); err != nil { + var inMsg interface{} = in + if v, ok := inMsg.(ValidatorInterface); ok { + if err := v.Validate(); err != nil { + return nil, err + } + } + + if res, err := c.Gateway.Invoke(ctx, CPaperChaincode_Delete, []interface{}{in}, &schema.CommercialPaper{}); err != nil { return nil, err } else { return res.(*schema.CommercialPaper), nil diff --git a/examples/cpaper_asservice/service/service.pb.go b/examples/cpaper_asservice/service/service.pb.go index 126a9aa6..f43ca883 100644 --- a/examples/cpaper_asservice/service/service.pb.go +++ b/examples/cpaper_asservice/service/service.pb.go @@ -1,26 +1,17 @@ // Code generated by protoc-gen-go. DO NOT EDIT. // source: service.proto -/* -Package service is a generated protocol buffer package. - -It is generated from these files: - service.proto - -It has these top-level messages: -*/ package service -import proto "github.com/golang/protobuf/proto" -import fmt "fmt" -import math "math" -import _ "google.golang.org/genproto/googleapis/api/annotations" -import google_protobuf1 "github.com/golang/protobuf/ptypes/empty" -import schema "github.com/s7techlab/cckit/examples/cpaper_asservice/schema" - import ( - context "golang.org/x/net/context" + context "context" + fmt "fmt" + proto "github.com/golang/protobuf/proto" + empty "github.com/golang/protobuf/ptypes/empty" + schema "github.com/s7techlab/cckit/examples/cpaper_asservice/schema" + _ "google.golang.org/genproto/googleapis/api/annotations" grpc "google.golang.org/grpc" + math "math" ) // Reference imports to suppress errors if they are not otherwise used. @@ -32,7 +23,37 @@ var _ = math.Inf // is compatible with the proto package it is being compiled against. // A compilation error at this line likely means your copy of the // proto package needs to be updated. -const _ = proto.ProtoPackageIsVersion2 // please upgrade the proto package +const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package + +func init() { proto.RegisterFile("service.proto", fileDescriptor_a0b84a42fa06f626) } + +var fileDescriptor_a0b84a42fa06f626 = []byte{ + // 380 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x92, 0xd1, 0xaa, 0xd3, 0x30, + 0x18, 0xc7, 0xc1, 0xcd, 0x0e, 0x23, 0x73, 0xf2, 0xa9, 0x13, 0xbb, 0x89, 0xd8, 0x1b, 0xc1, 0x8b, + 0x06, 0xf4, 0xc2, 0xfb, 0xcd, 0x31, 0x07, 0x03, 0x45, 0x10, 0xc4, 0x1b, 0x49, 0xd3, 0xcf, 0x2d, + 0xd8, 0x34, 0x25, 0x49, 0x65, 0x65, 0xec, 0xc6, 0x57, 0xf0, 0xd1, 0x7c, 0x85, 0xf3, 0x1e, 0xe7, + 0xd0, 0x34, 0xe1, 0xc0, 0x81, 0x72, 0x06, 0xe7, 0xaa, 0xfc, 0xfb, 0xe5, 0xff, 0xfb, 0x11, 0xbe, + 0x90, 0xb1, 0x41, 0xfd, 0x47, 0x70, 0x4c, 0x2b, 0xad, 0xac, 0x82, 0x91, 0x8f, 0xf1, 0x7c, 0xa7, + 0xd4, 0xae, 0x40, 0xca, 0x2a, 0x41, 0x59, 0x59, 0x2a, 0xcb, 0xac, 0x50, 0xa5, 0xe9, 0x8e, 0xc5, + 0x33, 0x3f, 0x75, 0x29, 0xab, 0x7f, 0x51, 0x94, 0x95, 0x6d, 0xfc, 0xf0, 0xd3, 0x4e, 0xd8, 0x7d, + 0x9d, 0xa5, 0x5c, 0x49, 0x6a, 0x3e, 0x58, 0xe4, 0xfb, 0x82, 0x65, 0x94, 0xf3, 0xdf, 0xc2, 0x52, + 0x3c, 0x30, 0x59, 0x15, 0x68, 0x28, 0xaf, 0x58, 0x85, 0xfa, 0x27, 0x33, 0x5e, 0x48, 0x0d, 0xdf, + 0xa3, 0x64, 0xfe, 0xd3, 0x91, 0xde, 0x5d, 0x0e, 0x49, 0xb4, 0xfc, 0xd2, 0x1e, 0x84, 0x2d, 0x19, + 0x6e, 0x85, 0xb1, 0x30, 0x4d, 0x3b, 0x75, 0x1a, 0xd4, 0xe9, 0xaa, 0x55, 0xc7, 0xb3, 0xd4, 0x37, + 0x97, 0x4a, 0x4a, 0xd4, 0x5c, 0xb0, 0xc2, 0x15, 0xdb, 0x52, 0x32, 0xf9, 0xfb, 0xff, 0xe2, 0xdf, + 0xbd, 0x07, 0x30, 0xf2, 0x5a, 0xc8, 0xc8, 0x60, 0x8d, 0x16, 0x5e, 0xf4, 0x94, 0x36, 0x79, 0xfc, + 0xbc, 0x67, 0x94, 0xbc, 0x71, 0xac, 0xd7, 0xf0, 0xca, 0xb3, 0xe8, 0x51, 0x18, 0x53, 0xa3, 0x3e, + 0xd1, 0x63, 0x77, 0xa5, 0xb2, 0x96, 0x19, 0xea, 0x13, 0xfc, 0x20, 0x93, 0x35, 0xda, 0x45, 0xb3, + 0x3a, 0x58, 0xd4, 0x25, 0x2b, 0x36, 0x39, 0x40, 0x80, 0x5e, 0xff, 0xeb, 0x17, 0xc5, 0x4e, 0xf4, + 0x14, 0x20, 0x88, 0xf0, 0x60, 0x45, 0x4e, 0x8f, 0x22, 0x3f, 0xc1, 0x37, 0x72, 0x7f, 0xd3, 0x5a, + 0x61, 0x1e, 0xda, 0x2e, 0xde, 0x40, 0xf4, 0xb3, 0x9f, 0x39, 0xf6, 0x24, 0x19, 0x07, 0xb6, 0xbb, + 0x03, 0x7c, 0x26, 0x83, 0x45, 0xdd, 0x40, 0x1c, 0x6a, 0x8b, 0xba, 0x39, 0x1b, 0xf9, 0xc4, 0x21, + 0xc7, 0xc9, 0xc3, 0x80, 0xcc, 0xea, 0x06, 0xbe, 0x93, 0xe8, 0x2b, 0xe6, 0x88, 0x12, 0x5e, 0x86, + 0x5e, 0x97, 0xcf, 0xc6, 0x4e, 0x1d, 0xf6, 0x71, 0xf2, 0x28, 0x60, 0x75, 0xc7, 0x43, 0x12, 0x7d, + 0xc4, 0x02, 0x2d, 0xde, 0x65, 0x89, 0x6f, 0x6f, 0x5b, 0x62, 0x16, 0xb9, 0x67, 0xf6, 0xfe, 0x2a, + 0x00, 0x00, 0xff, 0xff, 0x1d, 0xa1, 0x70, 0x55, 0x27, 0x03, 0x00, 0x00, +} // Reference imports to suppress errors if they are not otherwise used. var _ context.Context @@ -42,15 +63,23 @@ var _ grpc.ClientConn // is compatible with the grpc package it is being compiled against. const _ = grpc.SupportPackageIsVersion4 -// Client API for CPaper service - +// CPaperClient is the client API for CPaper service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. type CPaperClient interface { - List(ctx context.Context, in *google_protobuf1.Empty, opts ...grpc.CallOption) (*schema.CommercialPaperList, error) + // List method returns all registered commercial papers + List(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*schema.CommercialPaperList, error) + // Get method returns commercial paper data by id Get(ctx context.Context, in *schema.CommercialPaperId, opts ...grpc.CallOption) (*schema.CommercialPaper, error) + // GetByExternalId GetByExternalId(ctx context.Context, in *schema.ExternalId, opts ...grpc.CallOption) (*schema.CommercialPaper, error) + // Issue commercial paper Issue(ctx context.Context, in *schema.IssueCommercialPaper, opts ...grpc.CallOption) (*schema.CommercialPaper, error) + // Buy commercial paper Buy(ctx context.Context, in *schema.BuyCommercialPaper, opts ...grpc.CallOption) (*schema.CommercialPaper, error) + // Redeem commercial paper Redeem(ctx context.Context, in *schema.RedeemCommercialPaper, opts ...grpc.CallOption) (*schema.CommercialPaper, error) + // Delete commercial paper Delete(ctx context.Context, in *schema.CommercialPaperId, opts ...grpc.CallOption) (*schema.CommercialPaper, error) } @@ -62,9 +91,9 @@ func NewCPaperClient(cc *grpc.ClientConn) CPaperClient { return &cPaperClient{cc} } -func (c *cPaperClient) List(ctx context.Context, in *google_protobuf1.Empty, opts ...grpc.CallOption) (*schema.CommercialPaperList, error) { +func (c *cPaperClient) List(ctx context.Context, in *empty.Empty, opts ...grpc.CallOption) (*schema.CommercialPaperList, error) { out := new(schema.CommercialPaperList) - err := grpc.Invoke(ctx, "/service.CPaper/List", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/service.CPaper/List", in, out, opts...) if err != nil { return nil, err } @@ -73,7 +102,7 @@ func (c *cPaperClient) List(ctx context.Context, in *google_protobuf1.Empty, opt func (c *cPaperClient) Get(ctx context.Context, in *schema.CommercialPaperId, opts ...grpc.CallOption) (*schema.CommercialPaper, error) { out := new(schema.CommercialPaper) - err := grpc.Invoke(ctx, "/service.CPaper/Get", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/service.CPaper/Get", in, out, opts...) if err != nil { return nil, err } @@ -82,7 +111,7 @@ func (c *cPaperClient) Get(ctx context.Context, in *schema.CommercialPaperId, op func (c *cPaperClient) GetByExternalId(ctx context.Context, in *schema.ExternalId, opts ...grpc.CallOption) (*schema.CommercialPaper, error) { out := new(schema.CommercialPaper) - err := grpc.Invoke(ctx, "/service.CPaper/GetByExternalId", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/service.CPaper/GetByExternalId", in, out, opts...) if err != nil { return nil, err } @@ -91,7 +120,7 @@ func (c *cPaperClient) GetByExternalId(ctx context.Context, in *schema.ExternalI func (c *cPaperClient) Issue(ctx context.Context, in *schema.IssueCommercialPaper, opts ...grpc.CallOption) (*schema.CommercialPaper, error) { out := new(schema.CommercialPaper) - err := grpc.Invoke(ctx, "/service.CPaper/Issue", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/service.CPaper/Issue", in, out, opts...) if err != nil { return nil, err } @@ -100,7 +129,7 @@ func (c *cPaperClient) Issue(ctx context.Context, in *schema.IssueCommercialPape func (c *cPaperClient) Buy(ctx context.Context, in *schema.BuyCommercialPaper, opts ...grpc.CallOption) (*schema.CommercialPaper, error) { out := new(schema.CommercialPaper) - err := grpc.Invoke(ctx, "/service.CPaper/Buy", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/service.CPaper/Buy", in, out, opts...) if err != nil { return nil, err } @@ -109,7 +138,7 @@ func (c *cPaperClient) Buy(ctx context.Context, in *schema.BuyCommercialPaper, o func (c *cPaperClient) Redeem(ctx context.Context, in *schema.RedeemCommercialPaper, opts ...grpc.CallOption) (*schema.CommercialPaper, error) { out := new(schema.CommercialPaper) - err := grpc.Invoke(ctx, "/service.CPaper/Redeem", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/service.CPaper/Redeem", in, out, opts...) if err != nil { return nil, err } @@ -118,22 +147,28 @@ func (c *cPaperClient) Redeem(ctx context.Context, in *schema.RedeemCommercialPa func (c *cPaperClient) Delete(ctx context.Context, in *schema.CommercialPaperId, opts ...grpc.CallOption) (*schema.CommercialPaper, error) { out := new(schema.CommercialPaper) - err := grpc.Invoke(ctx, "/service.CPaper/Delete", in, out, c.cc, opts...) + err := c.cc.Invoke(ctx, "/service.CPaper/Delete", in, out, opts...) if err != nil { return nil, err } return out, nil } -// Server API for CPaper service - +// CPaperServer is the server API for CPaper service. type CPaperServer interface { - List(context.Context, *google_protobuf1.Empty) (*schema.CommercialPaperList, error) + // List method returns all registered commercial papers + List(context.Context, *empty.Empty) (*schema.CommercialPaperList, error) + // Get method returns commercial paper data by id Get(context.Context, *schema.CommercialPaperId) (*schema.CommercialPaper, error) + // GetByExternalId GetByExternalId(context.Context, *schema.ExternalId) (*schema.CommercialPaper, error) + // Issue commercial paper Issue(context.Context, *schema.IssueCommercialPaper) (*schema.CommercialPaper, error) + // Buy commercial paper Buy(context.Context, *schema.BuyCommercialPaper) (*schema.CommercialPaper, error) + // Redeem commercial paper Redeem(context.Context, *schema.RedeemCommercialPaper) (*schema.CommercialPaper, error) + // Delete commercial paper Delete(context.Context, *schema.CommercialPaperId) (*schema.CommercialPaper, error) } @@ -142,7 +177,7 @@ func RegisterCPaperServer(s *grpc.Server, srv CPaperServer) { } func _CPaper_List_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { - in := new(google_protobuf1.Empty) + in := new(empty.Empty) if err := dec(in); err != nil { return nil, err } @@ -154,7 +189,7 @@ func _CPaper_List_Handler(srv interface{}, ctx context.Context, dec func(interfa FullMethod: "/service.CPaper/List", } handler := func(ctx context.Context, req interface{}) (interface{}, error) { - return srv.(CPaperServer).List(ctx, req.(*google_protobuf1.Empty)) + return srv.(CPaperServer).List(ctx, req.(*empty.Empty)) } return interceptor(ctx, in, info, handler) } @@ -303,33 +338,3 @@ var _CPaper_serviceDesc = grpc.ServiceDesc{ Streams: []grpc.StreamDesc{}, Metadata: "service.proto", } - -func init() { proto.RegisterFile("service.proto", fileDescriptor0) } - -var fileDescriptor0 = []byte{ - // 380 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x92, 0xd1, 0xaa, 0xd3, 0x30, - 0x18, 0xc7, 0xc1, 0xcd, 0x0e, 0x23, 0x73, 0xf2, 0xa9, 0x13, 0xbb, 0x89, 0xd8, 0x1b, 0xc1, 0x8b, - 0x06, 0xf4, 0xc2, 0xfb, 0xcd, 0x31, 0x07, 0x03, 0x45, 0x10, 0xc4, 0x1b, 0x49, 0xd3, 0xcf, 0x2d, - 0xd8, 0x34, 0x25, 0x49, 0x65, 0x65, 0xec, 0xc6, 0x57, 0xf0, 0xd1, 0x7c, 0x85, 0xf3, 0x1e, 0xe7, - 0xd0, 0x34, 0xe1, 0xc0, 0x81, 0x72, 0x06, 0xe7, 0xaa, 0xfc, 0xfb, 0xe5, 0xff, 0xfb, 0x11, 0xbe, - 0x90, 0xb1, 0x41, 0xfd, 0x47, 0x70, 0x4c, 0x2b, 0xad, 0xac, 0x82, 0x91, 0x8f, 0xf1, 0x7c, 0xa7, - 0xd4, 0xae, 0x40, 0xca, 0x2a, 0x41, 0x59, 0x59, 0x2a, 0xcb, 0xac, 0x50, 0xa5, 0xe9, 0x8e, 0xc5, - 0x33, 0x3f, 0x75, 0x29, 0xab, 0x7f, 0x51, 0x94, 0x95, 0x6d, 0xfc, 0xf0, 0xd3, 0x4e, 0xd8, 0x7d, - 0x9d, 0xa5, 0x5c, 0x49, 0x6a, 0x3e, 0x58, 0xe4, 0xfb, 0x82, 0x65, 0x94, 0xf3, 0xdf, 0xc2, 0x52, - 0x3c, 0x30, 0x59, 0x15, 0x68, 0x28, 0xaf, 0x58, 0x85, 0xfa, 0x27, 0x33, 0x5e, 0x48, 0x0d, 0xdf, - 0xa3, 0x64, 0xfe, 0xd3, 0x91, 0xde, 0x5d, 0x0e, 0x49, 0xb4, 0xfc, 0xd2, 0x1e, 0x84, 0x2d, 0x19, - 0x6e, 0x85, 0xb1, 0x30, 0x4d, 0x3b, 0x75, 0x1a, 0xd4, 0xe9, 0xaa, 0x55, 0xc7, 0xb3, 0xd4, 0x37, - 0x97, 0x4a, 0x4a, 0xd4, 0x5c, 0xb0, 0xc2, 0x15, 0xdb, 0x52, 0x32, 0xf9, 0xfb, 0xff, 0xe2, 0xdf, - 0xbd, 0x07, 0x30, 0xf2, 0x5a, 0xc8, 0xc8, 0x60, 0x8d, 0x16, 0x5e, 0xf4, 0x94, 0x36, 0x79, 0xfc, - 0xbc, 0x67, 0x94, 0xbc, 0x71, 0xac, 0xd7, 0xf0, 0xca, 0xb3, 0xe8, 0x51, 0x18, 0x53, 0xa3, 0x3e, - 0xd1, 0x63, 0x77, 0xa5, 0xb2, 0x96, 0x19, 0xea, 0x13, 0xfc, 0x20, 0x93, 0x35, 0xda, 0x45, 0xb3, - 0x3a, 0x58, 0xd4, 0x25, 0x2b, 0x36, 0x39, 0x40, 0x80, 0x5e, 0xff, 0xeb, 0x17, 0xc5, 0x4e, 0xf4, - 0x14, 0x20, 0x88, 0xf0, 0x60, 0x45, 0x4e, 0x8f, 0x22, 0x3f, 0xc1, 0x37, 0x72, 0x7f, 0xd3, 0x5a, - 0x61, 0x1e, 0xda, 0x2e, 0xde, 0x40, 0xf4, 0xb3, 0x9f, 0x39, 0xf6, 0x24, 0x19, 0x07, 0xb6, 0xbb, - 0x03, 0x7c, 0x26, 0x83, 0x45, 0xdd, 0x40, 0x1c, 0x6a, 0x8b, 0xba, 0x39, 0x1b, 0xf9, 0xc4, 0x21, - 0xc7, 0xc9, 0xc3, 0x80, 0xcc, 0xea, 0x06, 0xbe, 0x93, 0xe8, 0x2b, 0xe6, 0x88, 0x12, 0x5e, 0x86, - 0x5e, 0x97, 0xcf, 0xc6, 0x4e, 0x1d, 0xf6, 0x71, 0xf2, 0x28, 0x60, 0x75, 0xc7, 0x43, 0x12, 0x7d, - 0xc4, 0x02, 0x2d, 0xde, 0x65, 0x89, 0x6f, 0x6f, 0x5b, 0x62, 0x16, 0xb9, 0x67, 0xf6, 0xfe, 0x2a, - 0x00, 0x00, 0xff, 0xff, 0x1d, 0xa1, 0x70, 0x55, 0x27, 0x03, 0x00, 0x00, -} diff --git a/examples/cpaper_asservice/service/service.proto b/examples/cpaper_asservice/service/service.proto index 3665a6e0..f9179404 100644 --- a/examples/cpaper_asservice/service/service.proto +++ b/examples/cpaper_asservice/service/service.proto @@ -4,48 +4,52 @@ package service; import "google/api/annotations.proto"; import "google/protobuf/empty.proto"; - - import "github.com/s7techlab/cckit/examples/cpaper_asservice/schema/schema.proto"; service CPaper { - + // List method returns all registered commercial papers rpc List (google.protobuf.Empty) returns (schema.CommercialPaperList) { option (google.api.http) = { get: "/cpaper" }; } + // Get method returns commercial paper data by id rpc Get (schema.CommercialPaperId) returns (schema.CommercialPaper) { option (google.api.http) = { get: "/cpaper/{issuer}/{paper_number}" }; } + // GetByExternalId rpc GetByExternalId (schema.ExternalId) returns (schema.CommercialPaper) { option (google.api.http) = { get: "/cpaper/extid/{id}" }; } + // Issue commercial paper rpc Issue (schema.IssueCommercialPaper) returns (schema.CommercialPaper) { option (google.api.http) = { post : "/cpaper/issue" }; } + // Buy commercial paper rpc Buy (schema.BuyCommercialPaper) returns (schema.CommercialPaper) { option (google.api.http) = { post: "/cpaper/buy" }; } + // Redeem commercial paper rpc Redeem (schema.RedeemCommercialPaper) returns (schema.CommercialPaper) { option (google.api.http) = { post: "/cpaper/redeem" }; } + // Delete commercial paper rpc Delete (schema.CommercialPaperId) returns (schema.CommercialPaper) { option (google.api.http) = { delete: "/cpaper/{issuer}/{paper_number}" diff --git a/examples/cpaper_asservice/service/service.swagger.json b/examples/cpaper_asservice/service/service.swagger.json index dff41bc4..e2daa5af 100644 --- a/examples/cpaper_asservice/service/service.swagger.json +++ b/examples/cpaper_asservice/service/service.swagger.json @@ -17,6 +17,7 @@ "paths": { "/cpaper": { "get": { + "summary": "List method returns all registered commercial papers", "operationId": "List", "responses": { "200": { @@ -33,6 +34,7 @@ }, "/cpaper/buy": { "post": { + "summary": "Buy commercial paper", "operationId": "Buy", "responses": { "200": { @@ -49,6 +51,7 @@ }, "/cpaper/extid/{id}": { "get": { + "summary": "GetByExternalId", "operationId": "GetByExternalId", "responses": { "200": { @@ -73,6 +76,7 @@ }, "/cpaper/issue": { "post": { + "summary": "Issue commercial paper", "operationId": "Issue", "responses": { "200": { @@ -89,6 +93,7 @@ }, "/cpaper/redeem": { "post": { + "summary": "Redeem commercial paper", "operationId": "Redeem", "responses": { "200": { @@ -105,6 +110,7 @@ }, "/cpaper/{issuer}/{paper_number}": { "get": { + "summary": "Get method returns commercial paper data by id", "operationId": "Get", "responses": { "200": { @@ -133,6 +139,7 @@ ] }, "delete": { + "summary": "Delete commercial paper", "operationId": "Delete", "responses": { "200": { diff --git a/gateway/README.md b/gateway/README.md new file mode 100644 index 00000000..bd869f14 --- /dev/null +++ b/gateway/README.md @@ -0,0 +1,280 @@ +# Hyperledger Fabric chaincode kit (CCKit) + +## Chaincode-as-service gateway generator + +With gRPC we can define chaincode interface once in a .proto file and API / SDK will be automatically created for this chaincode. +We also get all the advantages of working with protocol buffers, including efficient serialization, a simple IDL, +and easy interface updating. + +Chaincode-as-service gateway generator allows to generate from gRPC service definition: + +* Chaincode handlers interface +* Chaincode gateway - service, can act as chaincode SDK or can be exposed as gRPC or REST service + +### Install the generator + +`GO111MODULE=on go install github.com/s7techlab/cckit/gateway/protoc-gen-cc-gateway` + + +## Example + +### Commercial paper chaincode + +#### Data model + +[schema.proto](../examples/cpaper_asservice/schema/schema.proto) + +```proto +syntax = "proto3"; + +package schema; + +import "google/protobuf/timestamp.proto"; +import "github.com/mwitkow/go-proto-validators/validator.proto"; + +// Commercial Paper state entry +message CommercialPaper { + + enum State { + ISSUED = 0; + TRADING = 1; + REDEEMED = 2; + } + + // Issuer and Paper number comprises composite primary key of Commercial paper entry + string issuer = 1; + string paper_number = 2; + + string owner = 3; + google.protobuf.Timestamp issue_date = 4; + google.protobuf.Timestamp maturity_date = 5; + int32 face_value = 6; + State state = 7; + + // Additional unique field for entry + string external_id = 8; +} + +// CommercialPaperId identifier part +message CommercialPaperId { + string issuer = 1; + string paper_number = 2; +} + +// ExternalId +message ExternalId { + string id = 1; +} + +// Container for returning multiple entities +message CommercialPaperList { + repeated CommercialPaper items = 1; +} + +// IssueCommercialPaper event +message IssueCommercialPaper { + string issuer = 1 [(validator.field) = {string_not_empty : true}]; + string paper_number = 2 [(validator.field) = {string_not_empty : true}]; + google.protobuf.Timestamp issue_date = 3 [(validator.field) = {msg_exists : true}]; + google.protobuf.Timestamp maturity_date = 4 [(validator.field) = {msg_exists : true}]; + int32 face_value = 5 [(validator.field) = {int_gt : 0}]; + + // external_id - once more uniq id of state entry + string external_id = 6 [(validator.field) = {string_not_empty : true}]; +} + +// BuyCommercialPaper event +message BuyCommercialPaper { + string issuer = 1 [(validator.field) = {string_not_empty : true}]; + string paper_number = 2 [(validator.field) = {string_not_empty : true}]; + string current_owner = 3 [(validator.field) = {string_not_empty : true}]; + string new_owner = 4 [(validator.field) = {string_not_empty : true}]; + int32 price = 5 [(validator.field) = {int_gt : 0}]; + google.protobuf.Timestamp purchase_date = 6 [(validator.field) = {msg_exists : true}]; +} + +// RedeemCommercialPaper event +message RedeemCommercialPaper { + string issuer = 1 [(validator.field) = {string_not_empty : true}]; + string paper_number = 2 [(validator.field) = {string_not_empty : true}]; + string redeeming_owner = 3 [(validator.field) = {string_not_empty : true}]; + google.protobuf.Timestamp redeem_date = 4 [(validator.field) = {msg_exists : true}]; +} +``` + +#### Chaincode as service + +Chaincode interface can be described with gRPC [service](../examples/cpaper_asservice/service/service.proto) notation. +Using `grpc-gateway` option we can define mapping for chaincode REST-API. + +The `grpc-gateway` is a plugin of the Google protocol buffers compiler `protoc`. It reads protobuf service definitions and +generates a reverse-proxy server which 'translates a RESTful HTTP API into gRPC. This server is generated according + to the `google.api.http` annotations in your service definitions. + +```proto +syntax = "proto3"; + +package service; + +import "google/api/annotations.proto"; +import "google/protobuf/empty.proto"; +import "github.com/s7techlab/cckit/examples/cpaper_asservice/schema/schema.proto"; + +service CPaper { + // List method returns all registered commercial papers + rpc List (google.protobuf.Empty) returns (schema.CommercialPaperList) { + option (google.api.http) = { + get: "/cpaper" + }; + } + + // Get method returns commercial paper data by id + rpc Get (schema.CommercialPaperId) returns (schema.CommercialPaper) { + option (google.api.http) = { + get: "/cpaper/{issuer}/{paper_number}" + }; + } + + // GetByExternalId + rpc GetByExternalId (schema.ExternalId) returns (schema.CommercialPaper) { + option (google.api.http) = { + get: "/cpaper/extid/{id}" + }; + } + + // Issue commercial paper + rpc Issue (schema.IssueCommercialPaper) returns (schema.CommercialPaper) { + option (google.api.http) = { + post : "/cpaper/issue" + }; + } + + // Buy commercial paper + rpc Buy (schema.BuyCommercialPaper) returns (schema.CommercialPaper) { + option (google.api.http) = { + post: "/cpaper/buy" + }; + } + + // Redeem commercial paper + rpc Redeem (schema.RedeemCommercialPaper) returns (schema.CommercialPaper) { + option (google.api.http) = { + post: "/cpaper/redeem" + }; + } + + // Delete commercial paper + rpc Delete (schema.CommercialPaperId) returns (schema.CommercialPaper) { + option (google.api.http) = { + delete: "/cpaper/{issuer}/{paper_number}" + }; + } +} +``` + +#### Generator + +[Makefile](../examples/cpaper_asservice/Makefile) + +```makefile +.: generate + +generate: + @protoc --version + @echo "commercial paper schema proto generation" + @protoc -I=./schema/ \ + -I=../../vendor \ + --go_out=./schema/ \ + --govalidators_out=./schema/ \ + ./schema/schema.proto + + @echo "commercial paper service proto generation" + @protoc -I=./service/ \ + -I=../../../../../ \ + -I=../../vendor \ + -I=../../third_party/googleapis \ + --go_out=plugins=grpc:./service/ \ + --cc-gateway_out=logtostderr=true:./service/ \ + --grpc-gateway_out=logtostderr=true:./service/ \ + --swagger_out=logtostderr=true:./service/ \ + ./service/service.proto +``` + +#### Chaincode implementation + +Chaincode implementation must contain [state and event mappings](../examples/cpaper_asservice/service/service.pb.cc.go) + +```go +type CPaperImpl struct { +} + +func (cc *CPaperImpl) state(ctx router.Context) m.MappedState { + return m.WrapState(ctx.State(), m.StateMappings{}. + // Create mapping for Commercial Paper entity + Add(&schema.CommercialPaper{}, + m.PKeySchema(&schema.CommercialPaperId{}), // Key namespace will be <"CommercialPaper", Issuer, PaperNumber> + m.List(&schema.CommercialPaperList{}), // Structure of result for List method + m.UniqKey("ExternalId"), // External Id is unique + )) +} + +func (cc *CPaperImpl) event(ctx router.Context) state.Event { + return m.WrapEvent(ctx.Event(), m.EventMappings{}. + // Event name will be "IssueCommercialPaper", payload - same as issue payload + Add(&schema.IssueCommercialPaper{}). + // Event name will be "BuyCommercialPaper" + Add(&schema.BuyCommercialPaper{}). + // Event name will be "RedeemCommercialPaper" + Add(&schema.RedeemCommercialPaper{})) +} +``` + +Chaincode service implementation must conform to generated from service definition + [CPaperChaincode](../examples/cpaper_asservice/service/service.pb.cc.go) interface: + +```go +func (cc *CPaperImpl) List(ctx router.Context, in *empty.Empty) (*schema.CommercialPaperList, error) { + if res, err := cc.state(ctx).List(&schema.CommercialPaper{}); err != nil { + return nil, err + } else { + return res.(*schema.CommercialPaperList), nil + } +} + +func (cc *CPaperImpl) Get(ctx router.Context, id *schema.CommercialPaperId) (*schema.CommercialPaper, error) { + if res, err := cc.state(ctx).Get(id, &schema.CommercialPaper{}); err != nil { + return nil, err + } else { + return res.(*schema.CommercialPaper), nil + } +} + +func (cc *CPaperImpl) GetByExternalId(ctx router.Context, id *schema.ExternalId) (*schema.CommercialPaper, error) { + if res, err := cc.state(ctx).GetByUniqKey( + &schema.CommercialPaper{}, "ExternalId", []string{id.Id}, &schema.CommercialPaper{}); err != nil { + return nil, err + } else { + return res.(*schema.CommercialPaper), nil + } +} + +... + +``` + +Then, chaincode service implementation can be embedded into chaincode method router with generated +[RegisterCPaperChaincode](../examples/cpaper_asservice/service/service.pb.cc.go#L58) function: + +```go +func CCRouter(name string) (*router.Group, error) { + r := router.New(name) + // Store on the ledger the information about chaincode instantiation + r.Init(owner.InvokeSetFromCreator) + + if err := service.RegisterCPaperChaincode(r, &CPaperImpl{}); err != nil { + return nil, err + } + + return r, nil +} +``` \ No newline at end of file diff --git a/gateway/client.go b/gateway/client.go new file mode 100644 index 00000000..10067404 --- /dev/null +++ b/gateway/client.go @@ -0,0 +1,40 @@ +package gateway + +import ( + "github.com/hyperledger/fabric/core/chaincode/shim" + "github.com/s7techlab/cckit/state" +) + +type ChaincodeClient interface { + Query(stub shim.ChaincodeStubInterface, fn string, args []interface{}, target interface{}) (interface{}, error) +} + +type ClientOpt func(*chaincodeClient) + +type chaincodeClient struct { + Channel string + Chaincode string +} + +func NewChaincodeClient(channelName, chaincodeName string, opts ...Opt) *chaincodeClient { + c := &chaincodeClient{ + Channel: channelName, + Chaincode: chaincodeName, + } + + return c +} + +func (c *chaincodeClient) Query(stub shim.ChaincodeStubInterface, fn string, args []interface{}, target interface{}) (interface{}, error) { + + // if target chaincode is encrypted we only can invoke `stateGet` function + // for example < encrypted(`orgGet`), encrypted( &schema.OrganizationId { Id : `123` }) > will be < `stateGet`, []string { key } > + // we know target ( &schema.Organization ), know input parameter type ( &schema.OrganizationId ) + // if target has primary key with this type or uniq key with this type - we create state,Ke + //if 1==2 { + // fn = `stateGet` + // + //} + + return state.InvokeChaincode(stub, c.Chaincode, append([]interface{}{fn}, args...), c.Channel, target) +} diff --git a/gateway/event_stream.go b/gateway/event_stream.go index e6df5c27..d19017b0 100644 --- a/gateway/event_stream.go +++ b/gateway/event_stream.go @@ -42,7 +42,6 @@ func (*ChaincodeEventServerStream) SendHeader(metadata.MD) error { } func (*ChaincodeEventServerStream) SetTrailer(metadata.MD) { - return } func (s *ChaincodeEventServerStream) Context() context.Context { @@ -70,8 +69,8 @@ func (s *ChaincodeEventServerStream) Recv(e *peer.ChaincodeEvent) error { } func (s *ChaincodeEventServerStream) RecvMsg(m interface{}) error { - m, ok := <-s.events - if ok { + var ok bool + if m, ok = <-s.events; ok { return nil } return ErrEventChannelClosed diff --git a/gateway/gateway.go b/gateway/gateway.go index dee9d9f9..047316e4 100644 --- a/gateway/gateway.go +++ b/gateway/gateway.go @@ -9,6 +9,7 @@ import ( "github.com/s7techlab/cckit/gateway/service" ) +// Chaincode interface for work with chaincode type Chaincode interface { Query(ctx context.Context, fn string, args []interface{}, target interface{}) (interface{}, error) Invoke(ctx context.Context, fn string, args []interface{}, target interface{}) (interface{}, error) diff --git a/gateway/protoc-gen-cc-gateway/README.md b/gateway/protoc-gen-cc-gateway/README.md new file mode 100644 index 00000000..21dca2f4 --- /dev/null +++ b/gateway/protoc-gen-cc-gateway/README.md @@ -0,0 +1,20 @@ +# Hyperledger Fabric chaincode kit (CCKit) + +## Chaincode gateway + +With gRPC we can define chaincode interface once in a .proto file and API / SDK will be automatically created for this chaincode. +We also get all the advantages of working with protocol buffers, including efficient serialization, a simple IDL, +and easy interface updating. + +Chaincode-as-service gateway generator allows to generate from gRPC service definition: + +* Chaincode handlers interface +* Chaincode gateway - service, can act as chaincode SDK or can be exposed as gRPC or REST service + +### Install the generator + +`GO111MODULE=on go install github.com/s7techlab/cckit/gateway/protoc-gen-cc-gateway` + + + + diff --git a/gateway/protoc-gen-cc-gateway/generator/error.go b/gateway/protoc-gen-cc-gateway/generator/error.go new file mode 100644 index 00000000..e98ab261 --- /dev/null +++ b/gateway/protoc-gen-cc-gateway/generator/error.go @@ -0,0 +1,7 @@ +package generator + +import "errors" + +var ( + ErrNoTargetService = errors.New("no target service defined in the file") +) diff --git a/gateway/protoc-gen-cc-gateway/generator/generator.go b/gateway/protoc-gen-cc-gateway/generator/generator.go new file mode 100644 index 00000000..ee082548 --- /dev/null +++ b/gateway/protoc-gen-cc-gateway/generator/generator.go @@ -0,0 +1,172 @@ +package generator + +import ( + "bytes" + "fmt" + "go/format" + "path" + "path/filepath" + "strings" + + "github.com/golang/glog" + "github.com/golang/protobuf/proto" + plugin "github.com/golang/protobuf/protoc-gen-go/plugin" + "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor" +) + +type Generator struct { + reg *descriptor.Registry + imports []descriptor.GoPackage // common imports +} + +// New returns a new generator which generates handler wrappers. +func New(reg *descriptor.Registry) *Generator { + return &Generator{ + reg: reg, + } +} + +func (g *Generator) Generate(targets []*descriptor.File) ([]*plugin.CodeGeneratorResponse_File, error) { + var files []*plugin.CodeGeneratorResponse_File + for _, file := range targets { + if len(file.Services) == 0 { + continue + } + + if code, err := g.generateCC(file); err == nil { + files = append(files, code) + } else { + return nil, err + } + } + + return files, nil +} + +func (g *Generator) generateCC(file *descriptor.File) (*plugin.CodeGeneratorResponse_File, error) { + code, err := g.getCCTemplate(file) + if err != nil { + return nil, err + } + + formatted, err := format.Source([]byte(code)) + if err != nil { + glog.Errorf("%v: %s", err, annotateString(code)) + return nil, err + } + + name := filepath.Base(file.GetName()) + ext := filepath.Ext(name) + base := strings.TrimSuffix(name, ext) + + output := fmt.Sprintf(filepath.Join(file.GoPkg.Path, "%s.pb.cc.go"), base) + output = filepath.Clean(output) + + return &plugin.CodeGeneratorResponse_File{ + Name: proto.String(output), + Content: proto.String(string(formatted)), + }, nil +} + +func (g *Generator) getCCTemplate(f *descriptor.File) (string, error) { + pkgSeen := make(map[string]bool) + var imports []descriptor.GoPackage + for _, pkg := range g.imports { + pkgSeen[pkg.Path] = true + imports = append(imports, pkg) + } + + pkgs := [][]string{ + {"context", "context"}, + //"github.com/hyperledger/fabric/msp", + {"github.com/s7techlab/cckit/gateway", "cckit_gateway"}, + {"github.com/s7techlab/cckit/gateway/service", "cckit_ccservice"}, + {"github.com/s7techlab/cckit/router", "cckit_router"}, + {"github.com/s7techlab/cckit/router/param/defparam", "cckit_defparam"}, + } + + for _, pkg := range pkgs { + pkgSeen[pkg[0]] = true + imports = append(imports, g.newGoPackage(pkg[0], pkg[1])) + } + + for _, svc := range f.Services { + for _, m := range svc.Methods { + checkedAppend := func(pkg descriptor.GoPackage) { + // Add request type package to imports if needed + if m.Options == nil || pkg == f.GoPkg || pkgSeen[pkg.Path] { + return + } + pkgSeen[pkg.Path] = true + + // always generate alias for external packages, when types used in req/resp object + //if pkg.Alias == "" { + // pkg.Alias = pkg.Name + // pkgSeen[pkg.Path] = false + //} + + imports = append(imports, pkg) + } + + checkedAppend(m.RequestType.File.GoPkg) + checkedAppend(m.ResponseType.File.GoPkg) + } + } + + p := param{File: f, Imports: imports} + return applyTemplate(p) +} + +func (g *Generator) newGoPackage(pkgPath string, aalias ...string) descriptor.GoPackage { + gopkg := descriptor.GoPackage{ + Path: pkgPath, + Name: path.Base(pkgPath), + } + alias := gopkg.Name + if len(aalias) > 0 { + alias = aalias[0] + gopkg.Alias = alias + } + + reference := alias + if reference == "" { + reference = gopkg.Name + } + + for i := 0; ; i++ { + if err := g.reg.ReserveGoPackageAlias(alias, gopkg.Path); err == nil { + break + } + alias = fmt.Sprintf("%s_%d", gopkg.Name, i) + gopkg.Alias = alias + } + + pkg[reference] = alias + + return gopkg +} + +func applyTemplate(p param) (string, error) { + w := bytes.NewBuffer(nil) + if err := headerTemplate.Execute(w, p); err != nil { + return "", err + } + + if err := ccTemplate.Execute(w, p); err != nil { + return "", err + } + + if err := gatewayTemplate.Execute(w, p); err != nil { + return "", err + } + + return w.String(), nil +} + +func annotateString(str string) string { + strs := strings.Split(str, "\n") + for pos := range strs { + strs[pos] = fmt.Sprintf("%v: %v", pos, strs[pos]) + } + return strings.Join(strs, "\n") +} diff --git a/gateway/protoc-gen-cc-gateway/generator/template.go b/gateway/protoc-gen-cc-gateway/generator/template.go new file mode 100644 index 00000000..3916d83a --- /dev/null +++ b/gateway/protoc-gen-cc-gateway/generator/template.go @@ -0,0 +1,137 @@ +package generator + +import ( + "text/template" + + "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor" +) + +type param struct { + *descriptor.File + Imports []descriptor.GoPackage +} + +var ( + pkg = make(map[string]string) + + funcMap = template.FuncMap{ + "goTypeName": goTypeName, + "hasBindings": hasBindings, + "hasGetBinding": hasGetBinding, + } + + headerTemplate = template.Must(template.New("header").Funcs(funcMap).Parse(` +// Code generated by protoc-gen-cc-gateway. DO NOT EDIT. +// source: {{ .GetName }} + +/* +Package {{ .GoPkg.Name }} contains + * chaincode interface definition + * chaincode gateway definition + * chaincode service to cckit router registration func +*/ +package {{ .GoPkg.Name }} +import ( + {{ range $i := .Imports }}{{ if $i.Standard }}{{ $i | printf "%s\n" }}{{ end }}{{ end }} + + {{ range $i := .Imports }}{{ if not $i.Standard }}{{ $i | printf "%s\n" }}{{ end }}{{ end }} +) +`)) +) + +var ccTemplate = template.Must(template.New("chaincode").Funcs(funcMap).Option().Parse(` +{{ range $svc := .Services }} + +// {{ $svc.GetName }}Chaincode method names +const ( +{{ range $m := $svc.Methods }} + {{ $svc.GetName }}Chaincode_{{ $m.GetName }} = "{{ $m.GetName }}" +{{ end }} +) + + +// {{ $svc.GetName }}Chaincode chaincode methods interface +type {{ $svc.GetName }}Chaincode interface { +{{ range $m := $svc.Methods }} + {{ $m.GetName }} (cckit_router.Context, *{{$m.RequestType.GoType $m.Service.File.GoPkg.Path | goTypeName }}) (*{{ $m.ResponseType.GoType $m.Service.File.GoPkg.Path | goTypeName }}, error) +{{ end }} +} + + +// Register{{ $svc.GetName }}Chaincode registers service methods as chaincode router handlers +func Register{{ $svc.GetName }}Chaincode(r *cckit_router.Group, cc {{ $svc.GetName }}Chaincode) error { + + {{ range $m := $svc.Methods }} + {{ $method := "Invoke"}} + {{ if $m | hasGetBinding }}{{ $method = "Query"}}{{ end }} + + r.{{ $method }}( {{ $svc.GetName }}Chaincode_{{ $m.GetName }}, + func(ctx cckit_router.Context) (interface{}, error) { + return cc.{{ $m.GetName }}(ctx, ctx.Param().(*{{$m.RequestType.GoType $m.Service.File.GoPkg.Path | goTypeName }})) + }, + cckit_defparam.Proto(&{{$m.RequestType.GoType $m.Service.File.GoPkg.Path | goTypeName }}{})) + + {{ end }} + + return nil +} + +{{ end }} +`)) + +var gatewayTemplate = template.Must(template.New("gateway").Funcs(funcMap).Option().Parse(` +{{ range $svc := .Services }} + +// New{{ $svc.GetName }}Gateway creates gateway to access chaincode method via chaincode service +func New{{ $svc.GetName }}Gateway(ccService cckit_ccservice.Chaincode, channel, chaincode string, opts ...cckit_gateway.Opt) *{{ $svc.GetName }}Gateway { + return &{{ $svc.GetName }}Gateway{Gateway: cckit_gateway.NewChaincode(ccService, channel, chaincode, opts...)} +} + + +// gateway implementation +// gateway can be used as kind of SDK, GRPC or REST server ( via grpc-gateway or clay ) +type {{ $svc.GetName }}Gateway struct { + Gateway cckit_gateway.Chaincode +} + +// ApiDef returns service definition +func (c *{{ $svc.GetName }}Gateway) ApiDef() cckit_gateway.ServiceDef { + return cckit_gateway.ServiceDef{ + Desc: &_{{ $svc.GetName }}_serviceDesc, + Service: c, + HandlerFromEndpointRegister: Register{{ $svc.GetName }}HandlerFromEndpoint, + } +} + +// Events returns events subscription +func (c *{{ $svc.GetName }}Gateway) Events(ctx context.Context) (cckit_gateway.ChaincodeEventSub, error) { + return c.Gateway.Events(ctx) +} + + +type ValidatorInterface interface { + Validate() error +} + + {{ range $m := $svc.Methods }} + {{ $method := "Invoke"}} + {{ if $m | hasGetBinding }}{{ $method = "Query"}}{{ end }} + + func (c *{{ $svc.GetName }}Gateway) {{ $m.GetName }}(ctx context.Context, in *{{$m.RequestType.GoType $m.Service.File.GoPkg.Path | goTypeName }}) (*{{ $m.ResponseType.GoType $m.Service.File.GoPkg.Path | goTypeName }}, error) { + var inMsg interface{} = in + if v, ok := inMsg.(ValidatorInterface); ok { + if err := v.Validate(); err != nil { + return nil, err + } + } + + if res, err := c.Gateway.{{ $method }}(ctx, {{ $svc.GetName }}Chaincode_{{ $m.GetName }} , []interface{}{in}, &{{ $m.ResponseType.GoType $m.Service.File.GoPkg.Path | goTypeName }}{}); err != nil { + return nil, err + } else { + return res.(*{{ $m.ResponseType.GoType $m.Service.File.GoPkg.Path | goTypeName }}), nil + } + } + {{ end }} + +{{ end }} +`)) diff --git a/gateway/protoc-gen-cc-gateway/generator/template_func.go b/gateway/protoc-gen-cc-gateway/generator/template_func.go new file mode 100644 index 00000000..0e0be7fc --- /dev/null +++ b/gateway/protoc-gen-cc-gateway/generator/template_func.go @@ -0,0 +1,38 @@ +package generator + +import ( + "strings" + + "github.com/golang/protobuf/protoc-gen-go/generator" + "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor" +) + +func hasBindings(service *descriptor.Service) bool { + for _, m := range service.Methods { + if len(m.Bindings) > 0 { + return true + } + } + return false +} + +func hasGetBinding(method *descriptor.Method) bool { + for _, b := range method.Bindings { + if b.HTTPMethod == "GET" { + return true + } + } + return false +} + +func goTypeName(s string) string { + toks := strings.Split(s, ".") + i := 0 + if len(toks) > 1 { + i = 1 + } + for pos := range toks[i:] { + toks[pos+i] = generator.CamelCase(toks[pos+i]) + } + return strings.Join(toks, ".") +} diff --git a/gateway/protoc-gen-cc-gateway/main.go b/gateway/protoc-gen-cc-gateway/main.go new file mode 100644 index 00000000..9016d241 --- /dev/null +++ b/gateway/protoc-gen-cc-gateway/main.go @@ -0,0 +1,80 @@ +package main + +import ( + "flag" + "io" + "log" + "os" + + "github.com/golang/protobuf/proto" + plugin "github.com/golang/protobuf/protoc-gen-go/plugin" + "github.com/grpc-ecosystem/grpc-gateway/codegenerator" + "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor" + "github.com/s7techlab/cckit/gateway/protoc-gen-cc-gateway/generator" +) + +var ( + file = flag.String("file", "-", "where to load data from") +) + +func main() { + var err error + flag.Parse() + if err = flag.Lookup("logtostderr").Value.Set("true"); err != nil { + log.Fatal(err) + } + + reg := descriptor.NewRegistry() + fs := os.Stdin + if *file != "-" { + if fs, err = os.Open(*file); err != nil { + log.Fatal(err) + } + } + req, err := codegenerator.ParseRequest(fs) + if err != nil { + log.Fatal(err) + } + + if err = reg.Load(req); err != nil { + emitError(err) + return + } + + g := generator.New(reg) + var ( + targets []*descriptor.File + f *descriptor.File + ) + for _, target := range req.FileToGenerate { + if f, err = reg.LookupFile(target); err != nil { + log.Fatal(err) + } + targets = append(targets, f) + } + + out, err := g.Generate(targets) + if err != nil { + emitError(err) + return + } + emitFiles(os.Stdout, out) +} + +func emitFiles(w io.Writer, out []*plugin.CodeGeneratorResponse_File) { + emitResp(w, &plugin.CodeGeneratorResponse{File: out}) +} + +func emitError(err error) { + emitResp(os.Stdout, &plugin.CodeGeneratorResponse{Error: proto.String(err.Error())}) +} + +func emitResp(out io.Writer, resp *plugin.CodeGeneratorResponse) { + buf, err := proto.Marshal(resp) + if err != nil { + log.Fatal(err) + } + if _, err := out.Write(buf); err != nil { + log.Fatal(err) + } +} diff --git a/gateway/service/chaincode.go b/gateway/service/chaincode.go index e5cc3d1b..9aed6e2a 100644 --- a/gateway/service/chaincode.go +++ b/gateway/service/chaincode.go @@ -10,10 +10,12 @@ import ( ) type ( + // Chaincode service interface Chaincode = ChaincodeServer ChaincodeEventsServer = chaincodeEventsServer ) +// ChaincodeService implementation based of hlf-sdk-go type ChaincodeService struct { sdk api.Core } @@ -28,7 +30,15 @@ func (cs *ChaincodeService) Invoke(ctx context.Context, in *ChaincodeInput) (*pe return nil, err } - response, _, err := cs.sdk.Channel(in.Channel).Chaincode(in.Chaincode).Invoke(string(in.Args[0])).WithIdentity(signer).ArgBytes(in.Args[1:]).Transient(in.Transient).Do(ctx) + response, _, err := cs.sdk. + Channel(in.Channel). + Chaincode(in.Chaincode). + Invoke(string(in.Args[0])). + WithIdentity(signer). + ArgBytes(in.Args[1:]). + Transient(in.Transient). + Do(ctx) + if err != nil { return nil, err } diff --git a/gateway/service/mock.go b/gateway/service/mock.go new file mode 100644 index 00000000..6117e106 --- /dev/null +++ b/gateway/service/mock.go @@ -0,0 +1,143 @@ +package service + +import ( + "context" + "errors" + "fmt" + "sync" + + "github.com/hyperledger/fabric/core/chaincode/shim" + "github.com/hyperledger/fabric/msp" + "github.com/hyperledger/fabric/protos/peer" + "github.com/s7techlab/cckit/testing" +) + +const ( + MessageProtocolVersion = 1 +) + +type ( + ChannelMockStubs map[string]*testing.MockStub + + ChannelsMockStubs map[string]ChannelMockStubs + + MockChaincodeService struct { + // channel name -> chaincode name + ChannelCC ChannelsMockStubs + m sync.Mutex + } +) + +func NewMock() *MockChaincodeService { + return &MockChaincodeService{ + ChannelCC: make(ChannelsMockStubs), + } +} +func (cs *MockChaincodeService) Query(ctx context.Context, in *ChaincodeInput) (proposalResponse *peer.ProposalResponse, err error) { + var ( + mockStub *testing.MockStub + signer msp.SigningIdentity + response peer.Response + ) + + cs.m.Lock() + defer cs.m.Unlock() + + if mockStub, err = cs.Chaincode(in.Channel, in.Chaincode); err != nil { + return + } + + if signer, err = SignerFromContext(ctx); err != nil { + return + } + + if response = mockStub.From(signer).WithTransient(in.Transient).QueryBytes(in.Args...); response.Status == shim.ERROR { + return nil, errors.New(response.Message) + } + + return &peer.ProposalResponse{ + Version: MessageProtocolVersion, + Timestamp: mockStub.TxTimestamp, + Response: &response, + }, nil +} + +func (cs *MockChaincodeService) Invoke(ctx context.Context, in *ChaincodeInput) (proposalResponse *peer.ProposalResponse, err error) { + var ( + mockStub *testing.MockStub + signer msp.SigningIdentity + response peer.Response + ) + + cs.m.Lock() + defer cs.m.Unlock() + + if mockStub, err = cs.Chaincode(in.Channel, in.Chaincode); err != nil { + return + } + + if signer, err = SignerFromContext(ctx); err != nil { + return + } + + if response = mockStub.From(signer).WithTransient(in.Transient).InvokeBytes(in.Args...); response.Status == shim.ERROR { + return nil, errors.New(response.Message) + } + + return &peer.ProposalResponse{ + Version: MessageProtocolVersion, + Timestamp: mockStub.TxTimestamp, + Response: &response, + }, nil + +} + +func (cs *MockChaincodeService) Events(in *ChaincodeLocator, stream Chaincode_EventsServer) (err error) { + var ( + mockStub *testing.MockStub + ) + if mockStub, err = cs.Chaincode(in.Channel, in.Chaincode); err != nil { + return + } + events := mockStub.EventSubscription() + for { + e, ok := <-events + if !ok { + return nil + } + if err = stream.Send(e); err != nil { + return err + } + } +} + +func (cs *MockChaincodeService) WithChannel(channel string, mockStubs ...*testing.MockStub) *MockChaincodeService { + if _, ok := cs.ChannelCC[channel]; !ok { + cs.ChannelCC[channel] = make(ChannelMockStubs) + } + + for _, ms := range mockStubs { + cs.ChannelCC[channel][ms.Name] = ms + for chName, chnl := range cs.ChannelCC { + for ccName, cc := range chnl { + + // add visibility of added cc to all other cc + cc.MockPeerChaincode(ms.Name+`/`+channel, ms) + + // add visibility of other cc to added cc + ms.MockPeerChaincode(ccName+`/`+chName, cc) + } + } + } + + return cs +} + +func (cs *MockChaincodeService) Chaincode(channel string, chaincode string) (*testing.MockStub, error) { + ms, exists := cs.ChannelCC[channel][chaincode] + if !exists { + return nil, fmt.Errorf(`%s: channell=%s, chaincode=%s`, ErrChaincodeNotExists, channel, chaincode) + } + + return ms, nil +} diff --git a/go.mod b/go.mod index cde17f35..55dabda0 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/Knetic/govaluate v3.0.0+incompatible // indirect github.com/fsouza/go-dockerclient v1.4.0 // indirect github.com/gogo/protobuf v1.2.1 + github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b github.com/golang/mock v1.2.0 // indirect github.com/golang/protobuf v1.3.1 github.com/grpc-ecosystem/grpc-gateway v1.9.0 diff --git a/go.sum b/go.sum index 18edcd3e..26fd3430 100644 --- a/go.sum +++ b/go.sum @@ -72,6 +72,7 @@ github.com/fsouza/go-dockerclient v1.3.6 h1:oL0e3fpCjF+AHuUUBnwbkVcelFhxQifgTPQK github.com/fsouza/go-dockerclient v1.3.6/go.mod h1:ptN6nXBwrXuiHAz2TYGOFCBB1aKGr371sGjMFdJEr1A= github.com/fsouza/go-dockerclient v1.4.0 h1:Fhqy7UOYW+4ILvC3dtiY3Jzr3XXSSrbq56IhTPxkMnE= github.com/fsouza/go-dockerclient v1.4.0/go.mod h1:GmPog78dvaRLJqt7QU7fRLaJKUkYW2hYjxKCp1uwGwE= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= diff --git a/state/state.go b/state/state.go index 31750cd1..6cad20c3 100644 --- a/state/state.go +++ b/state/state.go @@ -6,6 +6,7 @@ import ( "strings" "github.com/hyperledger/fabric/core/chaincode/shim" + "github.com/hyperledger/fabric/protos/ledger/queryresult" "github.com/pkg/errors" "github.com/s7techlab/cckit/convert" ) @@ -448,7 +449,7 @@ func (s *Impl) ExistsPrivate(collection string, entry interface{}) (bool, error) // Keys - additional components of composite key // If usePrivateDataIterator is true, used private state for iterate over objects // if false, used public state for iterate over keys and GetPrivateData for each key -func (s *Impl) ListPrivate(collection string, usePrivateDataIterator bool, namespace interface{}, target ...interface{}) (result interface{}, err error) { +func (s *Impl) ListPrivate(collection string, usePrivateDataIterator bool, namespace interface{}, target ...interface{}) (interface{}, error) { stateList, err := NewStateList(target...) if err != nil { @@ -460,8 +461,7 @@ func (s *Impl) ListPrivate(collection string, usePrivateDataIterator bool, names } s.logger.Debugf(`state LIST namespace: %s`, key) - key, err = s.StateKeyTransformer(key) - if err != nil { + if key, err = s.StateKeyTransformer(key); err != nil { return nil, err } s.logger.Debugf(`state LIST with composite key: %s`, key) @@ -480,18 +480,21 @@ func (s *Impl) ListPrivate(collection string, usePrivateDataIterator bool, names return nil, errors.Wrap(err, `create list iterator`) } defer func() { _ = iter.Close() }() + + var ( + kv *queryresult.KV + objKey string + keyParts []string + ) for iter.HasNext() { - kv, err := iter.Next() - if err != nil { + if kv, err = iter.Next(); err != nil { return nil, errors.Wrap(err, `get key value`) } - objKey, keyParts, err := s.stub.SplitCompositeKey(kv.Key) - var curCompositeKey []string - curCompositeKey = append(curCompositeKey, objKey) - for _, part := range keyParts { - curCompositeKey = append(curCompositeKey, part) + if objKey, keyParts, err = s.stub.SplitCompositeKey(kv.Key); err != nil { + return nil, err } - object, err := s.GetPrivate(collection, curCompositeKey, target...) + + object, err := s.GetPrivate(collection, append([]string{objKey}, keyParts...), target...) if err != nil { return nil, err } diff --git a/testing/mockstub.go b/testing/mockstub.go index 7f9b81bc..3f6ba121 100644 --- a/testing/mockstub.go +++ b/testing/mockstub.go @@ -11,7 +11,7 @@ import ( "github.com/hyperledger/fabric/msp" "github.com/hyperledger/fabric/protos/ledger/queryresult" "github.com/hyperledger/fabric/protos/peer" - "github.com/op/go-logging" + gologging "github.com/op/go-logging" "github.com/pkg/errors" "github.com/s7techlab/cckit/convert" ) @@ -332,7 +332,7 @@ type PrivateMockStateRangeQueryIterator struct { } // Logger for the shim package. -var mockLogger = logging.MustGetLogger("mock") +var mockLogger = gologging.MustGetLogger("mock") // HasNext returns true if the range query iterator contains additional keys // and values. @@ -376,13 +376,13 @@ func (iter *PrivateMockStateRangeQueryIterator) HasNext() bool { // Next returns the next key and value in the range query iterator. func (iter *PrivateMockStateRangeQueryIterator) Next() (*queryresult.KV, error) { - if iter.Closed == true { + if iter.Closed { err := errors.New("PrivateMockStateRangeQueryIterator.Next() called after Close()") mockLogger.Errorf("%+v", err) return nil, err } - if iter.HasNext() == false { + if !iter.HasNext() { err := errors.New("PrivateMockStateRangeQueryIterator.Next() called when it does not HaveNext()") mockLogger.Errorf("%+v", err) return nil, err @@ -409,7 +409,7 @@ func (iter *PrivateMockStateRangeQueryIterator) Next() (*queryresult.KV, error) // Close closes the range query iterator. This should be called when done // reading from the iterator to free up resources. func (iter *PrivateMockStateRangeQueryIterator) Close() error { - if iter.Closed == true { + if iter.Closed { err := errors.New("PrivateMockStateRangeQueryIterator.Close() called after Close()") mockLogger.Errorf("%+v", err) return err @@ -451,13 +451,10 @@ func (iter *PrivateMockStateRangeQueryIterator) Print() { // PutPrivateData mocked func (stub *MockStub) PutPrivateData(collection string, key string, value []byte) error { - m, in := stub.PvtState[collection] - if !in { + if _, in := stub.PvtState[collection]; !in { stub.PvtState[collection] = make(map[string][]byte) - m, in = stub.PvtState[collection] } - - m[key] = value + stub.PvtState[collection][key] = value if _, ok := stub.PrivateKeys[collection]; !ok { stub.PrivateKeys[collection] = list.New()