├── .github └── workflows │ ├── go.yml │ └── golangci-lint.yml ├── .gitignore ├── LICENSE ├── MANTAINERS.md ├── Makefile ├── README.md ├── _config.yml ├── buf.gen.cc-with-service-prefix.yaml ├── buf.gen.cc.yaml ├── buf.gen.go.yaml ├── buf.gen.gw.yaml ├── buf.work.yaml ├── convert ├── convert.go ├── convert_test.go ├── from_bytes.go └── to_bytes.go ├── docs ├── chaincode-examples.md ├── create-test-cert.md ├── debug.md └── img │ ├── blockchain-app.png │ ├── cc-code-gen.png │ ├── gateway-cpaper-swagger.png │ ├── gateway-mocked-start.png │ ├── gateway-mocked-usage.png │ └── mockstub-hlf-peer.png ├── examples ├── buf.yaml ├── cars │ ├── README.md │ ├── bin │ │ ├── cars │ │ │ └── main.go │ │ └── cars_noac │ │ │ └── main.go │ ├── cars.go │ ├── cars_noacces_control.go │ ├── cars_proxy.go │ ├── cars_test.go │ └── payloads.go ├── cpaper │ ├── README.md │ ├── chaincode.go │ ├── chaincode_test.go │ └── img │ │ ├── cpaper-network.png │ │ └── state.png ├── cpaper_asservice │ ├── bin │ │ ├── api │ │ │ ├── _common.sh │ │ │ ├── get.sh │ │ │ ├── issue.sh │ │ │ ├── list.sh │ │ │ ├── mock │ │ │ │ └── main.go │ │ │ ├── payload_cpaper_1_issue.json │ │ │ └── payload_cpaper_2_issue.json │ │ └── chaincode │ │ │ └── main.go │ ├── chaincode.go │ ├── chaincode_test.go │ ├── cpaper.go │ ├── cpaper.pb.cc.go │ ├── cpaper.pb.go │ ├── cpaper.pb.gw.go │ ├── cpaper.proto │ ├── cpaper.swagger.json │ ├── cpaper.validator.pb.go │ ├── cpaper_test.go │ ├── state.go │ └── testdata │ │ ├── admin.key.pem │ │ ├── admin.pem │ │ └── cpaper.go ├── cpaper_extended │ ├── README.md │ ├── chaincode.go │ ├── chaincode_test.go │ ├── schema │ │ ├── payload.pb.go │ │ ├── payload.proto │ │ ├── payload.swagger.json │ │ ├── payload.validator.pb.go │ │ ├── state.pb.go │ │ ├── state.proto │ │ ├── state.swagger.json │ │ └── state.validator.pb.go │ └── testdata │ │ ├── admin.key.pem │ │ ├── admin.pem │ │ └── testdata.go ├── cpaper_proxy │ ├── README.md │ ├── chaincode.go │ ├── cpaper_proxy.go │ ├── cpaper_proxy.pb.cc.go │ ├── cpaper_proxy.pb.go │ ├── cpaper_proxy.pb.gw.go │ ├── cpaper_proxy.proto │ ├── cpaper_proxy.swagger.json │ ├── cpaper_proxy.validator.pb.go │ └── cpaper_proxy_test.go ├── doc.md ├── erc20 │ ├── README.md │ ├── bin │ │ └── main.go │ ├── erc20_fixedsupply.go │ ├── erc20_ops.go │ └── erc20_test.go ├── erc20_service │ ├── erc20.pb.cc.go │ ├── erc20.pb.go │ ├── erc20.pb.gw.go │ ├── erc20.proto │ ├── erc20.swagger.json │ └── erc20.validator.pb.go ├── fabcar │ ├── README.md │ ├── bin │ │ └── main.go │ ├── chaincode.go │ ├── fabcar.go │ ├── fabcar.pb.cc.go │ ├── fabcar.pb.go │ ├── fabcar.pb.gw.go │ ├── fabcar.proto │ ├── fabcar.swagger.json │ ├── fabcar.validator.pb.go │ ├── fabcar_mapper.go │ ├── fabcar_test.go │ ├── state.go │ └── testdata │ │ ├── car.go │ │ ├── car_detail.go │ │ ├── car_owner.go │ │ └── maker.go ├── insurance │ ├── README.md │ ├── app │ │ ├── bin │ │ │ └── main.go │ │ ├── data.go │ │ ├── images │ │ │ └── arch-blockchain-insurance2.png │ │ ├── invoke_insurance.go │ │ ├── invoke_police.go │ │ ├── invoke_repairshop.go │ │ ├── invoke_shop.go │ │ └── main.go │ ├── app_test.go │ ├── dto.go │ └── fixtures.go ├── payment │ ├── README.md │ ├── cc_enc_context_with_mapping.go │ ├── cc_enc_manual_ondemand_with_mapping.go │ ├── cc_enc_manual_required_without_mapping.go │ ├── mapping.go │ └── schema │ │ ├── Makefile │ │ ├── payment.pb.go │ │ ├── payment.proto │ │ ├── payment.swagger.json │ │ └── payment.validator.pb.go ├── private_cars │ ├── README.md │ ├── bin │ │ ├── cars │ │ │ └── main.go │ │ └── cars_noac │ │ │ └── main.go │ ├── cars.go │ ├── cars_noacces_control.go │ ├── cars_proxy.go │ ├── cars_test.go │ └── payloads.go └── token │ ├── chaincode │ └── erc20 │ │ ├── README.md │ │ ├── erc20.go │ │ └── erc20_test.go │ └── service │ ├── account │ ├── account.go │ ├── account.pb.cc.go │ ├── account.pb.go │ ├── account.pb.gw.go │ ├── account.proto │ ├── account.swagger.json │ ├── account.validator.pb.go │ └── resolver.go │ ├── allowance │ ├── allowance.go │ ├── allowance.pb.cc.go │ ├── allowance.pb.go │ ├── allowance.pb.gw.go │ ├── allowance.proto │ ├── allowance.swagger.json │ ├── allowance.validator.pb.go │ ├── state.go │ └── store.go │ ├── balance │ ├── balance.go │ ├── balance.pb.cc.go │ ├── balance.pb.go │ ├── balance.pb.gw.go │ ├── balance.proto │ ├── balance.swagger.json │ ├── balance.validator.pb.go │ ├── state.go │ └── store.go │ ├── burnable │ ├── burnable.pb.cc.go │ ├── burnable.pb.go │ ├── burnable.pb.gw.go │ ├── burnable.proto │ ├── burnable.swagger.json │ └── burnable.validator.pb.go │ ├── config │ ├── config.pb.cc.go │ ├── config.pb.go │ ├── config.pb.gw.go │ ├── config.proto │ ├── config.swagger.json │ ├── config.validator.pb.go │ ├── config_state.go │ ├── state.go │ └── token.go │ └── config_erc20 │ ├── config_erc20.go │ ├── config_erc20.pb.cc.go │ ├── config_erc20.pb.go │ ├── config_erc20.pb.gw.go │ ├── config_erc20.proto │ ├── config_erc20.swagger.json │ └── config_erc20.validator.pb.go ├── extensions ├── buf.yaml ├── crosscc │ ├── README.md │ ├── cclocator_setting.go │ ├── cclocator_setting.pb.cc.go │ ├── cclocator_setting.pb.go │ ├── cclocator_setting.pb.gw.go │ ├── cclocator_setting.proto │ ├── cclocator_setting.swagger.json │ ├── cclocator_setting.validator.pb.go │ └── state.go ├── debug │ ├── debug_state.go │ ├── debug_state.pb.cc.go │ ├── debug_state.pb.go │ ├── debug_state.pb.gw.go │ ├── debug_state.proto │ ├── debug_state.swagger.json │ ├── debug_state.validator.pb.go │ ├── debug_state_test.go │ ├── doc.md │ ├── handler.go │ ├── handler_test.go │ └── state.go ├── doc.md ├── ecdh │ ├── ecdh.go │ └── ecdh_test.go ├── encryption │ ├── README.md │ ├── aes.go │ ├── encryption.go │ ├── encryption_test.go │ ├── event.go │ ├── invoke.go │ ├── middleware_after.go │ ├── middleware_pre.go │ ├── must.go │ ├── state.go │ ├── testdata │ │ └── cc_external.go │ └── testing │ │ ├── invoke.go │ │ └── mockstub.go ├── owner │ ├── README.md │ ├── chaincode_owner.go │ ├── chaincode_owner.pb.cc.go │ ├── chaincode_owner.pb.go │ ├── chaincode_owner.pb.gw.go │ ├── chaincode_owner.proto │ ├── chaincode_owner.swagger.json │ ├── chaincode_owner.validator.pb.go │ ├── chaincode_owner_test.go │ ├── handler.go │ ├── modifier.go │ ├── owner.go │ ├── owner_test.go │ └── state.go └── pinger │ ├── README.md │ ├── chaincode_pinger.go │ ├── chaincode_pinger.pb.cc.go │ ├── chaincode_pinger.pb.go │ ├── chaincode_pinger.pb.gw.go │ ├── chaincode_pinger.proto │ ├── chaincode_pinger.swagger.json │ ├── chaincode_pinger.validator.pb.go │ ├── chaincode_pinger_test.go │ ├── consts.go │ ├── pinger.go │ └── pinger_test.go ├── gateway ├── README.md ├── api.go ├── buf.yaml ├── chaincode.pb.go ├── chaincode.pb.gw.go ├── chaincode.proto ├── chaincode.swagger.json ├── chaincode.validator.pb.go ├── chaincode_event_service.go ├── chaincode_instance_event_service.go ├── chaincode_instance_service.go ├── chaincode_old.go ├── chaincode_service.go ├── chaincode_service_test.go ├── context.go ├── doc.md ├── errors.go ├── event_stream.go ├── invoker_instance.go ├── invoker_stub.go ├── opt.go ├── protoc-gen-cc-gateway │ ├── README.md │ ├── generator │ │ ├── error.go │ │ ├── generator.go │ │ ├── opts.go │ │ ├── template.go │ │ └── template_func.go │ └── main.go ├── raw_json.go ├── service │ └── service.go ├── service_def.go └── tutorial.md ├── generators ├── README.md ├── deps.go └── install.sh ├── go.mod ├── go.sum ├── identity ├── cert.go ├── cert_identity.go ├── cert_test.go ├── entry.go ├── equal.go ├── error.go ├── identity.go ├── key.go ├── schema.proto └── testdata │ ├── s7techlab.key.pem │ ├── s7techlab.pem │ ├── some-person.key.pem │ ├── some-person.pem │ ├── testdata.go │ ├── victor-nosov.key.pem │ └── victor-nosov.pem ├── response └── response.go ├── router ├── README.md ├── chaincode.go ├── context.go ├── errors.go ├── handler.go ├── param │ ├── defparam │ │ └── type.go │ ├── middleware.go │ ├── parameters.go │ └── type.go ├── request.go ├── response.go ├── router.go └── router_test.go ├── sdk ├── all.go ├── event.go └── invoker.go ├── state ├── README.md ├── buf.yaml ├── doc.md ├── errors.go ├── event.go ├── interface.go ├── invoke.go ├── key.go ├── mapping │ ├── README.md │ ├── errors.go │ ├── event.go │ ├── event_instance.go │ ├── event_mapping.go │ ├── mapper.go │ ├── mapping_test.go │ ├── middleware.go │ ├── state.go │ ├── state_instance.go │ ├── state_keyref.go │ ├── state_mapping.go │ ├── state_mapping_opt.go │ ├── state_mapping_test.go │ └── testdata │ │ ├── cc_complex_id.go │ │ ├── cc_composite_id.go │ │ ├── cc_indexes.go │ │ ├── cc_slice_id.go │ │ ├── cc_withconfig.go │ │ ├── schema │ │ ├── config.pb.go │ │ ├── config.proto │ │ ├── config.validator.pb.go │ │ ├── with_complex_id.pb.go │ │ ├── with_complex_id.proto │ │ ├── with_complex_id.validator.pb.go │ │ ├── with_composite_id.pb.go │ │ ├── with_composite_id.proto │ │ ├── with_composite_id.validator.pb.go │ │ ├── with_indexes.pb.go │ │ ├── with_indexes.proto │ │ ├── with_indexes.validator.pb.go │ │ ├── with_slice_id.pb.go │ │ ├── with_slice_id.proto │ │ └── with_slice_id.validator.pb.go │ │ └── testdata.go ├── private_state_test.go ├── schema │ ├── schema.pb.go │ ├── schema.proto │ └── schema.validator.pb.go ├── state.go ├── state_cached.go ├── state_cached_test.go ├── state_list.go ├── state_test.go ├── testdata │ ├── cc_books.go │ ├── cc_state_cached.go │ ├── schema │ │ └── book.go │ ├── testdata.go │ └── util.go └── tranformer.go ├── testing ├── README.md ├── chaincode_event.go ├── expect │ ├── event.go │ ├── response.go │ └── tx.go ├── gomega │ ├── matchers.go │ └── matchers │ │ ├── erroris.go │ │ └── stringer.go ├── identity.go ├── mocked_peer.go ├── mocked_peer_decorator.go ├── mocked_peer_test.go ├── mockstub.go ├── mockstub_test.go ├── must.go ├── pagination.go ├── pagination_test.go ├── testdata │ └── cc_tx_state_isolation.go ├── tx.go └── txcreator_transformer.go └── third_party ├── buf.yaml ├── google ├── api │ ├── annotations.proto │ ├── http.proto │ └── httpbody.proto ├── protobuf │ └── any.proto └── rpc │ ├── code.proto │ ├── error_details.proto │ └── status.proto ├── hyperledger └── fabric │ └── peer │ ├── chaincode_event.proto │ └── proposal_response.proto └── mwitkow └── go-proto-validators └── validator.proto /.github/workflows/go.yml: -------------------------------------------------------------------------------- 1 | name: Go Quality 2 | 3 | on: [ push, pull_request ] 4 | 5 | jobs: 6 | test: 7 | name: Test with Coverage 8 | runs-on: ubuntu-latest 9 | steps: 10 | 11 | - name: Set up Go 12 | uses: actions/setup-go@v1 13 | with: 14 | go-version: '1.16' 15 | 16 | - name: Check out code 17 | uses: actions/checkout@v2 18 | - name: Calc coverage 19 | run: go test -v -covermode=count -coverprofile=coverage.out.tmp ./... 20 | - name: Strip generated code 21 | run: cat coverage.out.tmp | grep -v ".pb.go" | grep -v ".pb.cc.go" | grep -v ".pb.gw.go" > coverage.out 22 | - name: Convert coverage to lcov 23 | uses: jandelgado/gcov2lcov-action@v1.0.5 24 | - name: Coveralls 25 | uses: coverallsapp/github-action@v1.1.2 26 | with: 27 | github-token: ${{ secrets.github_token }} 28 | path-to-lcov: coverage.lcov -------------------------------------------------------------------------------- /.github/workflows/golangci-lint.yml: -------------------------------------------------------------------------------- 1 | name: golangci-lint 2 | on: 3 | push: 4 | tags: 5 | - v* 6 | branches: 7 | - master 8 | - main 9 | pull_request: 10 | jobs: 11 | golangci: 12 | name: lint 13 | strategy: 14 | matrix: 15 | go-version: [ 1.16.x, 1.17.x ] 16 | os: [ ubuntu-latest ] 17 | runs-on: ${{ matrix.os }} 18 | steps: 19 | - uses: actions/checkout@v2 20 | - name: golangci-lint 21 | uses: golangci/golangci-lint-action@v2 22 | with: 23 | # Required: the version of golangci-lint is required and must be specified without patch version: we always use the latest patch version. 24 | version: v1.45 25 | 26 | # Optional: working directory, useful for monorepos 27 | # working-directory: somedir 28 | 29 | # Optional: golangci-lint command line arguments. 30 | args: --exclude SA1019 31 | 32 | # Optional: show only new issues if it's a pull request. The default value is `false`. 33 | # only-new-issues: true 34 | 35 | # Optional: if set to true then the action will use pre-installed Go 36 | # skip-go-installation: true -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | vendor 3 | .DS_Store 4 | /generators/bin/ 5 | /generators/dist/ 6 | -------------------------------------------------------------------------------- /MANTAINERS.md: -------------------------------------------------------------------------------- 1 | Maintainers 2 | =========== 3 | 4 | **Active Maintainers** 5 | 6 | | Name | GitHub | Chat | email 7 | |--------------|------------------|------------|---------------------- 8 | | Viktor Nosov | [vitiko](vitiko) | | 9 | 10 | [vitiko]: https://github.com/vitiko -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | GOFLAGS ?= -mod=vendor 2 | 3 | PROTO_PACKAGES_GO := state 4 | PROTO_PACKAGES_GW := gateway 5 | PROTO_PACKAGES_CC_WITH_SERVICE_PREFIX := extensions 6 | PROTO_PACKAGES_CC := examples 7 | 8 | test: 9 | @echo "go test -mod vendor ./..." 10 | @go test ./... 11 | 12 | refresh-deps: 13 | @echo "go mod tidy" 14 | @GOFLAGS='' GONOSUMDB=github.com/hyperledger/fabric go mod tidy 15 | @echo "go mod vendor" 16 | @GOFLAGS='' GONOSUMDB=github.com/hyperledger/fabric go mod vendor 17 | 18 | proto: clean 19 | @for pkg in $(PROTO_PACKAGES_CC) ;do echo $$pkg && buf generate --template buf.gen.cc.yaml $$pkg -o ./$$(echo $$pkg | cut -d "/" -f1); done 20 | @for pkg in $(PROTO_PACKAGES_CC_WITH_SERVICE_PREFIX) ;do echo $$pkg && buf generate --template buf.gen.cc-with-service-prefix.yaml $$pkg -o ./$$(echo $$pkg | cut -d "/" -f1); done 21 | @for pkg in $(PROTO_PACKAGES_GW) ;do echo $$pkg && buf generate --template buf.gen.gw.yaml $$pkg -o ./$$(echo $$pkg | cut -d "/" -f1); done 22 | @for pkg in $(PROTO_PACKAGES_GO) ;do echo $$pkg && buf generate --template buf.gen.go.yaml $$pkg -o ./$$(echo $$pkg | cut -d "/" -f1); done 23 | clean: 24 | @for pkg in $(PROTO_PACKAGES_CC); do find $$pkg \( -name '*.pb.go' -or -name '*.pb.cc.go' -or -name '*.pb.gw.go' -or -name '*.swagger.json' -or -name '*.pb.md' \) -delete;done 25 | @for pkg in $(PROTO_PACKAGES_CC_WITH_SERVICE_PREFIX); do find $$pkg \( -name '*.pb.go' -or -name '*.pb.cc.go' -or -name '*.pb.gw.go' -or -name '*.swagger.json' -or -name '*.pb.md' \) -delete;done 26 | @for pkg in $(PROTO_PACKAGES_GW); do find $$pkg \( -name '*.pb.go' -or -name '*.pb.gw.go' -or -name '*.swagger.json' -or -name '*.pb.md' \) -delete;done 27 | @for pkg in $(PROTO_PACKAGES_GO); do find $$pkg \( -name '*.pb.go' -or -name '*.pb.md' \) -delete;done 28 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-slate -------------------------------------------------------------------------------- /buf.gen.cc-with-service-prefix.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | 3 | plugins: 4 | 5 | - name: go 6 | path: generators/bin/protoc-gen-go-cckit 7 | out: . 8 | opt: 9 | - plugins=grpc 10 | - paths=source_relative 11 | 12 | - name: govalidators 13 | path: generators/bin/protoc-gen-govalidators-cckit 14 | out: . 15 | opt: 16 | - paths=source_relative 17 | 18 | - name: grpc-gateway 19 | path: generators/bin/protoc-gen-grpc-gateway-cckit 20 | out: . 21 | opt: 22 | - logtostderr=true 23 | - paths=source_relative 24 | 25 | - name: swagger 26 | path: generators/bin/protoc-gen-swagger-cckit 27 | out: . 28 | opt: 29 | - logtostderr=true 30 | 31 | - name: cc-gateway 32 | path: generators/bin/protoc-gen-cc-gateway-cckit 33 | out: . 34 | opt: 35 | - logtostderr=true 36 | - paths=source_relative 37 | - embed_swagger 38 | - service_name_method_prefix 39 | 40 | - name: doc 41 | path: generators/bin/protoc-gen-doc-cckit 42 | out: . 43 | opt: 44 | - markdown,doc.md 45 | 46 | 47 | -------------------------------------------------------------------------------- /buf.gen.cc.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | 3 | plugins: 4 | 5 | - name: go 6 | path: generators/bin/protoc-gen-go-cckit 7 | out: . 8 | opt: 9 | - plugins=grpc 10 | - paths=source_relative 11 | 12 | - name: govalidators 13 | path: generators/bin/protoc-gen-govalidators-cckit 14 | out: . 15 | opt: 16 | - paths=source_relative 17 | 18 | - name: grpc-gateway 19 | path: generators/bin/protoc-gen-grpc-gateway-cckit 20 | out: . 21 | opt: 22 | - logtostderr=true 23 | - paths=source_relative 24 | 25 | - name: swagger 26 | path: generators/bin/protoc-gen-swagger-cckit 27 | out: . 28 | opt: 29 | - logtostderr=true 30 | 31 | - name: cc-gateway 32 | path: generators/bin/protoc-gen-cc-gateway-cckit 33 | out: . 34 | opt: 35 | - logtostderr=true 36 | - paths=source_relative 37 | - embed_swagger 38 | - service_resolver 39 | 40 | - name: doc 41 | path: generators/bin/protoc-gen-doc-cckit 42 | out: . 43 | opt: 44 | - markdown,doc.md 45 | 46 | -------------------------------------------------------------------------------- /buf.gen.go.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | 3 | plugins: 4 | 5 | - name: go 6 | path: generators/bin/protoc-gen-go-cckit 7 | out: . 8 | opt: 9 | - plugins=grpc 10 | - paths=source_relative 11 | 12 | - name: govalidators 13 | path: generators/bin/protoc-gen-govalidators-cckit 14 | out: . 15 | opt: 16 | - paths=source_relative 17 | 18 | - name: doc 19 | path: generators/bin/protoc-gen-doc-cckit 20 | out: . 21 | opt: 22 | - markdown,doc.md 23 | -------------------------------------------------------------------------------- /buf.gen.gw.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | 3 | plugins: 4 | - name: go 5 | path: generators/bin/protoc-gen-go-cckit 6 | out: . 7 | opt: 8 | - plugins=grpc 9 | - paths=source_relative 10 | 11 | - name: govalidators 12 | path: generators/bin/protoc-gen-govalidators-cckit 13 | out: . 14 | opt: 15 | - paths=source_relative 16 | 17 | - name: grpc-gateway 18 | path: generators/bin/protoc-gen-grpc-gateway-cckit 19 | out: . 20 | opt: 21 | - logtostderr=true 22 | - paths=source_relative 23 | 24 | - name: swagger 25 | path: generators/bin/protoc-gen-swagger-cckit 26 | out: . 27 | opt: 28 | - logtostderr=true 29 | 30 | - name: doc 31 | path: generators/bin/protoc-gen-doc-cckit 32 | out: . 33 | opt: 34 | - markdown,doc.md -------------------------------------------------------------------------------- /buf.work.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | directories: 3 | - examples 4 | - extensions 5 | - gateway 6 | - state 7 | - third_party -------------------------------------------------------------------------------- /convert/convert.go: -------------------------------------------------------------------------------- 1 | // Package convert for transforming between json serialized []byte and go structs 2 | package convert 3 | 4 | import ( 5 | "errors" 6 | "time" 7 | 8 | "github.com/golang/protobuf/ptypes/timestamp" 9 | ) 10 | 11 | var ( 12 | // ErrUnableToConvertNilToStruct - nil cannot be converted to struct 13 | ErrUnableToConvertNilToStruct = errors.New(`unable to convert nil to [struct,array,slice,ptr]`) 14 | // ErrUnableToConvertValueToStruct - value cannot be converted to struct 15 | ErrUnableToConvertValueToStruct = errors.New(`unable to convert value to struct`) 16 | ) 17 | 18 | const TypeInt = 1 19 | const TypeString = `` 20 | const TypeBool = true 21 | 22 | type ( 23 | // FromByter interface supports FromBytes func for converting from slice of bytes to target type 24 | FromByter interface { 25 | FromBytes([]byte) (interface{}, error) 26 | } 27 | 28 | // ToByter interface supports ToBytes func for converting to slice of bytes from source type 29 | ToByter interface { 30 | ToBytes() ([]byte, error) 31 | } 32 | ) 33 | 34 | // TimestampToTime converts timestamp to time.Time 35 | func TimestampToTime(ts *timestamp.Timestamp) time.Time { 36 | return time.Unix(ts.GetSeconds(), int64(ts.GetNanos())) 37 | } 38 | -------------------------------------------------------------------------------- /convert/convert_test.go: -------------------------------------------------------------------------------- 1 | package convert_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | 9 | "github.com/s7techlab/cckit/convert" 10 | ) 11 | 12 | func TestState(t *testing.T) { 13 | RegisterFailHandler(Fail) 14 | RunSpecs(t, "State suite") 15 | } 16 | 17 | var _ = Describe(`Convert`, func() { 18 | 19 | It(`Bool`, func() { 20 | bTrue, err := convert.ToBytes(true) 21 | Expect(err).NotTo(HaveOccurred()) 22 | Expect(bTrue).To(Equal([]byte(`true`))) 23 | 24 | bFalse, err := convert.ToBytes(false) 25 | Expect(err).NotTo(HaveOccurred()) 26 | Expect(bFalse).To(Equal([]byte(`false`))) 27 | 28 | eTrue, err := convert.FromBytes(bTrue, convert.TypeBool) 29 | Expect(err).NotTo(HaveOccurred()) 30 | Expect(eTrue.(bool)).To(Equal(true)) 31 | 32 | eFalse, err := convert.FromBytes(bFalse, convert.TypeBool) 33 | Expect(err).NotTo(HaveOccurred()) 34 | Expect(eFalse.(bool)).To(Equal(false)) 35 | }) 36 | 37 | It(`String`, func() { 38 | const MyStr = `my-string` 39 | bStr, err := convert.ToBytes(MyStr) 40 | Expect(err).NotTo(HaveOccurred()) 41 | Expect(bStr).To(Equal([]byte(MyStr))) 42 | 43 | eStr, err := convert.FromBytes(bStr, convert.TypeString) 44 | Expect(err).NotTo(HaveOccurred()) 45 | Expect(eStr.(string)).To(Equal(MyStr)) 46 | }) 47 | 48 | It(`Nil`, func() { 49 | bNil, err := convert.ToBytes(nil) 50 | Expect(err).NotTo(HaveOccurred()) 51 | Expect(bNil).To(Equal([]byte{})) 52 | }) 53 | 54 | }) 55 | -------------------------------------------------------------------------------- /convert/to_bytes.go: -------------------------------------------------------------------------------- 1 | package convert 2 | 3 | import ( 4 | "encoding/json" 5 | "fmt" 6 | "reflect" 7 | "strconv" 8 | 9 | "github.com/golang/protobuf/proto" 10 | ) 11 | 12 | // ArgsToBytes converts func arguments to bytes 13 | func ArgsToBytes(iArgs ...interface{}) ([][]byte, error) { 14 | args := make([][]byte, len(iArgs)) 15 | 16 | for i, arg := range iArgs { 17 | val, err := ToBytes(arg) 18 | if err != nil { 19 | return nil, fmt.Errorf(`convert invoke arg[%d]: %w`, i, err) 20 | } 21 | args[i] = val 22 | } 23 | 24 | return args, nil 25 | } 26 | 27 | // ToBytes converts interface{} (string, []byte , struct to ToByter interface to []byte for storing in state 28 | func ToBytes(value interface{}) ([]byte, error) { 29 | if value == nil { 30 | return nil, nil 31 | } 32 | 33 | switch v := value.(type) { 34 | 35 | // first priority if value implements ToByter interface 36 | case ToByter: 37 | return v.ToBytes() 38 | case proto.Message: 39 | return proto.Marshal(proto.Clone(v)) 40 | case bool: 41 | return []byte(strconv.FormatBool(v)), nil 42 | case string: 43 | return []byte(v), nil 44 | case uint, int, int32: 45 | return []byte(fmt.Sprint(v)), nil 46 | case []byte: 47 | return v, nil 48 | 49 | default: 50 | valueType := reflect.TypeOf(value).Kind() 51 | 52 | switch valueType { 53 | case reflect.Ptr, reflect.Struct, reflect.Array, reflect.Map, reflect.Slice: 54 | return json.Marshal(value) 55 | // used when type based on string 56 | case reflect.String: 57 | return []byte(reflect.ValueOf(value).String()), nil 58 | 59 | default: 60 | return nil, fmt.Errorf( 61 | `toBytes converting supports ToByter interface,struct,array,slice,bool and string, current type is %s`, 62 | valueType) 63 | } 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /docs/chaincode-examples.md: -------------------------------------------------------------------------------- 1 | # Hyperledger Fabric chaincode examples 2 | 3 | * [Commercial paper](https://hyperledger-fabric.readthedocs.io/en/release-1.4/developapps/smartcontract.html) from official [Hyperledger Fabric documentation](https://hyperledger-fabric.readthedocs.io) 4 | * [Blockchain insurance application](https://github.com/IBM/build-blockchain-insurance-app) ( testing tutorial: how to [write tests for "insurance" chaincode](../examples/insurance) ) 5 | * [Marbles](https://github.com/hyperledger/fabric/blob/release-1.1/examples/chaincode/go/marbles02/marbles_chaincode.go) 6 | * [Marbles from IBM-Blockchain](https://github.com/IBM-Blockchain/marbles/blob/master/chaincode/src/marbles/marbles.go) 7 | -------------------------------------------------------------------------------- /docs/create-test-cert.md: -------------------------------------------------------------------------------- 1 | # Hyperledger Fabric chaincode kit (CCKit) 2 | 3 | ## X.509 certificate for testing 4 | 5 | generate self-signed certificate: 6 | 7 | ``` 8 | $ openssl ecparam -name secp384r1 -genkey | openssl pkcs8 -topk8 -nocrypt > some-person.key.pem 9 | $ openssl req -new -x509 -key some-person.key.pem -out some-person.pem -days 730 10 | ``` 11 | 12 | You are about to be asked to enter information that will be incorporated into your certificate request. 13 | What you are about to enter is what is called a Distinguished Name or a DN. 14 | 15 | 16 | examine the certificate: 17 | 18 | `$ openssl x509 -in some-person.pem -text -noout` 19 | 20 | 21 | ```` 22 | Certificate: 23 | Data: 24 | Version: 1 (0x0) 25 | Serial Number: 13222534896082439009 (0xb77fe16e97334b61) 26 | Signature Algorithm: sha256WithRSAEncryption 27 | Issuer: C=RU, ST=Moscow, L=Moscow, O=S7Techlab, OU=Blockchain dept, CN=Victor Nosov/emailAddress=vitiko@mail.ru 28 | Validity 29 | Not Before: Apr 24 07:49:10 2018 GMT 30 | Not After : Jul 6 07:49:10 2018 GMT 31 | Subject: C=RU, ST=Moscow, L=Moscow, O=S7Techlab, OU=Blockchain dept, CN=Victor Nosov/emailAddress=vitiko@mail.ru 32 | Subject Public Key Info 33 | ```` 34 | 35 | 36 | examine the key: 37 | 38 | `openssl ecparam -in some-person.pem.key -text -noout` 39 | 40 | 41 | ```` 42 | ASN1 OID: secp256k1 43 | 44 | ```` -------------------------------------------------------------------------------- /docs/debug.md: -------------------------------------------------------------------------------- 1 | # Hyperledger Fabric chaincode kit (CCKit) 2 | 3 | ## Chaincode debugging 4 | 5 | 6 | Use logger in chaincode 7 | 8 | `c.Logger().Debug("debug message")` 9 | 10 | 11 | 12 | Change logging level while performing tests 13 | 14 | `CORE_CHAINCODE_LOGGING_LEVEL=debug go test` 15 | -------------------------------------------------------------------------------- /docs/img/blockchain-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s7techlab/cckit/eb9bde26f023c8381e9760f0fa93dc895423dcca/docs/img/blockchain-app.png -------------------------------------------------------------------------------- /docs/img/cc-code-gen.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s7techlab/cckit/eb9bde26f023c8381e9760f0fa93dc895423dcca/docs/img/cc-code-gen.png -------------------------------------------------------------------------------- /docs/img/gateway-cpaper-swagger.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s7techlab/cckit/eb9bde26f023c8381e9760f0fa93dc895423dcca/docs/img/gateway-cpaper-swagger.png -------------------------------------------------------------------------------- /docs/img/gateway-mocked-start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s7techlab/cckit/eb9bde26f023c8381e9760f0fa93dc895423dcca/docs/img/gateway-mocked-start.png -------------------------------------------------------------------------------- /docs/img/gateway-mocked-usage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s7techlab/cckit/eb9bde26f023c8381e9760f0fa93dc895423dcca/docs/img/gateway-mocked-usage.png -------------------------------------------------------------------------------- /docs/img/mockstub-hlf-peer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s7techlab/cckit/eb9bde26f023c8381e9760f0fa93dc895423dcca/docs/img/mockstub-hlf-peer.png -------------------------------------------------------------------------------- /examples/buf.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | lint: 3 | use: 4 | - BASIC 5 | - FILE_LOWER_SNAKE_CASE 6 | - SERVICE_SUFFIX 7 | - ENUM_VALUE_PREFIX 8 | except: 9 | - PACKAGE_DIRECTORY_MATCH 10 | 11 | breaking: 12 | use: 13 | - FILE -------------------------------------------------------------------------------- /examples/cars/README.md: -------------------------------------------------------------------------------- 1 | # Cars registration Hyperledger Fabric chaincode 2 | 3 | Car registration chaincode use simple golang structure with json marshalling. Example with protobuf base chaincode schema 4 | is [here](../cpaper). 5 | 6 | [source code](cars.go), [tests](cars_test.go) -------------------------------------------------------------------------------- /examples/cars/bin/cars/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/hyperledger/fabric-chaincode-go/shim" 7 | 8 | "github.com/s7techlab/cckit/examples/cars" 9 | ) 10 | 11 | func main() { 12 | cc := cars.New() 13 | if err := shim.Start(cc); err != nil { 14 | fmt.Printf("Error starting Cars chaincode: %s", err) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/cars/bin/cars_noac/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/hyperledger/fabric-chaincode-go/shim" 7 | 8 | "github.com/s7techlab/cckit/examples/cars" 9 | ) 10 | 11 | func main() { 12 | cc := cars.NewWithoutAccessControl() 13 | if err := shim.Start(cc); err != nil { 14 | fmt.Printf("Error starting Cars chaincode: %s", err) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/cars/cars_noacces_control.go: -------------------------------------------------------------------------------- 1 | // Package cars simple CRUD chaincode for store information about cars 2 | package cars 3 | 4 | import ( 5 | "github.com/s7techlab/cckit/router" 6 | p "github.com/s7techlab/cckit/router/param" 7 | ) 8 | 9 | func NewWithoutAccessControl() *router.Chaincode { 10 | r := router.New(`cars_without_access_control`) // also initialized logger with "cars" prefix 11 | 12 | r.Group(`car`). 13 | Query(`List`, queryCars). // chain code method name is carList 14 | Query(`Get`, queryCar, p.String(`id`)). // chain code method name is carGet, method has 1 string argument "id" 15 | Invoke(`Register`, invokeCarRegister, p.Struct(`car`, &CarPayload{})) // allow access to everyone 16 | return router.NewChaincode(r) 17 | } 18 | -------------------------------------------------------------------------------- /examples/cars/cars_proxy.go: -------------------------------------------------------------------------------- 1 | package cars 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/hyperledger/fabric-chaincode-go/shim" 7 | 8 | "github.com/s7techlab/cckit/router" 9 | p "github.com/s7techlab/cckit/router/param" 10 | ) 11 | 12 | // NewProxy created chaincode, related to cars chaincode 13 | func NewProxy(carsChannel, carsChaincode string) *router.Chaincode { 14 | r := router.New(`cars_proxy`) // also initialized logger with "cars_related" prefix 15 | 16 | r.Init(invokeInit) 17 | 18 | r.Query(`carGet`, queryCarProxy, p.String(`id`)) 19 | 20 | return router.NewChaincode(r) 21 | } 22 | 23 | // 24 | func queryCarProxy(c router.Context) (interface{}, error) { 25 | var ( 26 | id = c.ParamString(`id`) 27 | ) 28 | 29 | // query external chaincode 30 | response := c.Stub().InvokeChaincode(`cars`, [][]byte{[]byte(`carGet`), []byte(id)}, `my_channel`) 31 | if response.Status == shim.ERROR { 32 | return nil, errors.New(response.Message) 33 | } 34 | return response.Payload, nil 35 | } 36 | -------------------------------------------------------------------------------- /examples/cars/payloads.go: -------------------------------------------------------------------------------- 1 | package cars 2 | 3 | var Payloads = []*Car{{ 4 | Id: `A777MP77`, 5 | Title: `BMW`, 6 | Owner: `victor-nosov`, 7 | }, { 8 | Id: `O888OO77`, 9 | Title: `TOYOTA`, 10 | Owner: `alexander`, 11 | }, { 12 | Id: `O222OO177`, 13 | Title: `Lambo`, 14 | Owner: `hodl`, 15 | }, { 16 | Id: `E333KX177`, 17 | Title: `Aurus`, 18 | Owner: `bigboss`, 19 | }} 20 | -------------------------------------------------------------------------------- /examples/cpaper/README.md: -------------------------------------------------------------------------------- 1 | # Commercial paper Hyperledger Fabric chaincode example 2 | 3 | This is a faithful reimplementation of the official example [Commercial paper scenario](https://hyperledger-fabric.readthedocs.io/en/release-1.4/developapps/scenario.html) 4 | 5 | Original code written in JavaScript using the Fabric Chaincode Node SDK is available [here](https://github.com/hyperledger/fabric-samples/tree/release-1.4/commercial-paper/organization/digibank/contract) 6 | -------------------------------------------------------------------------------- /examples/cpaper/img/cpaper-network.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s7techlab/cckit/eb9bde26f023c8381e9760f0fa93dc895423dcca/examples/cpaper/img/cpaper-network.png -------------------------------------------------------------------------------- /examples/cpaper/img/state.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s7techlab/cckit/eb9bde26f023c8381e9760f0fa93dc895423dcca/examples/cpaper/img/state.png -------------------------------------------------------------------------------- /examples/cpaper_asservice/bin/api/_common.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | API_HOST=${API_HOST:-''} 4 | VERBOSE=${VERBOSE:-'s'} 5 | 6 | if [[ -z "$API_HOST" ]]; then echo "need to set env API_HOST"; exit 1; fi 7 | 8 | 9 | GET(){ 10 | if [[ -z "$1" ]]; then echo "\$1 - PATH"; exit 1; fi 11 | 12 | SHOW "` 13 | curl -${VERBOSE} -X GET -H "Content-Type: application/json" \ 14 | ${API_HOST}$1 15 | `" 16 | } 17 | 18 | 19 | DELETE(){ 20 | if [[ -z "$1" ]]; then echo "\$1 - PATH"; exit 1; fi 21 | 22 | SHOW "` 23 | curl -${VERBOSE} -X DELETE -H "Content-Type: application/json" \ 24 | ${API_HOST}$1 25 | `" 26 | } 27 | 28 | POST(){ 29 | if [[ -z "$1" ]]; then echo "\$1 - PATH"; exit 1; fi 30 | if [[ -z "$2" ]]; then echo "\$2 - BODY"; exit 1; fi 31 | 32 | SHOW "` 33 | curl -${VERBOSE} -X POST -H "Content-Type: application/json" -d "$2" \ 34 | ${API_HOST}$1 35 | `" 36 | } 37 | 38 | 39 | PUT(){ 40 | if [[ -z "$1" ]]; then echo "\$1 - PATH"; exit 1; fi 41 | 42 | SHOW "` 43 | curl -${VERBOSE} -X PUT -H "Content-Type: application/json" \ 44 | ${API_HOST}$1 45 | `" 46 | } 47 | 48 | SHOW() { 49 | PRETTY=${PRETTY:-''} 50 | 51 | if [[ -z "$PRETTY" ]]; then 52 | echo "$1" 53 | elif [[ "$PRETTY" == "python" ]]; then 54 | printf '%s' "$1" | python -c 'import json,sys; print(json.dumps(sys.stdin.read()))' 55 | else 56 | echo $1 | jq 57 | fi 58 | } 59 | -------------------------------------------------------------------------------- /examples/cpaper_asservice/bin/api/get.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | if [[ -z "$1" ]]; then echo "\$1 - issuer"; exit 1; fi 3 | if [[ -z "$2" ]]; then echo "\$2 - paper number"; exit 1; fi 4 | source _common.sh 5 | 6 | GET /cpaper/$1/$2 -------------------------------------------------------------------------------- /examples/cpaper_asservice/bin/api/issue.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | if [[ -z "$1" ]]; then echo "\$1 - path to cpaper issue payload"; exit 1; fi 3 | source _common.sh 4 | 5 | POST /cpaper/issue @$1 -------------------------------------------------------------------------------- /examples/cpaper_asservice/bin/api/list.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | source _common.sh 4 | 5 | GET /cpaper -------------------------------------------------------------------------------- /examples/cpaper_asservice/bin/api/payload_cpaper_1_issue.json: -------------------------------------------------------------------------------- 1 | { 2 | "issuer": "SomeIssuer", 3 | "paper_number": "0001", 4 | "issue_date": "2019-07-20T15:00:00Z", 5 | "maturity_date": "2020-07-20T15:00:00Z", 6 | "face_value": 100000, 7 | "external_id": "EXT0001" 8 | } -------------------------------------------------------------------------------- /examples/cpaper_asservice/bin/api/payload_cpaper_2_issue.json: -------------------------------------------------------------------------------- 1 | { 2 | "issuer": "OtherIssuer", 3 | "paper_number": "0002", 4 | "issue_date": "2019-07-20T15:00:00Z", 5 | "maturity_date": "2021-07-20T15:00:00Z", 6 | "face_value": 200000, 7 | "external_id": "EXT0002" 8 | } -------------------------------------------------------------------------------- /examples/cpaper_asservice/bin/chaincode/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/hyperledger/fabric-chaincode-go/shim" 7 | 8 | "github.com/s7techlab/cckit/examples/cpaper_asservice" 9 | ) 10 | 11 | func main() { 12 | cc, err := cpaper_asservice.NewCC() 13 | if err != nil { 14 | fmt.Printf("Error creating chaincode: %s", err) 15 | } 16 | 17 | err = shim.Start(cc) 18 | if err != nil { 19 | fmt.Printf("Error starting chaincode: %s", err) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/cpaper_asservice/chaincode.go: -------------------------------------------------------------------------------- 1 | package cpaper_asservice 2 | 3 | import ( 4 | "github.com/s7techlab/cckit/extensions/encryption" 5 | "github.com/s7techlab/cckit/extensions/owner" 6 | "github.com/s7techlab/cckit/router" 7 | ) 8 | 9 | func CCRouter(name string) (*router.Group, error) { 10 | r := router.New(name) 11 | // Store on the ledger the information about chaincode instantiation 12 | r.Init(owner.InvokeSetFromCreator) 13 | 14 | if err := RegisterCPaperServiceChaincode(r, &CPaperService{}); err != nil { 15 | return nil, err 16 | } 17 | 18 | return r, nil 19 | } 20 | 21 | func NewCC() (*router.Chaincode, error) { 22 | r, err := CCRouter(`CommercialPaper`) 23 | if err != nil { 24 | return nil, err 25 | } 26 | 27 | return router.NewChaincode(r), nil 28 | } 29 | 30 | func NewCCEncrypted() (*router.Chaincode, error) { 31 | r, err := CCRouter(`CommercialPaperEncrypted`) 32 | if err != nil { 33 | return nil, err 34 | } 35 | 36 | r. 37 | // encryption key in transient map and encrypted args required 38 | Pre(encryption.ArgsDecrypt). 39 | // default Context replaced with EncryptedStateContext only if key is provided in transient map 40 | Use(encryption.EncStateContext). 41 | // invoke response will be encrypted because it will be placed in blocks 42 | After(encryption.EncryptInvokeResponse()) 43 | 44 | return router.NewChaincode(r), nil 45 | } 46 | -------------------------------------------------------------------------------- /examples/cpaper_asservice/state.go: -------------------------------------------------------------------------------- 1 | package cpaper_asservice 2 | 3 | import ( 4 | "github.com/s7techlab/cckit/router" 5 | "github.com/s7techlab/cckit/state" 6 | m "github.com/s7techlab/cckit/state/mapping" 7 | ) 8 | 9 | var ( 10 | StateMappings = m.StateMappings{}. 11 | // Create mapping for Commercial Paper entity 12 | Add(&CommercialPaper{}, 13 | m.PKeySchema(&CommercialPaperId{}), // Key namespace will be <"CommercialPaper", Issuer, PaperNumber> 14 | m.List(&CommercialPaperList{}), // Structure of result for List method 15 | m.UniqKey("ExternalId"), // External Id is unique 16 | ) 17 | 18 | EventMappings = m.EventMappings{}. 19 | // Event name will be "IssueCommercialPaper", payload - same as issue payload 20 | Add(&IssueCommercialPaper{}). 21 | // Event name will be "BuyCommercialPaper" 22 | Add(&BuyCommercialPaper{}). 23 | // Event name will be "RedeemCommercialPaper" 24 | Add(&RedeemCommercialPaper{}) 25 | ) 26 | 27 | // State with chaincode mappings 28 | func State(ctx router.Context) m.MappedState { 29 | return m.WrapState(ctx.State(), StateMappings) 30 | } 31 | 32 | // Event with chaincode mappings 33 | func Event(ctx router.Context) state.Event { 34 | return m.WrapEvent(ctx.Event(), EventMappings) 35 | } 36 | -------------------------------------------------------------------------------- /examples/cpaper_asservice/testdata/admin.key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBSoxUZoyY3jk1GD430 3 | 8tIWWLiEw8dGL9SnfkG7AGQNHsYLz8hbamYtN9TPVg7JbemhZANiAASAPNEhxmCz 4 | F7w+8rmE+iKHiTp+qinNnby69unp3eCpRD2XaI5zfPDiVZbPFm3uFsHskEGNwJyh 5 | G84Vc74/Nw5jrIDU6p83i1yXCV2JafT5oCBsSLNw1vR3ddXW4vK7fJ8= 6 | -----END PRIVATE KEY----- 7 | -------------------------------------------------------------------------------- /examples/cpaper_asservice/testdata/admin.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICTDCCAdECCQDXg5wOXASntDAKBggqhkjOPQQDAjCBjjELMAkGA1UEBhMCUlUx 3 | DzANBgNVBAgMBk1vc2NvdzEPMA0GA1UEBwwGTW9zY293MRIwEAYDVQQKDAlTN1Rl 4 | Y2hsYWIxEjAQBgNVBAsMCVM3VGVjaGxhYjESMBAGA1UEAwwJUzdUZWNobGFiMSEw 5 | HwYJKoZIhvcNAQkBFhJpbmZvQHRlY2hsYWIuczcucnUwHhcNMTgxMTEzMTMxMTI3 6 | WhcNMjAxMTEyMTMxMTI3WjCBjjELMAkGA1UEBhMCUlUxDzANBgNVBAgMBk1vc2Nv 7 | dzEPMA0GA1UEBwwGTW9zY293MRIwEAYDVQQKDAlTN1RlY2hsYWIxEjAQBgNVBAsM 8 | CVM3VGVjaGxhYjESMBAGA1UEAwwJUzdUZWNobGFiMSEwHwYJKoZIhvcNAQkBFhJp 9 | bmZvQHRlY2hsYWIuczcucnUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASAPNEhxmCz 10 | F7w+8rmE+iKHiTp+qinNnby69unp3eCpRD2XaI5zfPDiVZbPFm3uFsHskEGNwJyh 11 | G84Vc74/Nw5jrIDU6p83i1yXCV2JafT5oCBsSLNw1vR3ddXW4vK7fJ8wCgYIKoZI 12 | zj0EAwIDaQAwZgIxAMP56SfE7D8sjv5H4rU5CnXeJLoCmcDo20OQcMBbIoYNHiet 13 | ReJZlqytK5WoPm8wHQIxANdPnajvejR+ZE7MMe+pd18uwGZ8hh9Hp6C9ugoipv0q 14 | Oo4vB+J8+jEuRjSsXfMzPQ== 15 | -----END CERTIFICATE----- 16 | -------------------------------------------------------------------------------- /examples/cpaper_asservice/testdata/cpaper.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "time" 5 | 6 | "google.golang.org/protobuf/types/known/timestamppb" 7 | 8 | "github.com/s7techlab/cckit/examples/cpaper_asservice" 9 | testcc "github.com/s7techlab/cckit/testing" 10 | ) 11 | 12 | var ( 13 | Id1 = &cpaper_asservice.CommercialPaperId{ 14 | Issuer: "SomeIssuer", 15 | PaperNumber: "0001", 16 | } 17 | 18 | ExternalId1 = &cpaper_asservice.ExternalId{ 19 | Id: "EXT0001", 20 | } 21 | 22 | Issue1 = &cpaper_asservice.IssueCommercialPaper{ 23 | Issuer: Id1.Issuer, 24 | PaperNumber: Id1.PaperNumber, 25 | IssueDate: timestamppb.Now(), 26 | MaturityDate: testcc.MustProtoTimestamp(time.Now().AddDate(0, 2, 0)), 27 | FaceValue: 100000, 28 | ExternalId: ExternalId1.Id, 29 | } 30 | 31 | Buy1 = &cpaper_asservice.BuyCommercialPaper{ 32 | Issuer: Id1.Issuer, 33 | PaperNumber: Id1.PaperNumber, 34 | CurrentOwner: Id1.Issuer, 35 | NewOwner: "SomeBuyer", 36 | Price: 95000, 37 | PurchaseDate: timestamppb.Now(), 38 | } 39 | 40 | Redeem1 = &cpaper_asservice.RedeemCommercialPaper{ 41 | Issuer: Id1.Issuer, 42 | PaperNumber: Id1.PaperNumber, 43 | RedeemingOwner: Buy1.NewOwner, 44 | RedeemDate: timestamppb.Now(), 45 | } 46 | 47 | CPaperInState1 = &cpaper_asservice.CommercialPaper{ 48 | Issuer: Id1.Issuer, 49 | Owner: Id1.Issuer, 50 | State: cpaper_asservice.CommercialPaper_STATE_ISSUED, 51 | PaperNumber: Id1.PaperNumber, 52 | FaceValue: Issue1.FaceValue, 53 | IssueDate: Issue1.IssueDate, 54 | MaturityDate: Issue1.MaturityDate, 55 | ExternalId: Issue1.ExternalId, 56 | } 57 | ) 58 | -------------------------------------------------------------------------------- /examples/cpaper_extended/README.md: -------------------------------------------------------------------------------- 1 | # Commercial paper extended example 2 | 3 | This is an extended example of the official [Commercial paper scenario](https://hyperledger-fabric.readthedocs.io/en/release-1.4/developapps/scenario.html) 4 | 5 | ### Features 6 | 7 | * Protobuf transaction payload and event definitions 8 | * Protobuf chaincode state schema 9 | * Event emitting 10 | * Unique key external reference usage 11 | -------------------------------------------------------------------------------- /examples/cpaper_extended/schema/payload.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "github.com/s7techlab/cckit/examples/cpaper_extended/schema"; 4 | package examples.cpaper_extended.schema; 5 | 6 | import "google/protobuf/timestamp.proto"; 7 | import "mwitkow/go-proto-validators/validator.proto"; 8 | 9 | // IssueCommercialPaper event 10 | message IssueCommercialPaper { 11 | string issuer = 1 [(validator.field) = {string_not_empty : true}]; 12 | string paper_number = 2 [(validator.field) = {string_not_empty : true}]; 13 | google.protobuf.Timestamp issue_date = 3 [(validator.field) = {msg_exists : true}]; 14 | google.protobuf.Timestamp maturity_date = 4 [(validator.field) = {msg_exists : true}]; 15 | int32 face_value = 5 [(validator.field) = {int_gt : 0}]; 16 | 17 | // external_id - another unique constraint 18 | string external_id = 6 [(validator.field) = {string_not_empty : true}]; 19 | } 20 | 21 | // BuyCommercialPaper event 22 | message BuyCommercialPaper { 23 | string issuer = 1 [(validator.field) = {string_not_empty : true}]; 24 | string paper_number = 2 [(validator.field) = {string_not_empty : true}]; 25 | string current_owner = 3 [(validator.field) = {string_not_empty : true}]; 26 | string new_owner = 4 [(validator.field) = {string_not_empty : true}]; 27 | int32 price = 5 [(validator.field) = {int_gt : 0}]; 28 | google.protobuf.Timestamp purchase_date = 6 [(validator.field) = {msg_exists : true}]; 29 | } 30 | 31 | // RedeemCommercialPaper event 32 | message RedeemCommercialPaper { 33 | string issuer = 1 [(validator.field) = {string_not_empty : true}]; 34 | string paper_number = 2 [(validator.field) = {string_not_empty : true}]; 35 | string redeeming_owner = 3 [(validator.field) = {string_not_empty : true}]; 36 | google.protobuf.Timestamp redeem_date = 4 [(validator.field) = {msg_exists : true}]; 37 | } 38 | -------------------------------------------------------------------------------- /examples/cpaper_extended/schema/payload.swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "title": "cpaper_extended/schema/payload.proto", 5 | "version": "version not set" 6 | }, 7 | "consumes": [ 8 | "application/json" 9 | ], 10 | "produces": [ 11 | "application/json" 12 | ], 13 | "paths": {}, 14 | "definitions": { 15 | "protobufAny": { 16 | "type": "object", 17 | "properties": { 18 | "type_url": { 19 | "type": "string" 20 | }, 21 | "value": { 22 | "type": "string", 23 | "format": "byte" 24 | } 25 | } 26 | }, 27 | "runtimeError": { 28 | "type": "object", 29 | "properties": { 30 | "error": { 31 | "type": "string" 32 | }, 33 | "code": { 34 | "type": "integer", 35 | "format": "int32" 36 | }, 37 | "message": { 38 | "type": "string" 39 | }, 40 | "details": { 41 | "type": "array", 42 | "items": { 43 | "$ref": "#/definitions/protobufAny" 44 | } 45 | } 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /examples/cpaper_extended/schema/state.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "github.com/s7techlab/cckit/examples/cpaper_extended/schema"; 4 | package examples.cpaper_extended.schema; 5 | 6 | import "google/protobuf/timestamp.proto"; 7 | 8 | // Commercial Paper state entry 9 | message CommercialPaper { 10 | 11 | enum State { 12 | STATE_ISSUED = 0; 13 | STATE_TRADING = 1; 14 | STATE_REDEEMED = 2; 15 | } 16 | 17 | // Issuer and Paper number comprises composite primary key of Commercial paper entry 18 | string issuer = 1; 19 | string paper_number = 2; 20 | 21 | string owner = 3; 22 | google.protobuf.Timestamp issue_date = 4; 23 | google.protobuf.Timestamp maturity_date = 5; 24 | int32 face_value = 6; 25 | State state = 7; 26 | 27 | // Additional unique field for entry 28 | string external_id = 8; 29 | } 30 | 31 | // CommercialPaperId identifier part 32 | message CommercialPaperId { 33 | string issuer = 1; 34 | string paper_number = 2; 35 | } 36 | 37 | // Container for returning multiple entities 38 | message CommercialPaperList { 39 | repeated CommercialPaper items = 1; 40 | } 41 | -------------------------------------------------------------------------------- /examples/cpaper_extended/schema/state.swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "title": "cpaper_extended/schema/state.proto", 5 | "version": "version not set" 6 | }, 7 | "consumes": [ 8 | "application/json" 9 | ], 10 | "produces": [ 11 | "application/json" 12 | ], 13 | "paths": {}, 14 | "definitions": { 15 | "protobufAny": { 16 | "type": "object", 17 | "properties": { 18 | "type_url": { 19 | "type": "string" 20 | }, 21 | "value": { 22 | "type": "string", 23 | "format": "byte" 24 | } 25 | } 26 | }, 27 | "runtimeError": { 28 | "type": "object", 29 | "properties": { 30 | "error": { 31 | "type": "string" 32 | }, 33 | "code": { 34 | "type": "integer", 35 | "format": "int32" 36 | }, 37 | "message": { 38 | "type": "string" 39 | }, 40 | "details": { 41 | "type": "array", 42 | "items": { 43 | "$ref": "#/definitions/protobufAny" 44 | } 45 | } 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /examples/cpaper_extended/schema/state.validator.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-gogo. DO NOT EDIT. 2 | // source: cpaper_extended/schema/state.proto 3 | 4 | package schema 5 | 6 | import ( 7 | fmt "fmt" 8 | math "math" 9 | proto "github.com/golang/protobuf/proto" 10 | _ "google.golang.org/protobuf/types/known/timestamppb" 11 | github_com_mwitkow_go_proto_validators "github.com/mwitkow/go-proto-validators" 12 | ) 13 | 14 | // Reference imports to suppress errors if they are not otherwise used. 15 | var _ = proto.Marshal 16 | var _ = fmt.Errorf 17 | var _ = math.Inf 18 | 19 | func (this *CommercialPaper) Validate() error { 20 | if this.IssueDate != nil { 21 | if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.IssueDate); err != nil { 22 | return github_com_mwitkow_go_proto_validators.FieldError("IssueDate", err) 23 | } 24 | } 25 | if this.MaturityDate != nil { 26 | if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.MaturityDate); err != nil { 27 | return github_com_mwitkow_go_proto_validators.FieldError("MaturityDate", err) 28 | } 29 | } 30 | return nil 31 | } 32 | func (this *CommercialPaperId) Validate() error { 33 | return nil 34 | } 35 | func (this *CommercialPaperList) Validate() error { 36 | for _, item := range this.Items { 37 | if item != nil { 38 | if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(item); err != nil { 39 | return github_com_mwitkow_go_proto_validators.FieldError("Items", err) 40 | } 41 | } 42 | } 43 | return nil 44 | } 45 | -------------------------------------------------------------------------------- /examples/cpaper_extended/testdata/admin.key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBSoxUZoyY3jk1GD430 3 | 8tIWWLiEw8dGL9SnfkG7AGQNHsYLz8hbamYtN9TPVg7JbemhZANiAASAPNEhxmCz 4 | F7w+8rmE+iKHiTp+qinNnby69unp3eCpRD2XaI5zfPDiVZbPFm3uFsHskEGNwJyh 5 | G84Vc74/Nw5jrIDU6p83i1yXCV2JafT5oCBsSLNw1vR3ddXW4vK7fJ8= 6 | -----END PRIVATE KEY----- 7 | -------------------------------------------------------------------------------- /examples/cpaper_extended/testdata/admin.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICTDCCAdECCQDXg5wOXASntDAKBggqhkjOPQQDAjCBjjELMAkGA1UEBhMCUlUx 3 | DzANBgNVBAgMBk1vc2NvdzEPMA0GA1UEBwwGTW9zY293MRIwEAYDVQQKDAlTN1Rl 4 | Y2hsYWIxEjAQBgNVBAsMCVM3VGVjaGxhYjESMBAGA1UEAwwJUzdUZWNobGFiMSEw 5 | HwYJKoZIhvcNAQkBFhJpbmZvQHRlY2hsYWIuczcucnUwHhcNMTgxMTEzMTMxMTI3 6 | WhcNMjAxMTEyMTMxMTI3WjCBjjELMAkGA1UEBhMCUlUxDzANBgNVBAgMBk1vc2Nv 7 | dzEPMA0GA1UEBwwGTW9zY293MRIwEAYDVQQKDAlTN1RlY2hsYWIxEjAQBgNVBAsM 8 | CVM3VGVjaGxhYjESMBAGA1UEAwwJUzdUZWNobGFiMSEwHwYJKoZIhvcNAQkBFhJp 9 | bmZvQHRlY2hsYWIuczcucnUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASAPNEhxmCz 10 | F7w+8rmE+iKHiTp+qinNnby69unp3eCpRD2XaI5zfPDiVZbPFm3uFsHskEGNwJyh 11 | G84Vc74/Nw5jrIDU6p83i1yXCV2JafT5oCBsSLNw1vR3ddXW4vK7fJ8wCgYIKoZI 12 | zj0EAwIDaQAwZgIxAMP56SfE7D8sjv5H4rU5CnXeJLoCmcDo20OQcMBbIoYNHiet 13 | ReJZlqytK5WoPm8wHQIxANdPnajvejR+ZE7MMe+pd18uwGZ8hh9Hp6C9ugoipv0q 14 | Oo4vB+J8+jEuRjSsXfMzPQ== 15 | -----END CERTIFICATE----- 16 | -------------------------------------------------------------------------------- /examples/cpaper_extended/testdata/testdata.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | idtestdata "github.com/s7techlab/cckit/identity/testdata" 5 | ) 6 | 7 | var ( 8 | Certificates = idtestdata.Certs{{ 9 | CertFilename: `admin.pem`, PKeyFilename: `admin.key.pem`, 10 | }}. 11 | UseReadFile(idtestdata.ReadLocal()) 12 | ) 13 | -------------------------------------------------------------------------------- /examples/cpaper_proxy/README.md: -------------------------------------------------------------------------------- 1 | # Cross chaincode service calls example 2 | 3 | Chaincode reads from `Commercial Paper` chaincode. Location of external `Commercial Paper` configured via 4 | [crosscc](../../extensions/crosscc) extension -------------------------------------------------------------------------------- /examples/cpaper_proxy/chaincode.go: -------------------------------------------------------------------------------- 1 | package cpaper_proxy 2 | 3 | import ( 4 | "github.com/s7techlab/cckit/examples/cpaper_asservice" 5 | "github.com/s7techlab/cckit/extensions/crosscc" 6 | "github.com/s7techlab/cckit/router" 7 | ) 8 | 9 | func NewCCWithLocalCPaper() (*router.Chaincode, error) { 10 | r := router.New(`crosscc_local`) 11 | 12 | cPaperService := cpaper_asservice.NewService() 13 | crossCCService := NewServiceWithLocalCPaperResolver(cPaperService) 14 | 15 | // 2 services in one chaincode 16 | // both CPaper and CrossCC in one chaincode, that is why used local resolver 17 | if err := cpaper_asservice.RegisterCPaperServiceChaincode(r, cPaperService); err != nil { 18 | return nil, err 19 | } 20 | 21 | if err := RegisterCPaperProxyServiceChaincode(r, crossCCService); err != nil { 22 | return nil, err 23 | } 24 | 25 | return router.NewChaincode(r), nil 26 | } 27 | 28 | func NewCCWithRemoteCPaper() (*router.Chaincode, error) { 29 | r := router.New(`crosscc_remote`) 30 | 31 | crossCCSettingService := crosscc.NewSettingService() 32 | crossCCService := NewServiceWithRemoteCPaperResolver(crossCCSettingService) 33 | 34 | // crossCC service and CPaper service - in separate chaincodes 35 | // in crossCC chaincode there are two services: 36 | // 1. CrossCC itself 37 | // 2. Setting service to store information where (channel, chaincode) CPaper service located 38 | if err := crosscc.RegisterSettingServiceChaincode(r, crossCCSettingService); err != nil { 39 | return nil, err 40 | } 41 | 42 | if err := RegisterCPaperProxyServiceChaincode(r, crossCCService); err != nil { 43 | return nil, err 44 | } 45 | 46 | return router.NewChaincode(r), nil 47 | } 48 | -------------------------------------------------------------------------------- /examples/cpaper_proxy/cpaper_proxy.go: -------------------------------------------------------------------------------- 1 | package cpaper_proxy 2 | 3 | import ( 4 | "fmt" 5 | 6 | cpservice "github.com/s7techlab/cckit/examples/cpaper_asservice" 7 | "github.com/s7techlab/cckit/extensions/crosscc" 8 | "github.com/s7techlab/cckit/router" 9 | ) 10 | 11 | type ( 12 | CPaperProxyService struct { 13 | CPaperServiceResolver cpservice.CPaperServiceChaincodeResolver 14 | } 15 | ) 16 | 17 | // NewServiceWithLocalCPaperResolver - crosscc service and cpaper service in one chaincode 18 | func NewServiceWithLocalCPaperResolver(cPaperService cpservice.CPaperServiceChaincode) *CPaperProxyService { 19 | return &CPaperProxyService{ 20 | CPaperServiceResolver: cpservice.NewCPaperServiceChaincodeLocalResolver(cPaperService), 21 | } 22 | } 23 | 24 | func NewServiceWithRemoteCPaperResolver(setting crosscc.SettingServiceChaincode) *CPaperProxyService { 25 | return &CPaperProxyService{ 26 | CPaperServiceResolver: cpservice.NewCPaperServiceChaincodeResolver(crosscc.LocatorResolver(setting)), 27 | } 28 | } 29 | 30 | func (c *CPaperProxyService) GetFromCPaper(ctx router.Context, id *Id) (*InfoFromCPaper, error) { 31 | cPaperService, err := c.CPaperServiceResolver.Resolve(ctx) 32 | if err != nil { 33 | return nil, fmt.Errorf(`resolve Commercial Paper service: %w`, err) 34 | } 35 | // It can be cross chaincode invocation or local, if commercial paper service works in same chaincode 36 | cPaper, err := cPaperService.Get(ctx, &cpservice.CommercialPaperId{Issuer: id.Issuer, PaperNumber: id.PaperNumber}) 37 | if err != nil { 38 | return nil, fmt.Errorf(`get commercial paper from service: %w`, err) 39 | } 40 | 41 | return &InfoFromCPaper{ 42 | Issuer: cPaper.Issuer, 43 | PaperNumber: cPaper.PaperNumber, 44 | Owner: cPaper.Owner, 45 | }, nil 46 | } 47 | -------------------------------------------------------------------------------- /examples/cpaper_proxy/cpaper_proxy.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | // Commercial paper service 3 | option go_package = "github.com/s7techlab/cckit/examples/cpaper_proxy"; 4 | package examples.cpaper_proxy; 5 | 6 | import "google/api/annotations.proto"; 7 | import "google/protobuf/empty.proto"; 8 | import "google/protobuf/timestamp.proto"; 9 | 10 | service CPaperProxyService { 11 | // List method returns all registered commercial papers 12 | rpc GetFromCPaper (Id) returns (InfoFromCPaper) { 13 | option (google.api.http) = { 14 | get: "/cpaper/{issuer}/{paper_number}" 15 | }; 16 | } 17 | } 18 | 19 | message Id { 20 | string issuer = 1; 21 | string paper_number = 2; 22 | } 23 | 24 | 25 | message InfoFromCPaper { 26 | string issuer = 1; 27 | string paper_number = 2; 28 | string owner = 3; 29 | } -------------------------------------------------------------------------------- /examples/cpaper_proxy/cpaper_proxy.validator.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-gogo. DO NOT EDIT. 2 | // source: cpaper_proxy/cpaper_proxy.proto 3 | 4 | package cpaper_proxy 5 | 6 | import ( 7 | fmt "fmt" 8 | math "math" 9 | proto "github.com/golang/protobuf/proto" 10 | _ "google.golang.org/genproto/googleapis/api/annotations" 11 | _ "google.golang.org/protobuf/types/known/emptypb" 12 | _ "google.golang.org/protobuf/types/known/timestamppb" 13 | ) 14 | 15 | // Reference imports to suppress errors if they are not otherwise used. 16 | var _ = proto.Marshal 17 | var _ = fmt.Errorf 18 | var _ = math.Inf 19 | 20 | func (this *Id) Validate() error { 21 | return nil 22 | } 23 | func (this *InfoFromCPaper) Validate() error { 24 | return nil 25 | } 26 | -------------------------------------------------------------------------------- /examples/erc20/bin/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/hyperledger/fabric-chaincode-go/shim" 7 | 8 | "github.com/s7techlab/cckit/examples/erc20" 9 | ) 10 | 11 | func main() { 12 | err := shim.Start(erc20.NewErc20FixedSupply()) 13 | if err != nil { 14 | fmt.Printf("Error starting ERC-20 chaincode: %s", err) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/erc20_service/erc20.validator.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-gogo. DO NOT EDIT. 2 | // source: erc20_service/erc20.proto 3 | 4 | package erc20_service 5 | 6 | import ( 7 | fmt "fmt" 8 | math "math" 9 | proto "github.com/golang/protobuf/proto" 10 | _ "google.golang.org/protobuf/types/known/emptypb" 11 | _ "google.golang.org/genproto/googleapis/api/annotations" 12 | ) 13 | 14 | // Reference imports to suppress errors if they are not otherwise used. 15 | var _ = proto.Marshal 16 | var _ = fmt.Errorf 17 | var _ = math.Inf 18 | 19 | func (this *NameResponse) Validate() error { 20 | return nil 21 | } 22 | func (this *SymbolResponse) Validate() error { 23 | return nil 24 | } 25 | func (this *DecimalsResponse) Validate() error { 26 | return nil 27 | } 28 | func (this *TotalSupplyResponse) Validate() error { 29 | return nil 30 | } 31 | func (this *BalanceOfRequest) Validate() error { 32 | return nil 33 | } 34 | func (this *BalanceOfResponse) Validate() error { 35 | return nil 36 | } 37 | func (this *TransferRequest) Validate() error { 38 | return nil 39 | } 40 | func (this *TransferResponse) Validate() error { 41 | return nil 42 | } 43 | func (this *AllowanceRequest) Validate() error { 44 | return nil 45 | } 46 | func (this *AllowanceResponse) Validate() error { 47 | return nil 48 | } 49 | func (this *ApproveRequest) Validate() error { 50 | return nil 51 | } 52 | func (this *ApproveResponse) Validate() error { 53 | return nil 54 | } 55 | func (this *TransferFromRequest) Validate() error { 56 | return nil 57 | } 58 | func (this *TransferEvent) Validate() error { 59 | return nil 60 | } 61 | func (this *ApprovalEvent) Validate() error { 62 | return nil 63 | } 64 | -------------------------------------------------------------------------------- /examples/fabcar/bin/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/hyperledger/fabric-chaincode-go/shim" 7 | 8 | "github.com/s7techlab/cckit/examples/fabcar" 9 | ) 10 | 11 | func main() { 12 | cc, err := fabcar.New() 13 | if err != nil { 14 | fmt.Printf("error creating %s chaincode: %s", fabcar.ChaincodeName, err) 15 | return 16 | } 17 | 18 | if err = shim.Start(cc); err != nil { 19 | fmt.Printf("error starting %s chaincode: %s", fabcar.ChaincodeName, err) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/fabcar/chaincode.go: -------------------------------------------------------------------------------- 1 | package fabcar 2 | 3 | import ( 4 | "github.com/s7techlab/cckit/extensions/owner" 5 | "github.com/s7techlab/cckit/router" 6 | ) 7 | 8 | const ChaincodeName = `fabcar` 9 | 10 | func New() (*router.Chaincode, error) { 11 | 12 | r := router.New(ChaincodeName) 13 | 14 | r.Init(ChaincodeInitFunc()) 15 | 16 | if err := RegisterFabCarServiceChaincode(r, &FabCarService{}); err != nil { 17 | return nil, err 18 | } 19 | 20 | return router.NewChaincode(r), nil 21 | } 22 | 23 | func ChaincodeInitFunc() func(router.Context) (interface{}, error) { 24 | return func(ctx router.Context) (interface{}, error) { 25 | return owner.SetFromCreator(ctx) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /examples/fabcar/state.go: -------------------------------------------------------------------------------- 1 | package fabcar 2 | 3 | import ( 4 | "github.com/s7techlab/cckit/router" 5 | "github.com/s7techlab/cckit/state" 6 | m "github.com/s7techlab/cckit/state/mapping" 7 | ) 8 | 9 | var ( 10 | StateMappings = m.StateMappings{}. 11 | Add(&Maker{}, 12 | m.PKeySchema(&MakerName{}), 13 | m.List(&Makers{})). 14 | Add(&Car{}, 15 | m.PKeySchema(&CarId{}), 16 | m.List(&Cars{})). 17 | Add(&CarOwner{}, 18 | m.PKeySchema(&CarOwnerId{}), 19 | m.List(&CarOwners{})). 20 | Add(&CarDetail{}, 21 | m.PKeySchema(&CarDetailId{}), 22 | m.List(&CarDetails{})) 23 | 24 | EventMappings = m.EventMappings{}. 25 | Add(&MakerCreated{}). 26 | Add(&MakerDeleted{}). 27 | Add(&CarCreated{}). 28 | Add(&CarDeleted{}). 29 | Add(&CarUpdated{}). 30 | Add(&CarOwnerDeleted{}). 31 | Add(&CarOwnersUpdated{}). 32 | Add(&CarDetailDeleted{}). 33 | Add(&CarDetailsUpdated{}) 34 | ) 35 | 36 | func State(ctx router.Context) m.MappedState { 37 | return m.WrapState(ctx.State(), StateMappings) 38 | } 39 | 40 | func Event(ctx router.Context) state.Event { 41 | return m.WrapEvent(ctx.Event(), EventMappings) 42 | } 43 | -------------------------------------------------------------------------------- /examples/fabcar/testdata/car_detail.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | . "github.com/onsi/gomega" 5 | 6 | "github.com/s7techlab/cckit/examples/fabcar" 7 | ) 8 | 9 | func ExpectDetailsViewContain(setDetails []*fabcar.SetCarDetail, getDetails []*fabcar.CarDetail) { 10 | Expect(setDetails).To(HaveLen(len(getDetails))) 11 | 12 | length := len(setDetails) 13 | for i := 0; i < length; i++ { 14 | for j := 0; j < length; j++ { 15 | if setDetails[i].Type == getDetails[j].Type { 16 | Expect(setDetails[i].Type).To(Equal(getDetails[j].Type)) 17 | Expect(setDetails[i].Make).To(Equal(getDetails[j].Make)) 18 | } 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /examples/fabcar/testdata/car_owner.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | . "github.com/onsi/gomega" 5 | 6 | "github.com/s7techlab/cckit/examples/fabcar" 7 | ) 8 | 9 | func ExpectOwnersViewContain(setOwners []*fabcar.SetCarOwner, getOwners []*fabcar.CarOwner) { 10 | Expect(setOwners).To(HaveLen(len(getOwners))) 11 | 12 | length := len(setOwners) 13 | for i := 0; i < length; i++ { 14 | for j := 0; j < length; j++ { 15 | if setOwners[i].FirstName == getOwners[j].FirstName && setOwners[i].SecondName == getOwners[j].SecondName { 16 | Expect(setOwners[i].FirstName).To(Equal(getOwners[j].FirstName)) 17 | Expect(setOwners[i].SecondName).To(Equal(getOwners[j].SecondName)) 18 | Expect(setOwners[i].VehiclePassport).To(Equal(getOwners[j].VehiclePassport)) 19 | } 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/fabcar/testdata/maker.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "github.com/golang/protobuf/proto" 5 | . "github.com/onsi/gomega" 6 | 7 | "github.com/s7techlab/cckit/examples/fabcar" 8 | ) 9 | 10 | type ( 11 | MakerSample struct { 12 | Create *fabcar.CreateMakerRequest 13 | } 14 | ) 15 | 16 | func (ms *MakerSample) CreateClone() *fabcar.CreateMakerRequest { 17 | return proto.Clone(ms.Create).(*fabcar.CreateMakerRequest) 18 | } 19 | 20 | func (ms *MakerSample) ExpectEqual(maker *fabcar.Maker) { 21 | Expect(ms.Create.Name).To(Equal(maker.Name)) 22 | Expect(ms.Create.Country).To(Equal(maker.Country)) 23 | Expect(ms.Create.FoundationYear).To(Equal(maker.FoundationYear)) 24 | } 25 | 26 | var ( 27 | MakerNonexistent = MakerSample{ 28 | Create: &fabcar.CreateMakerRequest{ 29 | Name: "Nonexistent", 30 | Country: "Nonexistent", 31 | FoundationYear: 1884, 32 | }, 33 | } 34 | 35 | MakerToyota = MakerSample{ 36 | Create: &fabcar.CreateMakerRequest{ 37 | Name: "Toyota", 38 | Country: "Japan", 39 | FoundationYear: 1937, 40 | }, 41 | } 42 | 43 | MakerAudi = MakerSample{ 44 | Create: &fabcar.CreateMakerRequest{ 45 | Name: "Audi", 46 | Country: "German", 47 | FoundationYear: 1909, 48 | }, 49 | } 50 | 51 | MakerPeugeot = MakerSample{ 52 | Create: &fabcar.CreateMakerRequest{ 53 | Name: "Peugeot", 54 | Country: "France", 55 | FoundationYear: 1886, 56 | }, 57 | } 58 | 59 | MakerFord = MakerSample{ 60 | Create: &fabcar.CreateMakerRequest{ 61 | Name: "Ford", 62 | Country: "USA", 63 | FoundationYear: 1903, 64 | }, 65 | } 66 | ) 67 | -------------------------------------------------------------------------------- /examples/insurance/app/bin/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/hyperledger/fabric-chaincode-go/shim" 7 | 8 | "github.com/s7techlab/cckit/examples/insurance/app" 9 | ) 10 | 11 | func main() { 12 | 13 | err := shim.Start(new(app.SmartContract)) 14 | if err != nil { 15 | fmt.Printf("Error starting Simple chaincode: %s", err) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /examples/insurance/app/images/arch-blockchain-insurance2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/s7techlab/cckit/eb9bde26f023c8381e9760f0fa93dc895423dcca/examples/insurance/app/images/arch-blockchain-insurance2.png -------------------------------------------------------------------------------- /examples/insurance/dto.go: -------------------------------------------------------------------------------- 1 | package insurance 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/s7techlab/cckit/examples/insurance/app" 7 | ) 8 | 9 | // ContractTypesDTO type used in "Init" func (arg) in main.go and in "listContractTypes" (return) in "invoke_insurance.go" 10 | type ContractTypesDTO []ContractTypeDTO 11 | 12 | type ContractTypeDTO struct { 13 | UUID string `json:"uuid"` 14 | *app.ContractType 15 | } 16 | 17 | type ContractTypeActiveDTO struct { 18 | UUID string `json:"uuid"` 19 | Active bool `json:"active"` 20 | } 21 | 22 | type ShopTypeDTO struct { 23 | ShopType string `json:"shop_type"` 24 | } 25 | 26 | // CreateContractDTO type used in "createContract" func (arg) in invoke_shop.go 27 | type CreateContractDTO struct { 28 | UUID string `json:"uuid"` 29 | ContractTypeUUID string `json:"contract_type_uuid"` 30 | Username string `json:"username"` 31 | Password string `json:"password"` 32 | FirstName string `json:"first_name"` 33 | LastName string `json:"last_name"` 34 | Item app.Item `json:"item"` 35 | StartDate time.Time `json:"start_date"` 36 | EndDate time.Time `json:"end_date"` 37 | } 38 | 39 | // ContractCreateResponse type used in "createContract" func in invoke_shop.go 40 | type ContractCreateResponse struct { 41 | Username string `json:"username"` 42 | Password string `json:"password"` 43 | } 44 | 45 | // LsContractTypeDTO type used in "listContractTypes" in invoke_insurance.go 46 | type LsContractTypeDTO struct { 47 | ShopType string `json:"shop_type"` 48 | } 49 | 50 | // GetUserDTO type used in "getUser" func in invoke_insurance.go 51 | type GetUserDTO struct { 52 | Username string `json:"username"` 53 | } 54 | 55 | // ResponseUserDTO named type, from anonymous type used in "getUser" func in invoke_insurance.go 56 | type ResponseUserDTO struct { 57 | Username string `json:"username"` 58 | FirstName string `json:"first_name"` 59 | LastName string `json:"last_name"` 60 | } 61 | -------------------------------------------------------------------------------- /examples/insurance/fixtures.go: -------------------------------------------------------------------------------- 1 | package insurance 2 | 3 | import ( 4 | "time" 5 | 6 | "github.com/s7techlab/cckit/examples/insurance/app" 7 | ) 8 | 9 | var ( 10 | // ContractType1 ContractType fixture 11 | ContractType1 = ContractTypeDTO{ 12 | UUID: `12345`, 13 | ContractType: &app.ContractType{ 14 | ShopType: `shop-type-1`, 15 | FormulaPerDay: `aaa`, 16 | MaxSumInsured: 12345, 17 | TheftInsured: true, 18 | Description: `some_description`, 19 | Conditions: `some_conditions`, 20 | Active: true, 21 | MinDurationDays: 1, 22 | MaxDurationDays: 5, 23 | }} 24 | 25 | ContractType2 = ContractTypeDTO{ 26 | UUID: `7890`, 27 | ContractType: &app.ContractType{ 28 | ShopType: `shop-type-2`, 29 | FormulaPerDay: `bbb`, 30 | MaxSumInsured: 777, 31 | TheftInsured: false, 32 | Description: `once more description`, 33 | Conditions: `once more conditions`, 34 | Active: false, 35 | MinDurationDays: 2, 36 | MaxDurationDays: 10, 37 | }} 38 | 39 | // Contract1 Contract fixture 40 | Contract1 = CreateContractDTO{ 41 | UUID: `xxx-aaa-bbb`, 42 | ContractTypeUUID: `xxx-ddd-ccc`, 43 | Username: `vitiko`, 44 | Password: `Root123AsUsual`, 45 | FirstName: `Victor`, 46 | LastName: `Nosov`, 47 | Item: app.Item{ 48 | ID: 1, 49 | Brand: `NoName`, 50 | Model: `Model-XYZ`, 51 | Price: 123.45, 52 | Description: `Coolest thing ever`, 53 | SerialNo: `ooo-999-222`, 54 | }, 55 | StartDate: time.Now(), 56 | EndDate: time.Now(), 57 | } 58 | ) 59 | -------------------------------------------------------------------------------- /examples/payment/README.md: -------------------------------------------------------------------------------- 1 | # Payment - chaincode examples with encrypted state -------------------------------------------------------------------------------- /examples/payment/mapping.go: -------------------------------------------------------------------------------- 1 | package payment 2 | 3 | import ( 4 | "github.com/s7techlab/cckit/examples/payment/schema" 5 | m "github.com/s7techlab/cckit/state/mapping" 6 | ) 7 | 8 | var ( 9 | // StateMappings state mappings 10 | StateMappings = m.StateMappings{}.Add( 11 | &schema.Payment{}, // state entry value will contain marshaled protobuf schema.Payment 12 | m.PKeyAttr(`Type`, `Id`), // state entry key will be composite key <'Payment',{Type}, {Id}> 13 | m.List(&schema.PaymentList{})) // state.list() method will return marshaled protobuf schema.PaymentList 14 | // same same 15 | //Add(&schema.Payment{}, m.PKeySchema(&schema.PaymentId{})) 16 | 17 | // EventMappings event mappings 18 | EventMappings = m.EventMappings{}. 19 | Add(&schema.PaymentEvent{}) 20 | ) 21 | -------------------------------------------------------------------------------- /examples/payment/schema/Makefile: -------------------------------------------------------------------------------- 1 | .: generate 2 | 3 | generate: 4 | @echo "schema" 5 | @protoc -I=./ --go_out=./ ./payment.proto -------------------------------------------------------------------------------- /examples/payment/schema/payment.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "github.com/s7techlab/cckit/examples/payment/schema;schema"; 4 | package schema; 5 | 6 | message Payment { 7 | string type = 1; 8 | string id = 2; 9 | int32 amount = 3; 10 | } 11 | 12 | message PaymentList { 13 | repeated Payment items = 1; 14 | } 15 | 16 | message PaymentEvent { 17 | string type = 1; 18 | string id = 2; 19 | int32 amount = 3; 20 | } 21 | -------------------------------------------------------------------------------- /examples/payment/schema/payment.swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "title": "payment/schema/payment.proto", 5 | "version": "version not set" 6 | }, 7 | "consumes": [ 8 | "application/json" 9 | ], 10 | "produces": [ 11 | "application/json" 12 | ], 13 | "paths": {}, 14 | "definitions": { 15 | "protobufAny": { 16 | "type": "object", 17 | "properties": { 18 | "type_url": { 19 | "type": "string" 20 | }, 21 | "value": { 22 | "type": "string", 23 | "format": "byte" 24 | } 25 | } 26 | }, 27 | "runtimeError": { 28 | "type": "object", 29 | "properties": { 30 | "error": { 31 | "type": "string" 32 | }, 33 | "code": { 34 | "type": "integer", 35 | "format": "int32" 36 | }, 37 | "message": { 38 | "type": "string" 39 | }, 40 | "details": { 41 | "type": "array", 42 | "items": { 43 | "$ref": "#/definitions/protobufAny" 44 | } 45 | } 46 | } 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /examples/payment/schema/payment.validator.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-gogo. DO NOT EDIT. 2 | // source: payment/schema/payment.proto 3 | 4 | package schema 5 | 6 | import ( 7 | fmt "fmt" 8 | math "math" 9 | proto "github.com/golang/protobuf/proto" 10 | github_com_mwitkow_go_proto_validators "github.com/mwitkow/go-proto-validators" 11 | ) 12 | 13 | // Reference imports to suppress errors if they are not otherwise used. 14 | var _ = proto.Marshal 15 | var _ = fmt.Errorf 16 | var _ = math.Inf 17 | 18 | func (this *Payment) Validate() error { 19 | return nil 20 | } 21 | func (this *PaymentList) Validate() error { 22 | for _, item := range this.Items { 23 | if item != nil { 24 | if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(item); err != nil { 25 | return github_com_mwitkow_go_proto_validators.FieldError("Items", err) 26 | } 27 | } 28 | } 29 | return nil 30 | } 31 | func (this *PaymentEvent) Validate() error { 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /examples/private_cars/README.md: -------------------------------------------------------------------------------- 1 | # Cars private registration Hyperledger Fabric chaincode 2 | 3 | Car private registration chaincode use simple golang structure with json marshalling. Example with protobuf base chaincode schema 4 | is [here](../cpaper). 5 | 6 | [source code](cars.go), [tests](cars_test.go) -------------------------------------------------------------------------------- /examples/private_cars/bin/cars/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/hyperledger/fabric-chaincode-go/shim" 7 | 8 | "github.com/s7techlab/cckit/examples/cars" 9 | ) 10 | 11 | func main() { 12 | cc := cars.New() 13 | if err := shim.Start(cc); err != nil { 14 | fmt.Printf("Error starting Cars chaincode: %s", err) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/private_cars/bin/cars_noac/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/hyperledger/fabric-chaincode-go/shim" 7 | 8 | "github.com/s7techlab/cckit/examples/cars" 9 | ) 10 | 11 | func main() { 12 | cc := cars.NewWithoutAccessControl() 13 | if err := shim.Start(cc); err != nil { 14 | fmt.Printf("Error starting Cars chaincode: %s", err) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /examples/private_cars/cars_noacces_control.go: -------------------------------------------------------------------------------- 1 | // Package cars simple CRUD chaincode for store information about cars 2 | package cars 3 | 4 | import ( 5 | "github.com/s7techlab/cckit/router" 6 | p "github.com/s7techlab/cckit/router/param" 7 | ) 8 | 9 | func NewWithoutAccessControl() *router.Chaincode { 10 | r := router.New(`cars_without_access_control`) // also initialized logger with "cars" prefix 11 | 12 | r.Group(`car`). 13 | Query(`List`, queryCars). // chain code method name is carList 14 | Query(`Get`, queryCar, p.String(`id`)). // chain code method name is carGet, method has 1 string argument "id" 15 | Invoke(`Register`, invokeCarRegister, p.Struct(`car`, &CarPayload{})) // allow access to everyone 16 | return router.NewChaincode(r) 17 | } 18 | -------------------------------------------------------------------------------- /examples/private_cars/cars_proxy.go: -------------------------------------------------------------------------------- 1 | package cars 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/hyperledger/fabric-chaincode-go/shim" 7 | 8 | "github.com/s7techlab/cckit/router" 9 | p "github.com/s7techlab/cckit/router/param" 10 | ) 11 | 12 | // NewProxy created chaincode, related to cars chaincode 13 | func NewProxy(carsChannel, carsChaincode string) *router.Chaincode { 14 | r := router.New(`cars_proxy`) // also initialized logger with "cars_related" prefix 15 | 16 | r.Init(invokeInit) 17 | 18 | r.Query(`carGet`, queryCarProxy, p.String(`id`)) 19 | 20 | return router.NewChaincode(r) 21 | } 22 | 23 | // 24 | func queryCarProxy(c router.Context) (interface{}, error) { 25 | var ( 26 | id = c.ParamString(`id`) 27 | ) 28 | 29 | // query external chaincode 30 | response := c.Stub().InvokeChaincode(`cars`, [][]byte{[]byte(`carGet`), []byte(id)}, `my_channel`) 31 | if response.Status == shim.ERROR { 32 | return nil, errors.New(response.Message) 33 | } 34 | return response.Payload, nil 35 | } 36 | -------------------------------------------------------------------------------- /examples/private_cars/payloads.go: -------------------------------------------------------------------------------- 1 | package cars 2 | 3 | var Payloads = []*Car{{ 4 | Id: `A777MP77`, 5 | Title: `BMW`, 6 | Owner: `victor-nosov`, 7 | }, { 8 | Id: `O888OO77`, 9 | Title: `TOYOTA`, 10 | Owner: `alexander`, 11 | }, { 12 | Id: `O222OO177`, 13 | Title: `Lambo`, 14 | Owner: `hodl`, 15 | }, { 16 | Id: `E333KX177`, 17 | Title: `Aurus`, 18 | Owner: `bigboss`, 19 | }} 20 | -------------------------------------------------------------------------------- /examples/token/chaincode/erc20/README.md: -------------------------------------------------------------------------------- 1 | # ERC-20 token with fixed `total supply` 2 | 3 | With: 4 | 5 | * [addresses](../../service/account) 6 | * [config for 1 token per chaincode](../../service/config_erc20) 7 | * [balances and transfers between balances](../../service/balance) 8 | * [allowance and transfers by spender from owner account](../../service/allowance) 9 | -------------------------------------------------------------------------------- /examples/token/service/account/account.go: -------------------------------------------------------------------------------- 1 | package account 2 | 3 | import ( 4 | "encoding/base64" 5 | "errors" 6 | 7 | "google.golang.org/protobuf/types/known/emptypb" 8 | 9 | "github.com/s7techlab/cckit/identity" 10 | "github.com/s7techlab/cckit/router" 11 | ) 12 | 13 | type LocalService struct { 14 | } 15 | 16 | func NewLocalService() *LocalService { 17 | return &LocalService{} 18 | } 19 | 20 | func (l *LocalService) GetInvokerAddress(ctx router.Context, _ *emptypb.Empty) (*AddressId, error) { 21 | invoker, err := identity.FromStub(ctx.Stub()) 22 | if err != nil { 23 | return nil, err 24 | } 25 | 26 | return l.GetAddress(ctx, 27 | &GetAddressRequest{PublicKey: identity.MarshalPublicKey(invoker.Cert.PublicKey)}) 28 | } 29 | 30 | func (l *LocalService) GetAddress(ctx router.Context, req *GetAddressRequest) (*AddressId, error) { 31 | return &AddressId{ 32 | Address: base64.StdEncoding.EncodeToString(req.PublicKey), 33 | }, nil 34 | } 35 | 36 | func (l *LocalService) GetAccount(ctx router.Context, id *AccountId) (*Account, error) { 37 | return nil, errors.New(`no accounts implemented`) 38 | } 39 | 40 | type Remote struct { 41 | } 42 | -------------------------------------------------------------------------------- /examples/token/service/account/account.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "github.com/s7techlab/cckit/examples/token/service/account"; 4 | package examples.token.service.account; 5 | 6 | import "google/api/annotations.proto"; 7 | import "google/protobuf/empty.proto"; 8 | 9 | // Account 10 | service AccountService { 11 | 12 | rpc GetInvokerAddress (google.protobuf.Empty) returns (AddressId) { 13 | option (google.api.http) = { 14 | get: "/token/addresses/whoami" 15 | }; 16 | } 17 | 18 | rpc GetAddress (GetAddressRequest) returns (AddressId) { 19 | option (google.api.http) = { 20 | get: "/token/addresses/{public_key}" 21 | }; 22 | } 23 | 24 | rpc GetAccount (AccountId) returns (Account) { 25 | option (google.api.http) = { 26 | get: "/token/accounts/{address}" 27 | }; 28 | } 29 | 30 | } 31 | 32 | message GetAddressRequest { 33 | bytes public_key = 1; 34 | } 35 | 36 | message GetAccountRequest { 37 | string address = 1; 38 | } 39 | 40 | message Address { 41 | string address = 1; 42 | } 43 | 44 | message AddressId { 45 | string address = 1; 46 | } 47 | 48 | message AccountId { 49 | string address = 1; 50 | } 51 | 52 | message Account { 53 | string address = 1; 54 | AccountStatus status = 2; 55 | } 56 | 57 | message AccountKey { 58 | bytes public_key = 1; 59 | KeyStatus status = 2; 60 | } 61 | 62 | enum KeyStatus { 63 | KEY_STATUS_UNKNOWN = 0; 64 | KEY_STATUS_ENABLED = 1; 65 | KEY_STATUS_DISABLED = 2; 66 | } 67 | 68 | enum AccountStatus { 69 | ACCOUNT_STATUS_UNKNOWN = 0; 70 | ACCOUNT_STATUS_ENABLED = 1; 71 | ACCOUNT_STATUS_DISABLED = 2; 72 | } -------------------------------------------------------------------------------- /examples/token/service/account/account.validator.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-gogo. DO NOT EDIT. 2 | // source: token/service/account/account.proto 3 | 4 | package account 5 | 6 | import ( 7 | fmt "fmt" 8 | math "math" 9 | proto "github.com/golang/protobuf/proto" 10 | _ "google.golang.org/genproto/googleapis/api/annotations" 11 | _ "google.golang.org/protobuf/types/known/emptypb" 12 | ) 13 | 14 | // Reference imports to suppress errors if they are not otherwise used. 15 | var _ = proto.Marshal 16 | var _ = fmt.Errorf 17 | var _ = math.Inf 18 | 19 | func (this *GetAddressRequest) Validate() error { 20 | return nil 21 | } 22 | func (this *GetAccountRequest) Validate() error { 23 | return nil 24 | } 25 | func (this *Address) Validate() error { 26 | return nil 27 | } 28 | func (this *AddressId) Validate() error { 29 | return nil 30 | } 31 | func (this *AccountId) Validate() error { 32 | return nil 33 | } 34 | func (this *Account) Validate() error { 35 | return nil 36 | } 37 | func (this *AccountKey) Validate() error { 38 | return nil 39 | } 40 | -------------------------------------------------------------------------------- /examples/token/service/account/resolver.go: -------------------------------------------------------------------------------- 1 | package account 2 | 3 | import ( 4 | "google.golang.org/protobuf/types/known/emptypb" 5 | 6 | "github.com/s7techlab/cckit/router" 7 | ) 8 | 9 | type Getter interface { 10 | GetInvokerAddress(router.Context, *emptypb.Empty) (*AddressId, error) 11 | 12 | GetAddress(router.Context, *GetAddressRequest) (*AddressId, error) 13 | 14 | GetAccount(router.Context, *AccountId) (*Account, error) 15 | } 16 | -------------------------------------------------------------------------------- /examples/token/service/allowance/state.go: -------------------------------------------------------------------------------- 1 | package allowance 2 | 3 | import ( 4 | "github.com/s7techlab/cckit/router" 5 | "github.com/s7techlab/cckit/state" 6 | m "github.com/s7techlab/cckit/state/mapping" 7 | ) 8 | 9 | var ( 10 | StateMappings = m.StateMappings{}. 11 | // Create mapping for Allowance entity 12 | // key `Allowance`,`{OwnerAddress}`,`{SpenderAddress}`,`{Path[0]}`..., `{Path[n]` 13 | Add(&Allowance{}, 14 | m.PKeySchema(&AllowanceId{}), 15 | m.List(&Allowances{}), // Structure of result for List method 16 | ) 17 | 18 | EventMappings = m.EventMappings{}. 19 | Add(&Approved{}). 20 | Add(&TransferredFrom{}) 21 | ) 22 | 23 | // State with chaincode mappings 24 | func State(ctx router.Context) m.MappedState { 25 | return m.WrapState(ctx.State(), StateMappings) 26 | } 27 | 28 | // Event with chaincode mappings 29 | func Event(ctx router.Context) state.Event { 30 | return m.WrapEvent(ctx.Event(), EventMappings) 31 | } 32 | -------------------------------------------------------------------------------- /examples/token/service/allowance/store.go: -------------------------------------------------------------------------------- 1 | package allowance 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/s7techlab/cckit/examples/token/service/balance" 8 | "github.com/s7techlab/cckit/router" 9 | "github.com/s7techlab/cckit/state" 10 | ) 11 | 12 | type Store struct { 13 | state state.GetSettable 14 | path []string // token path 15 | } 16 | 17 | func NewStore(ctx router.Context) *Store { 18 | return &Store{ 19 | state: State(ctx), 20 | } 21 | } 22 | 23 | func (s *Store) Get(ownerAddress, spenderAddress string, token []string) (*Allowance, error) { 24 | allowance, err := s.state.Get(&AllowanceId{ 25 | OwnerAddress: ownerAddress, 26 | SpenderAddress: spenderAddress, 27 | Token: token}, &Allowance{}) 28 | if err != nil { 29 | if errors.Is(err, state.ErrKeyNotFound) { 30 | return &Allowance{ 31 | OwnerAddress: ownerAddress, 32 | SpenderAddress: spenderAddress, 33 | Token: token, 34 | Amount: 0, 35 | }, nil 36 | } 37 | return nil, fmt.Errorf(`get allowance: %w`, err) 38 | } 39 | 40 | return allowance.(*Allowance), nil 41 | } 42 | 43 | func (s *Store) Set(ownerAddress, spenderAddress string, token []string, amount uint64) (*Allowance, error) { 44 | allowance := &Allowance{ 45 | OwnerAddress: ownerAddress, 46 | SpenderAddress: spenderAddress, 47 | Token: token, 48 | Amount: amount, 49 | } 50 | 51 | if err := s.state.Put(allowance); err != nil { 52 | return nil, fmt.Errorf(`set allowance: %w`, err) 53 | } 54 | 55 | return allowance, nil 56 | } 57 | 58 | func (s *Store) Sub(ownerAddress, spenderAddress string, token []string, amount uint64) (*Allowance, error) { 59 | allowance, err := s.Get(ownerAddress, spenderAddress, token) 60 | if err != nil { 61 | return nil, err 62 | } 63 | 64 | if allowance.Amount < amount { 65 | return nil, balance.ErrAmountInsufficient 66 | } 67 | 68 | return s.Set(ownerAddress, spenderAddress, token, allowance.Amount-amount) 69 | } 70 | -------------------------------------------------------------------------------- /examples/token/service/balance/state.go: -------------------------------------------------------------------------------- 1 | package balance 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/s7techlab/cckit/router" 7 | "github.com/s7techlab/cckit/state" 8 | m "github.com/s7techlab/cckit/state/mapping" 9 | ) 10 | 11 | var ( 12 | ErrAmountInsufficient = errors.New(`amount insufficient`) 13 | 14 | StateMappings = m.StateMappings{}. 15 | // Create mapping for Balance entity 16 | // key will be `Balance`,`{Address}`,`{Path[0]}`..., `{Path[n]` 17 | Add(&Balance{}, 18 | m.PKeySchema(&BalanceId{}), 19 | m.List(&Balances{}), // Structure of result for List method 20 | ) 21 | 22 | EventMappings = m.EventMappings{}. 23 | Add(&Transferred{}) 24 | ) 25 | 26 | // State with chaincode mappings 27 | func State(ctx router.Context) m.MappedState { 28 | return m.WrapState(ctx.State(), StateMappings) 29 | } 30 | 31 | // Event with chaincode mappings 32 | func Event(ctx router.Context) state.Event { 33 | return m.WrapEvent(ctx.Event(), EventMappings) 34 | } 35 | -------------------------------------------------------------------------------- /examples/token/service/burnable/burnable.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "github.com/s7techlab/cckit/examples/token/service/burnable"; 4 | package examples.erc20_service.service.balance; 5 | 6 | import "google/api/annotations.proto"; 7 | import "google/protobuf/empty.proto"; 8 | import "mwitkow/go-proto-validators/validator.proto"; 9 | 10 | // Burnable balance 11 | service BurnableService { 12 | 13 | rpc Burn (BurnRequest) returns (BurnResponse) { 14 | option (google.api.http) = { 15 | post: "/burn" 16 | }; 17 | } 18 | } 19 | 20 | message BurnRequest { 21 | string address = 1 [(validator.field) = {string_not_empty : true}]; 22 | string token = 2; 23 | uint64 amount = 3 [(validator.field) = {int_gt: 0}]; 24 | } 25 | 26 | message BurnResponse { 27 | string sender_address = 1; 28 | string recipient_address = 2; 29 | string token = 3; 30 | uint64 amount = 4; 31 | } 32 | 33 | 34 | // Burned event is emitted when Transfer method has been invoked 35 | message Burned { 36 | string sender_address = 1; 37 | string recipient_address = 2; 38 | uint64 amount = 3; 39 | } -------------------------------------------------------------------------------- /examples/token/service/burnable/burnable.swagger.json: -------------------------------------------------------------------------------- 1 | { 2 | "swagger": "2.0", 3 | "info": { 4 | "title": "token/service/burnable/burnable.proto", 5 | "version": "version not set" 6 | }, 7 | "consumes": [ 8 | "application/json" 9 | ], 10 | "produces": [ 11 | "application/json" 12 | ], 13 | "paths": { 14 | "/burn": { 15 | "post": { 16 | "operationId": "BurnableService_Burn", 17 | "responses": { 18 | "200": { 19 | "description": "A successful response.", 20 | "schema": { 21 | "$ref": "#/definitions/balanceBurnResponse" 22 | } 23 | }, 24 | "default": { 25 | "description": "An unexpected error response.", 26 | "schema": { 27 | "$ref": "#/definitions/runtimeError" 28 | } 29 | } 30 | }, 31 | "tags": [ 32 | "BurnableService" 33 | ] 34 | } 35 | } 36 | }, 37 | "definitions": { 38 | "balanceBurnResponse": { 39 | "type": "object", 40 | "properties": { 41 | "sender_address": { 42 | "type": "string" 43 | }, 44 | "recipient_address": { 45 | "type": "string" 46 | }, 47 | "token": { 48 | "type": "string" 49 | }, 50 | "amount": { 51 | "type": "string", 52 | "format": "uint64" 53 | } 54 | } 55 | }, 56 | "protobufAny": { 57 | "type": "object", 58 | "properties": { 59 | "type_url": { 60 | "type": "string" 61 | }, 62 | "value": { 63 | "type": "string", 64 | "format": "byte" 65 | } 66 | } 67 | }, 68 | "runtimeError": { 69 | "type": "object", 70 | "properties": { 71 | "error": { 72 | "type": "string" 73 | }, 74 | "code": { 75 | "type": "integer", 76 | "format": "int32" 77 | }, 78 | "message": { 79 | "type": "string" 80 | }, 81 | "details": { 82 | "type": "array", 83 | "items": { 84 | "$ref": "#/definitions/protobufAny" 85 | } 86 | } 87 | } 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /examples/token/service/burnable/burnable.validator.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-gogo. DO NOT EDIT. 2 | // source: token/service/burnable/burnable.proto 3 | 4 | package burnable 5 | 6 | import ( 7 | fmt "fmt" 8 | math "math" 9 | proto "github.com/golang/protobuf/proto" 10 | _ "github.com/mwitkow/go-proto-validators" 11 | _ "google.golang.org/genproto/googleapis/api/annotations" 12 | _ "google.golang.org/protobuf/types/known/emptypb" 13 | github_com_mwitkow_go_proto_validators "github.com/mwitkow/go-proto-validators" 14 | ) 15 | 16 | // Reference imports to suppress errors if they are not otherwise used. 17 | var _ = proto.Marshal 18 | var _ = fmt.Errorf 19 | var _ = math.Inf 20 | 21 | func (this *BurnRequest) Validate() error { 22 | if this.Address == "" { 23 | return github_com_mwitkow_go_proto_validators.FieldError("Address", fmt.Errorf(`value '%v' must not be an empty string`, this.Address)) 24 | } 25 | if !(this.Amount > 0) { 26 | return github_com_mwitkow_go_proto_validators.FieldError("Amount", fmt.Errorf(`value '%v' must be greater than '0'`, this.Amount)) 27 | } 28 | return nil 29 | } 30 | func (this *BurnResponse) Validate() error { 31 | return nil 32 | } 33 | func (this *Burned) Validate() error { 34 | return nil 35 | } 36 | -------------------------------------------------------------------------------- /examples/token/service/config/state.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "github.com/s7techlab/cckit/router" 5 | "github.com/s7techlab/cckit/state" 6 | m "github.com/s7techlab/cckit/state/mapping" 7 | ) 8 | 9 | var ( 10 | StateMappings = m.StateMappings{}. 11 | Add(&TokenType{}, 12 | m.PKeySchema(&TokenTypeId{}), 13 | m.List(&TokenTypes{})). 14 | Add(&TokenGroup{}, 15 | m.PKeySchema(&TokenGroupId{}), 16 | m.List(&TokenGroups{})). 17 | Add(&Config{}, 18 | m.WithConstPKey()) 19 | 20 | EventMappings = m.EventMappings{}. 21 | Add(&TokenTypeCreated{}). 22 | Add(&TokenGroupCreated{}) 23 | ) 24 | 25 | // State with chaincode mappings 26 | func State(ctx router.Context) m.MappedState { 27 | return m.WrapState(ctx.State(), StateMappings) 28 | } 29 | 30 | // Event with chaincode mappings 31 | func Event(ctx router.Context) state.Event { 32 | return m.WrapEvent(ctx.Event(), EventMappings) 33 | } 34 | -------------------------------------------------------------------------------- /examples/token/service/config/token.go: -------------------------------------------------------------------------------- 1 | package config 2 | 3 | import ( 4 | "errors" 5 | 6 | "google.golang.org/protobuf/types/known/emptypb" 7 | 8 | "github.com/s7techlab/cckit/router" 9 | ) 10 | 11 | var ( 12 | ErrTokenAlreadyExists = errors.New(`token already exists`) 13 | ) 14 | 15 | type TokenGetter interface { 16 | GetToken(router.Context, *TokenId) (*Token, error) 17 | GetDefaultToken(router.Context, *emptypb.Empty) (*Token, error) 18 | } 19 | 20 | func TokenByTypeGroup(tokenType *TokenType, tokenGroup *TokenGroup) []string { 21 | return append([]string{tokenType.GetName()}, tokenGroup.GetName()...) 22 | } 23 | 24 | func CreateDefaultToken( 25 | ctx router.Context, configSvc ConfigServiceChaincode, createToken *CreateTokenTypeRequest) ([]string, error) { 26 | 27 | existsTokenType, _ := configSvc.GetTokenType(ctx, &TokenTypeId{Name: createToken.Name}) 28 | if existsTokenType != nil { 29 | return nil, ErrTokenAlreadyExists 30 | } 31 | 32 | // init token on first Init call 33 | tokenType, err := configSvc.CreateTokenType(ctx, createToken) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | token := TokenByTypeGroup(tokenType, nil) 39 | 40 | if _, err = configSvc.SetConfig(ctx, &Config{DefaultToken: token}); err != nil { 41 | return nil, err 42 | } 43 | 44 | return token, nil 45 | } 46 | -------------------------------------------------------------------------------- /examples/token/service/config_erc20/config_erc20.go: -------------------------------------------------------------------------------- 1 | package config_erc20 2 | 3 | import ( 4 | "google.golang.org/protobuf/types/known/emptypb" 5 | 6 | "github.com/s7techlab/cckit/examples/token/service/config" 7 | "github.com/s7techlab/cckit/router" 8 | ) 9 | 10 | type ERC20Service struct { 11 | Token config.TokenGetter 12 | } 13 | 14 | func (s *ERC20Service) GetName(ctx router.Context, e *emptypb.Empty) (*NameResponse, error) { 15 | token, err := s.Token.GetDefaultToken(ctx, e) 16 | if err != nil { 17 | return nil, err 18 | } 19 | 20 | return &NameResponse{Name: token.GetType().GetName()}, nil 21 | } 22 | 23 | func (s *ERC20Service) GetSymbol(ctx router.Context, e *emptypb.Empty) (*SymbolResponse, error) { 24 | token, err := s.Token.GetDefaultToken(ctx, e) 25 | if err != nil { 26 | return nil, err 27 | } 28 | 29 | return &SymbolResponse{Symbol: token.GetType().GetSymbol()}, nil 30 | } 31 | 32 | func (s *ERC20Service) GetDecimals(ctx router.Context, e *emptypb.Empty) (*DecimalsResponse, error) { 33 | token, err := s.Token.GetDefaultToken(ctx, e) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | return &DecimalsResponse{Decimals: token.GetType().GetDecimals()}, nil 39 | } 40 | 41 | func (s *ERC20Service) GetTotalSupply(ctx router.Context, e *emptypb.Empty) (*TotalSupplyResponse, error) { 42 | token, err := s.Token.GetDefaultToken(ctx, e) 43 | if err != nil { 44 | return nil, err 45 | } 46 | 47 | return &TotalSupplyResponse{TotalSupply: token.GetType().GetTotalSupply()}, nil 48 | } 49 | -------------------------------------------------------------------------------- /examples/token/service/config_erc20/config_erc20.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "github.com/s7techlab/cckit/examples/token/service/config_erc20"; 4 | package examples.erc20_service.service.config_erc20; 5 | 6 | import "google/api/annotations.proto"; 7 | import "google/protobuf/empty.proto"; 8 | 9 | // ERC-20 Config getters 10 | service ConfigERC20Service { 11 | 12 | // Returns the name of the token. 13 | rpc GetName(google.protobuf.Empty) returns (NameResponse) { 14 | option (google.api.http) = { 15 | get: "/name" 16 | }; 17 | } 18 | 19 | // Returns the symbol of the token, usually a shorter version of the name. 20 | rpc GetSymbol(google.protobuf.Empty) returns (SymbolResponse) { 21 | option (google.api.http) = { 22 | get: "/symbol" 23 | }; 24 | } 25 | 26 | // Returns the number of decimals used to get its user representation. 27 | // For example, if decimals equals 2, a balance of 505 tokens should be displayed to a user as 5,05 (505 / 10 ** 2). 28 | rpc GetDecimals (google.protobuf.Empty) returns (DecimalsResponse) { 29 | option (google.api.http) = { 30 | get: "/decimals" 31 | }; 32 | } 33 | 34 | // Returns the amount of tokens in existence. 35 | rpc GetTotalSupply (google.protobuf.Empty) returns (TotalSupplyResponse) { 36 | option (google.api.http) = { 37 | get: "/total-supply" 38 | }; 39 | } 40 | } 41 | 42 | message NameResponse { 43 | string name = 1; 44 | } 45 | 46 | message SymbolResponse { 47 | string symbol = 1; 48 | } 49 | 50 | message DecimalsResponse { 51 | uint32 decimals = 1; 52 | } 53 | 54 | message TotalSupplyResponse { 55 | uint64 total_supply = 1; 56 | } -------------------------------------------------------------------------------- /examples/token/service/config_erc20/config_erc20.validator.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-gogo. DO NOT EDIT. 2 | // source: token/service/config_erc20/config_erc20.proto 3 | 4 | package config_erc20 5 | 6 | import ( 7 | fmt "fmt" 8 | math "math" 9 | proto "github.com/golang/protobuf/proto" 10 | _ "google.golang.org/genproto/googleapis/api/annotations" 11 | _ "google.golang.org/protobuf/types/known/emptypb" 12 | ) 13 | 14 | // Reference imports to suppress errors if they are not otherwise used. 15 | var _ = proto.Marshal 16 | var _ = fmt.Errorf 17 | var _ = math.Inf 18 | 19 | func (this *NameResponse) Validate() error { 20 | return nil 21 | } 22 | func (this *SymbolResponse) Validate() error { 23 | return nil 24 | } 25 | func (this *DecimalsResponse) Validate() error { 26 | return nil 27 | } 28 | func (this *TotalSupplyResponse) Validate() error { 29 | return nil 30 | } 31 | -------------------------------------------------------------------------------- /extensions/buf.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | lint: 3 | use: 4 | - BASIC 5 | - FILE_LOWER_SNAKE_CASE 6 | - SERVICE_SUFFIX 7 | - ENUM_VALUE_PREFIX 8 | except: 9 | - PACKAGE_DIRECTORY_MATCH 10 | 11 | breaking: 12 | use: 13 | - FILE -------------------------------------------------------------------------------- /extensions/crosscc/README.md: -------------------------------------------------------------------------------- 1 | # Cross chaincode service calls extension -------------------------------------------------------------------------------- /extensions/crosscc/cclocator_setting.validator.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-gogo. DO NOT EDIT. 2 | // source: crosscc/cclocator_setting.proto 3 | 4 | package crosscc 5 | 6 | import ( 7 | fmt "fmt" 8 | math "math" 9 | proto "github.com/golang/protobuf/proto" 10 | _ "google.golang.org/protobuf/types/known/emptypb" 11 | _ "google.golang.org/genproto/googleapis/api/annotations" 12 | github_com_mwitkow_go_proto_validators "github.com/mwitkow/go-proto-validators" 13 | ) 14 | 15 | // Reference imports to suppress errors if they are not otherwise used. 16 | var _ = proto.Marshal 17 | var _ = fmt.Errorf 18 | var _ = math.Inf 19 | 20 | func (this *ServiceLocatorSetRequest) Validate() error { 21 | return nil 22 | } 23 | func (this *ServiceLocator) Validate() error { 24 | return nil 25 | } 26 | func (this *ServiceLocatorId) Validate() error { 27 | return nil 28 | } 29 | func (this *ServiceLocators) Validate() error { 30 | for _, item := range this.Items { 31 | if item != nil { 32 | if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(item); err != nil { 33 | return github_com_mwitkow_go_proto_validators.FieldError("Items", err) 34 | } 35 | } 36 | } 37 | return nil 38 | } 39 | func (this *ServiceLocatorSet) Validate() error { 40 | return nil 41 | } 42 | func (this *PingServiceResponse) Validate() error { 43 | if this.Locator != nil { 44 | if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.Locator); err != nil { 45 | return github_com_mwitkow_go_proto_validators.FieldError("Locator", err) 46 | } 47 | } 48 | return nil 49 | } 50 | func (this *PingServiceResponses) Validate() error { 51 | for _, item := range this.Responses { 52 | if item != nil { 53 | if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(item); err != nil { 54 | return github_com_mwitkow_go_proto_validators.FieldError("Responses", err) 55 | } 56 | } 57 | } 58 | return nil 59 | } 60 | -------------------------------------------------------------------------------- /extensions/crosscc/state.go: -------------------------------------------------------------------------------- 1 | package crosscc 2 | 3 | import ( 4 | "github.com/s7techlab/cckit/router" 5 | "github.com/s7techlab/cckit/state" 6 | m "github.com/s7techlab/cckit/state/mapping" 7 | ) 8 | 9 | var ( 10 | StateMappings = m.StateMappings{}. 11 | Add(&ServiceLocator{}, 12 | m.PKeySchema(&ServiceLocatorId{}), 13 | m.List(&ServiceLocators{})) 14 | 15 | EventMappings = m.EventMappings{}. 16 | Add(&ServiceLocatorSet{}) 17 | ) 18 | 19 | func State(ctx router.Context) m.MappedState { 20 | return m.WrapState(ctx.State(), StateMappings) 21 | } 22 | 23 | func Event(ctx router.Context) state.Event { 24 | return m.WrapEvent(ctx.Event(), EventMappings) 25 | } 26 | -------------------------------------------------------------------------------- /extensions/debug/debug_state.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "github.com/s7techlab/cckit/extensions/debug"; 4 | package extensions.debug; 5 | 6 | import "google/api/annotations.proto"; 7 | 8 | // State key prefix 9 | message Prefix { 10 | // parts of key 11 | repeated string key = 1; 12 | } 13 | 14 | message Prefixes { 15 | repeated Prefix prefixes = 1; 16 | } 17 | 18 | // State key prefix match count 19 | message PrefixesMatchCount { 20 | map matches = 1; 21 | } 22 | 23 | // State keys 24 | message CompositeKeys { 25 | repeated CompositeKey keys = 1; 26 | } 27 | 28 | // State key 29 | message CompositeKey { 30 | repeated string key = 1; 31 | } 32 | 33 | // State value 34 | message Value { 35 | repeated string key = 1; 36 | bytes value = 2; 37 | string json = 3; 38 | } 39 | 40 | // Debug state service 41 | // allows to directly manage chaincode state 42 | service DebugStateService { 43 | // Get keys list, returns all keys or, if prefixes are defined, only prefix matched 44 | rpc ListKeys (Prefix) returns (CompositeKeys) { 45 | option (google.api.http) = { 46 | get: "/debug/state/keys/{key}" 47 | }; 48 | } 49 | 50 | // Get state value by key 51 | rpc GetState (CompositeKey) returns (Value) { 52 | option (google.api.http) = { 53 | get: "/debug/state/{key}" 54 | }; 55 | } 56 | 57 | // Put state value 58 | rpc PutState (Value) returns (Value) { 59 | option (google.api.http) = { 60 | put : "/debug/state" 61 | body: "*" 62 | }; 63 | } 64 | 65 | // Delete state value 66 | rpc DeleteState (CompositeKey) returns (Value) { 67 | option (google.api.http) = { 68 | delete: "/debug/state/{key}" 69 | }; 70 | } 71 | 72 | // Delete all states or, if prefixes are defined, only prefix matched 73 | rpc DeleteStates (Prefixes) returns (PrefixesMatchCount) { 74 | option (google.api.http) = { 75 | post: "/debug/state/clean" 76 | body: "*" 77 | }; 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /extensions/debug/debug_state.validator.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-gogo. DO NOT EDIT. 2 | // source: debug/debug_state.proto 3 | 4 | package debug 5 | 6 | import ( 7 | fmt "fmt" 8 | math "math" 9 | proto "github.com/golang/protobuf/proto" 10 | _ "google.golang.org/genproto/googleapis/api/annotations" 11 | github_com_mwitkow_go_proto_validators "github.com/mwitkow/go-proto-validators" 12 | ) 13 | 14 | // Reference imports to suppress errors if they are not otherwise used. 15 | var _ = proto.Marshal 16 | var _ = fmt.Errorf 17 | var _ = math.Inf 18 | 19 | func (this *Prefix) Validate() error { 20 | return nil 21 | } 22 | func (this *Prefixes) Validate() error { 23 | for _, item := range this.Prefixes { 24 | if item != nil { 25 | if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(item); err != nil { 26 | return github_com_mwitkow_go_proto_validators.FieldError("Prefixes", err) 27 | } 28 | } 29 | } 30 | return nil 31 | } 32 | func (this *PrefixesMatchCount) Validate() error { 33 | // Validation of proto3 map<> fields is unsupported. 34 | return nil 35 | } 36 | func (this *CompositeKeys) Validate() error { 37 | for _, item := range this.Keys { 38 | if item != nil { 39 | if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(item); err != nil { 40 | return github_com_mwitkow_go_proto_validators.FieldError("Keys", err) 41 | } 42 | } 43 | } 44 | return nil 45 | } 46 | func (this *CompositeKey) Validate() error { 47 | return nil 48 | } 49 | func (this *Value) Validate() error { 50 | return nil 51 | } 52 | -------------------------------------------------------------------------------- /extensions/debug/state.go: -------------------------------------------------------------------------------- 1 | package debug 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/s7techlab/cckit/state" 7 | ) 8 | 9 | // DeleteStateByPrefixes deletes from state entries with matching key prefix 10 | // raw function, do not use State wrappers, like encryption 11 | func DeleteStateByPrefixes(s state.State, prefixes []string) (map[string]uint32, error) { 12 | prefixMatches := make(map[string]uint32) 13 | 14 | for _, prefix := range prefixes { 15 | 16 | keys, err := s.Keys(prefix) 17 | if err != nil { 18 | return nil, fmt.Errorf(`keys: %w`, err) 19 | } 20 | 21 | prefixMatches[prefix] = 0 22 | for _, key := range keys { 23 | if err = s.Delete(key); err != nil { 24 | return nil, err 25 | } 26 | prefixMatches[prefix]++ 27 | } 28 | } 29 | 30 | return prefixMatches, nil 31 | } 32 | -------------------------------------------------------------------------------- /extensions/ecdh/ecdh.go: -------------------------------------------------------------------------------- 1 | package ecdh 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | ) 6 | 7 | func Marshall(pubKey *ecdsa.PublicKey) []byte { 8 | byteLen := (pubKey.Curve.Params().BitSize + 7) >> 3 9 | 10 | ret := make([]byte, 1+2*byteLen) 11 | ret[0] = 4 // uncompressed point 12 | 13 | xBytes := pubKey.X.Bytes() 14 | copy(ret[1+byteLen-len(xBytes):], xBytes) 15 | yBytes := pubKey.Y.Bytes() 16 | copy(ret[1+2*byteLen-len(yBytes):], yBytes) 17 | return ret 18 | 19 | } 20 | 21 | func GenerateSharedSecret(privKey *ecdsa.PrivateKey, pubKey *ecdsa.PublicKey) ([]byte, error) { 22 | x, _ := pubKey.Curve.ScalarMult(pubKey.X, pubKey.Y, privKey.D.Bytes()) 23 | return x.Bytes(), nil 24 | } 25 | -------------------------------------------------------------------------------- /extensions/ecdh/ecdh_test.go: -------------------------------------------------------------------------------- 1 | package ecdh_test 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "crypto/elliptic" 6 | "fmt" 7 | "testing" 8 | 9 | . "github.com/onsi/ginkgo" 10 | . "github.com/onsi/gomega" 11 | 12 | "github.com/s7techlab/cckit/extensions/ecdh" 13 | "github.com/s7techlab/cckit/identity/testdata" 14 | ) 15 | 16 | func TestDebug(t *testing.T) { 17 | RegisterFailHandler(Fail) 18 | RunSpecs(t, "ECDH suite") 19 | } 20 | 21 | // load actor certificates 22 | var ( 23 | pubKey1 = testdata.Certificates[0].MustCert().PublicKey.(*ecdsa.PublicKey) 24 | pubKey2 = testdata.Certificates[1].MustCert().PublicKey.(*ecdsa.PublicKey) 25 | pubKey3 = testdata.Certificates[2].MustCert().PublicKey.(*ecdsa.PublicKey) 26 | 27 | privKey1 = testdata.Certificates[0].MustPKey() 28 | privKey2 = testdata.Certificates[1].MustPKey() 29 | privKey3 = testdata.Certificates[2].MustPKey() 30 | ) 31 | 32 | var _ = Describe(`ECDH`, func() { 33 | 34 | It("Allow to create shared key for 2 parties", func() { 35 | 36 | secret12, err := ecdh.GenerateSharedSecret(privKey1, pubKey2) 37 | Expect(err).To(BeNil()) 38 | secret21, err := ecdh.GenerateSharedSecret(privKey2, pubKey1) 39 | Expect(err).To(BeNil()) 40 | 41 | Expect(secret12).To(Equal(secret21)) 42 | 43 | secret23, err := ecdh.GenerateSharedSecret(privKey2, pubKey3) 44 | Expect(err).To(BeNil()) 45 | secret32, err := ecdh.GenerateSharedSecret(privKey3, pubKey2) 46 | Expect(err).To(BeNil()) 47 | 48 | Expect(secret23).To(Equal(secret32)) 49 | }) 50 | 51 | It("Allow to create shared key for 3 parties", func() { 52 | secret12, err := ecdh.GenerateSharedSecret(privKey1, pubKey2) 53 | Expect(err).To(BeNil()) 54 | 55 | x, y := elliptic.Unmarshal(pubKey1.Curve, secret12) 56 | 57 | fmt.Println(x, y) 58 | //if x == nil || y == nil { 59 | // return key, false 60 | //} 61 | //key = &ellipticPublicKey{ 62 | // Curve: e.curve, 63 | // X: x, 64 | // Y: y, 65 | //} 66 | }) 67 | }) 68 | -------------------------------------------------------------------------------- /extensions/encryption/encryption.go: -------------------------------------------------------------------------------- 1 | package encryption 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/s7techlab/cckit/convert" 7 | "github.com/s7techlab/cckit/router" 8 | ) 9 | 10 | const TransientMapKey = `ENCODE_KEY` 11 | 12 | // EncryptArgs convert args to [][]byte and encrypt args with key 13 | func EncryptArgs(key []byte, args ...interface{}) ([][]byte, error) { 14 | argBytes, err := convert.ArgsToBytes(args...) 15 | if err != nil { 16 | return nil, err 17 | } 18 | 19 | return EncryptArgsBytes(key, argBytes) 20 | } 21 | 22 | // EncryptArgsBytes encrypt args with key 23 | func EncryptArgsBytes(key []byte, argsBytes [][]byte) ([][]byte, error) { 24 | eArgs := make([][]byte, len(argsBytes)) 25 | for i, bb := range argsBytes { 26 | encrypted, err := EncryptBytes(key, bb) 27 | if err != nil { 28 | return nil, fmt.Errorf(`encryption error: %w`, err) 29 | } 30 | 31 | eArgs[i] = encrypted 32 | } 33 | return eArgs, nil 34 | } 35 | 36 | // DecryptArgs decrypt args 37 | func DecryptArgs(key []byte, args [][]byte) ([][]byte, error) { 38 | dargs := make([][]byte, len(args)) 39 | for i, a := range args { 40 | 41 | // do not try to decrypt init function 42 | if i == 0 && string(a) == router.InitFunc { 43 | dargs[i] = a 44 | continue 45 | } 46 | 47 | decrypted, err := DecryptBytes(key, a) 48 | if err != nil { 49 | return nil, fmt.Errorf(`decryption error: %w`, err) 50 | } 51 | dargs[i] = decrypted 52 | } 53 | return dargs, nil 54 | } 55 | 56 | // Encrypt converts value to []byte and encrypts its with key 57 | func Encrypt(key []byte, value interface{}) ([]byte, error) { 58 | // TODO: customize IV 59 | bb, err := convert.ToBytes(value) 60 | if err != nil { 61 | return nil, fmt.Errorf(`convert values to bytes: %w`, err) 62 | } 63 | 64 | return EncryptBytes(key, bb) 65 | } 66 | 67 | // Decrypt decrypts value with key 68 | func Decrypt(key, value []byte) ([]byte, error) { 69 | 70 | bb := make([]byte, len(value)) 71 | copy(bb, value) 72 | return DecryptBytes(key, bb) 73 | } 74 | 75 | // TransientMapWithKey creates transient map with encrypting/decrypting key 76 | func TransientMapWithKey(key []byte) map[string][]byte { 77 | return map[string][]byte{TransientMapKey: key} 78 | } 79 | -------------------------------------------------------------------------------- /extensions/encryption/invoke.go: -------------------------------------------------------------------------------- 1 | package encryption 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/hyperledger/fabric-chaincode-go/shim" 8 | 9 | "github.com/s7techlab/cckit/convert" 10 | "github.com/s7techlab/cckit/state" 11 | ) 12 | 13 | // InvokeChaincode decrypts received payload 14 | func InvokeChaincode( 15 | stub shim.ChaincodeStubInterface, encKey []byte, chaincodeName string, 16 | args []interface{}, channel string, target interface{}) (interface{}, error) { 17 | 18 | // args are not encrypted because we cannot pass encryption key in transient map while invoking cc from cc 19 | // thus target cc cannot decrypt args 20 | aa, err := convert.ArgsToBytes(args...) 21 | if err != nil { 22 | return nil, fmt.Errorf(`encrypt args: %w`, err) 23 | } 24 | 25 | response := stub.InvokeChaincode(chaincodeName, aa, channel) 26 | if response.Status != shim.OK { 27 | return nil, errors.New(response.Message) 28 | } 29 | 30 | if len(response.Payload) == 0 { 31 | return nil, state.ErrEmptyChaincodeResponsePayload 32 | } 33 | 34 | decrypted, err := Decrypt(encKey, response.Payload) 35 | if err != nil { 36 | return nil, fmt.Errorf(`decrypt payload: %w`, err) 37 | } 38 | return convert.FromBytes(decrypted, target) 39 | } 40 | -------------------------------------------------------------------------------- /extensions/encryption/middleware_after.go: -------------------------------------------------------------------------------- 1 | package encryption 2 | 3 | import ( 4 | "github.com/s7techlab/cckit/router" 5 | ) 6 | 7 | func encryptIfEncodeKeyExists(encryptionRequired bool) router.MiddlewareFunc { 8 | return func(pre router.HandlerFunc, pos ...int) router.HandlerFunc { 9 | return func(ctx router.Context) (interface{}, error) { 10 | res, err := pre(ctx) 11 | 12 | if err != nil || ctx.Handler().Type != router.MethodInvoke { 13 | return res, err 14 | } 15 | if _, err = KeyFromTransient(ctx); err != nil { 16 | if err == ErrKeyNotDefinedInTransientMap && !encryptionRequired { 17 | return res, nil 18 | } 19 | return nil, err 20 | } 21 | 22 | return EncryptWithTransientKey(ctx, res) 23 | } 24 | } 25 | } 26 | 27 | // EncryptInvokeResponse returns middleware function for encrypt chaincode invocation response 28 | func EncryptInvokeResponse() router.MiddlewareFunc { 29 | return encryptIfEncodeKeyExists(true) 30 | } 31 | 32 | // EncryptInvokeResponseIfKeyProvided returns middleware function for encrypt chaincode invocation response if 33 | // encryption key provided in transient map 34 | func EncryptInvokeResponseIfKeyProvided() router.MiddlewareFunc { 35 | return encryptIfEncodeKeyExists(false) 36 | } 37 | -------------------------------------------------------------------------------- /extensions/encryption/must.go: -------------------------------------------------------------------------------- 1 | package encryption 2 | 3 | import ( 4 | "github.com/hyperledger/fabric-protos-go/peer" 5 | ) 6 | 7 | // MustEncryptEvent helper for EncryptEvent. Panics in case of error. 8 | func MustEncryptEvent(encKey []byte, event *peer.ChaincodeEvent) *peer.ChaincodeEvent { 9 | encrypted, err := EncryptEvent(encKey, event) 10 | if err != nil { 11 | panic(err) 12 | } 13 | return encrypted 14 | } 15 | 16 | // MustDecryptEvent helper for DecryptEvent. Panics in case of error. 17 | func MustDecryptEvent(encKey []byte, event *peer.ChaincodeEvent) *peer.ChaincodeEvent { 18 | decrypted, err := DecryptEvent(encKey, event) 19 | if err != nil { 20 | panic(err) 21 | } 22 | 23 | return decrypted 24 | } 25 | -------------------------------------------------------------------------------- /extensions/encryption/testdata/cc_external.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "github.com/s7techlab/cckit/examples/payment" 5 | "github.com/s7techlab/cckit/examples/payment/schema" 6 | "github.com/s7techlab/cckit/extensions/encryption" 7 | "github.com/s7techlab/cckit/router" 8 | "github.com/s7techlab/cckit/router/param" 9 | ) 10 | 11 | // NewExternalCC test interaction with external encrypted chaincode (payments) 12 | // see example/payment 13 | func NewExternalCC(encCCName, channelName string) *router.Chaincode { 14 | r := router.New(`external`) 15 | 16 | r. 17 | // returns payment state entry from external encrypted cc 18 | Query(`checkPayment`, func(c router.Context) (interface{}, error) { 19 | var ( 20 | paymentType = c.ParamString(`paymentType`) 21 | paymentId = c.ParamString(`paymentId`) 22 | encKey, err = encryption.KeyFromTransient(c) 23 | ) 24 | if err != nil { 25 | return nil, err 26 | } 27 | 28 | // create state key using payments mapping 29 | paymentKey, err := payment.StateMappings.PrimaryKey(&schema.Payment{Type: paymentType, Id: paymentId}) 30 | if err != nil { 31 | return nil, err 32 | } 33 | 34 | // we need to encrypt state key, not all args (method name `debugStateGet` must remain unencrypted ) 35 | encPaymentKey, err := encryption.KeyEncryptor(encKey)(paymentKey) 36 | if err != nil { 37 | return nil, err 38 | } 39 | 40 | // payment state entry returns decrypted 41 | return encryption.InvokeChaincode(c.Stub(), encKey, 42 | encCCName, []interface{}{`debugStateGet`, []string(encPaymentKey)}, channelName, &schema.Payment{}) 43 | }, param.String(`paymentType`), param.String(`paymentId`)) 44 | 45 | return router.NewChaincode(r) 46 | } 47 | -------------------------------------------------------------------------------- /extensions/encryption/testing/invoke.go: -------------------------------------------------------------------------------- 1 | package testing 2 | 3 | import ( 4 | "github.com/hyperledger/fabric-protos-go/peer" 5 | 6 | "github.com/s7techlab/cckit/extensions/encryption" 7 | "github.com/s7techlab/cckit/response" 8 | testcc "github.com/s7techlab/cckit/testing" 9 | ) 10 | 11 | // MockInvoke helper for invoking MockStub with transient key and encrypted args 12 | func MockInvoke(cc *testcc.MockStub, encKey []byte, args ...interface{}) peer.Response { 13 | encArgs, err := encryption.EncryptArgs(encKey, args...) 14 | if err != nil { 15 | return response.Error(`unable to encrypt input args`) 16 | } 17 | return cc.AddTransient(encryption.TransientMapWithKey(encKey)).InvokeBytes(encArgs...) 18 | } 19 | 20 | // MockQuery helper for querying MockStub with transient key and encrypted args 21 | func MockQuery(cc *testcc.MockStub, encKey []byte, args ...interface{}) peer.Response { 22 | encArgs, err := encryption.EncryptArgs(encKey, args...) 23 | if err != nil { 24 | return response.Error(`unable to encrypt input args`) 25 | } 26 | return cc.AddTransient(encryption.TransientMapWithKey(encKey)).QueryBytes(encArgs...) 27 | } 28 | -------------------------------------------------------------------------------- /extensions/owner/README.md: -------------------------------------------------------------------------------- 1 | # Owner - access control hyperledger fabric chaincode extension 2 | 3 | In many cases during chaincode instantiating we need to define permissions for chaincode functions - 4 | "who is allowed to do this thing", incredibly important in the world of smart contracts, 5 | but in many examples access control implemented at the application level but not at the blockchain layer. 6 | 7 | The most common and basic form of access control is the concept of `ownership`: there's one or more accounts 8 | (in case of Hyperledger Fabric chaincode model - combination of MSP and certificate) that is the 9 | owners and can do administrative tasks on contracts. This 10 | approach is perfectly reasonable for contracts that only have a single administrative user. 11 | 12 | CCKit provides `owner` extension for implementing ownership and access control in Hyperledger Fabric chaincodes. 13 | 14 | Owner extension implemented in two version: 15 | 16 | 1. As chaincode [handlers](handler.go) 17 | 2. As [service](chaincode_owner.proto), that can be embedded in chaincode, using [chaincode-as-service mode](../../gateway) -------------------------------------------------------------------------------- /extensions/owner/handler.go: -------------------------------------------------------------------------------- 1 | package owner 2 | 3 | import ( 4 | "github.com/s7techlab/cckit/router" 5 | ) 6 | 7 | const QueryMethod = `owner` 8 | 9 | // Query returns raw data (serialized Grant) of current chain code owner 10 | func Query(c router.Context) (interface{}, error) { 11 | return c.State().Get(OwnerStateKey) 12 | } 13 | 14 | // InvokeSetFromCreator sets tx creator as chaincode owner, if owner not previously set 15 | func InvokeSetFromCreator(c router.Context) (interface{}, error) { 16 | return SetFromCreator(c) 17 | } 18 | 19 | // InvokeSetFromArgs gets owner data from args[0] (Msp Id) and arg[1] (cert) 20 | func InvokeSetFromArgs(c router.Context) (interface{}, error) { 21 | return SetFromArgs(c) 22 | } 23 | -------------------------------------------------------------------------------- /extensions/owner/modifier.go: -------------------------------------------------------------------------------- 1 | package owner 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/s7techlab/cckit/router" 8 | ) 9 | 10 | var ( 11 | // ErrOwnerOnly error occurs when trying to invoke chaincode func protected by onlyOwner middleware (modifier) 12 | ErrOwnerOnly = errors.New(`owner only`) 13 | ) 14 | 15 | // Only allow access from chain code owner 16 | func Only(next router.HandlerFunc, _ ...int) router.HandlerFunc { 17 | return func(c router.Context) (interface{}, error) { 18 | err := IsTxCreator(c) 19 | if err == nil { 20 | return next(c) 21 | } 22 | return nil, fmt.Errorf(`%s: %w`, err, ErrOwnerOnly) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /extensions/owner/state.go: -------------------------------------------------------------------------------- 1 | package owner 2 | 3 | import ( 4 | "github.com/s7techlab/cckit/router" 5 | "github.com/s7techlab/cckit/state" 6 | m "github.com/s7techlab/cckit/state/mapping" 7 | ) 8 | 9 | // OwnerStateKey key used to store owner grant struct in chain code state 10 | // "handler" part of owner extension supports only one owner 11 | // "service" part of owner extension supports multiple owners 12 | const OwnerStateKey = `OWNER` 13 | 14 | var ( 15 | StateMappings = m.StateMappings{}. 16 | Add(&ChaincodeOwner{}, 17 | m.PKeySchema(&OwnerId{}), 18 | m.List(&ChaincodeOwners{})) 19 | 20 | EventMappings = m.EventMappings{}. 21 | Add(&ChaincodeOwnerCreated{}). 22 | Add(&ChaincodeOwnerUpdated{}). 23 | Add(&ChaincodeOwnerDeleted{}) 24 | ) 25 | 26 | func State(ctx router.Context) m.MappedState { 27 | return m.WrapState(ctx.State(), StateMappings) 28 | } 29 | 30 | func Event(ctx router.Context) state.Event { 31 | return m.WrapEvent(ctx.Event(), EventMappings) 32 | } 33 | -------------------------------------------------------------------------------- /extensions/pinger/README.md: -------------------------------------------------------------------------------- 1 | # Pinger - hyperledger fabric chaincode ping extension 2 | 3 | Often there is a need to find out if everything is correct with chaincode. To do it we would like to ping CC 4 | and know some information. 5 | 6 | CCKit provides `pinger` extension for implementing ping opportunity in Hyperledger Fabric chaincodes. When chaincode will be pinged, 7 | you get information about invoker, his certificate and time. 8 | 9 | Pinger implemented in two version: 10 | 11 | 1. As chaincode [handlers](pinger.go) 12 | 2. As [service](chaincode_pinger.proto), that can be embedded in chaincode, using [chaincode-as-service mode](../../gateway) -------------------------------------------------------------------------------- /extensions/pinger/chaincode_pinger.go: -------------------------------------------------------------------------------- 1 | package pinger 2 | 3 | import ( 4 | "github.com/golang/protobuf/ptypes/empty" 5 | 6 | "github.com/s7techlab/cckit/router" 7 | ) 8 | 9 | func NewService() *ChaincodePinger { 10 | return &ChaincodePinger{} 11 | } 12 | 13 | type ChaincodePinger struct{} 14 | 15 | func (c *ChaincodePinger) Ping(ctx router.Context, _ *empty.Empty) (*PingInfo, error) { 16 | i, err := Ping(ctx) 17 | if err != nil { 18 | return nil, err 19 | } 20 | 21 | return i.(*PingInfo), err 22 | } 23 | -------------------------------------------------------------------------------- /extensions/pinger/chaincode_pinger.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "github.com/s7techlab/cckit/extensions/pinger"; 4 | 5 | package extensions.pinger; 6 | 7 | import "google/api/annotations.proto"; 8 | import "google/protobuf/empty.proto"; 9 | import "google/protobuf/timestamp.proto"; 10 | 11 | service ChaincodePingerService { 12 | // ping chaincode 13 | rpc Ping (google.protobuf.Empty) returns (PingInfo) { 14 | option (google.api.http) = { 15 | post: "/chaincode/pinger/ping" 16 | body: "*" 17 | }; 18 | } 19 | } 20 | 21 | // stores time and certificate of ping tx creator 22 | message PingInfo { 23 | string invoker_id = 1; 24 | bytes invoker_cert = 2; 25 | google.protobuf.Timestamp endorsing_server_time = 3; 26 | google.protobuf.Timestamp tx_time = 4; 27 | } 28 | -------------------------------------------------------------------------------- /extensions/pinger/chaincode_pinger.validator.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-gogo. DO NOT EDIT. 2 | // source: pinger/chaincode_pinger.proto 3 | 4 | package pinger 5 | 6 | import ( 7 | fmt "fmt" 8 | math "math" 9 | proto "github.com/golang/protobuf/proto" 10 | _ "google.golang.org/genproto/googleapis/api/annotations" 11 | _ "google.golang.org/protobuf/types/known/emptypb" 12 | _ "google.golang.org/protobuf/types/known/timestamppb" 13 | github_com_mwitkow_go_proto_validators "github.com/mwitkow/go-proto-validators" 14 | ) 15 | 16 | // Reference imports to suppress errors if they are not otherwise used. 17 | var _ = proto.Marshal 18 | var _ = fmt.Errorf 19 | var _ = math.Inf 20 | 21 | func (this *PingInfo) Validate() error { 22 | if this.EndorsingServerTime != nil { 23 | if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.EndorsingServerTime); err != nil { 24 | return github_com_mwitkow_go_proto_validators.FieldError("EndorsingServerTime", err) 25 | } 26 | } 27 | if this.TxTime != nil { 28 | if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.TxTime); err != nil { 29 | return github_com_mwitkow_go_proto_validators.FieldError("TxTime", err) 30 | } 31 | } 32 | return nil 33 | } 34 | -------------------------------------------------------------------------------- /extensions/pinger/chaincode_pinger_test.go: -------------------------------------------------------------------------------- 1 | package pinger_test 2 | 3 | import ( 4 | "github.com/golang/protobuf/ptypes/empty" 5 | . "github.com/onsi/ginkgo" 6 | . "github.com/onsi/gomega" 7 | 8 | "github.com/s7techlab/cckit/extensions/pinger" 9 | "github.com/s7techlab/cckit/identity/testdata" 10 | testcc "github.com/s7techlab/cckit/testing" 11 | ) 12 | 13 | var _ = Describe(`Chaincode pinger`, func() { 14 | 15 | var ( 16 | Someone = testdata.Certificates[0].MustIdentity(`SOME_MSP`) 17 | 18 | pingerSvc = pinger.NewService() 19 | cc, ctx = testcc.NewTxHandler(`Chaincode owner`) 20 | ) 21 | 22 | It(`Ping`, func() { 23 | cc.From(Someone).Tx(func() { 24 | pingInfo, err := pingerSvc.Ping(ctx, &empty.Empty{}) 25 | Expect(err).NotTo(HaveOccurred()) 26 | 27 | Expect(pingInfo.InvokerId).To(Equal(Someone.GetID())) 28 | Expect(pingInfo.InvokerCert).To(Equal(Someone.GetPEM())) 29 | Expect(pingInfo.EndorsingServerTime).To(Not(BeNil())) 30 | Expect(pingInfo.TxTime).To(Not(BeNil())) 31 | }) 32 | }) 33 | }) 34 | -------------------------------------------------------------------------------- /extensions/pinger/consts.go: -------------------------------------------------------------------------------- 1 | package pinger 2 | 3 | const ( 4 | // PingKeyPrefix prefix for PingInfo composite key in chain code state 5 | PingKeyPrefix = `PING` 6 | // PingEvent event name 7 | PingEvent = `PING` 8 | // FuncPingConstant func name 9 | FuncPingConstant = `pingLocal` 10 | // FuncPing func name 11 | FuncPing = `ping` 12 | // FuncPings func name 13 | FuncPings = `pings` 14 | ) 15 | -------------------------------------------------------------------------------- /extensions/pinger/pinger.go: -------------------------------------------------------------------------------- 1 | // Package pinger contains structure and functions for checking chain code accessibility 2 | package pinger 3 | 4 | import ( 5 | "github.com/hyperledger/fabric-chaincode-go/pkg/cid" 6 | "google.golang.org/protobuf/types/known/timestamppb" 7 | 8 | "github.com/s7techlab/cckit/identity" 9 | r "github.com/s7techlab/cckit/router" 10 | ) 11 | 12 | // Ping create PingInfo struct with tx creator ID and certificate in PEM format 13 | func Ping(ctx r.Context) (interface{}, error) { 14 | id, err := cid.GetID(ctx.Stub()) 15 | if err != nil { 16 | return nil, err 17 | } 18 | 19 | //take certificate from creator 20 | invoker, err := identity.FromStub(ctx.Stub()) 21 | if err != nil { 22 | return nil, err 23 | } 24 | 25 | txTime, err := ctx.Time() 26 | if err != nil { 27 | return nil, err 28 | } 29 | 30 | return &PingInfo{ 31 | InvokerId: id, 32 | InvokerCert: invoker.GetPEM(), 33 | EndorsingServerTime: timestamppb.Now(), 34 | TxTime: timestamppb.New(txTime), 35 | }, nil 36 | } 37 | -------------------------------------------------------------------------------- /extensions/pinger/pinger_test.go: -------------------------------------------------------------------------------- 1 | package pinger 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | 9 | "github.com/s7techlab/cckit/identity/testdata" 10 | "github.com/s7techlab/cckit/router" 11 | testcc "github.com/s7techlab/cckit/testing" 12 | expectcc "github.com/s7techlab/cckit/testing/expect" 13 | ) 14 | 15 | func TestPinger(t *testing.T) { 16 | RegisterFailHandler(Fail) 17 | RunSpecs(t, "Pinger suite") 18 | } 19 | 20 | var ( 21 | Someone = testdata.Certificates[1].MustIdentity(`SOME_MSP`) 22 | ) 23 | 24 | func New() *router.Chaincode { 25 | r := router.New(`pingable`). 26 | Init(router.EmptyContextHandler). 27 | Invoke(FuncPing, Ping) 28 | return router.NewChaincode(r) 29 | } 30 | 31 | var _ = Describe(`Pinger`, func() { 32 | 33 | // Create chaincode mock 34 | cc := testcc.NewMockStub(`cars`, New()) 35 | 36 | Describe("Pinger", func() { 37 | 38 | It("Allow anyone to invoke ping method", func() { 39 | //invoke chaincode method from authority actor 40 | pingInfo := expectcc.PayloadIs(cc.From(Someone).Invoke(FuncPing), &PingInfo{}).(*PingInfo) 41 | Expect(pingInfo.InvokerId).To(Equal(Someone.GetID())) 42 | Expect(pingInfo.InvokerCert).To(Equal(Someone.GetPEM())) 43 | Expect(pingInfo.EndorsingServerTime).To(Not(BeNil())) 44 | Expect(pingInfo.TxTime).To(Not(BeNil())) 45 | }) 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /gateway/README.md: -------------------------------------------------------------------------------- 1 | # CCKit gateway 2 | 3 | -------------------------------------------------------------------------------- /gateway/api.go: -------------------------------------------------------------------------------- 1 | package gateway 2 | 3 | type () 4 | -------------------------------------------------------------------------------- /gateway/buf.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | lint: 3 | use: 4 | - BASIC 5 | - FILE_LOWER_SNAKE_CASE 6 | - SERVICE_SUFFIX 7 | - ENUM_VALUE_PREFIX 8 | except: 9 | - PACKAGE_DIRECTORY_MATCH 10 | breaking: 11 | use: 12 | - FILE 13 | -------------------------------------------------------------------------------- /gateway/chaincode_event_service.go: -------------------------------------------------------------------------------- 1 | package gateway 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/s7techlab/cckit/sdk" 7 | ) 8 | 9 | type ( 10 | ChaincodeEventService struct { 11 | EventDelivery sdk.EventDelivery 12 | } 13 | 14 | // ChaincodeEventsServer gateway/chaincode.go needs access to grpc stream 15 | ChaincodeEventsServer = chaincodeServiceEventsStreamServer 16 | ) 17 | 18 | var _ ChaincodeEventsServiceServer = &ChaincodeEventService{} 19 | 20 | // ServiceDef returns service definition 21 | func (ce *ChaincodeEventService) ServiceDef() ServiceDef { 22 | return ServiceDef{ 23 | Desc: &_ChaincodeEventsService_serviceDesc, 24 | Service: ce, 25 | HandlerFromEndpointRegister: RegisterChaincodeEventsServiceHandlerFromEndpoint, 26 | } 27 | } 28 | 29 | func (ce *ChaincodeEventService) Events(ctx context.Context, req *ChaincodeEventsRequest) (*ChaincodeEvents, error) { 30 | return NewChaincodeInstanceEventService(ce.EventDelivery, req.Locator.Channel, req.Locator.Chaincode). 31 | Events(ctx, &ChaincodeInstanceEventsRequest{ 32 | FromBlock: req.FromBlock, 33 | ToBlock: req.ToBlock, 34 | EventName: req.EventName, 35 | Limit: req.Limit, 36 | }) 37 | } 38 | 39 | func (ce *ChaincodeEventService) EventsStream(req *ChaincodeEventsStreamRequest, stream ChaincodeEventsService_EventsStreamServer) error { 40 | return NewChaincodeInstanceEventService(ce.EventDelivery, req.Locator.Channel, req.Locator.Chaincode). 41 | EventsStream(&ChaincodeInstanceEventsStreamRequest{ 42 | FromBlock: req.FromBlock, 43 | ToBlock: req.ToBlock, 44 | EventName: req.EventName, 45 | }, stream) 46 | } 47 | 48 | func (ce *ChaincodeEventService) EventsChan( 49 | ctx context.Context, req *ChaincodeEventsStreamRequest) (_ chan *ChaincodeEvent, closer func() error, _ error) { 50 | return NewChaincodeInstanceEventService(ce.EventDelivery, req.Locator.Channel, req.Locator.Chaincode). 51 | EventsChan(ctx, &ChaincodeInstanceEventsStreamRequest{ 52 | FromBlock: req.FromBlock, 53 | ToBlock: req.ToBlock, 54 | EventName: req.EventName, 55 | }) 56 | } 57 | -------------------------------------------------------------------------------- /gateway/errors.go: -------------------------------------------------------------------------------- 1 | package gateway 2 | 3 | import "errors" 4 | 5 | var ( 6 | ErrEventChannelClosed = errors.New(`event channel is closed`) 7 | 8 | // ErrChaincodeNotExists occurs when attempting to invoke a nonexistent external chaincode 9 | ErrChaincodeNotExists = errors.New(`chaincode not exists`) 10 | 11 | // ErrSignerNotDefinedInContext msp.SigningIdentity is not defined in context 12 | ErrSignerNotDefinedInContext = errors.New(`signer is not defined in context`) 13 | 14 | // ErrUnknownInvocationType query or invoke 15 | ErrUnknownInvocationType = errors.New(`unknown invocation type`) 16 | ) 17 | -------------------------------------------------------------------------------- /gateway/invoker_stub.go: -------------------------------------------------------------------------------- 1 | package gateway 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/hyperledger/fabric-chaincode-go/shim" 8 | 9 | "github.com/s7techlab/cckit/router" 10 | ) 11 | 12 | var ErrInvokeMethodNotAllowed = errors.New(`invoke method not allowed`) 13 | 14 | type ( 15 | ChaincodeLocatorResolver func(ctx router.Context, serviceName string) (*ChaincodeLocator, error) 16 | // ChaincodeStubInvoker for cross chaincode calls - only query (read) methods 17 | // context argument is router.Context, not context.Context 18 | ChaincodeStubInvoker interface { 19 | Query(stub shim.ChaincodeStubInterface, fn string, args []interface{}, target interface{}) (interface{}, error) 20 | } 21 | 22 | LocatorChaincodeStubInvoker struct { 23 | Locator *ChaincodeLocator 24 | } 25 | ) 26 | 27 | func (c *LocatorChaincodeStubInvoker) Query( 28 | stub shim.ChaincodeStubInterface, fn string, args []interface{}, target interface{}) (interface{}, error) { 29 | 30 | argsBytes, err := InvokerArgs(fn, args) 31 | if err != nil { 32 | return nil, err 33 | } 34 | 35 | // if target chaincode is encrypted we can only access to target chaincode via dummy `StateGet`, 36 | // without method name and arg decryption on target chaincode side 37 | // because transient data cannot be passed via stub.InvokeChaincode call 38 | 39 | response := stub.InvokeChaincode(c.Locator.Chaincode, argsBytes, c.Locator.Channel) 40 | if response.Status != shim.OK { 41 | return nil, fmt.Errorf(`cross chaincode=%s, channel=%s invoke: %w`, 42 | c.Locator.Chaincode, c.Locator.Channel, errors.New(response.Message)) 43 | } 44 | 45 | return ccOutput(&response, target) 46 | } 47 | -------------------------------------------------------------------------------- /gateway/protoc-gen-cc-gateway/README.md: -------------------------------------------------------------------------------- 1 | # Hyperledger Fabric chaincode kit (CCKit) 2 | 3 | ## Chaincode gateway 4 | 5 | With gRPC we can define chaincode interface once in a .proto file and API / SDK will be automatically created for this chaincode. 6 | We also get all the advantages of working with protocol buffers, including efficient serialization, a simple IDL, 7 | and easy interface updating. 8 | 9 | Chaincode-as-service gateway generator allows to generate from gRPC service definition: 10 | 11 | * Chaincode handlers interface 12 | * Chaincode gateway - service, can act as chaincode SDK or can be exposed as gRPC or REST service 13 | 14 | ### Install the generator 15 | 16 | `GO111MODULE=on go install github.com/s7techlab/cckit/gateway/protoc-gen-cc-gateway` 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /gateway/protoc-gen-cc-gateway/generator/error.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import "errors" 4 | 5 | var ( 6 | ErrNoTargetService = errors.New("no target service defined in the file") 7 | ) 8 | -------------------------------------------------------------------------------- /gateway/protoc-gen-cc-gateway/generator/opts.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "strings" 5 | ) 6 | 7 | const ( 8 | ParamPaths = `paths` 9 | ParamPathsSourceRelative = `source_relative` 10 | ParamEmbedSwagger = `embed_swagger` 11 | ParamChaincodeMethodServicePrefix = `service_name_method_prefix` 12 | ParamServiceChaincodeResolver = `service_resolver` 13 | ) 14 | 15 | // Opts by default all opts are disabled 16 | type Opts struct { 17 | PathsSourceRelative bool 18 | EmbedSwagger bool // generate var with embed annotation to include generated swagger fie 19 | ChaincodeMethodServicePrefix bool // add prefix with service name to chaincode method 20 | ServiceChaincodeResolver bool 21 | } 22 | 23 | func isOptEnabled(paramValue string) bool { 24 | if paramValue == `0` || paramValue == `false` { 25 | return false 26 | } 27 | 28 | return true 29 | } 30 | 31 | func OptsFromParams(params string) Opts { 32 | opts := Opts{} 33 | for _, param := range strings.Split(params, ",") { 34 | var value string 35 | if i := strings.Index(param, "="); i >= 0 { 36 | value = param[i+1:] 37 | param = param[0:i] 38 | } 39 | switch param { 40 | case ParamPaths: 41 | switch value { 42 | case ParamPathsSourceRelative: 43 | opts.PathsSourceRelative = true 44 | } 45 | 46 | case ParamEmbedSwagger: 47 | opts.EmbedSwagger = isOptEnabled(value) 48 | 49 | case ParamChaincodeMethodServicePrefix: 50 | opts.ChaincodeMethodServicePrefix = isOptEnabled(value) 51 | 52 | case ParamServiceChaincodeResolver: 53 | opts.ServiceChaincodeResolver = isOptEnabled(value) 54 | } 55 | 56 | } 57 | 58 | return opts 59 | } 60 | -------------------------------------------------------------------------------- /gateway/protoc-gen-cc-gateway/generator/template_func.go: -------------------------------------------------------------------------------- 1 | package generator 2 | 3 | import ( 4 | "strings" 5 | "text/template" 6 | 7 | "github.com/golang/protobuf/protoc-gen-go/generator" 8 | "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor" 9 | ) 10 | 11 | var ( 12 | funcMap = template.FuncMap{ 13 | "goTypeName": goTypeName, 14 | "hasBindings": hasBindings, 15 | "hasGetBinding": hasGetBinding, 16 | "removeExtension": removeExtension, 17 | } 18 | ) 19 | 20 | func hasBindings(service *descriptor.Service) bool { 21 | for _, m := range service.Methods { 22 | if len(m.Bindings) > 0 { 23 | return true 24 | } 25 | } 26 | return false 27 | } 28 | 29 | func hasGetBinding(method *descriptor.Method) bool { 30 | for _, b := range method.Bindings { 31 | if b.HTTPMethod == "GET" { 32 | return true 33 | } 34 | } 35 | return false 36 | } 37 | 38 | func removeExtension(fileName string) string { 39 | startPos := 0 40 | slashPos := strings.LastIndex(fileName, `/`) 41 | 42 | if slashPos != -1 { 43 | startPos = slashPos + 1 44 | } 45 | return fileName[startPos:strings.LastIndex(fileName, `.`)] 46 | } 47 | 48 | func goTypeName(s string) string { 49 | toks := strings.Split(s, ".") 50 | i := 0 51 | if len(toks) > 1 { 52 | i = 1 53 | } 54 | for pos := range toks[i:] { 55 | toks[pos+i] = generator.CamelCase(toks[pos+i]) 56 | } 57 | return strings.Join(toks, ".") 58 | } 59 | -------------------------------------------------------------------------------- /gateway/protoc-gen-cc-gateway/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "flag" 5 | "io" 6 | "log" 7 | "os" 8 | 9 | "github.com/golang/protobuf/proto" 10 | plugin "github.com/golang/protobuf/protoc-gen-go/plugin" 11 | "github.com/grpc-ecosystem/grpc-gateway/codegenerator" 12 | "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway/descriptor" 13 | 14 | "github.com/s7techlab/cckit/gateway/protoc-gen-cc-gateway/generator" 15 | ) 16 | 17 | var ( 18 | file = flag.String("file", "-", "where to load data from") 19 | ) 20 | 21 | func main() { 22 | var err error 23 | flag.Parse() 24 | if err = flag.Lookup("logtostderr").Value.Set("true"); err != nil { 25 | log.Fatal(err) 26 | } 27 | 28 | reg := descriptor.NewRegistry() 29 | fs := os.Stdin 30 | if *file != "-" { 31 | if fs, err = os.Open(*file); err != nil { 32 | log.Fatal(err) 33 | } 34 | } 35 | req, err := codegenerator.ParseRequest(fs) 36 | if err != nil { 37 | log.Fatal(err) 38 | } 39 | 40 | if err = reg.Load(req); err != nil { 41 | emitError(err) 42 | return 43 | } 44 | 45 | g := generator.New(reg) 46 | g.Opts = generator.OptsFromParams(req.GetParameter()) 47 | 48 | var ( 49 | targets []*descriptor.File 50 | f *descriptor.File 51 | ) 52 | for _, target := range req.FileToGenerate { 53 | if f, err = reg.LookupFile(target); err != nil { 54 | log.Fatal(err) 55 | } 56 | targets = append(targets, f) 57 | } 58 | 59 | out, err := g.Generate(targets) 60 | if err != nil { 61 | emitError(err) 62 | return 63 | } 64 | emitFiles(os.Stdout, out) 65 | } 66 | 67 | func emitFiles(w io.Writer, out []*plugin.CodeGeneratorResponse_File) { 68 | emitResp(w, &plugin.CodeGeneratorResponse{File: out}) 69 | } 70 | 71 | func emitError(err error) { 72 | emitResp(os.Stdout, &plugin.CodeGeneratorResponse{Error: proto.String(err.Error())}) 73 | } 74 | 75 | func emitResp(out io.Writer, resp *plugin.CodeGeneratorResponse) { 76 | buf, err := proto.Marshal(resp) 77 | if err != nil { 78 | log.Fatal(err) 79 | } 80 | if _, err := out.Write(buf); err != nil { 81 | log.Fatal(err) 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /gateway/raw_json.go: -------------------------------------------------------------------------------- 1 | package gateway 2 | 3 | import ( 4 | "github.com/golang/protobuf/jsonpb" 5 | ) 6 | 7 | func (x *RawJson) MarshalJSON() ([]byte, error) { 8 | if x.GetValue() == nil { 9 | return []byte("null"), nil 10 | } 11 | 12 | return x.GetValue(), nil 13 | } 14 | 15 | func (x *RawJson) MarshalJSONPB(_ *jsonpb.Marshaler) ([]byte, error) { 16 | if x.GetValue() == nil { 17 | return []byte("null"), nil 18 | } 19 | 20 | return x.GetValue(), nil 21 | } 22 | -------------------------------------------------------------------------------- /gateway/service/service.go: -------------------------------------------------------------------------------- 1 | // Deprecated: use github.com/s7techlab/cckit/gateway package 2 | package service 3 | 4 | import ( 5 | "github.com/s7techlab/cckit/gateway" 6 | ) 7 | 8 | type ( 9 | // Deprecated: use gateway.ChaincodeServiceServer 10 | Chaincode = gateway.ChaincodeServiceServer 11 | // Deprecated: use gateway.ChaincodeServiceServer 12 | ChaincodeServer = gateway.ChaincodeServiceServer 13 | ) 14 | -------------------------------------------------------------------------------- /gateway/service_def.go: -------------------------------------------------------------------------------- 1 | package gateway 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/grpc-ecosystem/grpc-gateway/runtime" 7 | "google.golang.org/grpc" 8 | ) 9 | 10 | type ( 11 | RegisterHandlerFromEndpoint func(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) 12 | 13 | ServiceDef struct { 14 | name string 15 | swagger []byte 16 | Desc *grpc.ServiceDesc 17 | Service interface{} 18 | HandlerFromEndpointRegister RegisterHandlerFromEndpoint 19 | } 20 | 21 | Service interface { 22 | Name() string 23 | Swagger() []byte 24 | GRPCDesc() *grpc.ServiceDesc 25 | Impl() interface{} 26 | GRPCGatewayRegister() func(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) 27 | } 28 | ) 29 | 30 | func NewServiceDef(name string, swagger []byte, desc *grpc.ServiceDesc, service interface{}, registerHandler RegisterHandlerFromEndpoint) ServiceDef { 31 | return ServiceDef{ 32 | name: name, 33 | swagger: swagger, 34 | Desc: desc, 35 | Service: service, 36 | HandlerFromEndpointRegister: registerHandler, 37 | } 38 | } 39 | 40 | func (s ServiceDef) Name() string { 41 | return s.name 42 | } 43 | 44 | func (s ServiceDef) Swagger() []byte { 45 | return s.swagger 46 | } 47 | 48 | func (s ServiceDef) GRPCDesc() *grpc.ServiceDesc { 49 | return s.Desc 50 | } 51 | 52 | func (s ServiceDef) Impl() interface{} { 53 | return s.Service 54 | } 55 | 56 | func (s ServiceDef) GRPCGatewayRegister() func(ctx context.Context, mux *runtime.ServeMux, endpoint string, opts []grpc.DialOption) (err error) { 57 | return s.HandlerFromEndpointRegister 58 | } 59 | -------------------------------------------------------------------------------- /generators/README.md: -------------------------------------------------------------------------------- 1 | # Code generation 2 | 3 | ССKit use code generation with [Buf](https://docs.buf.build/) 4 | 5 | ## Using generators 6 | 7 | ### 1. Install Buf 8 | 9 | https://docs.buf.build/installation 10 | 11 | ### 2. Install generators 12 | 13 | With go package versions from CCKit [go.mod](../go.mod): 14 | 15 | ``` 16 | cd generators 17 | ./install.sh 18 | ``` 19 | 20 | ### 3. Run buf 21 | 22 | Generation command defined in [Makefile](../Makefile) 23 | ``` 24 | make proto 25 | ``` 26 | -------------------------------------------------------------------------------- /generators/deps.go: -------------------------------------------------------------------------------- 1 | //go:build tools 2 | // +build tools 3 | 4 | package generators 5 | 6 | import ( 7 | // chaincode gateway 8 | _ "github.com/s7techlab/cckit/gateway/protoc-gen-cc-gateway" 9 | // proto/grpc 10 | _ "github.com/golang/protobuf/protoc-gen-go" 11 | // json gateway 12 | _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-grpc-gateway" 13 | // docs 14 | _ "github.com/grpc-ecosystem/grpc-gateway/protoc-gen-swagger" 15 | // validation schema 16 | _ "github.com/mwitkow/go-proto-validators/protoc-gen-govalidators" 17 | // protoc docs 18 | _ "github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc" 19 | ) 20 | -------------------------------------------------------------------------------- /generators/install.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | GENERATOR_DIR=$PWD 4 | 5 | VER="21.1" 6 | ARCH="aarch" 7 | unameOut="$(uname -s)" 8 | case "${unameOut}" in 9 | Linux*) OS=linux;; 10 | Darwin*) OS=osx;; 11 | *) OS="xxx" 12 | esac 13 | 14 | echo ${GENERATOR_DIR} ${OS} $VER $ARCH 15 | 16 | if [ ! -d ${GENERATOR_DIR}/bin ]; then 17 | mkdir ${GENERATOR_DIR}/bin 18 | fi 19 | if [ ! -d ${GENERATOR_DIR}/dist/protoc ]; then 20 | mkdir -p ${GENERATOR_DIR}/dist/protoc 21 | fi 22 | 23 | if [ ! -f ${GENERATOR_DIR}/dist/protoc/protoc.zip ]; then 24 | echo "download protoc https://github.com/protocolbuffers/protobuf/releases/download/v$VER/protoc-$VER-$OS-${ARCH}_64.zip" 25 | curl https://github.com/protocolbuffers/protobuf/releases/download/v$VER/protoc-$VER-$OS-${ARCH}_64.zip -o $GENERATOR_DIR/dist/protoc/protoc.zip -L 26 | fi 27 | 28 | (./dist/protoc && unzip -o protoc.zip) 29 | cp -f ${GENERATOR_DIR}/dist/protoc/bin/protoc ${GENERATOR_DIR}/bin/protoc-cckit 30 | echo "installed "`${GENERATOR_DIR}/bin/protoc-cckit --version` 31 | 32 | 33 | pwd 34 | for genpkg in `go list -f '{{ join .Imports "\n" }}' deps.go` 35 | do 36 | echo "building $genpkg..." 37 | go build -mod=vendor -o ${GENERATOR_DIR}/bin/`basename $genpkg`-cckit -trimpath ${genpkg} 38 | echo "installed $genpkg" 39 | done 40 | 41 | -------------------------------------------------------------------------------- /go.mod: -------------------------------------------------------------------------------- 1 | module github.com/s7techlab/cckit 2 | 3 | go 1.16 4 | 5 | require ( 6 | github.com/gogo/protobuf v1.3.2 7 | github.com/golang/protobuf v1.5.2 8 | github.com/google/go-cmp v0.5.6 // indirect 9 | github.com/grpc-ecosystem/grpc-gateway v1.16.0 10 | github.com/hyperledger/fabric v1.4.0-rc1.0.20200930182727-344fda602252 11 | github.com/hyperledger/fabric-chaincode-go v0.0.0-20220131132609-1476cf1d3206 12 | github.com/hyperledger/fabric-protos-go v0.0.0-20201028172056-a3136dde2354 13 | github.com/mwitkow/go-proto-validators v0.3.2 14 | github.com/onsi/ginkgo v1.8.0 15 | github.com/onsi/gomega v1.9.0 16 | github.com/pelletier/go-toml v1.4.0 // indirect 17 | github.com/pseudomuto/protoc-gen-doc v1.3.1 18 | github.com/spf13/afero v1.2.2 // indirect 19 | github.com/spf13/viper v1.4.0 // indirect 20 | github.com/sykesm/zap-logfmt v0.0.3 // indirect 21 | go.uber.org/zap v1.14.1 22 | golang.org/x/net v0.0.0-20210503060351-7fd8e65b6420 // indirect 23 | golang.org/x/sys v0.0.0-20210917161153-d61c044b1678 // indirect 24 | google.golang.org/genproto v0.0.0-20210917145530-b395a37504d4 25 | google.golang.org/grpc v1.40.0 26 | google.golang.org/protobuf v1.27.1 27 | gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 // indirect 28 | gopkg.in/yaml.v2 v2.4.0 // indirect 29 | honnef.co/go/tools v0.0.1-2020.1.4 // indirect 30 | ) 31 | -------------------------------------------------------------------------------- /identity/cert_test.go: -------------------------------------------------------------------------------- 1 | package identity_test 2 | 3 | import ( 4 | "testing" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | 9 | "github.com/s7techlab/cckit/identity" 10 | "github.com/s7techlab/cckit/identity/testdata" 11 | ) 12 | 13 | func TestIdentity(t *testing.T) { 14 | RegisterFailHandler(Fail) 15 | RunSpecs(t, "Router suite") 16 | } 17 | 18 | var ( 19 | certA = testdata.Certificates[0].MustCertBytes() 20 | certB = testdata.Certificates[1].MustCertBytes() 21 | ) 22 | 23 | var _ = Describe(`Cert`, func() { 24 | 25 | BeforeSuite(func() { 26 | 27 | }) 28 | 29 | It(`Allow to compare certificate subject `, func() { 30 | 31 | certEq, err := identity.CertSubjEqual(certA, certB) 32 | 33 | Expect(certEq).To(BeFalse()) 34 | Expect(err).NotTo(HaveOccurred()) 35 | 36 | certEq, err = identity.CertSubjEqual(certA, certA) 37 | 38 | Expect(certEq).To(BeTrue()) 39 | Expect(err).NotTo(HaveOccurred()) 40 | }) 41 | 42 | }) 43 | -------------------------------------------------------------------------------- /identity/equal.go: -------------------------------------------------------------------------------- 1 | package identity 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | var ( 8 | // ErrMSPIdentifierNotEqual occurs when msp id did not match 9 | ErrMSPIdentifierNotEqual = errors.New(`msp identifier not equal`) 10 | 11 | ErrSubjectNotEqual = errors.New(`certificate subject not equal`) 12 | ErrIssuerNotEqual = errors.New(`certificate issuer not equal`) 13 | ) 14 | 15 | type ( 16 | SubjectIssuer interface { 17 | GetSubject() string 18 | GetIssuer() string 19 | } 20 | 21 | IdentityAttrs interface { 22 | SubjectIssuer 23 | GetMSPIdentifier() string 24 | } 25 | ) 26 | 27 | // Equal checks identity attributes (Msp id, cert subject and cert issuer) equality 28 | func Equal(identity1, identity2 IdentityAttrs) error { 29 | if identity1.GetMSPIdentifier() != identity2.GetMSPIdentifier() { 30 | return ErrMSPIdentifierNotEqual 31 | } 32 | 33 | return CertEqual(identity1, identity2) 34 | } 35 | 36 | // CertEqual checks certificate equality 37 | func CertEqual(cert1, cert2 SubjectIssuer) error { 38 | 39 | if cert1 == nil || cert2 == nil { 40 | return errors.New(`certificate empty`) 41 | } 42 | if cert1.GetSubject() != cert2.GetSubject() { 43 | return ErrSubjectNotEqual 44 | } 45 | 46 | if cert1.GetIssuer() != cert2.GetIssuer() { 47 | return ErrIssuerNotEqual 48 | } 49 | 50 | return nil 51 | } 52 | -------------------------------------------------------------------------------- /identity/error.go: -------------------------------------------------------------------------------- 1 | package identity 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | var ( 8 | ErrInvalidPEMStructure = errors.New(`invalid pem structure`) 9 | 10 | // ErrPemEncodedExpected pem format error 11 | ErrPemEncodedExpected = errors.New(`expecting a PEM-encoded X509 certificate; PEM block not found`) 12 | ) 13 | -------------------------------------------------------------------------------- /identity/identity.go: -------------------------------------------------------------------------------- 1 | package identity 2 | 3 | import ( 4 | "github.com/hyperledger/fabric/msp" 5 | ) 6 | 7 | // Identity interface for invoker (tx creator) and grants, stored in chain code state 8 | type Identity interface { 9 | msp.Identity 10 | 11 | // GetSubject string representation of X.509 cert subject 12 | GetSubject() string 13 | // GetIssuer string representation of X.509 cert issuer 14 | GetIssuer() string 15 | 16 | // GetPublicKey *rsa.PublicKey or *dsa.PublicKey or *ecdsa.PublicKey: 17 | GetPublicKey() interface{} 18 | GetPEM() []byte 19 | } 20 | -------------------------------------------------------------------------------- /identity/key.go: -------------------------------------------------------------------------------- 1 | package identity 2 | 3 | import ( 4 | "crypto/ecdsa" 5 | "crypto/x509" 6 | "encoding/pem" 7 | "fmt" 8 | ) 9 | 10 | func PrivateKey(keyBytes []byte) (*ecdsa.PrivateKey, error) { 11 | keyPEM, _ := pem.Decode(keyBytes) 12 | if keyPEM == nil { 13 | return nil, ErrInvalidPEMStructure 14 | } 15 | 16 | key, err := x509.ParsePKCS8PrivateKey(keyPEM.Bytes) 17 | if err != nil { 18 | return nil, fmt.Errorf(`parse private key: %w`, err) 19 | } 20 | return key.(*ecdsa.PrivateKey), nil 21 | } 22 | -------------------------------------------------------------------------------- /identity/schema.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | option go_package = "github.com/s7techlab/cckit/identity"; 4 | package s7techlab.cckit.identity; 5 | 6 | import "google/protobuf/timestamp.proto"; 7 | 8 | message Identity { 9 | string msp_id = 1; // MSP identifier 10 | bytes pem = 2; // certificate 11 | } 12 | 13 | message ActionInfo { 14 | google.protobuf.Timestamp at = 1; // time of action 15 | Identity by = 2; // identity, initiates action 16 | } -------------------------------------------------------------------------------- /identity/testdata/s7techlab.key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDBSoxUZoyY3jk1GD430 3 | 8tIWWLiEw8dGL9SnfkG7AGQNHsYLz8hbamYtN9TPVg7JbemhZANiAASAPNEhxmCz 4 | F7w+8rmE+iKHiTp+qinNnby69unp3eCpRD2XaI5zfPDiVZbPFm3uFsHskEGNwJyh 5 | G84Vc74/Nw5jrIDU6p83i1yXCV2JafT5oCBsSLNw1vR3ddXW4vK7fJ8= 6 | -----END PRIVATE KEY----- 7 | -------------------------------------------------------------------------------- /identity/testdata/s7techlab.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICTDCCAdECCQDXg5wOXASntDAKBggqhkjOPQQDAjCBjjELMAkGA1UEBhMCUlUx 3 | DzANBgNVBAgMBk1vc2NvdzEPMA0GA1UEBwwGTW9zY293MRIwEAYDVQQKDAlTN1Rl 4 | Y2hsYWIxEjAQBgNVBAsMCVM3VGVjaGxhYjESMBAGA1UEAwwJUzdUZWNobGFiMSEw 5 | HwYJKoZIhvcNAQkBFhJpbmZvQHRlY2hsYWIuczcucnUwHhcNMTgxMTEzMTMxMTI3 6 | WhcNMjAxMTEyMTMxMTI3WjCBjjELMAkGA1UEBhMCUlUxDzANBgNVBAgMBk1vc2Nv 7 | dzEPMA0GA1UEBwwGTW9zY293MRIwEAYDVQQKDAlTN1RlY2hsYWIxEjAQBgNVBAsM 8 | CVM3VGVjaGxhYjESMBAGA1UEAwwJUzdUZWNobGFiMSEwHwYJKoZIhvcNAQkBFhJp 9 | bmZvQHRlY2hsYWIuczcucnUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAASAPNEhxmCz 10 | F7w+8rmE+iKHiTp+qinNnby69unp3eCpRD2XaI5zfPDiVZbPFm3uFsHskEGNwJyh 11 | G84Vc74/Nw5jrIDU6p83i1yXCV2JafT5oCBsSLNw1vR3ddXW4vK7fJ8wCgYIKoZI 12 | zj0EAwIDaQAwZgIxAMP56SfE7D8sjv5H4rU5CnXeJLoCmcDo20OQcMBbIoYNHiet 13 | ReJZlqytK5WoPm8wHQIxANdPnajvejR+ZE7MMe+pd18uwGZ8hh9Hp6C9ugoipv0q 14 | Oo4vB+J8+jEuRjSsXfMzPQ== 15 | -----END CERTIFICATE----- 16 | -------------------------------------------------------------------------------- /identity/testdata/some-person.key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDC1lcEXgTOA8oUWxUrw 3 | CR5CuKzpidiOSfbcjDbhxmu8nqIMSZW5Pe2E8EkM6hBFOQahZANiAAR5Rbf3It1g 4 | UrekPyjpWDW7id0FyLStTYrDyQhnJJUidID9VAdelLnUhVPwA2upQQB/fDX76BLw 5 | bH/CZKIQk+dKqOaUl+cGJq0MSxN84VfWJOcyPAjHM6t/3+5fgg3O468= 6 | -----END PRIVATE KEY----- 7 | -------------------------------------------------------------------------------- /identity/testdata/some-person.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICRDCCAcsCCQDNyNGKuPK+3zAKBggqhkjOPQQDAjCBizELMAkGA1UEBhMCUlUx 3 | DzANBgNVBAgMBk1vc2NvdzEPMA0GA1UEBwwGTW9zY293MREwDwYDVQQKDAhTb21l 4 | IG9yZzESMBAGA1UECwwJc29tZSB1bml0MRQwEgYDVQQDDAtTb21lIFBlcnNvbjEd 5 | MBsGCSqGSIb3DQEJARYOc29tZUBwZXJzb24ubWUwHhcNMTgxMTIyMTE1MTExWhcN 6 | MjAxMTIxMTE1MTExWjCBizELMAkGA1UEBhMCUlUxDzANBgNVBAgMBk1vc2NvdzEP 7 | MA0GA1UEBwwGTW9zY293MREwDwYDVQQKDAhTb21lIG9yZzESMBAGA1UECwwJc29t 8 | ZSB1bml0MRQwEgYDVQQDDAtTb21lIFBlcnNvbjEdMBsGCSqGSIb3DQEJARYOc29t 9 | ZUBwZXJzb24ubWUwdjAQBgcqhkjOPQIBBgUrgQQAIgNiAAR5Rbf3It1gUrekPyjp 10 | WDW7id0FyLStTYrDyQhnJJUidID9VAdelLnUhVPwA2upQQB/fDX76BLwbH/CZKIQ 11 | k+dKqOaUl+cGJq0MSxN84VfWJOcyPAjHM6t/3+5fgg3O468wCgYIKoZIzj0EAwID 12 | ZwAwZAIwOlDOrh7oU/RoezCVUaKOSx4vUjz5g9X7Dgixh+XyK1/4OtrU2JJLY/lP 13 | tHxcI4AHAjBNCOsSB8LRfDzv79mKgrMcNdE8lmTj1Mamii6+x0Ul9jAkyc8jMoB7 14 | 36WqzNmi0Ag= 15 | -----END CERTIFICATE----- 16 | -------------------------------------------------------------------------------- /identity/testdata/victor-nosov.key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDDk/+BCmzpnpPPNJjkP 3 | r0+E7kMqWgviaGAb7vgyQ7T1QUSH0WSebtqjaJA7/QR+t7ShZANiAAQ8NpxcItc9 4 | 8tjx/CTQEjiB0nqRq+OM1qSt5iHAZHPPvAGbdOM0qbOqud/65zZjpE3dh1KRTMUb 5 | R2fv6sMqMu+7i6ylap1arVyvfmZsLnFen3ltTRILW7udUXWaXE/FylM= 6 | -----END PRIVATE KEY----- 7 | -------------------------------------------------------------------------------- /identity/testdata/victor-nosov.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIICUzCCAdkCCQDq7Z/TOmD8mDAKBggqhkjOPQQDAjCBkjELMAkGA1UEBhMCUlUx 3 | DzANBgNVBAgMBk1vc2NvdzEPMA0GA1UEBwwGTW9zY293MRIwEAYDVQQKDAlTN1Rl 4 | Y2hsYWIxGDAWBgNVBAsMD0Jsb2NrY2hhaW4gZGVwdDEVMBMGA1UEAwwMVmljdG9y 5 | IE5vc292MRwwGgYJKoZIhvcNAQkBFg12Lm5vc292QHM3LnJ1MB4XDTE4MTExMzEz 6 | MDYzOVoXDTIwMTExMjEzMDYzOVowgZIxCzAJBgNVBAYTAlJVMQ8wDQYDVQQIDAZN 7 | b3Njb3cxDzANBgNVBAcMBk1vc2NvdzESMBAGA1UECgwJUzdUZWNobGFiMRgwFgYD 8 | VQQLDA9CbG9ja2NoYWluIGRlcHQxFTATBgNVBAMMDFZpY3RvciBOb3NvdjEcMBoG 9 | CSqGSIb3DQEJARYNdi5ub3NvdkBzNy5ydTB2MBAGByqGSM49AgEGBSuBBAAiA2IA 10 | BDw2nFwi1z3y2PH8JNASOIHSepGr44zWpK3mIcBkc8+8AZt04zSps6q53/rnNmOk 11 | Td2HUpFMxRtHZ+/qwyoy77uLrKVqnVqtXK9+ZmwucV6feW1NEgtbu51RdZpcT8XK 12 | UzAKBggqhkjOPQQDAgNoADBlAjEA6bZ3t84z9ZDOuxiQWu+LHIPz9mc1MNKEm794 13 | FGU/O5wijYpGLCb1zAswYzEEVKk+AjB3mskHYlOmZIPTA7N8lQTcrT65BIhFRCxm 14 | WdtEEGYJlPcgHp0VA96sT/r9JuW9nYA= 15 | -----END CERTIFICATE----- 16 | -------------------------------------------------------------------------------- /response/response.go: -------------------------------------------------------------------------------- 1 | package response 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/s7techlab/cckit/convert" 8 | 9 | "github.com/hyperledger/fabric-chaincode-go/shim" 10 | "github.com/hyperledger/fabric-protos-go/peer" 11 | ) 12 | 13 | // Error returns shim.Error 14 | func Error(err interface{}) peer.Response { 15 | return shim.Error(fmt.Sprintf("%s", err)) 16 | } 17 | 18 | // Success returns shim.Success with serialized json if necessary 19 | func Success(data interface{}) peer.Response { 20 | bb, err := convert.ToBytes(data) 21 | if err != nil { 22 | return shim.Success(nil) 23 | } 24 | return shim.Success(bb) 25 | } 26 | 27 | // Create returns peer.Response (Success or Error) depending on value of err 28 | // if err is (bool) false or is error interface - returns shim.Error 29 | func Create(data interface{}, err interface{}) peer.Response { 30 | var errObj error 31 | 32 | switch e := err.(type) { 33 | case nil: 34 | errObj = nil 35 | case bool: 36 | if !e { 37 | errObj = errors.New(`boolean error: false`) 38 | } 39 | case string: 40 | if e != `` { 41 | errObj = errors.New(e) 42 | } 43 | case error: 44 | errObj = e 45 | default: 46 | panic(fmt.Sprintf(`unknowm error type %s`, err)) 47 | 48 | } 49 | 50 | if errObj != nil { 51 | return Error(errObj) 52 | } 53 | return Success(data) 54 | } 55 | 56 | // Transformer type transforms data 57 | type Transformer struct { 58 | data interface{} 59 | err error 60 | } 61 | 62 | // With func transformer 63 | func (t Transformer) With(transformer func(interface{}) interface{}) peer.Response { 64 | return Create(transformer(t.data), t.err) 65 | } 66 | 67 | // Transform creates Transformer struct 68 | func Transform(data interface{}, err error) *Transformer { 69 | return &Transformer{data, err} 70 | } 71 | -------------------------------------------------------------------------------- /router/chaincode.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/hyperledger/fabric-chaincode-go/shim" 5 | "github.com/hyperledger/fabric-protos-go/peer" 6 | ) 7 | 8 | // Chaincode default chaincode implementation with router 9 | type Chaincode struct { 10 | router *Group 11 | } 12 | 13 | // NewChaincode new default chaincode implementation 14 | func NewChaincode(r *Group) *Chaincode { 15 | return &Chaincode{r} 16 | } 17 | 18 | // Init initializes chain code - sets chaincode "owner" 19 | //======== Base methods ==================================== 20 | func (cc *Chaincode) Init(stub shim.ChaincodeStubInterface) peer.Response { 21 | // delegate handling to router 22 | return cc.router.HandleInit(stub) 23 | } 24 | 25 | // Invoke - entry point for chain code invocations 26 | func (cc *Chaincode) Invoke(stub shim.ChaincodeStubInterface) peer.Response { 27 | // delegate handling to router 28 | return cc.router.Handle(stub) 29 | } 30 | -------------------------------------------------------------------------------- /router/errors.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import "errors" 4 | 5 | var ( 6 | // ErrEmptyArgs occurs when trying to invoke chaincode method with empty args 7 | ErrEmptyArgs = errors.New(`empty args`) 8 | 9 | // ErrMethodNotFound occurs when trying to invoke non-existent chaincode method 10 | ErrMethodNotFound = errors.New(`chaincode method not found`) 11 | 12 | // ErrArgsNumMismatch occurs when the number of declared and the number of arguments passed does not match 13 | ErrArgsNumMismatch = errors.New(`chaincode method args count mismatch`) 14 | 15 | // ErrHandlerError error in handler 16 | ErrHandlerError = errors.New(`router handler error`) 17 | ) 18 | -------------------------------------------------------------------------------- /router/handler.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | func EmptyContextHandler(c Context) (interface{}, error) { 4 | return nil, nil 5 | } 6 | -------------------------------------------------------------------------------- /router/param/defparam/type.go: -------------------------------------------------------------------------------- 1 | package defparam 2 | 3 | import ( 4 | "github.com/s7techlab/cckit/router" 5 | "github.com/s7techlab/cckit/router/param" 6 | ) 7 | 8 | func Proto(target interface{}, argPoss ...int) router.MiddlewareFunc { 9 | return param.Proto(router.DefaultParam, target, argPoss...) 10 | } 11 | 12 | func String(argPoss ...int) router.MiddlewareFunc { 13 | return param.String(router.DefaultParam, argPoss...) 14 | } 15 | -------------------------------------------------------------------------------- /router/param/middleware.go: -------------------------------------------------------------------------------- 1 | package param 2 | 3 | import ( 4 | "github.com/s7techlab/cckit/router" 5 | ) 6 | 7 | // StrictKnown allows passing arguments to chaincode func only if parameters are defined in router 8 | func StrictKnown(next router.HandlerFunc, _ ...int) router.HandlerFunc { 9 | return func(c router.Context) (interface{}, error) { 10 | return next(c) 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /router/param/type.go: -------------------------------------------------------------------------------- 1 | package param 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/golang/protobuf/proto" 8 | 9 | "github.com/s7techlab/cckit/convert" 10 | "github.com/s7techlab/cckit/router" 11 | ) 12 | 13 | var ( 14 | ErrProtoExpected = errors.New(`protobuf expected`) 15 | ) 16 | 17 | // String creates middleware for converting to string chaincode method parameter 18 | func String(name string, argPoss ...int) router.MiddlewareFunc { 19 | return Param(name, convert.TypeString, argPoss...) 20 | } 21 | 22 | func Strings(name string, argPoss ...int) router.MiddlewareFunc { 23 | return Param(name, []string{}, argPoss...) 24 | } 25 | 26 | // Int creates middleware for converting to integer chaincode method parameter 27 | func Int(name string, argPoss ...int) router.MiddlewareFunc { 28 | return Param(name, convert.TypeInt, argPoss...) 29 | } 30 | 31 | // Bool creates middleware for converting to bool chaincode method parameter 32 | func Bool(name string, argPoss ...int) router.MiddlewareFunc { 33 | return Param(name, convert.TypeBool, argPoss...) 34 | } 35 | 36 | // Struct creates middleware for converting to struct chaincode method parameter 37 | func Struct(name string, target interface{}, argPoss ...int) router.MiddlewareFunc { 38 | return Param(name, target, argPoss...) 39 | } 40 | 41 | // Bytes creates middleware for converting to []byte chaincode method parameter 42 | func Bytes(name string, argPoss ...int) router.MiddlewareFunc { 43 | return Param(name, []byte{}, argPoss...) 44 | } 45 | 46 | // Proto creates middleware for converting to protobuf chaincode method parameter 47 | func Proto(name string, target interface{}, argPoss ...int) router.MiddlewareFunc { 48 | if _, ok := target.(proto.Message); !ok { 49 | TypeErrorMiddleware(name, ErrProtoExpected) 50 | } 51 | return Param(name, target, argPoss...) 52 | } 53 | 54 | func TypeErrorMiddleware(name string, err error) router.MiddlewareFunc { 55 | return func(next router.HandlerFunc, pos ...int) router.HandlerFunc { 56 | return func(c router.Context) (interface{}, error) { 57 | return nil, fmt.Errorf(`%s: %s`, err, name) 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /router/request.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | ) 7 | 8 | var ErrInvalidRequest = errors.New(`invalid request`) 9 | 10 | type ( 11 | Validator interface { 12 | Validate() error 13 | } 14 | ) 15 | 16 | // ValidateRequest use Validator interface and create error, allow to use error.Is(ErrInvalidRequest) 17 | func ValidateRequest(request Validator) error { 18 | if err := request.Validate(); err != nil { 19 | return fmt.Errorf(`%s: %w`, err.Error(), ErrInvalidRequest) 20 | } 21 | 22 | return nil 23 | } 24 | -------------------------------------------------------------------------------- /router/response.go: -------------------------------------------------------------------------------- 1 | package router 2 | 3 | import ( 4 | "github.com/hyperledger/fabric-chaincode-go/shim" 5 | "github.com/hyperledger/fabric-protos-go/peer" 6 | "go.uber.org/zap" 7 | 8 | "github.com/s7techlab/cckit/response" 9 | ) 10 | 11 | // Response chaincode interface 12 | type Response interface { 13 | Error(err interface{}) peer.Response 14 | Success(data interface{}) peer.Response 15 | Create(data interface{}, err interface{}) peer.Response 16 | } 17 | 18 | // ContextResponse implementation 19 | type ContextResponse struct { 20 | context Context 21 | } 22 | 23 | // Error response 24 | func (c ContextResponse) Error(err interface{}) peer.Response { 25 | res := response.Error(err) 26 | c.context.Logger().Error(`router handler error`, zap.String(`path`, c.context.Path()), zap.String(`message`, res.Message)) 27 | return res 28 | } 29 | 30 | // Success response 31 | func (c ContextResponse) Success(data interface{}) peer.Response { 32 | res := response.Success(data) 33 | c.context.Logger().Debug(`route handle success`, zap.String(`path`, c.context.Path()), zap.ByteString(`data`, res.Payload)) 34 | return res 35 | } 36 | 37 | // Create returns error response if err != nil 38 | func (c ContextResponse) Create(data interface{}, err interface{}) peer.Response { 39 | result := response.Create(data, err) 40 | 41 | if result.Status == shim.ERROR { 42 | return c.Error(result.Message) 43 | } 44 | return c.Success(result.Payload) 45 | } 46 | -------------------------------------------------------------------------------- /router/router_test.go: -------------------------------------------------------------------------------- 1 | package router_test 2 | 3 | import ( 4 | "testing" 5 | 6 | "github.com/hyperledger/fabric-chaincode-go/shim" 7 | "github.com/hyperledger/fabric-protos-go/peer" 8 | . "github.com/onsi/ginkgo" 9 | . "github.com/onsi/gomega" 10 | 11 | "github.com/s7techlab/cckit/router" 12 | testcc "github.com/s7techlab/cckit/testing" 13 | ) 14 | 15 | func TestRouter(t *testing.T) { 16 | RegisterFailHandler(Fail) 17 | RunSpecs(t, "Router suite") 18 | } 19 | 20 | func New() *router.Chaincode { 21 | r := router.New(`router`). 22 | Init(router.EmptyContextHandler). 23 | Invoke(`empty`, func(c router.Context) (interface{}, error) { 24 | return nil, nil 25 | }) 26 | 27 | return router.NewChaincode(r) 28 | } 29 | 30 | var cc *testcc.MockStub 31 | 32 | var _ = Describe(`Router`, func() { 33 | 34 | BeforeSuite(func() { 35 | cc = testcc.NewMockStub(`Router`, New()) 36 | }) 37 | 38 | It(`Allow empty response`, func() { 39 | 40 | Expect(cc.Invoke(`empty`)).To(BeEquivalentTo(peer.Response{ 41 | Status: shim.OK, 42 | Payload: nil, 43 | Message: ``, 44 | })) 45 | }) 46 | 47 | }) 48 | -------------------------------------------------------------------------------- /sdk/all.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | // SDK interface for deal with Hyperledger Fabric SDK 4 | // client from github.com/s7techlab/hlf-sdk-go implements this interface 5 | type SDK interface { 6 | Invoker 7 | EventDelivery 8 | } 9 | -------------------------------------------------------------------------------- /sdk/event.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/golang/protobuf/ptypes/timestamp" 7 | "github.com/hyperledger/fabric-protos-go/peer" 8 | "github.com/hyperledger/fabric/msp" 9 | ) 10 | 11 | type ( 12 | EventDelivery interface { 13 | 14 | // Events returns peer chaincode events stream 15 | // blockRange: 16 | // * [] -> api.SeekNewest()}, 17 | // * [0] - from oldest block to maxBlock 18 | // * [{blockFrom}] - from specified block to maxBlock 19 | // * [-{blockFrom}] - specified blocks back from channel height 20 | // * [0,0] from the oldest block to current channel height 21 | // * [-{blockFrom},0] - specified blocks back from channel height to current channel height 22 | // * [-{blockFrom}, -{blockTo}} -{blockFrom} blocks back from channel height to -{blockTo} block from channel height 23 | // * [-{blockFrom}, {blockTo}} -{blockFrom} blocks back from channel height to block {blockTo} 24 | Events( 25 | ctx context.Context, 26 | channelName string, 27 | ccName string, 28 | identity msp.SigningIdentity, 29 | blockRange ...int64, 30 | ) (events chan interface { 31 | Event() *peer.ChaincodeEvent 32 | Block() uint64 33 | TxTimestamp() *timestamp.Timestamp 34 | }, closer func() error, err error) 35 | } 36 | ) 37 | -------------------------------------------------------------------------------- /sdk/invoker.go: -------------------------------------------------------------------------------- 1 | package sdk 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/hyperledger/fabric-protos-go/peer" 7 | "github.com/hyperledger/fabric/msp" 8 | ) 9 | 10 | type Invoker interface { 11 | CurrentIdentity() msp.SigningIdentity 12 | 13 | Query( 14 | ctx context.Context, 15 | chanName string, 16 | ccName string, 17 | args [][]byte, 18 | identity msp.SigningIdentity, 19 | transient map[string][]byte, 20 | ) (*peer.Response, error) 21 | 22 | Invoke( 23 | ctx context.Context, 24 | chanName string, 25 | ccName string, 26 | args [][]byte, 27 | identity msp.SigningIdentity, 28 | transient map[string][]byte, 29 | txWaiterType string, 30 | ) (res *peer.Response, chaincodeTx string, err error) 31 | } 32 | -------------------------------------------------------------------------------- /state/buf.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | lint: 3 | use: 4 | - BASIC 5 | - FILE_LOWER_SNAKE_CASE 6 | - ENUM_VALUE_PREFIX 7 | except: 8 | - PACKAGE_DIRECTORY_MATCH 9 | 10 | breaking: 11 | use: 12 | - FILE -------------------------------------------------------------------------------- /state/errors.go: -------------------------------------------------------------------------------- 1 | package state 2 | 3 | import ( 4 | "errors" 5 | ) 6 | 7 | var ( 8 | // ErrUnableToCreateStateKey can occur while creating composite key for entry 9 | ErrUnableToCreateStateKey = errors.New(`unable to create state key`) 10 | 11 | // ErrUnableToCreateEventName can occur while creating composite key for entry 12 | ErrUnableToCreateEventName = errors.New(`unable to create event name`) 13 | 14 | // ErrKeyAlreadyExists can occur when trying to insert entry with existing key 15 | ErrKeyAlreadyExists = errors.New(`state key already exists`) 16 | 17 | // ErrKeyNotFound key not found in chaincode state 18 | ErrKeyNotFound = errors.New(`state entry not found`) 19 | 20 | // ErrAllowOnlyOneValue can occur when trying to call Insert or Put with more than 2 arguments 21 | ErrAllowOnlyOneValue = errors.New(`allow only one value`) 22 | 23 | // ErrStateEntryNotSupportKeyerInterface can occur when trying to Insert or Put struct 24 | // providing key and struct without Keyer interface support 25 | ErrStateEntryNotSupportKeyerInterface = errors.New(`state entry not support keyer interface`) 26 | 27 | ErrEventEntryNotSupportNamerInterface = errors.New(`event entry not support name interface`) 28 | 29 | // ErrKeyPartsLength can occur when trying to create key consisting of zero parts 30 | ErrKeyPartsLength = errors.New(`key parts length must be greater than zero`) 31 | ) 32 | -------------------------------------------------------------------------------- /state/invoke.go: -------------------------------------------------------------------------------- 1 | package state 2 | 3 | import ( 4 | "errors" 5 | 6 | "github.com/hyperledger/fabric-chaincode-go/shim" 7 | 8 | "github.com/s7techlab/cckit/convert" 9 | ) 10 | 11 | var ( 12 | ErrEmptyChaincodeResponsePayload = errors.New(`empty chaincode response payload`) 13 | ) 14 | 15 | // InvokeChaincode locally calls the specified chaincode and converts result into target data type 16 | func InvokeChaincode( 17 | stub shim.ChaincodeStubInterface, chaincodeName string, args []interface{}, 18 | channel string, target interface{}) (interface{}, error) { 19 | 20 | convArgs, err := convert.ArgsToBytes(args...) 21 | if err != nil { 22 | return nil, err 23 | } 24 | 25 | response := stub.InvokeChaincode(chaincodeName, convArgs, channel) 26 | if response.Status != shim.OK { 27 | return nil, errors.New(response.Message) 28 | } 29 | 30 | if len(response.Payload) == 0 { 31 | return nil, ErrEmptyChaincodeResponsePayload 32 | } 33 | 34 | return convert.FromBytes(response.Payload, target) 35 | } 36 | -------------------------------------------------------------------------------- /state/mapping/README.md: -------------------------------------------------------------------------------- 1 | # State mappings 2 | 3 | 4 | ## Primary key 5 | 6 | ### Primary key based on one or multiple fields 7 | 8 | ### Entity (schema) as primary keyer 9 | 10 | 11 | 12 | ## Additional indexes (keys) 13 | 14 | ### Unique key 15 | 16 | ### Unique Key with multiple values -------------------------------------------------------------------------------- /state/mapping/errors.go: -------------------------------------------------------------------------------- 1 | package mapping 2 | 3 | import "errors" 4 | 5 | var ( 6 | // ErrEntryTypeNotSupported entry type has no appropriate mapper type 7 | ErrEntryTypeNotSupported = errors.New(`entry type not supported for mapping`) 8 | 9 | // ErrStateMappingNotFound occurs when mapping for state entry is not defined 10 | ErrStateMappingNotFound = errors.New(`state mapping not found`) 11 | 12 | // ErrEventMappingNotFound occurs when mapping for event is not defined 13 | ErrEventMappingNotFound = errors.New(`event mapping not found`) 14 | 15 | // ErrFieldTypeNotSupportedForKeyExtraction key cannot extract from field 16 | ErrFieldTypeNotSupportedForKeyExtraction = errors.New(`field type not supported for key extraction`) 17 | 18 | ErrMappingUniqKeyExists = errors.New(`mapping uniq key exists`) 19 | 20 | ErrFieldNotExists = errors.New(`field is not exists`) 21 | ErrPrimaryKeyerNotDefined = errors.New(`primary keyer is not defined`) 22 | 23 | // ErrIndexAlreadyExists occurs when trying to add index to mapping with existent name 24 | ErrIndexAlreadyExists = errors.New(`index already exists`) 25 | 26 | // ErrIndexReferenceNotFound occurs when trying to find entry by index 27 | ErrIndexReferenceNotFound = errors.New(`index reference not found`) 28 | ) 29 | -------------------------------------------------------------------------------- /state/mapping/event.go: -------------------------------------------------------------------------------- 1 | package mapping 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/hyperledger/fabric-chaincode-go/shim" 7 | 8 | "github.com/s7techlab/cckit/state" 9 | ) 10 | 11 | type ( 12 | EventImpl struct { 13 | event state.Event 14 | mappings EventMappings 15 | } 16 | ) 17 | 18 | func NewEvent(stub shim.ChaincodeStubInterface, mappings EventMappings) *EventImpl { 19 | return &EventImpl{ 20 | event: state.NewEvent(stub), 21 | mappings: mappings, 22 | } 23 | } 24 | 25 | func WrapEvent(event state.Event, mappings EventMappings) *EventImpl { 26 | return &EventImpl{ 27 | event: event, 28 | mappings: mappings, 29 | } 30 | } 31 | 32 | func (e *EventImpl) mapIfMappingExists(entry interface{}) (mapped interface{}, exists bool, err error) { 33 | if !e.mappings.Exists(entry) { 34 | return entry, false, nil 35 | } 36 | mapped, err = e.mappings.Map(entry) 37 | return mapped, true, err 38 | } 39 | 40 | func (e *EventImpl) Set(entry interface{}, value ...interface{}) error { 41 | mapped, exists, err := e.mapIfMappingExists(entry) 42 | if err != nil { 43 | return err 44 | } 45 | 46 | if !exists && len(value) == 0 { 47 | return fmt.Errorf(`%s: %s`, ErrEventMappingNotFound, mapKey(entry)) 48 | } 49 | return e.event.Set(mapped, value...) 50 | } 51 | 52 | func (e *EventImpl) UseNameTransformer(nt state.StringTransformer) state.Event { 53 | return e.event.UseNameTransformer(nt) 54 | } 55 | 56 | func (e *EventImpl) UseSetTransformer(tb state.ToBytesTransformer) state.Event { 57 | return e.event.UseSetTransformer(tb) 58 | } 59 | -------------------------------------------------------------------------------- /state/mapping/event_instance.go: -------------------------------------------------------------------------------- 1 | package mapping 2 | 3 | import ( 4 | "github.com/s7techlab/cckit/state" 5 | ) 6 | 7 | type ( 8 | EventInstance struct { 9 | instance interface{} 10 | eventMapper EventMapper 11 | serializer state.Serializer 12 | } 13 | ) 14 | 15 | func NewEventInstance(instance interface{}, eventMapper EventMapper, serializer state.Serializer) (*EventInstance, error) { 16 | return &EventInstance{ 17 | instance: instance, 18 | eventMapper: eventMapper, 19 | serializer: serializer, 20 | }, nil 21 | } 22 | 23 | func (ei EventInstance) Name() (string, error) { 24 | return ei.eventMapper.Name(ei.instance) 25 | } 26 | 27 | func (ei EventInstance) ToBytes() ([]byte, error) { 28 | return ei.serializer.ToBytes(ei.instance) 29 | } 30 | -------------------------------------------------------------------------------- /state/mapping/mapper.go: -------------------------------------------------------------------------------- 1 | package mapping 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/s7techlab/cckit/state" 8 | ) 9 | 10 | var ( 11 | ErrEventPayloadEmpty = errors.New(`event payload empty`) 12 | ) 13 | 14 | type ( 15 | EntryMapper struct { 16 | Commands Commands 17 | Event *Event 18 | } 19 | Commands []Command 20 | 21 | Command interface { 22 | Execute(state.State) error 23 | fmt.Stringer 24 | } 25 | 26 | Query interface { 27 | Query(state.State) error 28 | } 29 | 30 | CommandInsert struct { 31 | Entry interface{} 32 | } 33 | 34 | CommandPut struct { 35 | Entry interface{} 36 | } 37 | ) 38 | 39 | func NewEntryMapper() *EntryMapper { 40 | return &EntryMapper{ 41 | Event: &Event{}, 42 | } 43 | } 44 | 45 | func (em *EntryMapper) Apply(state state.State, event state.Event) error { 46 | for _, cmd := range em.Commands { 47 | if err := cmd.Execute(state); err != nil { 48 | return fmt.Errorf(`execute command=%s: %w`, cmd, err) 49 | } 50 | } 51 | 52 | if em.Event != nil { 53 | if em.Event.Name != `` && em.Event.Payload == nil { 54 | return ErrEventPayloadEmpty 55 | } 56 | 57 | if em.Event.Payload != nil { 58 | if em.Event.Name != `` { 59 | return event.Set(em.Event.Name, em.Event.Payload) 60 | } 61 | return event.Set(em.Event.Payload) 62 | } 63 | } 64 | 65 | return nil 66 | } 67 | 68 | func (cc *Commands) Insert(entry interface{}) *Commands { 69 | *cc = append(*cc, &CommandInsert{ 70 | Entry: entry, 71 | }) 72 | return cc 73 | } 74 | 75 | func (cc *Commands) Put(entry interface{}) *Commands { 76 | *cc = append(*cc, &CommandPut{ 77 | Entry: entry, 78 | }) 79 | return cc 80 | } 81 | 82 | func (ci *CommandInsert) Execute(state state.State) error { 83 | return state.Insert(ci.Entry) 84 | } 85 | 86 | func (ci *CommandInsert) String() string { 87 | return fmt.Sprintf(`insert<%T>`, ci.Entry) 88 | } 89 | 90 | func (ci *CommandPut) Execute(state state.State) error { 91 | return state.Put(ci.Entry) 92 | } 93 | 94 | func (ci *CommandPut) String() string { 95 | return fmt.Sprintf(`put<%T>`, ci.Entry) 96 | } 97 | -------------------------------------------------------------------------------- /state/mapping/middleware.go: -------------------------------------------------------------------------------- 1 | package mapping 2 | 3 | import ( 4 | "github.com/s7techlab/cckit/router" 5 | ) 6 | 7 | func MapStates(stateMappings StateMappings) router.MiddlewareFunc { 8 | return func(next router.HandlerFunc, pos ...int) router.HandlerFunc { 9 | return func(c router.Context) (interface{}, error) { 10 | c.UseState(WrapState(c.State(), stateMappings)) 11 | return next(c) 12 | } 13 | } 14 | } 15 | 16 | func MapEvents(eventMappings EventMappings) router.MiddlewareFunc { 17 | return func(next router.HandlerFunc, pos ...int) router.HandlerFunc { 18 | return func(c router.Context) (interface{}, error) { 19 | c.UseEvent(WrapEvent(c.Event(), eventMappings)) 20 | return next(c) 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /state/mapping/state_instance.go: -------------------------------------------------------------------------------- 1 | package mapping 2 | 3 | import ( 4 | "github.com/s7techlab/cckit/state" 5 | ) 6 | 7 | type ( 8 | StateInstance struct { 9 | // instance can be instanced itself or key for instance 10 | // key can be proto or Key ( []string ) 11 | instance interface{} 12 | stateMapper StateMapper 13 | serializer state.Serializer 14 | } 15 | ) 16 | 17 | func NewStateInstance(instance interface{}, stateMapper StateMapper, serializer state.Serializer) *StateInstance { 18 | return &StateInstance{ 19 | instance: instance, 20 | stateMapper: stateMapper, 21 | serializer: serializer, 22 | } 23 | } 24 | 25 | func (si *StateInstance) Key() (state.Key, error) { 26 | switch instance := si.instance.(type) { 27 | case []string: 28 | return instance, nil 29 | default: 30 | return si.stateMapper.PrimaryKey(instance) 31 | } 32 | } 33 | 34 | func (si *StateInstance) Keys() ([]state.KeyValue, error) { 35 | return si.stateMapper.Keys(si.instance) 36 | } 37 | 38 | func (si *StateInstance) ToBytes() ([]byte, error) { 39 | return si.serializer.ToBytes(si.instance) 40 | } 41 | 42 | func (si *StateInstance) Mapper() StateMapper { 43 | return si.stateMapper 44 | } 45 | -------------------------------------------------------------------------------- /state/mapping/state_keyref.go: -------------------------------------------------------------------------------- 1 | package mapping 2 | 3 | import ( 4 | "strings" 5 | 6 | "github.com/s7techlab/cckit/state" 7 | "github.com/s7techlab/cckit/state/schema" 8 | ) 9 | 10 | // KeyRefNamespace namespace for uniq indexes 11 | const KeyRefNamespace = `_idx` 12 | 13 | // KeyRefIDKeyer keyer for KeyRef entity 14 | var KeyRefIDKeyer = attrsKeyer([]string{`Schema`, `Idx`, `RefKey`}) 15 | 16 | var KeyRefMapper = &StateMapping{ 17 | schema: &schema.KeyRef{}, 18 | namespace: state.Key{KeyRefNamespace}, 19 | primaryKeyer: KeyRefIDKeyer, 20 | } 21 | 22 | var KeyRefIDMapper = &StateMapping{ 23 | schema: &schema.KeyRefId{}, 24 | namespace: state.Key{KeyRefNamespace}, 25 | primaryKeyer: KeyRefIDKeyer, 26 | } 27 | 28 | func NewKeyRef(target interface{}, idx string, refKey, pKey state.Key) *schema.KeyRef { 29 | return &schema.KeyRef{ 30 | Schema: strings.Join(SchemaNamespace(target), `-`), 31 | Idx: idx, 32 | RefKey: refKey, 33 | PKey: pKey, 34 | } 35 | } 36 | 37 | func NewKeyRefID(target interface{}, idx string, refKey state.Key) *schema.KeyRefId { 38 | return &schema.KeyRefId{ 39 | Schema: strings.Join(SchemaNamespace(target), `-`), 40 | Idx: idx, 41 | RefKey: refKey, 42 | } 43 | } 44 | 45 | func NewKeyRefInstance(target interface{}, idx string, refKey, pKey state.Key) *StateInstance { 46 | return NewStateInstance(NewKeyRef(target, idx, refKey, pKey), KeyRefMapper, DefaultSerializer) 47 | } 48 | 49 | func NewKeyRefIDInstance(target interface{}, idx string, refKey state.Key) *StateInstance { 50 | return NewStateInstance( 51 | NewKeyRefID(target, idx, refKey), 52 | KeyRefIDMapper, 53 | DefaultSerializer, 54 | ) 55 | } 56 | -------------------------------------------------------------------------------- /state/mapping/state_mapping_test.go: -------------------------------------------------------------------------------- 1 | package mapping_test 2 | 3 | import ( 4 | "errors" 5 | 6 | . "github.com/onsi/ginkgo" 7 | . "github.com/onsi/gomega" 8 | 9 | "github.com/s7techlab/cckit/state" 10 | m "github.com/s7techlab/cckit/state/mapping" 11 | "github.com/s7techlab/cckit/state/mapping/testdata" 12 | "github.com/s7techlab/cckit/state/mapping/testdata/schema" 13 | ) 14 | 15 | var _ = Describe(`State mappings`, func() { 16 | 17 | mappings := m.StateMappings{}. 18 | //key will be <`EntityWithComplexId`, {Id.IdPart1}, {Id.IdPart2} > 19 | Add(&schema.EntityWithComplexId{}, m.PKeyComplexId(&schema.EntityComplexId{})) 20 | 21 | It(`Got error if namespace not exists`, func() { 22 | _, err := mappings.GetByNamespace(state.Key{`this-namespace-not-exists`}) 23 | Expect(errors.Is(err, m.ErrStateMappingNotFound)).To(BeTrue()) 24 | 25 | _, err = mappings.Get([]string{`this-namespace-not-exists`}) 26 | Expect(errors.Is(err, m.ErrStateMappingNotFound)).To(BeTrue()) 27 | }) 28 | 29 | It(`Allow to get mapping by namespace`, func() { 30 | mapping, err := mappings.GetByNamespace(state.Key{`EntityWithComplexId`}) 31 | Expect(err).NotTo(HaveOccurred()) 32 | 33 | Expect(mapping.Namespace()).To(Equal(state.Key{`EntityWithComplexId`})) 34 | 35 | mapping, err = mappings.Get([]string{`EntityWithComplexId`}) 36 | Expect(err).NotTo(HaveOccurred()) 37 | Expect(mapping.Namespace()).To(Equal(state.Key{`EntityWithComplexId`})) 38 | }) 39 | 40 | It(`Allow to create primary key`, func() { 41 | mapping, _ := mappings.Get(&schema.EntityWithComplexId{}) 42 | key, err := mapping.PrimaryKey(testdata.CreateEntityWithComplexId[0]) 43 | 44 | Expect(err).NotTo(HaveOccurred()) 45 | Expect(key).To(Equal(state.Key{`EntityWithComplexId`, `aaa`, `bb`, `ccc`, `2020-01-28`})) 46 | }) 47 | }) 48 | -------------------------------------------------------------------------------- /state/mapping/testdata/cc_complex_id.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "github.com/s7techlab/cckit/extensions/debug" 5 | "github.com/s7techlab/cckit/extensions/owner" 6 | "github.com/s7techlab/cckit/router" 7 | "github.com/s7techlab/cckit/router/param/defparam" 8 | m "github.com/s7techlab/cckit/state/mapping" 9 | "github.com/s7techlab/cckit/state/mapping/testdata/schema" 10 | ) 11 | 12 | func NewComplexIdCC() *router.Chaincode { 13 | r := router.New(`complexId`) 14 | debug.AddHandlers(r, `debug`, owner.Only) 15 | 16 | // Mappings for chaincode state 17 | r.Use(m.MapStates(m.StateMappings{}. 18 | //key will be <`EntityWithComplexId`, {Id.IdPart1}, {Id.IdPart2} > 19 | Add(&schema.EntityWithComplexId{}, m.PKeyComplexId(&schema.EntityComplexId{})))) 20 | 21 | r.Init(owner.InvokeSetFromCreator) 22 | 23 | r.Group(`entity`). 24 | Invoke(`List`, entityList). 25 | Invoke(`Get`, entityGet, defparam.Proto(&schema.EntityComplexId{})). 26 | Invoke(`Insert`, entityInsert, defparam.Proto(&schema.EntityWithComplexId{})) 27 | 28 | return router.NewChaincode(r) 29 | } 30 | 31 | func entityList(c router.Context) (interface{}, error) { 32 | return c.State().List(&schema.EntityWithComplexId{}) 33 | } 34 | 35 | func entityInsert(c router.Context) (interface{}, error) { 36 | var ( 37 | entity = c.Param() 38 | ) 39 | 40 | mapper := m.NewEntryMapper() 41 | mapper.Commands.Insert(entity) 42 | mapper.Event.Name = `EntityInserted` 43 | mapper.Event.Payload = entity // same as entity 44 | 45 | return entity, mapper.Apply(c.State(), c.Event()) 46 | } 47 | 48 | func entityGet(c router.Context) (interface{}, error) { 49 | var ( 50 | id = c.Param() 51 | ) 52 | return c.State().Get(id) 53 | } 54 | -------------------------------------------------------------------------------- /state/mapping/testdata/cc_slice_id.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "github.com/s7techlab/cckit/extensions/debug" 5 | "github.com/s7techlab/cckit/extensions/owner" 6 | "github.com/s7techlab/cckit/router" 7 | "github.com/s7techlab/cckit/router/param" 8 | "github.com/s7techlab/cckit/router/param/defparam" 9 | "github.com/s7techlab/cckit/state" 10 | m "github.com/s7techlab/cckit/state/mapping" 11 | "github.com/s7techlab/cckit/state/mapping/testdata/schema" 12 | ) 13 | 14 | func NewSliceIdCC() *router.Chaincode { 15 | r := router.New(`complexId`) 16 | debug.AddHandlers(r, `debug`, owner.Only) 17 | 18 | // Mappings for chaincode state 19 | r.Use(m.MapStates(m.StateMappings{}. 20 | //key will be <`EntityWithSliceId`, {Id[0]}, {Id[1]},... {Id[len(Id)-1]} > 21 | Add(&schema.EntityWithSliceId{}, m.PKeyId()))) 22 | 23 | r.Init(owner.InvokeSetFromCreator) 24 | 25 | r.Group(`entity`). 26 | Invoke(`List`, func(c router.Context) (interface{}, error) { 27 | return c.State().List(&schema.EntityWithSliceId{}) 28 | }). 29 | Invoke(`Get`, func(c router.Context) (interface{}, error) { 30 | return c.State().Get(&schema.EntityWithSliceId{Id: state.StringsIdFromStr(c.ParamString(`Id`))}) 31 | }, param.String(`Id`)). 32 | Invoke(`Insert`, func(c router.Context) (interface{}, error) { 33 | return nil, c.State().Insert(c.Param()) 34 | }, defparam.Proto(&schema.EntityWithSliceId{})) 35 | 36 | return router.NewChaincode(r) 37 | } 38 | -------------------------------------------------------------------------------- /state/mapping/testdata/cc_withconfig.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "github.com/s7techlab/cckit/extensions/owner" 5 | "github.com/s7techlab/cckit/router" 6 | "github.com/s7techlab/cckit/router/param/defparam" 7 | m "github.com/s7techlab/cckit/state/mapping" 8 | "github.com/s7techlab/cckit/state/mapping/testdata/schema" 9 | ) 10 | 11 | func NewCCWithConfig() *router.Chaincode { 12 | r := router.New(`withConfig`) 13 | 14 | // Mappings for chaincode state 15 | r.Use(m.MapStates(m.StateMappings{}. 16 | //key will be <`config`> 17 | Add(&schema.Config{}, m.WithConstPKey()))) 18 | 19 | r.Init(owner.InvokeSetFromCreator) 20 | 21 | r.Group(`config`). 22 | Invoke(`Set`, configSet, defparam.Proto(&schema.Config{})). 23 | Invoke(`Get`, configGet) 24 | 25 | return router.NewChaincode(r) 26 | } 27 | 28 | func configGet(c router.Context) (interface{}, error) { 29 | return c.State().Get(&schema.Config{}, &schema.Config{}) 30 | } 31 | 32 | func configSet(c router.Context) (interface{}, error) { 33 | conf := c.Param() 34 | return conf, c.State().Put(conf) 35 | } 36 | -------------------------------------------------------------------------------- /state/mapping/testdata/schema/config.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package schema; 4 | option go_package = "github.com/s7techlab/cckit/state/mapping/testdata/schema"; 5 | 6 | message Config { 7 | string field1 = 1; 8 | string field2 = 2; 9 | } 10 | 11 | -------------------------------------------------------------------------------- /state/mapping/testdata/schema/config.validator.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-gogo. DO NOT EDIT. 2 | // source: mapping/testdata/schema/config.proto 3 | 4 | package schema 5 | 6 | import ( 7 | fmt "fmt" 8 | math "math" 9 | proto "github.com/golang/protobuf/proto" 10 | ) 11 | 12 | // Reference imports to suppress errors if they are not otherwise used. 13 | var _ = proto.Marshal 14 | var _ = fmt.Errorf 15 | var _ = math.Inf 16 | 17 | func (this *Config) Validate() error { 18 | return nil 19 | } 20 | -------------------------------------------------------------------------------- /state/mapping/testdata/schema/with_complex_id.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package schema; 4 | option go_package = "github.com/s7techlab/cckit/state/mapping/testdata/schema"; 5 | 6 | import "google/protobuf/timestamp.proto"; 7 | 8 | message EntityWithComplexId { 9 | EntityComplexId id = 1; 10 | google.protobuf.Timestamp some_date = 2; 11 | } 12 | 13 | // EntityComplexId 14 | message EntityComplexId { 15 | repeated string id_part1 = 1; 16 | string id_part2 = 2; 17 | google.protobuf.Timestamp id_part3 = 3; 18 | } 19 | -------------------------------------------------------------------------------- /state/mapping/testdata/schema/with_complex_id.validator.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-gogo. DO NOT EDIT. 2 | // source: mapping/testdata/schema/with_complex_id.proto 3 | 4 | package schema 5 | 6 | import ( 7 | fmt "fmt" 8 | math "math" 9 | proto "github.com/golang/protobuf/proto" 10 | _ "google.golang.org/protobuf/types/known/timestamppb" 11 | github_com_mwitkow_go_proto_validators "github.com/mwitkow/go-proto-validators" 12 | ) 13 | 14 | // Reference imports to suppress errors if they are not otherwise used. 15 | var _ = proto.Marshal 16 | var _ = fmt.Errorf 17 | var _ = math.Inf 18 | 19 | func (this *EntityWithComplexId) Validate() error { 20 | if this.Id != nil { 21 | if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.Id); err != nil { 22 | return github_com_mwitkow_go_proto_validators.FieldError("Id", err) 23 | } 24 | } 25 | if this.SomeDate != nil { 26 | if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.SomeDate); err != nil { 27 | return github_com_mwitkow_go_proto_validators.FieldError("SomeDate", err) 28 | } 29 | } 30 | return nil 31 | } 32 | func (this *EntityComplexId) Validate() error { 33 | if this.IdPart3 != nil { 34 | if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.IdPart3); err != nil { 35 | return github_com_mwitkow_go_proto_validators.FieldError("IdPart3", err) 36 | } 37 | } 38 | return nil 39 | } 40 | -------------------------------------------------------------------------------- /state/mapping/testdata/schema/with_composite_id.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package schema; 4 | option go_package = "github.com/s7techlab/cckit/state/mapping/testdata/schema"; 5 | 6 | import "google/protobuf/timestamp.proto"; 7 | 8 | // EntityWithCompositeId 9 | message EntityWithCompositeId { 10 | string id_first_part = 1; // part of composite primary key 11 | uint32 id_second_part = 2; // part of composite primary key 12 | google.protobuf.Timestamp id_third_part = 3; // part of composite primary key 13 | 14 | string name = 4; 15 | int32 value = 5; 16 | } 17 | 18 | // EntityCompositeId - container for composite primary key 19 | message EntityCompositeId { 20 | string id_first_part = 1; 21 | uint32 id_second_part = 2; 22 | google.protobuf.Timestamp id_third_part = 3; 23 | } 24 | 25 | // EntityWithCompositeIdList 26 | message EntityWithCompositeIdList { 27 | repeated EntityWithCompositeId items = 1; 28 | } 29 | 30 | // CreateEntityWithCompositeId 31 | message CreateEntityWithCompositeId { 32 | string id_first_part = 1; 33 | uint32 id_second_part = 2; 34 | google.protobuf.Timestamp id_third_part = 3; 35 | string name = 4; 36 | int32 value = 5; 37 | } 38 | 39 | // UpdateEntityWithCompositeId 40 | message UpdateEntityWithCompositeId { 41 | string id_first_part = 1; 42 | uint32 id_second_part = 2; 43 | google.protobuf.Timestamp id_third_part = 3; 44 | string name = 4; 45 | int32 value = 5; 46 | } 47 | -------------------------------------------------------------------------------- /state/mapping/testdata/schema/with_composite_id.validator.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-gogo. DO NOT EDIT. 2 | // source: mapping/testdata/schema/with_composite_id.proto 3 | 4 | package schema 5 | 6 | import ( 7 | fmt "fmt" 8 | math "math" 9 | proto "github.com/golang/protobuf/proto" 10 | _ "google.golang.org/protobuf/types/known/timestamppb" 11 | github_com_mwitkow_go_proto_validators "github.com/mwitkow/go-proto-validators" 12 | ) 13 | 14 | // Reference imports to suppress errors if they are not otherwise used. 15 | var _ = proto.Marshal 16 | var _ = fmt.Errorf 17 | var _ = math.Inf 18 | 19 | func (this *EntityWithCompositeId) Validate() error { 20 | if this.IdThirdPart != nil { 21 | if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.IdThirdPart); err != nil { 22 | return github_com_mwitkow_go_proto_validators.FieldError("IdThirdPart", err) 23 | } 24 | } 25 | return nil 26 | } 27 | func (this *EntityCompositeId) Validate() error { 28 | if this.IdThirdPart != nil { 29 | if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.IdThirdPart); err != nil { 30 | return github_com_mwitkow_go_proto_validators.FieldError("IdThirdPart", err) 31 | } 32 | } 33 | return nil 34 | } 35 | func (this *EntityWithCompositeIdList) Validate() error { 36 | for _, item := range this.Items { 37 | if item != nil { 38 | if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(item); err != nil { 39 | return github_com_mwitkow_go_proto_validators.FieldError("Items", err) 40 | } 41 | } 42 | } 43 | return nil 44 | } 45 | func (this *CreateEntityWithCompositeId) Validate() error { 46 | if this.IdThirdPart != nil { 47 | if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.IdThirdPart); err != nil { 48 | return github_com_mwitkow_go_proto_validators.FieldError("IdThirdPart", err) 49 | } 50 | } 51 | return nil 52 | } 53 | func (this *UpdateEntityWithCompositeId) Validate() error { 54 | if this.IdThirdPart != nil { 55 | if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.IdThirdPart); err != nil { 56 | return github_com_mwitkow_go_proto_validators.FieldError("IdThirdPart", err) 57 | } 58 | } 59 | return nil 60 | } 61 | -------------------------------------------------------------------------------- /state/mapping/testdata/schema/with_indexes.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package schema; 4 | option go_package = "github.com/s7techlab/cckit/state/mapping/testdata/schema"; 5 | 6 | // EntityWithIndexes 7 | message EntityWithIndexes { 8 | string id = 1; 9 | // one external id 10 | string external_id = 2; 11 | 12 | // required multiple external ids (minimum 1) 13 | repeated string required_external_ids = 3; 14 | // optional multiple external ids (minimum 0) 15 | repeated string optional_external_ids = 4; 16 | 17 | int32 value = 5; 18 | } 19 | 20 | // EntityWithIndexesList 21 | message EntityWithIndexesList { 22 | repeated EntityWithIndexes items = 1; 23 | } 24 | 25 | // CreateEntityWithIndexes 26 | message CreateEntityWithIndexes { 27 | string id = 1; 28 | // one external id 29 | string external_id = 2; 30 | 31 | // required multiple external ids (minimum 1) 32 | repeated string required_external_ids = 3; 33 | // optional multiple external ids (minimum 0) 34 | repeated string optional_external_ids = 4; 35 | 36 | int32 value = 5; 37 | } 38 | 39 | // UpdateEntityEntityWithIndexes 40 | message UpdateEntityWithIndexes { 41 | string id = 1; 42 | // one external id 43 | string external_id = 2; 44 | 45 | // required multiple external ids (minimum 1) 46 | repeated string required_external_ids = 3; 47 | // optional multiple external ids (minimum 0) 48 | repeated string optional_external_ids = 4; 49 | 50 | int32 value = 5; 51 | } -------------------------------------------------------------------------------- /state/mapping/testdata/schema/with_indexes.validator.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-gogo. DO NOT EDIT. 2 | // source: mapping/testdata/schema/with_indexes.proto 3 | 4 | package schema 5 | 6 | import ( 7 | fmt "fmt" 8 | math "math" 9 | proto "github.com/golang/protobuf/proto" 10 | github_com_mwitkow_go_proto_validators "github.com/mwitkow/go-proto-validators" 11 | ) 12 | 13 | // Reference imports to suppress errors if they are not otherwise used. 14 | var _ = proto.Marshal 15 | var _ = fmt.Errorf 16 | var _ = math.Inf 17 | 18 | func (this *EntityWithIndexes) Validate() error { 19 | return nil 20 | } 21 | func (this *EntityWithIndexesList) Validate() error { 22 | for _, item := range this.Items { 23 | if item != nil { 24 | if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(item); err != nil { 25 | return github_com_mwitkow_go_proto_validators.FieldError("Items", err) 26 | } 27 | } 28 | } 29 | return nil 30 | } 31 | func (this *CreateEntityWithIndexes) Validate() error { 32 | return nil 33 | } 34 | func (this *UpdateEntityWithIndexes) Validate() error { 35 | return nil 36 | } 37 | -------------------------------------------------------------------------------- /state/mapping/testdata/schema/with_slice_id.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package schema; 4 | option go_package = "github.com/s7techlab/cckit/state/mapping/testdata/schema"; 5 | 6 | import "google/protobuf/timestamp.proto"; 7 | 8 | message EntityWithSliceId { 9 | repeated string id = 1; 10 | google.protobuf.Timestamp some_date = 2; 11 | } 12 | -------------------------------------------------------------------------------- /state/mapping/testdata/schema/with_slice_id.validator.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-gogo. DO NOT EDIT. 2 | // source: mapping/testdata/schema/with_slice_id.proto 3 | 4 | package schema 5 | 6 | import ( 7 | fmt "fmt" 8 | math "math" 9 | proto "github.com/golang/protobuf/proto" 10 | _ "google.golang.org/protobuf/types/known/timestamppb" 11 | github_com_mwitkow_go_proto_validators "github.com/mwitkow/go-proto-validators" 12 | ) 13 | 14 | // Reference imports to suppress errors if they are not otherwise used. 15 | var _ = proto.Marshal 16 | var _ = fmt.Errorf 17 | var _ = math.Inf 18 | 19 | func (this *EntityWithSliceId) Validate() error { 20 | if this.SomeDate != nil { 21 | if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(this.SomeDate); err != nil { 22 | return github_com_mwitkow_go_proto_validators.FieldError("SomeDate", err) 23 | } 24 | } 25 | return nil 26 | } 27 | -------------------------------------------------------------------------------- /state/mapping/testdata/testdata.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "github.com/s7techlab/cckit/state/mapping/testdata/schema" 5 | "github.com/s7techlab/cckit/testing" 6 | ) 7 | 8 | var ( 9 | Dates = []string{`2021-02-15`, `2021-02-16`, `2021-03-15`} 10 | 11 | CreateEntityWithComplexId = []*schema.EntityWithComplexId{{ 12 | Id: &schema.EntityComplexId{ 13 | IdPart1: []string{`aaa`, `bb`}, 14 | IdPart2: `ccc`, 15 | IdPart3: testing.MustTime(`2020-01-28T17:00:00Z`), 16 | }, 17 | }} 18 | 19 | CreateEntityWithCompositeId = []*schema.CreateEntityWithCompositeId{{ 20 | IdFirstPart: "A", 21 | IdSecondPart: 1, 22 | IdThirdPart: testing.MustTime(Dates[0] + `T00:00:00Z`), 23 | Name: "Lorem", 24 | Value: 1, 25 | }, { 26 | IdFirstPart: "B", 27 | IdSecondPart: 1, 28 | IdThirdPart: testing.MustTime(Dates[1] + `T00:00:00Z`), 29 | Name: "Ipsum", 30 | Value: 2, 31 | }, { 32 | IdFirstPart: "B", 33 | IdSecondPart: 2, 34 | IdThirdPart: testing.MustTime(Dates[2] + `T00:00:00Z`), 35 | Name: "Dolor", 36 | Value: 3, 37 | }} 38 | 39 | CreateEntityWithIndexes = []*schema.CreateEntityWithIndexes{{ 40 | Id: `aaa`, 41 | ExternalId: `aaa_aaa`, 42 | Value: 1, 43 | }, { 44 | Id: `bbb`, 45 | ExternalId: `bbb_bbb`, 46 | OptionalExternalIds: []string{`bbb_opt1`, `bbb_opt2`}, 47 | Value: 1, 48 | }} 49 | ) 50 | -------------------------------------------------------------------------------- /state/schema/schema.proto: -------------------------------------------------------------------------------- 1 | syntax = "proto3"; 2 | 3 | package state.schema; 4 | option go_package = "github.com/s7techlab/cckit/state/schema"; 5 | 6 | import "google/protobuf/any.proto"; 7 | 8 | // KeyRefId id part of key reference 9 | message KeyRefId { 10 | // PK of key ref will be 11 | // <`_idx`,{SchemaName},{idxName}, {RefKey[1]},... {RefKey[n}}>s 12 | // `idx` - const prefix for all indexes 13 | // SchemaName} - string representation of schema 14 | 15 | // entity type 16 | string schema = 1; 17 | // idx name from entity type 18 | string idx = 2; 19 | // referred key 20 | repeated string ref_key = 3; 21 | } 22 | 23 | 24 | message KeyRef { 25 | // entity type 26 | string schema = 1; 27 | // idx name from entity type 28 | string idx = 2; 29 | // referred key 30 | repeated string ref_key = 3; 31 | // primary key instance linked to 32 | repeated string p_key = 4; 33 | } 34 | 35 | message List { 36 | repeated google.protobuf.Any items = 1; 37 | } -------------------------------------------------------------------------------- /state/schema/schema.validator.pb.go: -------------------------------------------------------------------------------- 1 | // Code generated by protoc-gen-gogo. DO NOT EDIT. 2 | // source: schema/schema.proto 3 | 4 | package schema 5 | 6 | import ( 7 | fmt "fmt" 8 | math "math" 9 | proto "github.com/golang/protobuf/proto" 10 | _ "google.golang.org/protobuf/types/known/anypb" 11 | github_com_mwitkow_go_proto_validators "github.com/mwitkow/go-proto-validators" 12 | ) 13 | 14 | // Reference imports to suppress errors if they are not otherwise used. 15 | var _ = proto.Marshal 16 | var _ = fmt.Errorf 17 | var _ = math.Inf 18 | 19 | func (this *KeyRefId) Validate() error { 20 | return nil 21 | } 22 | func (this *KeyRef) Validate() error { 23 | return nil 24 | } 25 | func (this *List) Validate() error { 26 | for _, item := range this.Items { 27 | if item != nil { 28 | if err := github_com_mwitkow_go_proto_validators.CallValidatorIfExists(item); err != nil { 29 | return github_com_mwitkow_go_proto_validators.FieldError("Items", err) 30 | } 31 | } 32 | } 33 | return nil 34 | } 35 | -------------------------------------------------------------------------------- /state/state_cached_test.go: -------------------------------------------------------------------------------- 1 | package state_test 2 | 3 | import ( 4 | . "github.com/onsi/ginkgo" 5 | . "github.com/onsi/gomega" 6 | 7 | "github.com/s7techlab/cckit/state/testdata" 8 | testcc "github.com/s7techlab/cckit/testing" 9 | expectcc "github.com/s7techlab/cckit/testing/expect" 10 | ) 11 | 12 | const ( 13 | StateCachedChaincode = `state_cached` 14 | ) 15 | 16 | var _ = Describe(`State caching`, func() { 17 | 18 | //Create chaincode mocks 19 | stateCachedCC := testcc.NewMockStub(StateCachedChaincode, testdata.NewStateCachedCC()) 20 | 21 | It("Read after write returns non empty entry", func() { 22 | resp := expectcc.PayloadIs(stateCachedCC.Invoke(testdata.TxStateCachedReadAfterWrite), &testdata.Value{}) 23 | Expect(resp).To(Equal(testdata.KeyValue(testdata.Keys[0]))) 24 | }) 25 | 26 | It("Read after delete returns empty entry", func() { 27 | resp := stateCachedCC.Invoke(testdata.TxStateCachedReadAfterDelete) 28 | Expect(resp.Payload).To(Equal([]byte{})) 29 | }) 30 | 31 | It("List after write returns list", func() { 32 | resp := expectcc.PayloadIs( 33 | stateCachedCC.Invoke(testdata.TxStateCachedListAfterWrite), &[]testdata.Value{}).([]testdata.Value) 34 | 35 | // all key exists 36 | Expect(resp).To(Equal([]testdata.Value{ 37 | testdata.KeyValue(testdata.Keys[0]), testdata.KeyValue(testdata.Keys[1]), testdata.KeyValue(testdata.Keys[2])})) 38 | }) 39 | 40 | It("List after delete returns list without deleted item", func() { 41 | resp := expectcc.PayloadIs( 42 | stateCachedCC.Invoke(testdata.TxStateCachedListAfterDelete), &[]testdata.Value{}).([]testdata.Value) 43 | 44 | // first key is deleted 45 | Expect(resp).To(Equal([]testdata.Value{ 46 | testdata.KeyValue(testdata.Keys[1]), testdata.KeyValue(testdata.Keys[2])})) 47 | }) 48 | }) 49 | -------------------------------------------------------------------------------- /state/state_list.go: -------------------------------------------------------------------------------- 1 | package state 2 | 3 | import ( 4 | "fmt" 5 | "reflect" 6 | 7 | "github.com/hyperledger/fabric-chaincode-go/shim" 8 | "google.golang.org/protobuf/proto" 9 | "google.golang.org/protobuf/types/known/anypb" 10 | 11 | "github.com/s7techlab/cckit/state/schema" 12 | ) 13 | 14 | type ( 15 | StateList struct { 16 | itemTarget interface{} 17 | listTarget interface{} 18 | list []interface{} 19 | } 20 | ) 21 | 22 | func NewStateList(config ...interface{}) (sl *StateList, err error) { 23 | var ( 24 | itemTarget, listTarget interface{} 25 | ) 26 | if len(config) > 0 { 27 | itemTarget = config[0] 28 | } 29 | if len(config) > 1 { 30 | listTarget = config[1] 31 | } 32 | 33 | return &StateList{itemTarget: itemTarget, listTarget: listTarget}, nil 34 | } 35 | 36 | func (sl *StateList) Fill(iter shim.StateQueryIteratorInterface, fromBytes FromBytesTransformer) (list interface{}, err error) { 37 | 38 | for iter.HasNext() { 39 | kv, err := iter.Next() 40 | if err != nil { 41 | return nil, err 42 | } 43 | item, err := fromBytes(kv.Value, sl.itemTarget) 44 | if err != nil { 45 | return nil, fmt.Errorf(`transform list entry: %w`, err) 46 | } 47 | sl.list = append(sl.list, item) 48 | } 49 | return sl.Get() 50 | } 51 | 52 | func (sl *StateList) Get() (list interface{}, err error) { 53 | 54 | // custom list proto.Message 55 | if _, isListProto := sl.listTarget.(proto.Message); isListProto { 56 | 57 | customList := proto.Clone(sl.listTarget.(proto.Message)) 58 | items := reflect.ValueOf(customList).Elem().FieldByName(`Items`) 59 | for _, v := range sl.list { 60 | items.Set(reflect.Append(items, reflect.ValueOf(v))) 61 | } 62 | return customList, nil 63 | 64 | // default list proto.Message (with repeated Any) 65 | } else if _, isItemProto := sl.itemTarget.(proto.Message); isItemProto { 66 | defList := &schema.List{} 67 | 68 | for _, item := range sl.list { 69 | newAny, err := anypb.New(item.(proto.Message)) 70 | if err != nil { 71 | return nil, err 72 | } 73 | defList.Items = append(defList.Items, newAny) 74 | } 75 | return defList, nil 76 | } 77 | 78 | return sl.list, nil 79 | } 80 | 81 | func (sl *StateList) AddElementToList(elem interface{}) { 82 | sl.list = append(sl.list, elem) 83 | } 84 | -------------------------------------------------------------------------------- /state/testdata/schema/book.go: -------------------------------------------------------------------------------- 1 | package schema 2 | 3 | const BookEntity = `BOOK` 4 | 5 | type Book struct { 6 | Id string 7 | Title string 8 | Chapters []BookChapter 9 | } 10 | 11 | func (b Book) Key() ([]string, error) { 12 | return []string{BookEntity, b.Id}, nil 13 | } 14 | 15 | type BookChapter struct { 16 | Pos int 17 | Title string 18 | } 19 | 20 | const PrivateBookEntity = `PRIVATE_BOOK` 21 | 22 | type PrivateBook struct { 23 | Id string 24 | Title string 25 | Chapters []BookChapter 26 | } 27 | 28 | func (pb PrivateBook) Key() ([]string, error) { 29 | return []string{PrivateBookEntity, pb.Id}, nil 30 | } 31 | 32 | type BookListRequest struct { 33 | PageSize int32 34 | Bookmark string 35 | } 36 | 37 | type BookList struct { 38 | Items []*Book 39 | Next string 40 | } 41 | -------------------------------------------------------------------------------- /state/testdata/testdata.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "github.com/s7techlab/cckit/state/testdata/schema" 5 | ) 6 | 7 | var Books = []schema.Book{{ 8 | Id: `ISBN-111`, 9 | Title: `first title`, Chapters: []schema.BookChapter{ 10 | {Pos: 1, Title: `chapter 111.1`}, {Pos: 2, Title: `chapter 111.2`}}}, 11 | 12 | { 13 | Id: `ISBN-222`, Title: `second title`, Chapters: []schema.BookChapter{ 14 | {Pos: 1, Title: `chapter 222.1`}, {Pos: 2, Title: `chapter 222.2`}, {Pos: 3, Title: `chapter 222.3`}}}, 15 | 16 | { 17 | Id: `ISBN-333`, Title: `third title`, Chapters: []schema.BookChapter{ 18 | {Pos: 1, Title: `chapter 333.1`}, {Pos: 2, Title: `chapter 333.2`}, {Pos: 3, Title: `chapter 333.3`}, {Pos: 4, Title: `chapter 333.4`}}}, 19 | } 20 | 21 | var PrivateBooks = []schema.PrivateBook{{ 22 | Id: `ISBN-111`, 23 | Title: `first title`, Chapters: []schema.BookChapter{ 24 | {Pos: 1, Title: `chapter 111.1`}, {Pos: 2, Title: `chapter 111.2`}}}, 25 | 26 | { 27 | Id: `ISBN-222`, Title: `second title`, Chapters: []schema.BookChapter{ 28 | {Pos: 1, Title: `chapter 222.1`}, {Pos: 2, Title: `chapter 222.2`}, {Pos: 3, Title: `chapter 222.3`}}}, 29 | 30 | { 31 | Id: `ISBN-333`, Title: `third title`, Chapters: []schema.BookChapter{ 32 | {Pos: 1, Title: `chapter 333.1`}, {Pos: 2, Title: `chapter 333.2`}, {Pos: 3, Title: `chapter 333.3`}, {Pos: 4, Title: `chapter 333.4`}}}, 33 | } 34 | -------------------------------------------------------------------------------- /state/testdata/util.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import "github.com/hyperledger/fabric-chaincode-go/shim" 4 | 5 | func MustCreateCompositeKey(objectType string, attributes []string) string { 6 | key, err := shim.CreateCompositeKey(objectType, attributes) 7 | if err != nil { 8 | panic(err) 9 | } 10 | 11 | return key 12 | } 13 | -------------------------------------------------------------------------------- /state/tranformer.go: -------------------------------------------------------------------------------- 1 | package state 2 | 3 | import ( 4 | "encoding/json" 5 | 6 | "github.com/golang/protobuf/proto" 7 | 8 | "github.com/s7techlab/cckit/convert" 9 | ) 10 | 11 | type ( 12 | // FromBytesTransformer is used after getState operation for convert value 13 | FromBytesTransformer func(bb []byte, config ...interface{}) (interface{}, error) 14 | 15 | // ToBytesTransformer is used before putState operation for convert payload 16 | ToBytesTransformer func(v interface{}, config ...interface{}) ([]byte, error) 17 | 18 | // KeyTransformer is used before putState operation for convert key 19 | KeyTransformer func(Key) (Key, error) 20 | 21 | // StringTransformer is used before setEvent operation for convert name 22 | StringTransformer func(string) (string, error) 23 | 24 | Serializer interface { 25 | ToBytes(interface{}) ([]byte, error) 26 | FromBytes(serialized []byte, target interface{}) (interface{}, error) 27 | } 28 | 29 | ProtoSerializer struct { 30 | } 31 | 32 | JSONSerializer struct { 33 | } 34 | ) 35 | 36 | func ConvertFromBytes(bb []byte, config ...interface{}) (interface{}, error) { 37 | // conversion is not needed 38 | if len(config) == 0 { 39 | return bb, nil 40 | } 41 | return convert.FromBytes(bb, config[0]) 42 | } 43 | 44 | func ConvertToBytes(v interface{}, _ ...interface{}) ([]byte, error) { 45 | return convert.ToBytes(v) 46 | } 47 | 48 | // KeyAsIs returns string parts of composite key 49 | func KeyAsIs(key Key) (Key, error) { 50 | return key, nil 51 | } 52 | 53 | func NameAsIs(name string) (string, error) { 54 | return name, nil 55 | } 56 | 57 | func (ps *ProtoSerializer) ToBytes(entry interface{}) ([]byte, error) { 58 | return proto.Marshal(entry.(proto.Message)) 59 | } 60 | 61 | func (ps *ProtoSerializer) FromBytes(serialized []byte, target interface{}) (interface{}, error) { 62 | return convert.FromBytes(serialized, target) 63 | } 64 | 65 | func (js *JSONSerializer) ToBytes(entry interface{}) ([]byte, error) { 66 | return json.Marshal(entry) 67 | } 68 | 69 | func (js *JSONSerializer) FromBytes(serialized []byte, target interface{}) (interface{}, error) { 70 | return convert.JSONUnmarshalPtr(serialized, target) 71 | } 72 | -------------------------------------------------------------------------------- /testing/chaincode_event.go: -------------------------------------------------------------------------------- 1 | package testing 2 | 3 | import ( 4 | "github.com/golang/protobuf/ptypes/timestamp" 5 | "github.com/hyperledger/fabric-protos-go/peer" 6 | ) 7 | 8 | type ( 9 | ChaincodeEvent struct { 10 | event *peer.ChaincodeEvent 11 | block uint64 12 | txTimestamp *timestamp.Timestamp 13 | } 14 | ) 15 | 16 | func (eb *ChaincodeEvent) Event() *peer.ChaincodeEvent { 17 | return eb.event 18 | } 19 | 20 | func (eb *ChaincodeEvent) Block() uint64 { 21 | return eb.block 22 | } 23 | 24 | func (eb *ChaincodeEvent) TxTimestamp() *timestamp.Timestamp { 25 | return eb.txTimestamp 26 | } 27 | -------------------------------------------------------------------------------- /testing/expect/event.go: -------------------------------------------------------------------------------- 1 | package expect 2 | 3 | import ( 4 | "github.com/hyperledger/fabric-protos-go/peer" 5 | g "github.com/onsi/gomega" 6 | 7 | "github.com/s7techlab/cckit/convert" 8 | "github.com/s7techlab/cckit/state/mapping" 9 | "github.com/s7techlab/cckit/testing/gomega" 10 | ) 11 | 12 | // EventIs expects ChaincodeEvent name is equal to expectName and event payload can be marshaled to expectPayload 13 | func EventIs(event *peer.ChaincodeEvent, expectName string, expectPayload interface{}) interface{} { 14 | g.Expect(event.EventName).To(g.Equal(expectName), `event name not match`) 15 | 16 | return EventPayloadIs(event, expectPayload) 17 | } 18 | 19 | // EventStringerEqual expects ChaincodeEvent name is equal to expectName and 20 | // event payload String() equal expectPayload String() 21 | func EventStringerEqual(event *peer.ChaincodeEvent, expectName string, expectPayload interface{}) { 22 | payload := EventIs(event, expectName, expectPayload) 23 | 24 | g.Expect(payload).To(gomega.StringerEqual(expectPayload)) 25 | } 26 | 27 | // EventPayloadIs expects peer.ChaincodeEvent payload can be marshaled to 28 | // target interface{} and returns converted value 29 | func EventPayloadIs(event *peer.ChaincodeEvent, target interface{}) interface{} { 30 | g.Expect(event).NotTo(g.BeNil()) 31 | data, err := convert.FromBytes(event.Payload, target) 32 | description := `` 33 | if err != nil { 34 | description = err.Error() 35 | } 36 | g.Expect(err).To(g.BeNil(), description) 37 | return data 38 | } 39 | 40 | // EventEqual expects that *peer.ChaincodeEvent stringer equal to mapping.Event 41 | func EventEqual(event *peer.ChaincodeEvent, expected *mapping.Event) { 42 | EventStringerEqual(event, expected.Name, expected.Payload) 43 | } 44 | 45 | func EventPayloadEqual(event *peer.ChaincodeEvent, expectedPayload interface{}) { 46 | EventEqual(event, mapping.EventFromPayload(expectedPayload)) 47 | } 48 | -------------------------------------------------------------------------------- /testing/expect/tx.go: -------------------------------------------------------------------------------- 1 | package expect 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/hyperledger/fabric-protos-go/peer" 7 | g "github.com/onsi/gomega" 8 | ) 9 | 10 | type ( 11 | Stringer interface { 12 | String() string 13 | } 14 | 15 | TxRes struct { 16 | Result interface{} 17 | Err error 18 | Event *peer.ChaincodeEvent 19 | } 20 | ) 21 | 22 | func (r *TxRes) HasError(err interface{}) *TxRes { 23 | if err == nil { 24 | g.Expect(r.Err).NotTo(g.HaveOccurred()) 25 | } else { 26 | g.Expect(r.Err).To(g.HaveOccurred()) 27 | g.Expect(fmt.Sprintf(`%s`, r.Err)).To(g.ContainSubstring(fmt.Sprintf(`%s`, err))) 28 | } 29 | return r 30 | } 31 | 32 | func (r *TxRes) HasNoError() *TxRes { 33 | return r.HasError(nil) 34 | } 35 | 36 | func (r *TxRes) Is(expectedResult interface{}) *TxRes { 37 | r.HasNoError() 38 | 39 | _, ok1 := r.Result.(Stringer) 40 | _, ok2 := expectedResult.(Stringer) 41 | if ok1 && ok2 { 42 | g.Expect(r.Result.(Stringer).String()).To(g.Equal(expectedResult.(Stringer).String())) 43 | } else { 44 | g.Expect(r.Result).To(g.BeEquivalentTo(expectedResult)) 45 | } 46 | 47 | return r 48 | } 49 | 50 | // ProduceEvent expects that tx produces event with particular payload 51 | func (r *TxRes) ProduceEvent(eventName string, eventPayload interface{}) *TxRes { 52 | r.HasNoError() 53 | EventIs(r.Event, eventName, eventPayload) 54 | return r 55 | } 56 | -------------------------------------------------------------------------------- /testing/gomega/matchers.go: -------------------------------------------------------------------------------- 1 | package gomega 2 | 3 | import ( 4 | "github.com/onsi/gomega/types" 5 | 6 | "github.com/s7techlab/cckit/testing/gomega/matchers" 7 | ) 8 | 9 | func StringerEqual(expected interface{}) types.GomegaMatcher { 10 | return &matchers.StringerEqualMatcher{ 11 | Expected: expected, 12 | } 13 | } 14 | 15 | func ErrorIs(expected interface{}) types.GomegaMatcher { 16 | return &matchers.ErrorIslMatcher{ 17 | Expected: expected, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /testing/gomega/matchers/erroris.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | 7 | "github.com/onsi/gomega/format" 8 | ) 9 | 10 | type ErrorIslMatcher struct { 11 | Expected interface{} 12 | } 13 | 14 | func (matcher *ErrorIslMatcher) Match(actual interface{}) (success bool, err error) { 15 | if actual == nil && matcher.Expected == nil { 16 | return false, fmt.Errorf("Refusing to compare to .\nBe explicit and use BeNil() instead. This is to avoid mistakes where both sides of an assertion are erroneously uninitialized.") 17 | } 18 | 19 | // 1.12 20 | return strings.Contains( 21 | fmt.Sprintf(`%s`, actual), 22 | fmt.Sprintf(`%s`, matcher.Expected)), nil 23 | } 24 | 25 | func (matcher *ErrorIslMatcher) FailureMessage(actual interface{}) (message string) { 26 | return format.Message(actual, "to match error", matcher.Expected) 27 | } 28 | 29 | func (matcher *ErrorIslMatcher) NegatedFailureMessage(actual interface{}) (message string) { 30 | return format.Message(actual, "not to match error", matcher.Expected) 31 | } 32 | -------------------------------------------------------------------------------- /testing/gomega/matchers/stringer.go: -------------------------------------------------------------------------------- 1 | package matchers 2 | 3 | import ( 4 | "errors" 5 | "fmt" 6 | 7 | "github.com/onsi/gomega/format" 8 | ) 9 | 10 | type StringerEqualMatcher struct { 11 | Expected interface{} 12 | } 13 | 14 | type Stringer interface { 15 | String() string 16 | } 17 | 18 | func (matcher *StringerEqualMatcher) Match(actual interface{}) (success bool, err error) { 19 | if actual == nil && matcher.Expected == nil { 20 | return false, fmt.Errorf("Refusing to compare to .\nBe explicit and use BeNil() instead. This is to avoid mistakes where both sides of an assertion are erroneously uninitialized.") 21 | } 22 | 23 | actualStringer, okActual := actual.(Stringer) 24 | if !okActual { 25 | return false, errors.New("refusing to compare non-stringer actual value") 26 | } 27 | 28 | expectedStringer, okExpected := matcher.Expected.(Stringer) 29 | if !okExpected { 30 | return false, errors.New("refusing to compare non-stringer expected value") 31 | } 32 | return actualStringer.String() == expectedStringer.String(), nil 33 | } 34 | 35 | func (matcher *StringerEqualMatcher) FailureMessage(actual interface{}) (message string) { 36 | actualStringer, actualOK := actual.(Stringer) 37 | expectedStringer, expectedOK := matcher.Expected.(Stringer) 38 | if actualOK && expectedOK { 39 | return format.MessageWithDiff(actualStringer.String(), "to equal", expectedStringer.String()) 40 | } 41 | 42 | return format.Message(actual, "to equal", matcher.Expected) 43 | } 44 | 45 | func (matcher *StringerEqualMatcher) NegatedFailureMessage(actual interface{}) (message string) { 46 | return format.Message(actual, "not to equal", matcher.Expected) 47 | } 48 | -------------------------------------------------------------------------------- /testing/identity.go: -------------------------------------------------------------------------------- 1 | package testing 2 | 3 | import ( 4 | "github.com/hyperledger/fabric/msp" 5 | 6 | "github.com/s7techlab/cckit/identity" 7 | ) 8 | 9 | type ( 10 | Identities map[string]identity.Identity 11 | 12 | ReadFile func(string) ([]byte, error) 13 | 14 | IdentitySample interface { 15 | SigningIdentity() msp.SigningIdentity 16 | } 17 | ) 18 | 19 | func MustIdentityFromPem(mspID string, certPEM []byte) *identity.CertIdentity { 20 | if id, err := identity.New(mspID, certPEM); err != nil { 21 | panic(err) 22 | } else { 23 | return id 24 | } 25 | } 26 | 27 | // IdentitiesFromPem returns CertIdentity (MSP ID and X.509 cert) converted PEM content 28 | func IdentitiesFromPem(mspID string, certPEMs map[string][]byte) (ids Identities, err error) { 29 | identities := make(Identities) 30 | for role, certPEM := range certPEMs { 31 | if identities[role], err = identity.New(mspID, certPEM); err != nil { 32 | return 33 | } 34 | } 35 | return identities, nil 36 | } 37 | 38 | // IdentitiesFromFiles returns map of CertIdentity, loaded from PEM files 39 | func IdentitiesFromFiles(mspID string, files map[string]string, readFile ReadFile) (Identities, error) { 40 | contents := make(map[string][]byte) 41 | for key, filename := range files { 42 | content, err := readFile(filename) 43 | if err != nil { 44 | return nil, err 45 | } 46 | contents[key] = content 47 | } 48 | return IdentitiesFromPem(mspID, contents) 49 | } 50 | 51 | // IdentityFromFile returns Identity struct containing mspId and certificate 52 | func IdentityFromFile(mspID string, file string, readFile ReadFile) (*identity.CertIdentity, error) { 53 | content, err := readFile(file) 54 | if err != nil { 55 | return nil, err 56 | } 57 | 58 | return identity.New(mspID, content) 59 | } 60 | 61 | func MustIdentitiesFromFiles(mspID string, files map[string]string, readFile ReadFile) Identities { 62 | ids, err := IdentitiesFromFiles(mspID, files, readFile) 63 | if err != nil { 64 | panic(err) 65 | } else { 66 | return ids 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /testing/mocked_peer_test.go: -------------------------------------------------------------------------------- 1 | package testing_test 2 | 3 | import ( 4 | "context" 5 | 6 | "github.com/golang/protobuf/ptypes/empty" 7 | . "github.com/onsi/ginkgo" 8 | . "github.com/onsi/gomega" 9 | 10 | "github.com/s7techlab/cckit/examples/cpaper_asservice" 11 | cpservice "github.com/s7techlab/cckit/examples/cpaper_asservice" 12 | "github.com/s7techlab/cckit/gateway" 13 | idtestdata "github.com/s7techlab/cckit/identity/testdata" 14 | testcc "github.com/s7techlab/cckit/testing" 15 | ) 16 | 17 | var _ = Describe(`Service`, func() { 18 | 19 | const ( 20 | ChaincodeName = `commercial_paper` 21 | ) 22 | 23 | var ( 24 | peer *testcc.MockedPeerDecorator 25 | cPaperGateway *cpservice.CPaperServiceGateway 26 | 27 | ctx = gateway.ContextWithSigner( 28 | context.Background(), 29 | idtestdata.Certificates[0].MustIdentity(idtestdata.DefaultMSP), 30 | ) 31 | ) 32 | 33 | It("Init", func() { 34 | ccImpl, err := cpaper_asservice.NewCC() 35 | Expect(err).NotTo(HaveOccurred()) 36 | 37 | // peer imitation 38 | peer = testcc.NewPeerDecorator(testcc.NewPeer().WithChannel(Channel, testcc.NewMockStub(ChaincodeName, ccImpl))) 39 | 40 | // "sdk" for deal with cpaper chaincode 41 | cPaperGateway = cpservice.NewCPaperServiceGateway(peer, Channel, ChaincodeName) 42 | }) 43 | 44 | It("Default invoker", func() { 45 | _, err := cPaperGateway.List(ctx, &empty.Empty{}) 46 | Expect(err).NotTo(HaveOccurred()) 47 | }) 48 | 49 | It("Allow to imitate error while access to peer", func() { 50 | peer.FailChaincode(ChaincodeName) 51 | 52 | _, err := cPaperGateway.List(ctx, &empty.Empty{}) 53 | Expect(err).To(HaveOccurred()) 54 | }) 55 | }) 56 | -------------------------------------------------------------------------------- /testing/must.go: -------------------------------------------------------------------------------- 1 | package testing 2 | 3 | import ( 4 | "encoding/json" 5 | "time" 6 | 7 | "github.com/golang/protobuf/proto" 8 | "github.com/golang/protobuf/ptypes/timestamp" 9 | "google.golang.org/protobuf/types/known/timestamppb" 10 | 11 | "github.com/s7techlab/cckit/convert" 12 | ) 13 | 14 | func PanicIfError(err error) { 15 | if err != nil { 16 | panic(err) 17 | } 18 | } 19 | 20 | // MustProtoMarshal marshals proto.Message, panics if error 21 | func MustProtoMarshal(pb proto.Message) []byte { 22 | bb, err := proto.Marshal(pb) 23 | PanicIfError(err) 24 | 25 | return bb 26 | } 27 | 28 | func MustJSONMarshal(val interface{}) []byte { 29 | bb, err := json.Marshal(val) 30 | PanicIfError(err) 31 | return bb 32 | } 33 | 34 | // MustProtoUnmarshal unmarshal proto.Message, panics if error 35 | func MustProtoUnmarshal(bb []byte, pm proto.Message) proto.Message { 36 | p := proto.Clone(pm) 37 | PanicIfError(proto.Unmarshal(bb, p)) 38 | return p 39 | } 40 | 41 | // MustProtoTimestamp creates proto.Timestamp, panics if error 42 | func MustProtoTimestamp(t time.Time) *timestamp.Timestamp { 43 | return timestamppb.New(t) 44 | } 45 | 46 | func MustConvertFromBytes(bb []byte, target interface{}) interface{} { 47 | v, err := convert.FromBytes(bb, target) 48 | PanicIfError(err) 49 | return v 50 | } 51 | 52 | // MustTime returns Timestamp for date string or panic 53 | func MustTime(s string) *timestamp.Timestamp { 54 | t, err := time.Parse(time.RFC3339, s) 55 | if err != nil { 56 | panic(err) 57 | } 58 | 59 | return timestamppb.New(t) 60 | } 61 | -------------------------------------------------------------------------------- /testing/testdata/cc_tx_state_isolation.go: -------------------------------------------------------------------------------- 1 | package testdata 2 | 3 | import ( 4 | "github.com/s7techlab/cckit/router" 5 | ) 6 | 7 | const ( 8 | Key1 = `abc` 9 | 10 | TxIsolationReadAfterWrite = `ReadAfterWrite` 11 | TxIsolationReadAfterDelete = `ReadAfterDelete` 12 | ) 13 | 14 | var ( 15 | Value1 = []byte(`cde`) 16 | ) 17 | 18 | func NewTxIsolationCC() *router.Chaincode { 19 | r := router.New(`tx_isolation`) 20 | 21 | r.Query(TxIsolationReadAfterWrite, ReadAfterWrite). 22 | Query(TxIsolationReadAfterDelete, ReadAfterDelete) 23 | 24 | return router.NewChaincode(r) 25 | } 26 | 27 | func ReadAfterWrite(c router.Context) (interface{}, error) { 28 | if err := c.State().Put(Key1, Value1); err != nil { 29 | return nil, err 30 | } 31 | 32 | // return empty, cause state changes cannot be read 33 | res, _ := c.State().Get(Key1) 34 | // if we return error - state changes will not apply 35 | return res, nil 36 | } 37 | 38 | func ReadAfterDelete(c router.Context) (interface{}, error) { 39 | if err := c.State().Delete(Key1); err != nil { 40 | return nil, err 41 | } 42 | // return non-empty, cause state changes, include deletion, cannot be read 43 | return c.State().Get(Key1) 44 | } 45 | -------------------------------------------------------------------------------- /testing/txcreator_transformer.go: -------------------------------------------------------------------------------- 1 | package testing 2 | 3 | import ( 4 | "github.com/golang/protobuf/proto" 5 | pmsp "github.com/hyperledger/fabric-protos-go/msp" 6 | "github.com/hyperledger/fabric/msp" 7 | 8 | "github.com/s7techlab/cckit/identity" 9 | ) 10 | 11 | func CreatorFromSigningIdentity(creator msp.SigningIdentity) (mspID string, certPEM []byte, err error) { 12 | serialized, err := creator.Serialize() 13 | if err != nil { 14 | return ``, nil, err 15 | } 16 | 17 | sid := &pmsp.SerializedIdentity{} 18 | if err = proto.Unmarshal(serialized, sid); err != nil { 19 | return ``, nil, err 20 | } 21 | return sid.Mspid, sid.IdBytes, nil 22 | } 23 | 24 | // TransformCreator transforms arbitrary tx creator (pmsp.SerializedIdentity etc) to mspID string, certPEM []byte, 25 | func TransformCreator(txCreator ...interface{}) (mspID string, certPEM []byte, err error) { 26 | if len(txCreator) == 1 { 27 | switch c := txCreator[0].(type) { 28 | 29 | case identity.CertIdentity: 30 | return c.MspID, c.GetPEM(), nil 31 | 32 | case *identity.CertIdentity: 33 | return c.MspID, c.GetPEM(), nil 34 | 35 | case pmsp.SerializedIdentity: 36 | return c.Mspid, c.IdBytes, nil 37 | 38 | case IdentitySample: 39 | id := c.SigningIdentity() 40 | return CreatorFromSigningIdentity(id) 41 | 42 | case msp.SigningIdentity: 43 | return CreatorFromSigningIdentity(c) 44 | 45 | case [2]string: 46 | // array with 2 elements - mspId and ca cert 47 | return c[0], []byte(c[1]), nil 48 | } 49 | } else if len(txCreator) == 2 { 50 | return txCreator[0].(string), txCreator[1].([]byte), nil 51 | } 52 | 53 | return ``, nil, ErrUnknownFromArgsType 54 | } 55 | -------------------------------------------------------------------------------- /third_party/buf.yaml: -------------------------------------------------------------------------------- 1 | version: v1 2 | breaking: 3 | use: 4 | - FILE 5 | lint: 6 | use: 7 | - MINIMAL 8 | except: 9 | - PACKAGE_DIRECTORY_MATCH 10 | -------------------------------------------------------------------------------- /third_party/google/api/annotations.proto: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2015, Google Inc. 2 | // 3 | // Licensed under the Apache License, Version 2.0 (the "License"); 4 | // you may not use this file except in compliance with the License. 5 | // You may obtain a copy of the License at 6 | // 7 | // http://www.apache.org/licenses/LICENSE-2.0 8 | // 9 | // Unless required by applicable law or agreed to in writing, software 10 | // distributed under the License is distributed on an "AS IS" BASIS, 11 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | // See the License for the specific language governing permissions and 13 | // limitations under the License. 14 | 15 | syntax = "proto3"; 16 | 17 | package google.api; 18 | 19 | import "google/api/http.proto"; 20 | import "google/protobuf/descriptor.proto"; 21 | 22 | option go_package = "google.golang.org/genproto/googleapis/api/annotations;annotations"; 23 | option java_multiple_files = true; 24 | option java_outer_classname = "AnnotationsProto"; 25 | option java_package = "com.google.api"; 26 | option objc_class_prefix = "GAPI"; 27 | 28 | extend google.protobuf.MethodOptions { 29 | // See `HttpRule`. 30 | HttpRule http = 72295728; 31 | } 32 | -------------------------------------------------------------------------------- /third_party/hyperledger/fabric/peer/chaincode_event.proto: -------------------------------------------------------------------------------- 1 | // Copyright the Hyperledger Fabric contributors. All rights reserved. 2 | // 3 | // SPDX-License-Identifier: Apache-2.0 4 | 5 | syntax = "proto3"; 6 | 7 | option java_package = "org.hyperledger.fabric.protos.peer"; 8 | option java_outer_classname = "ChaincodeEventPackage"; 9 | option go_package = "github.com/hyperledger/fabric-protos-go/peer"; 10 | 11 | package protos; 12 | 13 | //ChaincodeEvent is used for events and registrations that are specific to chaincode 14 | //string type - "chaincode" 15 | message ChaincodeEvent { 16 | string chaincode_id = 1; 17 | string tx_id = 2; 18 | string event_name = 3; 19 | bytes payload = 4; 20 | } 21 | --------------------------------------------------------------------------------