diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 6b5f75cb..00000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright the Hyperledger Fabric contributors. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -name: fabric-chaincode-go - -on: - workflow_dispatch: - workflow_call: - pull_request: - branches: - - main - -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version: "1.21" - - name: install Tools - working-directory: ci/tools - run: | - go install golang.org/x/lint/golint - go install golang.org/x/tools/cmd/goimports - - name: Vet and lint - run: ci/scripts/lint.sh - - name: Run tests - run: go test -race ./... - - build-v2: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v4 - - uses: actions/setup-go@v5 - with: - go-version: "1.21" - - name: Staticcheck - run: make staticcheck - - name: golangci-lint - uses: golangci/golangci-lint-action@v6 - with: - version: latest - working-directory: v2 - - name: Unit test - run: make unit-test diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml new file mode 100644 index 00000000..b2df5ce0 --- /dev/null +++ b/.github/workflows/pull_request.yml @@ -0,0 +1,24 @@ +# Copyright the Hyperledger Fabric contributors. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +name: Pull request + +on: + pull_request: + branches: + - main + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + build: + uses: ./.github/workflows/test.yml + + pull-request: + needs: build + name: Pull request success + runs-on: ubuntu-latest + steps: + - run: "true" diff --git a/.github/workflows/schedule.yml b/.github/workflows/schedule.yml index 791e3776..c519142c 100644 --- a/.github/workflows/schedule.yml +++ b/.github/workflows/schedule.yml @@ -7,4 +7,4 @@ on: jobs: main: - uses: ./.github/workflows/build.yml + uses: ./.github/workflows/test.yml diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 00000000..80709d7a --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,24 @@ +# Copyright the Hyperledger Fabric contributors. All rights reserved. +# SPDX-License-Identifier: Apache-2.0 + +name: Test + +on: + workflow_call: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-go@v5 + with: + go-version: "1.21" + - name: Staticcheck + run: make staticcheck + - name: golangci-lint + uses: golangci/golangci-lint-action@v6 + with: + version: latest + - name: Unit test + run: make unit-test diff --git a/.github/workflows/vulnerability-scan.yml b/.github/workflows/vulnerability-scan.yml index a74cd0cd..6fd591bf 100644 --- a/.github/workflows/vulnerability-scan.yml +++ b/.github/workflows/vulnerability-scan.yml @@ -8,12 +8,6 @@ on: jobs: scan: runs-on: ubuntu-latest - strategy: - fail-fast: false - matrix: - target: - - v1 - - v2 steps: - uses: actions/checkout@v4 - name: Set up Go @@ -22,4 +16,4 @@ jobs: go-version: stable check-latest: true - name: Scan - run: make scan-${{ matrix.target }} + run: make scan diff --git a/.gitignore b/.gitignore index 82795a1c..cb12146a 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ .#* .*.sw* /build +cover.out diff --git a/Makefile b/Makefile index f4913fee..44ad55d5 100644 --- a/Makefile +++ b/Makefile @@ -4,19 +4,20 @@ base_dir := $(patsubst %/,%,$(dir $(realpath $(lastword $(MAKEFILE_LIST))))) -v2_dir := $(base_dir)/v2 - go_bin_dir := $(shell go env GOPATH)/bin +.PHONY: test +test: lint unit-test + .PHONY: unit-test unit-test: - cd '$(v2_dir)' && \ - go test -timeout 10s -race -coverprofile=cover.out ./... + cd '$(base_dir)' && \ + go test -timeout 30s -race -coverprofile=cover.out ./... .PHONY: generate generate: go install github.com/maxbrunsfeld/counterfeiter/v6@latest - cd '$(v2_dir)' && \ + cd '$(base_dir)' && \ go generate ./... .PHONY: lint @@ -25,7 +26,7 @@ lint: staticcheck golangci-lint .PHONY: staticcheck staticcheck: go install honnef.co/go/tools/cmd/staticcheck@latest - cd '$(v2_dir)' && \ + cd '$(base_dir)' && \ staticcheck -f stylish ./... .PHONY: install-golangci-lint @@ -37,17 +38,11 @@ $(go_bin_dir)/golangci-lint: .PHONY: golangci-lint golangci-lint: $(go_bin_dir)/golangci-lint - cd '$(v2_dir)' && \ + cd '$(base_dir)' && \ golangci-lint run -.PHONY: scan-v2 -scan-v2: - go install golang.org/x/vuln/cmd/govulncheck@latest - cd '$(v2_dir)' && \ - govulncheck ./... - -.PHONY: scan-v1 -scan-v1: +.PHONY: scan +scan: go install golang.org/x/vuln/cmd/govulncheck@latest cd '$(base_dir)' && \ govulncheck ./... diff --git a/ci/scripts/install_tools.sh b/ci/scripts/install_tools.sh deleted file mode 100755 index 27af6ac3..00000000 --- a/ci/scripts/install_tools.sh +++ /dev/null @@ -1,11 +0,0 @@ -# Copyright the Hyperledger Fabric contributors. All rights reserved. -# SPDX-License-Identifier: Apache-2.0 - -set -euo pipefail - -export GOBIN=/go/bin - -pushd ci/tools -go install golang.org/x/lint/golint -go install golang.org/x/tools/cmd/goimports -popd diff --git a/ci/scripts/lint.sh b/ci/scripts/lint.sh deleted file mode 100755 index 405354b4..00000000 --- a/ci/scripts/lint.sh +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/bash - -# Copyright the Hyperledger Fabric contributors. All rights reserved. -# -# SPDX-License-Identifier: Apache-2.0 - -set -euo pipefail - -## Formatting -echo "running gofmt..." -gofmt_output="$(gofmt -l -s .)" -if [ -n "$gofmt_output" ]; then - echo "The following files contain gofmt errors:" - echo "$gofmt_output" - echo "Please run 'gofmt -l -s -w' for these files." - exit 1 -fi - -## Import management -echo "running goimports..." -goimports_output="$(goimports -l .)" -if [ -n "$goimports_output" ]; then - echo "The following files contain goimport errors:" - echo "$goimports_output" - echo "Please run 'goimports -l -w' for these files." - exit 1 -fi - -## go vet -echo "running go vet..." -go vet ./... - -## golint -echo "running golint..." -golint -set_exit_status ./... diff --git a/ci/tools/go.mod b/ci/tools/go.mod deleted file mode 100644 index 71e9a689..00000000 --- a/ci/tools/go.mod +++ /dev/null @@ -1,8 +0,0 @@ -module tools - -go 1.14 - -require ( - golang.org/x/lint v0.0.0-20200302205851-738671d3881b - golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65 -) diff --git a/ci/tools/go.sum b/ci/tools/go.sum deleted file mode 100644 index 1851a25c..00000000 --- a/ci/tools/go.sum +++ /dev/null @@ -1,24 +0,0 @@ -github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= -golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b h1:Wh+f8QHJXR411sJR8/vRBTZ7YapZaRvUcLFFJhusH0k= -golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= -golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= -golang.org/x/mod v0.2.0 h1:KU7oHjnv3XNWfa5COkzUifxZmxp1TyI7ImMXqFxLwvQ= -golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= -golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= -golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= -golang.org/x/tools v0.0.0-20200130002326-2f3ba24bd6e7/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= -golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65 h1:1KSbntBked74wYsKq0jzXYy7ZwcjAUtrl7EmPE97Iiw= -golang.org/x/tools v0.0.0-20200329025819-fd4102a86c65/go.mod h1:Sl4aGygMT6LrqrWclx+PTx3U+LnKx/seiNR+3G19Ar8= -golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543 h1:E7g+9GITq07hpfrRu66IVDexMakfv52eLZ2CXBWiKr4= -golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/ci/tools/tools.go b/ci/tools/tools.go deleted file mode 100644 index 9758b6e5..00000000 --- a/ci/tools/tools.go +++ /dev/null @@ -1,13 +0,0 @@ -//go:build tools -// +build tools - -// Copyright the Hyperledger Fabric contributors. All rights reserved. -// -// SPDX-License-Identifier: Apache-2.0 - -package tools - -import ( - _ "golang.org/x/lint/golint" - _ "golang.org/x/tools/cmd/goimports" -) diff --git a/go.mod b/go.mod index e76bf265..46c002b0 100644 --- a/go.mod +++ b/go.mod @@ -1,23 +1,22 @@ -module github.com/hyperledger/fabric-chaincode-go +module github.com/hyperledger/fabric-chaincode-go/v2 go 1.21.0 require ( - github.com/golang/protobuf v1.5.4 - github.com/hyperledger/fabric-protos-go v0.3.3 + github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3 github.com/stretchr/testify v1.9.0 google.golang.org/grpc v1.65.0 + google.golang.org/protobuf v1.34.1 ) require ( github.com/davecgh/go-spew v1.1.1 // indirect - github.com/kr/pretty v0.3.0 // indirect + github.com/kr/text v0.2.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect golang.org/x/net v0.25.0 // indirect golang.org/x/sys v0.20.0 // indirect golang.org/x/text v0.15.0 // indirect google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect - google.golang.org/protobuf v1.34.1 // indirect gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 69e5350a..1300a7ad 100644 --- a/go.sum +++ b/go.sum @@ -1,24 +1,18 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= -github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/hyperledger/fabric-protos-go v0.3.3 h1:0nssqz8QWJNVNBVQz+IIfAd2j1ku7QPKFSM/1anKizI= -github.com/hyperledger/fabric-protos-go v0.3.3/go.mod h1:BPXse9gIOQwyAePQrwQVUcc44bTW4bB5V3tujuvyArk= -github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3 h1:Xpd6fzG/KjAOHJsq7EQXY2l+qi/y8muxBaY7R6QWABk= +github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3/go.mod h1:2pq0ui6ZWA0cC8J+eCErgnMDCS1kPOEYVY+06ZAK0qE= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= -github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= -github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= @@ -34,9 +28,7 @@ google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjr google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/pkg/attrmgr/attrmgr.go b/pkg/attrmgr/attrmgr.go index 46dbdbd7..48b686c7 100644 --- a/pkg/attrmgr/attrmgr.go +++ b/pkg/attrmgr/attrmgr.go @@ -13,8 +13,8 @@ import ( "errors" "fmt" - "github.com/golang/protobuf/proto" - "github.com/hyperledger/fabric-protos-go/msp" + "github.com/hyperledger/fabric-protos-go-apiv2/msp" + "google.golang.org/protobuf/proto" ) var ( diff --git a/pkg/attrmgr/attrmgr_test.go b/pkg/attrmgr/attrmgr_test.go index b337cb8f..4aee51b1 100644 --- a/pkg/attrmgr/attrmgr_test.go +++ b/pkg/attrmgr/attrmgr_test.go @@ -8,7 +8,7 @@ import ( "encoding/base64" "testing" - "github.com/hyperledger/fabric-chaincode-go/pkg/attrmgr" + "github.com/hyperledger/fabric-chaincode-go/v2/pkg/attrmgr" "github.com/stretchr/testify/assert" ) @@ -78,6 +78,7 @@ func TestIdemixAttrs(t *testing.T) { assert.NoError(t, err, "Failed to base64 decode creator string") attrs, err := mgr.GetAttributesFromIdemix(creatorBytes) + assert.NoError(t, err, "GetAttributesFromIdemix") numAttrs := len(attrs.Names()) assert.True(t, numAttrs == 2, "expecting 2 attributes but found %d", numAttrs) checkAttr(t, "ou", "org1.department1", attrs) diff --git a/pkg/cid/README.md b/pkg/cid/README.md index 828e34e0..6c763ad9 100644 --- a/pkg/cid/README.md +++ b/pkg/cid/README.md @@ -25,7 +25,7 @@ All code samples below assume two things: 2. You have added the following import statement to your chaincode. ```golang - import "github.com/hyperledger/fabric-chaincode-go/pkg/cid" + import "github.com/hyperledger/fabric-chaincode-go/v2/pkg/cid" ``` ### Getting the client's ID diff --git a/pkg/cid/cid.go b/pkg/cid/cid.go index 80eec6c8..a6812e57 100644 --- a/pkg/cid/cid.go +++ b/pkg/cid/cid.go @@ -12,9 +12,9 @@ import ( "encoding/pem" "fmt" - "github.com/golang/protobuf/proto" - "github.com/hyperledger/fabric-chaincode-go/pkg/attrmgr" - "github.com/hyperledger/fabric-protos-go/msp" + "github.com/hyperledger/fabric-chaincode-go/v2/pkg/attrmgr" + "github.com/hyperledger/fabric-protos-go-apiv2/msp" + "google.golang.org/protobuf/proto" ) // GetID returns the ID associated with the invoking identity. This ID @@ -192,20 +192,23 @@ func (c *ClientID) getIdentity() (*msp.SerializedIdentity, error) { sid := &msp.SerializedIdentity{} creator, err := c.stub.GetCreator() if err != nil || creator == nil { - return nil, fmt.Errorf("failed to get transaction invoker's identity from the chaincode stub: %s", err) + return nil, fmt.Errorf("failed to get transaction invoker's identity from the chaincode stub: %w", err) } err = proto.Unmarshal(creator, sid) if err != nil { - return nil, fmt.Errorf("failed to unmarshal transaction invoker's identity: %s", err) + return nil, fmt.Errorf("failed to unmarshal transaction invoker's identity: %w", err) } return sid, nil } func (c *ClientID) getAttributesFromIdemix() error { creator, err := c.stub.GetCreator() + if err != nil || creator == nil { + return fmt.Errorf("failed to get transaction invoker's identity from the chaincode stub: %w", err) + } attrs, err := attrmgr.New().GetAttributesFromIdemix(creator) if err != nil { - return fmt.Errorf("failed to get attributes from the transaction invoker's idemix credential: %s", err) + return fmt.Errorf("failed to get attributes from the transaction invoker's idemix credential: %w", err) } c.attrs = attrs return nil diff --git a/pkg/cid/cid_test.go b/pkg/cid/cid_test.go index 23163821..04b9db18 100644 --- a/pkg/cid/cid_test.go +++ b/pkg/cid/cid_test.go @@ -7,10 +7,10 @@ import ( "encoding/base64" "testing" - "github.com/golang/protobuf/proto" - "github.com/hyperledger/fabric-chaincode-go/pkg/cid" - "github.com/hyperledger/fabric-protos-go/msp" + "github.com/hyperledger/fabric-chaincode-go/v2/pkg/cid" + "github.com/hyperledger/fabric-protos-go-apiv2/msp" "github.com/stretchr/testify/assert" + "google.golang.org/protobuf/proto" ) const certWithOutAttrs = `-----BEGIN CERTIFICATE----- @@ -44,6 +44,7 @@ svTTvBqLR5JeQSctJuz3zaqGRqSs2iW+QB3FAiAIP0mGWKcgSGRMMBvaqaLytBYo -----END CERTIFICATE----- ` +// #nosec G101 const idemixCred = `CiAGTPr9iYBj7gqHeU90RgXXklBhgIKbtVtUzMDnhJ9bCRIggb1mWLzNEO1ac+PfDNSpbCC02eY5OqRooSH1mnhlftEaQgoMaWRlbWl4TVNQSUQyEhBvcmcxLmRlcGFydG1lbnQxGiCEOl1qWZ+TRhDD3X6Cl59utnDnW8oio0y2vuJ1xHNMMiIOCgxpZGVtaXhNU1BJRDIq5gYKRAog1MgGWhcd/jgQpbpdO17LktSoelSsQJKfAmFhspaM6+QSICMcxQLs4JRPeSbyWG81KNepmLIi8C1AOyrgytJYMmKIEkQKICCwfD7vfGKqzGFEa7H7cBbR81kImbXcECJnDbj4QQNUEiB64Bi0jRahh18QuZzqnw6sksn8GBCi2sVsrjtLTKsvMRpECiBD9miof2CyCjHOr6s/JAiALRzjdogv0xQHEyqNAfIDABIgx56y7lUllGc0XtYsFIdq7CulDE55Re5xT1wvzRNhITUiIPSaozvr294lNGF3Wy5Yd7wlNW/IZBpBcXda/dQfGci9KiBbO2o2bWD1P4HOMfI//ebo8WrwTNgmPfmlqNBxzKuQMjIg5AwmQGEYnKN/pOVDFMjm/3a9hJDv9R2svI42aVBms0M6IHMSFIZ8j/yZH5nHtCkwpQMCuBFmI6krD2CfTjCiOUfoQiDO7cyRnCt9uEGIhQsBiwnSEXH+G9Il9qvfkUrAiZlbrkogv6dmb1xijfB3gsyVWxgfKlRNRtf78dMwjSf76jEnSrBSIJTkD7lSBwBepMFROxYneTHuG6JcSZpdoeOGqFl0drJWUiC8ndC2y9LsFJLKs2ddFqsFW7kNg+vROXuSLQdglSBffVog/eDzc90wTBEZu2T6LhWEbcP5oZ5TYdE/o+cOUfPgV4RiRAogBkz6/YmAY+4Kh3lPdEYF15JQYYCCm7VbVMzA54SfWwkSIIG9Zli8zRDtWnPj3wzUqWwgtNnmOTqkaKEh9Zp4ZX7RaiCXNeWrQz2UPkuAEZrt++TP/DbmAFF7cBQlYkb81jrn/nKIAQog/gwzULTJbCAoVg9XfCiROs4cU5oSv4Q80iYWtonAnvsSIE6mYFdzisBU21rhxjfYE7kk3Xjih9A1idJp7TSjfmorGiBwIEbnxUKjs3Z3DXUSTj5R78skdY1hWEjpCbSBvtwn/yIgBVTjvNOIwpBC7qZJKX6yn4tMvoCCGpiz4BKBEUqtBJt6ZzBlAjEAoBaHzX1HjvrnPMDXajqcLeHR5//AIIGDDcGQ+4GNqJu9Wawlw6Zs58Nnkpmh29ivAjBJNHeGNvX9sQb9lyzLAtCa5Il4xKNGGpGZ+uhQAjtNpRAZLtv2hgSqJAy0X6HwNXeAAQGKAQA=` func TestClient(t *testing.T) { @@ -70,6 +71,7 @@ func TestClient(t *testing.T) { assert.NoError(t, err, "Error getting X509 cert of the submitter of the transaction") assert.True(t, found) found, err = cid.HasOUValue(stub, "foo") + assert.NoError(t, err, "HasOUValue") assert.False(t, found, "OU 'foo' should not be found in the submitter cert") stub, err = getMockStubWithAttrs() @@ -90,17 +92,18 @@ func TestClient(t *testing.T) { assert.Error(t, err, "Assert should have failed; value was val1, not val2") found, err = cid.HasOUValue(stub, "foo") assert.NoError(t, err, "Error getting X509 cert of the submitter of the transaction") + assert.False(t, found, "HasOUValue") // Error case1 stub, err = getMockStubWithNilCreator() assert.NoError(t, err, "Failed to get mock submitter") - sinfo, err = cid.New(stub) + _, err = cid.New(stub) assert.Error(t, err, "NewSubmitterInfo should have returned an error when submitter with nil creator is passed") // Error case2 stub, err = getMockStubWithFakeCreator() assert.NoError(t, err, "Failed to get mock submitter") - sinfo, err = cid.New(stub) + _, err = cid.New(stub) assert.Error(t, err, "NewSubmitterInfo should have returned an error when submitter with fake creator is passed") } @@ -123,7 +126,8 @@ func TestIdemix(t *testing.T) { assert.NoError(t, err, "Error getting 'role' of the submitter of the transaction") assert.True(t, found, "Attribute 'role' should be found in the submitter cert") assert.Equal(t, attrVal, "member", "Value of attribute 'attr1' should be 'val1'") - attrVal, found, err = sinfo.GetAttributeValue("id") + _, found, err = sinfo.GetAttributeValue("id") + assert.NoError(t, err, "GetAttributeValue") assert.False(t, found, "Attribute 'id' should not be found in the submitter cert") } diff --git a/pkg/statebased/interfaces.go b/pkg/statebased/interfaces.go index 331e0055..7f410afd 100644 --- a/pkg/statebased/interfaces.go +++ b/pkg/statebased/interfaces.go @@ -46,6 +46,6 @@ type KeyEndorsementPolicy interface { // policy for this KVS key. DelOrgs(organizations ...string) - // ListOrgs returns an array of channel orgs that are required to endorse changes + // ListOrgs returns an array of channel orgs that are required to endorse changes. ListOrgs() []string } diff --git a/pkg/statebased/statebasedimpl.go b/pkg/statebased/statebasedimpl.go index db992c9b..ae21152a 100644 --- a/pkg/statebased/statebasedimpl.go +++ b/pkg/statebased/statebasedimpl.go @@ -7,9 +7,9 @@ import ( "fmt" "sort" - "github.com/golang/protobuf/proto" - "github.com/hyperledger/fabric-protos-go/common" - "github.com/hyperledger/fabric-protos-go/msp" + "github.com/hyperledger/fabric-protos-go-apiv2/common" + "github.com/hyperledger/fabric-protos-go-apiv2/msp" + "google.golang.org/protobuf/proto" ) // stateEP implements the KeyEndorsementPolicy @@ -35,7 +35,7 @@ func NewStateEP(policy []byte) (KeyEndorsementPolicy, error) { return s, nil } -// Policy returns the endorsement policy as bytes +// Policy returns the endorsement policy as bytes. func (s *stateEP) Policy() ([]byte, error) { spe, err := s.policyFromMSPIDs() if err != nil { @@ -48,7 +48,7 @@ func (s *stateEP) Policy() ([]byte, error) { return spBytes, nil } -// AddOrgs adds the specified channel orgs to the existing key-level EP +// AddOrgs adds the specified channel orgs to the existing key-level EP. func (s *stateEP) AddOrgs(role RoleType, neworgs ...string) error { var mspRole msp.MSPRole_MSPRoleType switch role { @@ -68,14 +68,14 @@ func (s *stateEP) AddOrgs(role RoleType, neworgs ...string) error { return nil } -// DelOrgs delete the specified channel orgs from the existing key-level EP +// DelOrgs delete the specified channel orgs from the existing key-level EP. func (s *stateEP) DelOrgs(delorgs ...string) { for _, delorg := range delorgs { delete(s.orgs, delorg) } } -// ListOrgs returns an array of channel orgs that are required to endorse changes +// ListOrgs returns an array of channel orgs that are required to endorse changes. func (s *stateEP) ListOrgs() []string { orgNames := make([]string, 0, len(s.orgs)) for mspid := range s.orgs { diff --git a/pkg/statebased/statebasedimpl_test.go b/pkg/statebased/statebasedimpl_test.go index b62668fc..fd0e8202 100644 --- a/pkg/statebased/statebasedimpl_test.go +++ b/pkg/statebased/statebasedimpl_test.go @@ -6,11 +6,11 @@ package statebased_test import ( "testing" - "github.com/golang/protobuf/proto" - "github.com/hyperledger/fabric-chaincode-go/pkg/statebased" - "github.com/hyperledger/fabric-protos-go/common" - "github.com/hyperledger/fabric-protos-go/msp" + "github.com/hyperledger/fabric-chaincode-go/v2/pkg/statebased" + "github.com/hyperledger/fabric-protos-go-apiv2/common" + "github.com/hyperledger/fabric-protos-go-apiv2/msp" "github.com/stretchr/testify/assert" + "google.golang.org/protobuf/proto" ) func TestAddOrg(t *testing.T) { @@ -40,6 +40,7 @@ func TestListOrgs(t *testing.T) { // retrieve the orgs ep, err := statebased.NewStateEP(expectedEPBytes) + assert.NoError(t, err, "NewStateEP") orgs := ep.ListOrgs() assert.Equal(t, []string{"Org1"}, orgs) } @@ -49,13 +50,15 @@ func TestDelAddOrg(t *testing.T) { expectedEPBytes, err := proto.Marshal(expectedEP) assert.NoError(t, err) ep, err := statebased.NewStateEP(expectedEPBytes) + assert.NoError(t, err) // retrieve the orgs orgs := ep.ListOrgs() assert.Equal(t, []string{"Org1"}, orgs) // mod the endorsement policy - ep.AddOrgs(statebased.RoleTypePeer, "Org2") + err = ep.AddOrgs(statebased.RoleTypePeer, "Org2") + assert.NoError(t, err) ep.DelOrgs("Org1") // check whether what is stored is correct diff --git a/shim/chaincodeserver.go b/shim/chaincodeserver.go index 2904e967..455ec4a1 100644 --- a/shim/chaincodeserver.go +++ b/shim/chaincodeserver.go @@ -7,8 +7,8 @@ import ( "crypto/tls" "errors" - "github.com/hyperledger/fabric-chaincode-go/shim/internal" - pb "github.com/hyperledger/fabric-protos-go/peer" + "github.com/hyperledger/fabric-chaincode-go/v2/shim/internal" + "github.com/hyperledger/fabric-protos-go-apiv2/peer" "google.golang.org/grpc/keepalive" ) @@ -38,7 +38,7 @@ type ChaincodeServer struct { } // Connect the bidi stream entry point called by chaincode to register with the Peer. -func (cs *ChaincodeServer) Connect(stream pb.Chaincode_ConnectServer) error { +func (cs *ChaincodeServer) Connect(stream peer.Chaincode_ConnectServer) error { return chatWithPeer(cs.CCID, stream, cs.CC) } @@ -72,7 +72,7 @@ func (cs *ChaincodeServer) Start() error { } // register the server with grpc ... - pb.RegisterChaincodeServer(server.Server, cs) + peer.RegisterChaincodeServer(server.Server, cs) // ... and start return server.Start() diff --git a/shim/handler.go b/shim/handler.go index be05c45a..a465ae26 100644 --- a/shim/handler.go +++ b/shim/handler.go @@ -8,8 +8,8 @@ import ( "fmt" "sync" - "github.com/golang/protobuf/proto" - pb "github.com/hyperledger/fabric-protos-go/peer" + "github.com/hyperledger/fabric-protos-go-apiv2/peer" + "google.golang.org/protobuf/proto" ) type state string @@ -23,8 +23,8 @@ const ( // PeerChaincodeStream is the common stream interface for Peer - chaincode communication. // Both chaincode-as-server and chaincode-as-client patterns need to support this type PeerChaincodeStream interface { - Send(*pb.ChaincodeMessage) error - Recv() (*pb.ChaincodeMessage, error) + Send(*peer.ChaincodeMessage) error + Recv() (*peer.ChaincodeMessage, error) } // ClientStream supports the (original) chaincode-as-client interaction pattern @@ -52,7 +52,7 @@ type Handler struct { // need lock to protect chaincode from attempting // concurrent requests to the peer responseChannelsMutex sync.Mutex - responseChannels map[string]chan pb.ChaincodeMessage + responseChannels map[string]chan *peer.ChaincodeMessage } func shorttxid(txid string) string { @@ -63,7 +63,7 @@ func shorttxid(txid string) string { } // serialSend serializes calls to Send on the gRPC client. -func (h *Handler) serialSend(msg *pb.ChaincodeMessage) error { +func (h *Handler) serialSend(msg *peer.ChaincodeMessage) error { h.serialLock.Lock() defer h.serialLock.Unlock() @@ -73,7 +73,7 @@ func (h *Handler) serialSend(msg *pb.ChaincodeMessage) error { // serialSendAsync sends the provided message asynchronously in a separate // goroutine. The result of the send is communicated back to the caller via // errc. -func (h *Handler) serialSendAsync(msg *pb.ChaincodeMessage, errc chan<- error) { +func (h *Handler) serialSendAsync(msg *peer.ChaincodeMessage, errc chan<- error) { go func() { errc <- h.serialSend(msg) }() @@ -85,7 +85,7 @@ func transactionContextID(chainID, txid string) string { return chainID + txid } -func (h *Handler) createResponseChannel(channelID, txid string) (<-chan pb.ChaincodeMessage, error) { +func (h *Handler) createResponseChannel(channelID, txid string) (<-chan *peer.ChaincodeMessage, error) { h.responseChannelsMutex.Lock() defer h.responseChannelsMutex.Unlock() @@ -98,7 +98,7 @@ func (h *Handler) createResponseChannel(channelID, txid string) (<-chan pb.Chain return nil, fmt.Errorf("[%s] channel exists", shorttxid(txCtxID)) } - responseChan := make(chan pb.ChaincodeMessage) + responseChan := make(chan *peer.ChaincodeMessage) h.responseChannels[txCtxID] = responseChan return responseChan, nil } @@ -112,7 +112,7 @@ func (h *Handler) deleteResponseChannel(channelID, txid string) { } } -func (h *Handler) handleResponse(msg *pb.ChaincodeMessage) error { +func (h *Handler) handleResponse(msg *peer.ChaincodeMessage) error { h.responseChannelsMutex.Lock() defer h.responseChannelsMutex.Unlock() @@ -125,7 +125,7 @@ func (h *Handler) handleResponse(msg *pb.ChaincodeMessage) error { if responseCh == nil { return fmt.Errorf("[%s] responseChannel does not exist", shorttxid(msg.Txid)) } - responseCh <- *msg + responseCh <- msg return nil } @@ -133,10 +133,10 @@ func (h *Handler) handleResponse(msg *pb.ChaincodeMessage) error { // the provided responseChan. On success, the response message will be // returned. An error will be returned msg was not successfully sent to the // peer. -func (h *Handler) sendReceive(msg *pb.ChaincodeMessage, responseChan <-chan pb.ChaincodeMessage) (pb.ChaincodeMessage, error) { +func (h *Handler) sendReceive(msg *peer.ChaincodeMessage, responseChan <-chan *peer.ChaincodeMessage) (*peer.ChaincodeMessage, error) { err := h.serialSend(msg) if err != nil { - return pb.ChaincodeMessage{}, err + return &peer.ChaincodeMessage{}, err } outmsg := <-responseChan @@ -148,25 +148,25 @@ func newChaincodeHandler(peerChatStream PeerChaincodeStream, chaincode Chaincode return &Handler{ chatStream: peerChatStream, cc: chaincode, - responseChannels: map[string]chan pb.ChaincodeMessage{}, + responseChannels: map[string]chan *peer.ChaincodeMessage{}, state: created, } } -type stubHandlerFunc func(*pb.ChaincodeMessage) (*pb.ChaincodeMessage, error) +type stubHandlerFunc func(*peer.ChaincodeMessage) (*peer.ChaincodeMessage, error) -func (h *Handler) handleStubInteraction(handler stubHandlerFunc, msg *pb.ChaincodeMessage, errc chan<- error) { +func (h *Handler) handleStubInteraction(handler stubHandlerFunc, msg *peer.ChaincodeMessage, errc chan<- error) { resp, err := handler(msg) if err != nil { - resp = &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: []byte(err.Error()), Txid: msg.Txid, ChannelId: msg.ChannelId} + resp = &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_ERROR, Payload: []byte(err.Error()), Txid: msg.Txid, ChannelId: msg.ChannelId} } h.serialSendAsync(resp, errc) } // handleInit calls the Init function of the associated chaincode. -func (h *Handler) handleInit(msg *pb.ChaincodeMessage) (*pb.ChaincodeMessage, error) { +func (h *Handler) handleInit(msg *peer.ChaincodeMessage) (*peer.ChaincodeMessage, error) { // Get the function and args from Payload - input := &pb.ChaincodeInput{} + input := &peer.ChaincodeInput{} err := proto.Unmarshal(msg.Payload, input) if err != nil { return nil, fmt.Errorf("failed to unmarshal input: %s", err) @@ -180,21 +180,21 @@ func (h *Handler) handleInit(msg *pb.ChaincodeMessage) (*pb.ChaincodeMessage, er res := h.cc.Init(stub) if res.Status >= ERROR { - return &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: []byte(res.Message), Txid: msg.Txid, ChaincodeEvent: stub.chaincodeEvent, ChannelId: msg.ChannelId}, nil + return &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_ERROR, Payload: []byte(res.Message), Txid: msg.Txid, ChaincodeEvent: stub.chaincodeEvent, ChannelId: msg.ChannelId}, nil } - resBytes, err := proto.Marshal(&res) + resBytes, err := proto.Marshal(res) if err != nil { return nil, fmt.Errorf("failed to marshal response: %s", err) } - return &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Payload: resBytes, Txid: msg.Txid, ChaincodeEvent: stub.chaincodeEvent, ChannelId: stub.ChannelID}, nil + return &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_COMPLETED, Payload: resBytes, Txid: msg.Txid, ChaincodeEvent: stub.chaincodeEvent, ChannelId: stub.ChannelID}, nil } // handleTransaction calls Invoke on the associated chaincode. -func (h *Handler) handleTransaction(msg *pb.ChaincodeMessage) (*pb.ChaincodeMessage, error) { +func (h *Handler) handleTransaction(msg *peer.ChaincodeMessage) (*peer.ChaincodeMessage, error) { // Get the function and args from Payload - input := &pb.ChaincodeInput{} + input := &peer.ChaincodeInput{} err := proto.Unmarshal(msg.Payload, input) if err != nil { return nil, fmt.Errorf("failed to unmarshal input: %s", err) @@ -209,21 +209,21 @@ func (h *Handler) handleTransaction(msg *pb.ChaincodeMessage) (*pb.ChaincodeMess res := h.cc.Invoke(stub) // Endorser will handle error contained in Response. - resBytes, err := proto.Marshal(&res) + resBytes, err := proto.Marshal(res) if err != nil { return nil, fmt.Errorf("failed to marshal response: %s", err) } - return &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_COMPLETED, Payload: resBytes, Txid: msg.Txid, ChaincodeEvent: stub.chaincodeEvent, ChannelId: stub.ChannelID}, nil + return &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_COMPLETED, Payload: resBytes, Txid: msg.Txid, ChaincodeEvent: stub.chaincodeEvent, ChannelId: stub.ChannelID}, nil } // callPeerWithChaincodeMsg sends a chaincode message to the peer for the given // txid and channel and receives the response. -func (h *Handler) callPeerWithChaincodeMsg(msg *pb.ChaincodeMessage, channelID, txid string) (pb.ChaincodeMessage, error) { +func (h *Handler) callPeerWithChaincodeMsg(msg *peer.ChaincodeMessage, channelID, txid string) (*peer.ChaincodeMessage, error) { // Create the channel on which to communicate the response from the peer respChan, err := h.createResponseChannel(channelID, txid) if err != nil { - return pb.ChaincodeMessage{}, err + return &peer.ChaincodeMessage{}, err } defer h.deleteResponseChannel(channelID, txid) @@ -233,66 +233,66 @@ func (h *Handler) callPeerWithChaincodeMsg(msg *pb.ChaincodeMessage, channelID, // handleGetState communicates with the peer to fetch the requested state information from the ledger. func (h *Handler) handleGetState(collection string, key string, channelID string, txid string) ([]byte, error) { // Construct payload for GET_STATE - payloadBytes := marshalOrPanic(&pb.GetState{Collection: collection, Key: key}) + payloadBytes := marshalOrPanic(&peer.GetState{Collection: collection, Key: key}) - msg := &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_GET_STATE, Payload: payloadBytes, Txid: txid, ChannelId: channelID} + msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_GET_STATE, Payload: payloadBytes, Txid: txid, ChannelId: channelID} responseMsg, err := h.callPeerWithChaincodeMsg(msg, channelID, txid) if err != nil { - return nil, fmt.Errorf("[%s] error sending %s: %s", shorttxid(txid), pb.ChaincodeMessage_GET_STATE, err) + return nil, fmt.Errorf("[%s] error sending %s: %s", shorttxid(txid), peer.ChaincodeMessage_GET_STATE, err) } - if responseMsg.Type == pb.ChaincodeMessage_RESPONSE { + if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { // Success response return responseMsg.Payload, nil } - if responseMsg.Type == pb.ChaincodeMessage_ERROR { + if responseMsg.Type == peer.ChaincodeMessage_ERROR { // Error response return nil, fmt.Errorf("%s", responseMsg.Payload[:]) } // Incorrect chaincode message received - return nil, fmt.Errorf("[%s] incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, pb.ChaincodeMessage_RESPONSE, pb.ChaincodeMessage_ERROR) + return nil, fmt.Errorf("[%s] incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR) } func (h *Handler) handleGetPrivateDataHash(collection string, key string, channelID string, txid string) ([]byte, error) { // Construct payload for GET_PRIVATE_DATA_HASH - payloadBytes := marshalOrPanic(&pb.GetState{Collection: collection, Key: key}) + payloadBytes := marshalOrPanic(&peer.GetState{Collection: collection, Key: key}) - msg := &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_GET_PRIVATE_DATA_HASH, Payload: payloadBytes, Txid: txid, ChannelId: channelID} + msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_GET_PRIVATE_DATA_HASH, Payload: payloadBytes, Txid: txid, ChannelId: channelID} responseMsg, err := h.callPeerWithChaincodeMsg(msg, channelID, txid) if err != nil { - return nil, fmt.Errorf("[%s] error sending %s: %s", shorttxid(txid), pb.ChaincodeMessage_GET_PRIVATE_DATA_HASH, err) + return nil, fmt.Errorf("[%s] error sending %s: %s", shorttxid(txid), peer.ChaincodeMessage_GET_PRIVATE_DATA_HASH, err) } - if responseMsg.Type == pb.ChaincodeMessage_RESPONSE { + if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { // Success response return responseMsg.Payload, nil } - if responseMsg.Type == pb.ChaincodeMessage_ERROR { + if responseMsg.Type == peer.ChaincodeMessage_ERROR { // Error response return nil, fmt.Errorf("%s", responseMsg.Payload[:]) } // Incorrect chaincode message received - return nil, fmt.Errorf("[%s] incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, pb.ChaincodeMessage_RESPONSE, pb.ChaincodeMessage_ERROR) + return nil, fmt.Errorf("[%s] incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR) } func (h *Handler) handleGetStateMetadata(collection string, key string, channelID string, txID string) (map[string][]byte, error) { // Construct payload for GET_STATE_METADATA - payloadBytes := marshalOrPanic(&pb.GetStateMetadata{Collection: collection, Key: key}) + payloadBytes := marshalOrPanic(&peer.GetStateMetadata{Collection: collection, Key: key}) - msg := &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_GET_STATE_METADATA, Payload: payloadBytes, Txid: txID, ChannelId: channelID} + msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_GET_STATE_METADATA, Payload: payloadBytes, Txid: txID, ChannelId: channelID} responseMsg, err := h.callPeerWithChaincodeMsg(msg, channelID, txID) if err != nil { - return nil, fmt.Errorf("[%s] error sending %s: %s", shorttxid(txID), pb.ChaincodeMessage_GET_STATE_METADATA, err) + return nil, fmt.Errorf("[%s] error sending %s: %s", shorttxid(txID), peer.ChaincodeMessage_GET_STATE_METADATA, err) } - if responseMsg.Type == pb.ChaincodeMessage_RESPONSE { + if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { // Success response - var mdResult pb.StateMetadataResult + var mdResult peer.StateMetadataResult err := proto.Unmarshal(responseMsg.Payload, &mdResult) if err != nil { - return nil, errors.New("Could not unmarshal metadata response") + return nil, errors.New("could not unmarshal metadata response") } metadata := make(map[string][]byte) for _, md := range mdResult.Entries { @@ -301,127 +301,127 @@ func (h *Handler) handleGetStateMetadata(collection string, key string, channelI return metadata, nil } - if responseMsg.Type == pb.ChaincodeMessage_ERROR { + if responseMsg.Type == peer.ChaincodeMessage_ERROR { // Error response return nil, fmt.Errorf("%s", responseMsg.Payload[:]) } // Incorrect chaincode message received - return nil, fmt.Errorf("[%s] incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, pb.ChaincodeMessage_RESPONSE, pb.ChaincodeMessage_ERROR) + return nil, fmt.Errorf("[%s] incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR) } // handlePutState communicates with the peer to put state information into the ledger. func (h *Handler) handlePutState(collection string, key string, value []byte, channelID string, txid string) error { // Construct payload for PUT_STATE - payloadBytes := marshalOrPanic(&pb.PutState{Collection: collection, Key: key, Value: value}) + payloadBytes := marshalOrPanic(&peer.PutState{Collection: collection, Key: key, Value: value}) - msg := &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_PUT_STATE, Payload: payloadBytes, Txid: txid, ChannelId: channelID} + msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_PUT_STATE, Payload: payloadBytes, Txid: txid, ChannelId: channelID} // Execute the request and get response responseMsg, err := h.callPeerWithChaincodeMsg(msg, channelID, txid) if err != nil { - return fmt.Errorf("[%s] error sending %s: %s", msg.Txid, pb.ChaincodeMessage_PUT_STATE, err) + return fmt.Errorf("[%s] error sending %s: %s", msg.Txid, peer.ChaincodeMessage_PUT_STATE, err) } - if responseMsg.Type == pb.ChaincodeMessage_RESPONSE { + if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { // Success response return nil } - if responseMsg.Type == pb.ChaincodeMessage_ERROR { + if responseMsg.Type == peer.ChaincodeMessage_ERROR { // Error response return fmt.Errorf("%s", responseMsg.Payload[:]) } // Incorrect chaincode message received - return fmt.Errorf("[%s] incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, pb.ChaincodeMessage_RESPONSE, pb.ChaincodeMessage_ERROR) + return fmt.Errorf("[%s] incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR) } func (h *Handler) handlePutStateMetadataEntry(collection string, key string, metakey string, metadata []byte, channelID string, txID string) error { // Construct payload for PUT_STATE_METADATA - md := &pb.StateMetadata{Metakey: metakey, Value: metadata} - payloadBytes := marshalOrPanic(&pb.PutStateMetadata{Collection: collection, Key: key, Metadata: md}) + md := &peer.StateMetadata{Metakey: metakey, Value: metadata} + payloadBytes := marshalOrPanic(&peer.PutStateMetadata{Collection: collection, Key: key, Metadata: md}) - msg := &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_PUT_STATE_METADATA, Payload: payloadBytes, Txid: txID, ChannelId: channelID} + msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_PUT_STATE_METADATA, Payload: payloadBytes, Txid: txID, ChannelId: channelID} // Execute the request and get response responseMsg, err := h.callPeerWithChaincodeMsg(msg, channelID, txID) if err != nil { - return fmt.Errorf("[%s] error sending %s: %s", msg.Txid, pb.ChaincodeMessage_PUT_STATE_METADATA, err) + return fmt.Errorf("[%s] error sending %s: %s", msg.Txid, peer.ChaincodeMessage_PUT_STATE_METADATA, err) } - if responseMsg.Type == pb.ChaincodeMessage_RESPONSE { + if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { // Success response return nil } - if responseMsg.Type == pb.ChaincodeMessage_ERROR { + if responseMsg.Type == peer.ChaincodeMessage_ERROR { // Error response return fmt.Errorf("%s", responseMsg.Payload[:]) } // Incorrect chaincode message received - return fmt.Errorf("[%s]incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, pb.ChaincodeMessage_RESPONSE, pb.ChaincodeMessage_ERROR) + return fmt.Errorf("[%s]incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR) } // handleDelState communicates with the peer to delete a key from the state in the ledger. func (h *Handler) handleDelState(collection string, key string, channelID string, txid string) error { - payloadBytes := marshalOrPanic(&pb.DelState{Collection: collection, Key: key}) - msg := &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_DEL_STATE, Payload: payloadBytes, Txid: txid, ChannelId: channelID} + payloadBytes := marshalOrPanic(&peer.DelState{Collection: collection, Key: key}) + msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_DEL_STATE, Payload: payloadBytes, Txid: txid, ChannelId: channelID} // Execute the request and get response responseMsg, err := h.callPeerWithChaincodeMsg(msg, channelID, txid) if err != nil { - return fmt.Errorf("[%s] error sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_DEL_STATE) + return fmt.Errorf("[%s] error sending %s", shorttxid(msg.Txid), peer.ChaincodeMessage_DEL_STATE) } - if responseMsg.Type == pb.ChaincodeMessage_RESPONSE { + if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { // Success response return nil } - if responseMsg.Type == pb.ChaincodeMessage_ERROR { + if responseMsg.Type == peer.ChaincodeMessage_ERROR { // Error response return fmt.Errorf("%s", responseMsg.Payload[:]) } // Incorrect chaincode message received - return fmt.Errorf("[%s] incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, pb.ChaincodeMessage_RESPONSE, pb.ChaincodeMessage_ERROR) + return fmt.Errorf("[%s] incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR) } // handlePurgeState communicates with the peer to purge a state from private data func (h *Handler) handlePurgeState(collection string, key string, channelID string, txid string) error { - payloadBytes := marshalOrPanic(&pb.DelState{Collection: collection, Key: key}) - msg := &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_PURGE_PRIVATE_DATA, Payload: payloadBytes, Txid: txid, ChannelId: channelID} + payloadBytes := marshalOrPanic(&peer.DelState{Collection: collection, Key: key}) + msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_PURGE_PRIVATE_DATA, Payload: payloadBytes, Txid: txid, ChannelId: channelID} // Execute the request and get response responseMsg, err := h.callPeerWithChaincodeMsg(msg, channelID, txid) if err != nil { - return fmt.Errorf("[%s] error sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_DEL_STATE) + return fmt.Errorf("[%s] error sending %s", shorttxid(msg.Txid), peer.ChaincodeMessage_DEL_STATE) } - if responseMsg.Type == pb.ChaincodeMessage_RESPONSE { + if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { // Success response return nil } - if responseMsg.Type == pb.ChaincodeMessage_ERROR { + if responseMsg.Type == peer.ChaincodeMessage_ERROR { // Error response return fmt.Errorf("%s", responseMsg.Payload[:]) } // Incorrect chaincode message received - return fmt.Errorf("[%s] incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, pb.ChaincodeMessage_RESPONSE, pb.ChaincodeMessage_ERROR) + return fmt.Errorf("[%s] incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR) } func (h *Handler) handleGetStateByRange(collection, startKey, endKey string, metadata []byte, - channelID string, txid string) (*pb.QueryResponse, error) { + channelID string, txid string) (*peer.QueryResponse, error) { // Send GET_STATE_BY_RANGE message to peer chaincode support - payloadBytes := marshalOrPanic(&pb.GetStateByRange{Collection: collection, StartKey: startKey, EndKey: endKey, Metadata: metadata}) - msg := &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_GET_STATE_BY_RANGE, Payload: payloadBytes, Txid: txid, ChannelId: channelID} + payloadBytes := marshalOrPanic(&peer.GetStateByRange{Collection: collection, StartKey: startKey, EndKey: endKey, Metadata: metadata}) + msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_GET_STATE_BY_RANGE, Payload: payloadBytes, Txid: txid, ChannelId: channelID} responseMsg, err := h.callPeerWithChaincodeMsg(msg, channelID, txid) if err != nil { - return nil, fmt.Errorf("[%s] error sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_GET_STATE_BY_RANGE) + return nil, fmt.Errorf("[%s] error sending %s", shorttxid(msg.Txid), peer.ChaincodeMessage_GET_STATE_BY_RANGE) } - if responseMsg.Type == pb.ChaincodeMessage_RESPONSE { + if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { // Success response - rangeQueryResponse := &pb.QueryResponse{} + rangeQueryResponse := &peer.QueryResponse{} err = proto.Unmarshal(responseMsg.Payload, rangeQueryResponse) if err != nil { return nil, fmt.Errorf("[%s] GetStateByRangeResponse unmarshall error", shorttxid(responseMsg.Txid)) @@ -429,16 +429,16 @@ func (h *Handler) handleGetStateByRange(collection, startKey, endKey string, met return rangeQueryResponse, nil } - if responseMsg.Type == pb.ChaincodeMessage_ERROR { + if responseMsg.Type == peer.ChaincodeMessage_ERROR { // Error response return nil, fmt.Errorf("%s", responseMsg.Payload[:]) } // Incorrect chaincode message received - return nil, fmt.Errorf("incorrect chaincode message %s received. Expecting %s or %s", responseMsg.Type, pb.ChaincodeMessage_RESPONSE, pb.ChaincodeMessage_ERROR) + return nil, fmt.Errorf("incorrect chaincode message %s received. Expecting %s or %s", responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR) } -func (h *Handler) handleQueryStateNext(id, channelID, txid string) (*pb.QueryResponse, error) { +func (h *Handler) handleQueryStateNext(id, channelID, txid string) (*peer.QueryResponse, error) { // Create the channel on which to communicate the response from validating peer respChan, err := h.createResponseChannel(channelID, txid) if err != nil { @@ -447,35 +447,35 @@ func (h *Handler) handleQueryStateNext(id, channelID, txid string) (*pb.QueryRes defer h.deleteResponseChannel(channelID, txid) // Send QUERY_STATE_NEXT message to peer chaincode support - payloadBytes := marshalOrPanic(&pb.QueryStateNext{Id: id}) + payloadBytes := marshalOrPanic(&peer.QueryStateNext{Id: id}) - msg := &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_QUERY_STATE_NEXT, Payload: payloadBytes, Txid: txid, ChannelId: channelID} + msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_QUERY_STATE_NEXT, Payload: payloadBytes, Txid: txid, ChannelId: channelID} - var responseMsg pb.ChaincodeMessage + var responseMsg *peer.ChaincodeMessage if responseMsg, err = h.sendReceive(msg, respChan); err != nil { - return nil, fmt.Errorf("[%s] error sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_QUERY_STATE_NEXT) + return nil, fmt.Errorf("[%s] error sending %s", shorttxid(msg.Txid), peer.ChaincodeMessage_QUERY_STATE_NEXT) } - if responseMsg.Type == pb.ChaincodeMessage_RESPONSE { + if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { // Success response - queryResponse := &pb.QueryResponse{} + queryResponse := &peer.QueryResponse{} if err = proto.Unmarshal(responseMsg.Payload, queryResponse); err != nil { return nil, fmt.Errorf("[%s] unmarshal error", shorttxid(responseMsg.Txid)) } return queryResponse, nil } - if responseMsg.Type == pb.ChaincodeMessage_ERROR { + if responseMsg.Type == peer.ChaincodeMessage_ERROR { // Error response return nil, fmt.Errorf("%s", responseMsg.Payload[:]) } // Incorrect chaincode message received - return nil, fmt.Errorf("incorrect chaincode message %s received. Expecting %s or %s", responseMsg.Type, pb.ChaincodeMessage_RESPONSE, pb.ChaincodeMessage_ERROR) + return nil, fmt.Errorf("incorrect chaincode message %s received. Expecting %s or %s", responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR) } -func (h *Handler) handleQueryStateClose(id, channelID, txid string) (*pb.QueryResponse, error) { +func (h *Handler) handleQueryStateClose(id, channelID, txid string) (*peer.QueryResponse, error) { // Create the channel on which to communicate the response from validating peer respChan, err := h.createResponseChannel(channelID, txid) if err != nil { @@ -484,63 +484,63 @@ func (h *Handler) handleQueryStateClose(id, channelID, txid string) (*pb.QueryRe defer h.deleteResponseChannel(channelID, txid) // Send QUERY_STATE_CLOSE message to peer chaincode support - payloadBytes := marshalOrPanic(&pb.QueryStateClose{Id: id}) + payloadBytes := marshalOrPanic(&peer.QueryStateClose{Id: id}) - msg := &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_QUERY_STATE_CLOSE, Payload: payloadBytes, Txid: txid, ChannelId: channelID} + msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_QUERY_STATE_CLOSE, Payload: payloadBytes, Txid: txid, ChannelId: channelID} - var responseMsg pb.ChaincodeMessage + var responseMsg *peer.ChaincodeMessage if responseMsg, err = h.sendReceive(msg, respChan); err != nil { - return nil, fmt.Errorf("[%s] error sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_QUERY_STATE_CLOSE) + return nil, fmt.Errorf("[%s] error sending %s", shorttxid(msg.Txid), peer.ChaincodeMessage_QUERY_STATE_CLOSE) } - if responseMsg.Type == pb.ChaincodeMessage_RESPONSE { + if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { // Success response - queryResponse := &pb.QueryResponse{} + queryResponse := &peer.QueryResponse{} if err = proto.Unmarshal(responseMsg.Payload, queryResponse); err != nil { return nil, fmt.Errorf("[%s] unmarshal error", shorttxid(responseMsg.Txid)) } return queryResponse, nil } - if responseMsg.Type == pb.ChaincodeMessage_ERROR { + if responseMsg.Type == peer.ChaincodeMessage_ERROR { // Error response return nil, fmt.Errorf("%s", responseMsg.Payload[:]) } // Incorrect chaincode message received - return nil, fmt.Errorf("incorrect chaincode message %s received. Expecting %s or %s", responseMsg.Type, pb.ChaincodeMessage_RESPONSE, pb.ChaincodeMessage_ERROR) + return nil, fmt.Errorf("incorrect chaincode message %s received. Expecting %s or %s", responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR) } func (h *Handler) handleGetQueryResult(collection string, query string, metadata []byte, - channelID string, txid string) (*pb.QueryResponse, error) { + channelID string, txid string) (*peer.QueryResponse, error) { // Send GET_QUERY_RESULT message to peer chaincode support - payloadBytes := marshalOrPanic(&pb.GetQueryResult{Collection: collection, Query: query, Metadata: metadata}) - msg := &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_GET_QUERY_RESULT, Payload: payloadBytes, Txid: txid, ChannelId: channelID} + payloadBytes := marshalOrPanic(&peer.GetQueryResult{Collection: collection, Query: query, Metadata: metadata}) + msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_GET_QUERY_RESULT, Payload: payloadBytes, Txid: txid, ChannelId: channelID} responseMsg, err := h.callPeerWithChaincodeMsg(msg, channelID, txid) if err != nil { - return nil, fmt.Errorf("[%s] error sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_GET_QUERY_RESULT) + return nil, fmt.Errorf("[%s] error sending %s", shorttxid(msg.Txid), peer.ChaincodeMessage_GET_QUERY_RESULT) } - if responseMsg.Type == pb.ChaincodeMessage_RESPONSE { + if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { // Success response - executeQueryResponse := &pb.QueryResponse{} + executeQueryResponse := &peer.QueryResponse{} if err = proto.Unmarshal(responseMsg.Payload, executeQueryResponse); err != nil { return nil, fmt.Errorf("[%s] unmarshal error", shorttxid(responseMsg.Txid)) } return executeQueryResponse, nil } - if responseMsg.Type == pb.ChaincodeMessage_ERROR { + if responseMsg.Type == peer.ChaincodeMessage_ERROR { // Error response return nil, fmt.Errorf("%s", responseMsg.Payload[:]) } // Incorrect chaincode message received - return nil, fmt.Errorf("incorrect chaincode message %s received. Expecting %s or %s", responseMsg.Type, pb.ChaincodeMessage_RESPONSE, pb.ChaincodeMessage_ERROR) + return nil, fmt.Errorf("incorrect chaincode message %s received. Expecting %s or %s", responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR) } -func (h *Handler) handleGetHistoryForKey(key string, channelID string, txid string) (*pb.QueryResponse, error) { +func (h *Handler) handleGetHistoryForKey(key string, channelID string, txid string) (*peer.QueryResponse, error) { // Create the channel on which to communicate the response from validating peer respChan, err := h.createResponseChannel(channelID, txid) if err != nil { @@ -549,40 +549,40 @@ func (h *Handler) handleGetHistoryForKey(key string, channelID string, txid stri defer h.deleteResponseChannel(channelID, txid) // Send GET_HISTORY_FOR_KEY message to peer chaincode support - payloadBytes := marshalOrPanic(&pb.GetHistoryForKey{Key: key}) + payloadBytes := marshalOrPanic(&peer.GetHistoryForKey{Key: key}) - msg := &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_GET_HISTORY_FOR_KEY, Payload: payloadBytes, Txid: txid, ChannelId: channelID} - var responseMsg pb.ChaincodeMessage + msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_GET_HISTORY_FOR_KEY, Payload: payloadBytes, Txid: txid, ChannelId: channelID} + var responseMsg *peer.ChaincodeMessage if responseMsg, err = h.sendReceive(msg, respChan); err != nil { - return nil, fmt.Errorf("[%s] error sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_GET_HISTORY_FOR_KEY) + return nil, fmt.Errorf("[%s] error sending %s", shorttxid(msg.Txid), peer.ChaincodeMessage_GET_HISTORY_FOR_KEY) } - if responseMsg.Type == pb.ChaincodeMessage_RESPONSE { + if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { // Success response - getHistoryForKeyResponse := &pb.QueryResponse{} + getHistoryForKeyResponse := &peer.QueryResponse{} if err = proto.Unmarshal(responseMsg.Payload, getHistoryForKeyResponse); err != nil { return nil, fmt.Errorf("[%s] unmarshal error", shorttxid(responseMsg.Txid)) } return getHistoryForKeyResponse, nil } - if responseMsg.Type == pb.ChaincodeMessage_ERROR { + if responseMsg.Type == peer.ChaincodeMessage_ERROR { // Error response return nil, fmt.Errorf("%s", responseMsg.Payload[:]) } // Incorrect chaincode message received - return nil, fmt.Errorf("incorrect chaincode message %s received. Expecting %s or %s", responseMsg.Type, pb.ChaincodeMessage_RESPONSE, pb.ChaincodeMessage_ERROR) + return nil, fmt.Errorf("incorrect chaincode message %s received. Expecting %s or %s", responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR) } -func (h *Handler) createResponse(status int32, payload []byte) pb.Response { - return pb.Response{Status: status, Payload: payload} +func (h *Handler) createResponse(status int32, payload []byte) *peer.Response { + return &peer.Response{Status: status, Payload: payload} } // handleInvokeChaincode communicates with the peer to invoke another chaincode. -func (h *Handler) handleInvokeChaincode(chaincodeName string, args [][]byte, channelID string, txid string) pb.Response { - payloadBytes := marshalOrPanic(&pb.ChaincodeSpec{ChaincodeId: &pb.ChaincodeID{Name: chaincodeName}, Input: &pb.ChaincodeInput{Args: args}}) +func (h *Handler) handleInvokeChaincode(chaincodeName string, args [][]byte, channelID string, txid string) *peer.Response { + payloadBytes := marshalOrPanic(&peer.ChaincodeSpec{ChaincodeId: &peer.ChaincodeID{Name: chaincodeName}, Input: &peer.ChaincodeInput{Args: args}}) // Create the channel on which to communicate the response from validating peer respChan, err := h.createResponseChannel(channelID, txid) @@ -592,54 +592,54 @@ func (h *Handler) handleInvokeChaincode(chaincodeName string, args [][]byte, cha defer h.deleteResponseChannel(channelID, txid) // Send INVOKE_CHAINCODE message to peer chaincode support - msg := &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_INVOKE_CHAINCODE, Payload: payloadBytes, Txid: txid, ChannelId: channelID} + msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_INVOKE_CHAINCODE, Payload: payloadBytes, Txid: txid, ChannelId: channelID} - var responseMsg pb.ChaincodeMessage + var responseMsg *peer.ChaincodeMessage if responseMsg, err = h.sendReceive(msg, respChan); err != nil { - errStr := fmt.Sprintf("[%s] error sending %s", shorttxid(msg.Txid), pb.ChaincodeMessage_INVOKE_CHAINCODE) + errStr := fmt.Sprintf("[%s] error sending %s", shorttxid(msg.Txid), peer.ChaincodeMessage_INVOKE_CHAINCODE) return h.createResponse(ERROR, []byte(errStr)) } - if responseMsg.Type == pb.ChaincodeMessage_RESPONSE { + if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { // Success response - respMsg := &pb.ChaincodeMessage{} + respMsg := &peer.ChaincodeMessage{} if err := proto.Unmarshal(responseMsg.Payload, respMsg); err != nil { return h.createResponse(ERROR, []byte(err.Error())) } - if respMsg.Type == pb.ChaincodeMessage_COMPLETED { + if respMsg.Type == peer.ChaincodeMessage_COMPLETED { // Success response - res := &pb.Response{} + res := &peer.Response{} if err = proto.Unmarshal(respMsg.Payload, res); err != nil { return h.createResponse(ERROR, []byte(err.Error())) } - return *res + return res } return h.createResponse(ERROR, responseMsg.Payload) } - if responseMsg.Type == pb.ChaincodeMessage_ERROR { + if responseMsg.Type == peer.ChaincodeMessage_ERROR { // Error response return h.createResponse(ERROR, responseMsg.Payload) } // Incorrect chaincode message received - return h.createResponse(ERROR, []byte(fmt.Sprintf("[%s] Incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, pb.ChaincodeMessage_RESPONSE, pb.ChaincodeMessage_ERROR))) + return h.createResponse(ERROR, []byte(fmt.Sprintf("[%s] Incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR))) } // handleReady handles messages received from the peer when the handler is in the "ready" state. -func (h *Handler) handleReady(msg *pb.ChaincodeMessage, errc chan error) error { +func (h *Handler) handleReady(msg *peer.ChaincodeMessage, errc chan error) error { switch msg.Type { - case pb.ChaincodeMessage_RESPONSE, pb.ChaincodeMessage_ERROR: + case peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR: if err := h.handleResponse(msg); err != nil { return err } return nil - case pb.ChaincodeMessage_INIT: + case peer.ChaincodeMessage_INIT: go h.handleStubInteraction(h.handleInit, msg, errc) return nil - case pb.ChaincodeMessage_TRANSACTION: + case peer.ChaincodeMessage_TRANSACTION: go h.handleStubInteraction(h.handleTransaction, msg, errc) return nil @@ -649,8 +649,8 @@ func (h *Handler) handleReady(msg *pb.ChaincodeMessage, errc chan error) error { } // handleEstablished handles messages received from the peer when the handler is in the "established" state. -func (h *Handler) handleEstablished(msg *pb.ChaincodeMessage, errc chan error) error { - if msg.Type != pb.ChaincodeMessage_READY { +func (h *Handler) handleEstablished(msg *peer.ChaincodeMessage) error { + if msg.Type != peer.ChaincodeMessage_READY { return fmt.Errorf("[%s] Chaincode h cannot handle message (%s) while in state: %s", msg.Txid, msg.Type, h.state) } @@ -659,8 +659,8 @@ func (h *Handler) handleEstablished(msg *pb.ChaincodeMessage, errc chan error) e } // hanndleCreated handles messages received from the peer when the handler is in the "created" state. -func (h *Handler) handleCreated(msg *pb.ChaincodeMessage, errc chan error) error { - if msg.Type != pb.ChaincodeMessage_REGISTERED { +func (h *Handler) handleCreated(msg *peer.ChaincodeMessage) error { + if msg.Type != peer.ChaincodeMessage_REGISTERED { return fmt.Errorf("[%s] Chaincode h cannot handle message (%s) while in state: %s", msg.Txid, msg.Type, h.state) } @@ -669,8 +669,8 @@ func (h *Handler) handleCreated(msg *pb.ChaincodeMessage, errc chan error) error } // handleMessage message handles loop for shim side of chaincode/peer stream. -func (h *Handler) handleMessage(msg *pb.ChaincodeMessage, errc chan error) error { - if msg.Type == pb.ChaincodeMessage_KEEPALIVE { +func (h *Handler) handleMessage(msg *peer.ChaincodeMessage, errc chan error) error { + if msg.Type == peer.ChaincodeMessage_KEEPALIVE { h.serialSendAsync(msg, errc) return nil } @@ -680,17 +680,17 @@ func (h *Handler) handleMessage(msg *pb.ChaincodeMessage, errc chan error) error case ready: err = h.handleReady(msg, errc) case established: - err = h.handleEstablished(msg, errc) + err = h.handleEstablished(msg) case created: - err = h.handleCreated(msg, errc) + err = h.handleCreated(msg) default: panic(fmt.Sprintf("invalid handler state: %s", h.state)) } if err != nil { payload := []byte(err.Error()) - errorMsg := &pb.ChaincodeMessage{Type: pb.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} - h.serialSend(errorMsg) + errorMsg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} + h.serialSend(errorMsg) //nolint:errcheck return err } diff --git a/shim/handler_test.go b/shim/handler_test.go index 75a74859..f9d325c0 100644 --- a/shim/handler_test.go +++ b/shim/handler_test.go @@ -7,32 +7,33 @@ import ( "fmt" "testing" - "github.com/hyperledger/fabric-chaincode-go/shim/internal/mock" - peerpb "github.com/hyperledger/fabric-protos-go/peer" + "github.com/hyperledger/fabric-chaincode-go/v2/shim/internal/mock" + "github.com/hyperledger/fabric-protos-go-apiv2/peer" "github.com/stretchr/testify/assert" ) //go:generate counterfeiter -o internal/mock/peer_chaincode_stream.go --fake-name PeerChaincodeStream . peerChaincodeStream +//lint:ignore U1000 Required to avoid circular dependency with mock type peerChaincodeStream interface{ PeerChaincodeStream } //go:generate counterfeiter -o internal/mock/client_stream.go --fake-name ClientStream . clientStream +//lint:ignore U1000 Required to avoid circular dependency with mock type clientStream interface{ ClientStream } type mockChaincode struct { - errMsg string initCalled bool invokeCalled bool } -func (mcc *mockChaincode) Init(stub ChaincodeStubInterface) peerpb.Response { +func (mcc *mockChaincode) Init(stub ChaincodeStubInterface) *peer.Response { mcc.initCalled = true return Success(nil) } -func (mcc *mockChaincode) Invoke(stub ChaincodeStubInterface) peerpb.Response { +func (mcc *mockChaincode) Invoke(stub ChaincodeStubInterface) *peer.Response { mcc.invokeCalled = true return Success(nil) } @@ -46,7 +47,7 @@ func TestNewHandler_CreatedState(t *testing.T) { expected := &Handler{ chatStream: chatStream, cc: cc, - responseChannels: map[string]chan peerpb.ChaincodeMessage{}, + responseChannels: map[string]chan *peer.ChaincodeMessage{}, state: created, } @@ -63,52 +64,52 @@ func TestHandlerState(t *testing.T) { var tests = []struct { name string state state - msg *peerpb.ChaincodeMessage + msg *peer.ChaincodeMessage expectedErr string }{ { name: "created", state: created, - msg: &peerpb.ChaincodeMessage{ - Type: peerpb.ChaincodeMessage_REGISTERED, + msg: &peer.ChaincodeMessage{ + Type: peer.ChaincodeMessage_REGISTERED, }, }, { name: "wrong message type in created state", state: created, - msg: &peerpb.ChaincodeMessage{ - Type: peerpb.ChaincodeMessage_READY, + msg: &peer.ChaincodeMessage{ + Type: peer.ChaincodeMessage_READY, }, - expectedErr: fmt.Sprintf("cannot handle message (%s)", peerpb.ChaincodeMessage_READY), + expectedErr: fmt.Sprintf("cannot handle message (%s)", peer.ChaincodeMessage_READY), }, { name: "established", state: established, - msg: &peerpb.ChaincodeMessage{ - Type: peerpb.ChaincodeMessage_READY, + msg: &peer.ChaincodeMessage{ + Type: peer.ChaincodeMessage_READY, }, }, { name: "wrong message type in established state", state: established, - msg: &peerpb.ChaincodeMessage{ - Type: peerpb.ChaincodeMessage_REGISTERED, + msg: &peer.ChaincodeMessage{ + Type: peer.ChaincodeMessage_REGISTERED, }, - expectedErr: fmt.Sprintf("cannot handle message (%s)", peerpb.ChaincodeMessage_REGISTERED), + expectedErr: fmt.Sprintf("cannot handle message (%s)", peer.ChaincodeMessage_REGISTERED), }, { name: "wrong message type in ready state", state: ready, - msg: &peerpb.ChaincodeMessage{ - Type: peerpb.ChaincodeMessage_REGISTERED, + msg: &peer.ChaincodeMessage{ + Type: peer.ChaincodeMessage_REGISTERED, }, - expectedErr: fmt.Sprintf("cannot handle message (%s)", peerpb.ChaincodeMessage_REGISTERED), + expectedErr: fmt.Sprintf("cannot handle message (%s)", peer.ChaincodeMessage_REGISTERED), }, { name: "keepalive", state: established, - msg: &peerpb.ChaincodeMessage{ - Type: peerpb.ChaincodeMessage_KEEPALIVE, + msg: &peer.ChaincodeMessage{ + Type: peer.ChaincodeMessage_KEEPALIVE, }, }, } @@ -137,54 +138,54 @@ func TestHandleMessage(t *testing.T) { var tests = []struct { name string - msg *peerpb.ChaincodeMessage - msgType peerpb.ChaincodeMessage_Type + msg *peer.ChaincodeMessage + msgType peer.ChaincodeMessage_Type expectedErr string invokeCalled bool initCalled bool }{ { name: "INIT", - msg: &peerpb.ChaincodeMessage{ - Type: peerpb.ChaincodeMessage_INIT, + msg: &peer.ChaincodeMessage{ + Type: peer.ChaincodeMessage_INIT, }, - msgType: peerpb.ChaincodeMessage_COMPLETED, + msgType: peer.ChaincodeMessage_COMPLETED, initCalled: true, invokeCalled: false, }, { name: "INIT with bad payload", - msg: &peerpb.ChaincodeMessage{ - Type: peerpb.ChaincodeMessage_INIT, + msg: &peer.ChaincodeMessage{ + Type: peer.ChaincodeMessage_INIT, Payload: []byte{1}, }, - msgType: peerpb.ChaincodeMessage_ERROR, + msgType: peer.ChaincodeMessage_ERROR, initCalled: false, invokeCalled: false, }, { name: "INVOKE", - msg: &peerpb.ChaincodeMessage{ - Type: peerpb.ChaincodeMessage_TRANSACTION, + msg: &peer.ChaincodeMessage{ + Type: peer.ChaincodeMessage_TRANSACTION, }, - msgType: peerpb.ChaincodeMessage_COMPLETED, + msgType: peer.ChaincodeMessage_COMPLETED, initCalled: false, invokeCalled: true, }, { name: "INVOKE with bad payload", - msg: &peerpb.ChaincodeMessage{ - Type: peerpb.ChaincodeMessage_TRANSACTION, + msg: &peer.ChaincodeMessage{ + Type: peer.ChaincodeMessage_TRANSACTION, Payload: []byte{1}, }, - msgType: peerpb.ChaincodeMessage_ERROR, + msgType: peer.ChaincodeMessage_ERROR, initCalled: false, invokeCalled: false, }, { name: "RESPONSE with no responseChannel", - msg: &peerpb.ChaincodeMessage{ - Type: peerpb.ChaincodeMessage_RESPONSE, + msg: &peer.ChaincodeMessage{ + Type: peer.ChaincodeMessage_RESPONSE, }, expectedErr: "responseChannel does not exist", }, @@ -198,8 +199,8 @@ func TestHandleMessage(t *testing.T) { chatStream := &mock.PeerChaincodeStream{} cc := &mockChaincode{} - msgChan := make(chan *peerpb.ChaincodeMessage) - chatStream.SendStub = func(msg *peerpb.ChaincodeMessage) error { + msgChan := make(chan *peer.ChaincodeMessage) + chatStream.SendStub = func(msg *peer.ChaincodeMessage) error { go func() { msgChan <- msg }() @@ -210,7 +211,7 @@ func TestHandleMessage(t *testing.T) { handler := &Handler{ chatStream: chatStream, cc: cc, - responseChannels: map[string]chan peerpb.ChaincodeMessage{}, + responseChannels: map[string]chan *peer.ChaincodeMessage{}, state: ready, } @@ -234,20 +235,21 @@ func TestHandlePeerCalls(t *testing.T) { payload := []byte("error") h := &Handler{ cc: &mockChaincode{}, - responseChannels: map[string]chan peerpb.ChaincodeMessage{}, + responseChannels: map[string]chan *peer.ChaincodeMessage{}, state: ready, } chatStream := &mock.PeerChaincodeStream{} - chatStream.SendStub = func(msg *peerpb.ChaincodeMessage) error { + chatStream.SendStub = func(msg *peer.ChaincodeMessage) error { go func() { - h.handleResponse( - &peerpb.ChaincodeMessage{ - Type: peerpb.ChaincodeMessage_ERROR, + err := h.handleResponse( + &peer.ChaincodeMessage{ + Type: peer.ChaincodeMessage_ERROR, ChannelId: msg.GetChannelId(), Txid: msg.GetTxid(), Payload: payload, }, ) + assert.NoError(t, err, "handleResponse") }() return nil } diff --git a/shim/interfaces.go b/shim/interfaces.go index 87ecd81d..7e6a6431 100644 --- a/shim/interfaces.go +++ b/shim/interfaces.go @@ -4,9 +4,9 @@ package shim import ( - "github.com/golang/protobuf/ptypes/timestamp" - "github.com/hyperledger/fabric-protos-go/ledger/queryresult" - pb "github.com/hyperledger/fabric-protos-go/peer" + "github.com/hyperledger/fabric-protos-go-apiv2/ledger/queryresult" + "github.com/hyperledger/fabric-protos-go-apiv2/peer" + "google.golang.org/protobuf/types/known/timestamppb" ) // Chaincode interface must be implemented by all chaincodes. The fabric runs @@ -15,12 +15,12 @@ type Chaincode interface { // Init is called during Instantiate transaction after the chaincode container // has been established for the first time, allowing the chaincode to // initialize its internal data - Init(stub ChaincodeStubInterface) pb.Response + Init(stub ChaincodeStubInterface) *peer.Response // Invoke is called to update or query the ledger in a proposal transaction. // Updated state variables are not committed to the ledger until the // transaction is committed. - Invoke(stub ChaincodeStubInterface) pb.Response + Invoke(stub ChaincodeStubInterface) *peer.Response } // ChaincodeStubInterface is used by deployable chaincode apps to access and @@ -47,13 +47,13 @@ type ChaincodeStubInterface interface { // GetTxID returns the tx_id of the transaction proposal, which is unique per // transaction and per client. See - // https://godoc.org/github.com/hyperledger/fabric-protos-go/common#ChannelHeader + // https://godoc.org/github.com/hyperledger/fabric-protos-go-apiv2/common#ChannelHeader // for further details. GetTxID() string // GetChannelID returns the channel the proposal is sent to for chaincode to process. // This would be the channel_id of the transaction proposal (see - // https://godoc.org/github.com/hyperledger/fabric-protos-go/common#ChannelHeader ) + // https://godoc.org/github.com/hyperledger/fabric-protos-go-apiv2/common#ChannelHeader ) // except where the chaincode is calling another on a different channel. GetChannelID() string @@ -71,7 +71,7 @@ type ChaincodeStubInterface interface { // the called chaincode on a different channel is a `Query`, which does not // participate in state validation checks in subsequent commit phase. // If `channel` is empty, the caller's channel is assumed. - InvokeChaincode(chaincodeName string, args [][]byte, channel string) pb.Response + InvokeChaincode(chaincodeName string, args [][]byte, channel string) *peer.Response // GetState returns the value of the specified `key` from the // ledger. Note that GetState doesn't read data from the writeset, which @@ -134,7 +134,7 @@ type ChaincodeStubInterface interface { // Call Close() on the returned StateQueryIteratorInterface object when done. // This call is only supported in a read only transaction. GetStateByRangeWithPagination(startKey, endKey string, pageSize int32, - bookmark string) (StateQueryIteratorInterface, *pb.QueryResponseMetadata, error) + bookmark string) (StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) // GetStateByPartialCompositeKey queries the state in the ledger based on // a given partial composite key. This function returns an iterator @@ -173,7 +173,7 @@ type ChaincodeStubInterface interface { // a partial composite key. For a full composite key, an iter with empty response // would be returned. GetStateByPartialCompositeKeyWithPagination(objectType string, keys []string, - pageSize int32, bookmark string) (StateQueryIteratorInterface, *pb.QueryResponseMetadata, error) + pageSize int32, bookmark string) (StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) // CreateCompositeKey combines the given `attributes` to form a composite // key. The objectType and attributes are expected to have only valid utf8 @@ -219,7 +219,7 @@ type ChaincodeStubInterface interface { // must be passed as bookmark. // This call is only supported in a read only transaction. GetQueryResultWithPagination(query string, pageSize int32, - bookmark string) (StateQueryIteratorInterface, *pb.QueryResponseMetadata, error) + bookmark string) (StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) // GetHistoryForKey returns a history of key values across time. // For each historic key update, the historic value and associated @@ -355,12 +355,12 @@ type ChaincodeStubInterface interface { // GetSignedProposal returns the SignedProposal object, which contains all // data elements part of a transaction proposal. - GetSignedProposal() (*pb.SignedProposal, error) + GetSignedProposal() (*peer.SignedProposal, error) // GetTxTimestamp returns the timestamp when the transaction was created. This // is taken from the transaction ChannelHeader, therefore it will indicate the // client's timestamp and will have the same value across all endorsers. - GetTxTimestamp() (*timestamp.Timestamp, error) + GetTxTimestamp() (*timestamppb.Timestamp, error) // SetEvent allows the chaincode to set an event on the response to the // proposal to be included as part of a transaction. The event will be @@ -403,11 +403,3 @@ type HistoryQueryIteratorInterface interface { // Next returns the next key and value in the history query iterator. Next() (*queryresult.KeyModification, error) } - -// MockQueryIteratorInterface allows a chaincode to iterate over a set of -// key/value pairs returned by range query. -// TODO: Once the execute query and history query are implemented in MockStub, -// we need to update this interface -type MockQueryIteratorInterface interface { - StateQueryIteratorInterface -} diff --git a/shim/internal/client.go b/shim/internal/client.go index 6b6dde0e..4516375d 100644 --- a/shim/internal/client.go +++ b/shim/internal/client.go @@ -8,9 +8,10 @@ import ( "crypto/tls" "time" - peerpb "github.com/hyperledger/fabric-protos-go/peer" + "github.com/hyperledger/fabric-protos-go-apiv2/peer" "google.golang.org/grpc" "google.golang.org/grpc/credentials" + "google.golang.org/grpc/credentials/insecure" "google.golang.org/grpc/keepalive" ) @@ -29,8 +30,6 @@ func NewClientConn( dialOpts := []grpc.DialOption{ grpc.WithKeepaliveParams(kaOpts), - grpc.WithBlock(), - grpc.FailOnNonTempDialError(true), grpc.WithDefaultCallOptions( grpc.MaxCallRecvMsgSize(maxRecvMessageSize), grpc.MaxCallSendMsgSize(maxSendMessageSize), @@ -41,15 +40,13 @@ func NewClientConn( creds := credentials.NewTLS(tlsConf) dialOpts = append(dialOpts, grpc.WithTransportCredentials(creds)) } else { - dialOpts = append(dialOpts, grpc.WithInsecure()) + dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials())) } - ctx, cancel := context.WithTimeout(context.Background(), dialTimeout) - defer cancel() - return grpc.DialContext(ctx, address, dialOpts...) + return grpc.NewClient(address, dialOpts...) } // NewRegisterClient ... -func NewRegisterClient(conn *grpc.ClientConn) (peerpb.ChaincodeSupport_RegisterClient, error) { - return peerpb.NewChaincodeSupportClient(conn).Register(context.Background()) +func NewRegisterClient(conn *grpc.ClientConn) (peer.ChaincodeSupport_RegisterClient, error) { + return peer.NewChaincodeSupportClient(conn).Register(context.Background()) } diff --git a/shim/internal/client_test.go b/shim/internal/client_test.go index d1af8e3f..ff888c5f 100644 --- a/shim/internal/client_test.go +++ b/shim/internal/client_test.go @@ -9,19 +9,19 @@ import ( "testing" "time" - pb "github.com/hyperledger/fabric-protos-go/peer" + "github.com/hyperledger/fabric-protos-go-apiv2/peer" "github.com/stretchr/testify/assert" "google.golang.org/grpc" "google.golang.org/grpc/keepalive" ) type testServer struct { - receivedMessages chan<- *pb.ChaincodeMessage - sendMessages <-chan *pb.ChaincodeMessage + receivedMessages chan<- *peer.ChaincodeMessage + sendMessages <-chan *peer.ChaincodeMessage waitTime time.Duration } -func (t *testServer) Register(registerServer pb.ChaincodeSupport_RegisterServer) error { +func (t *testServer) Register(registerServer peer.ChaincodeSupport_RegisterServer) error { for { recv, err := registerServer.Recv() if err != nil { @@ -55,8 +55,8 @@ func TestMessageSizes(t *testing.T) { assert.NoError(t, err, "listen failed") defer lis.Close() - sendMessages := make(chan *pb.ChaincodeMessage, 1) - receivedMessages := make(chan *pb.ChaincodeMessage, 1) + sendMessages := make(chan *peer.ChaincodeMessage, 1) + receivedMessages := make(chan *peer.ChaincodeMessage, 1) testServer := &testServer{ receivedMessages: receivedMessages, sendMessages: sendMessages, @@ -67,7 +67,7 @@ func TestMessageSizes(t *testing.T) { grpc.MaxSendMsgSize(2*maxSendMessageSize), grpc.MaxRecvMsgSize(2*maxRecvMessageSize), ) - pb.RegisterChaincodeSupportServer(server, testServer) + peer.RegisterChaincodeSupportServer(server, testServer) serveCompleteCh := make(chan error, 1) go func() { serveCompleteCh <- server.Serve(lis) }() @@ -79,7 +79,7 @@ func TestMessageSizes(t *testing.T) { assert.NoError(t, err, "failed to create register client") t.Run("acceptable messaages", func(t *testing.T) { - acceptableMessage := &pb.ChaincodeMessage{ + acceptableMessage := &peer.ChaincodeMessage{ Payload: make([]byte, maxSendMessageSize-100), } sendMessages <- acceptableMessage @@ -99,10 +99,10 @@ func TestMessageSizes(t *testing.T) { }) t.Run("response message is too large", func(t *testing.T) { - sendMessages <- &pb.ChaincodeMessage{ + sendMessages <- &peer.ChaincodeMessage{ Payload: make([]byte, maxSendMessageSize+1), } - err = regClient.Send(&pb.ChaincodeMessage{}) + err = regClient.Send(&peer.ChaincodeMessage{}) assert.NoError(t, err, "sending messge below size threshold should succeed") select { @@ -117,7 +117,7 @@ func TestMessageSizes(t *testing.T) { }) t.Run("sent message is too large", func(t *testing.T) { - tooBig := &pb.ChaincodeMessage{ + tooBig := &peer.ChaincodeMessage{ Payload: make([]byte, maxSendMessageSize+1), } err = regClient.Send(tooBig) diff --git a/shim/internal/config.go b/shim/internal/config.go index bed67ab2..e7c2c4b5 100644 --- a/shim/internal/config.go +++ b/shim/internal/config.go @@ -9,7 +9,6 @@ import ( "encoding/base64" "errors" "fmt" - "io/ioutil" "os" "strconv" "time" @@ -49,12 +48,12 @@ func LoadConfig() (Config, error) { var key []byte path, set := os.LookupEnv("CORE_TLS_CLIENT_KEY_FILE") if set { - key, err = ioutil.ReadFile(path) + key, err = os.ReadFile(path) if err != nil { return Config{}, fmt.Errorf("failed to read private key file: %s", err) } } else { - data, err := ioutil.ReadFile(os.Getenv("CORE_TLS_CLIENT_KEY_PATH")) + data, err := os.ReadFile(os.Getenv("CORE_TLS_CLIENT_KEY_PATH")) if err != nil { return Config{}, fmt.Errorf("failed to read private key file: %s", err) } @@ -67,12 +66,12 @@ func LoadConfig() (Config, error) { var cert []byte path, set = os.LookupEnv("CORE_TLS_CLIENT_CERT_FILE") if set { - cert, err = ioutil.ReadFile(path) + cert, err = os.ReadFile(path) if err != nil { return Config{}, fmt.Errorf("failed to read public key file: %s", err) } } else { - data, err := ioutil.ReadFile(os.Getenv("CORE_TLS_CLIENT_CERT_PATH")) + data, err := os.ReadFile(os.Getenv("CORE_TLS_CLIENT_CERT_PATH")) if err != nil { return Config{}, fmt.Errorf("failed to read public key file: %s", err) } @@ -82,7 +81,7 @@ func LoadConfig() (Config, error) { } } - root, err := ioutil.ReadFile(os.Getenv("CORE_PEER_TLS_ROOTCERT_FILE")) + root, err := os.ReadFile(os.Getenv("CORE_PEER_TLS_ROOTCERT_FILE")) if err != nil { return Config{}, fmt.Errorf("failed to read root cert file: %s", err) } diff --git a/shim/internal/config_test.go b/shim/internal/config_test.go index 4cfb7ea9..63586fd8 100644 --- a/shim/internal/config_test.go +++ b/shim/internal/config_test.go @@ -8,13 +8,12 @@ import ( "crypto/tls" "crypto/x509" "encoding/base64" - "io/ioutil" "os" "testing" "time" - . "github.com/hyperledger/fabric-chaincode-go/shim/internal" - peerpb "github.com/hyperledger/fabric-protos-go/peer" + . "github.com/hyperledger/fabric-chaincode-go/v2/shim/internal" + "github.com/hyperledger/fabric-protos-go-apiv2/peer" "github.com/stretchr/testify/assert" "google.golang.org/grpc" "google.golang.org/grpc/credentials" @@ -63,6 +62,7 @@ bHHlP/A/QkbGqJL4HQ== -----END CERTIFICATE----- ` +// #nosec G101 var clientKeyPEM = `-----BEGIN EC PRIVATE KEY----- MHcCAQEEINVHep4/z6iPa151Ipp4MmCb1l/VKkY3vuMfUQf3LhQboAoGCCqGSM49 AwEHoUQDQgAEcE6hZ7muszSi5wXIVKPdIuLYPTIxQxj+jekPRfFnJF/RJKM0Nj3T @@ -102,13 +102,13 @@ XIlJQdS/9afDi32qZWZfe3kAUAs0 func TestLoadBase64EncodedConfig(t *testing.T) { // setup key/cert files - testDir, err := ioutil.TempDir("", "shiminternal") + testDir, err := os.MkdirTemp("", "shiminternal") if err != nil { t.Fatalf("Failed to test directory: %s", err) } defer os.RemoveAll(testDir) - keyFile, err := ioutil.TempFile(testDir, "testKey") + keyFile, err := os.CreateTemp(testDir, "testKey") if err != nil { t.Fatalf("Failed to create key file: %s", err) } @@ -117,7 +117,7 @@ func TestLoadBase64EncodedConfig(t *testing.T) { t.Fatalf("Failed to write to key file: %s", err) } - certFile, err := ioutil.TempFile(testDir, "testCert") + certFile, err := os.CreateTemp(testDir, "testCert") if err != nil { t.Fatalf("Failed to create cert file: %s", err) } @@ -126,7 +126,7 @@ func TestLoadBase64EncodedConfig(t *testing.T) { t.Fatalf("Failed to write to cert file: %s", err) } - rootFile, err := ioutil.TempFile(testDir, "testRoot") + rootFile, err := os.CreateTemp(testDir, "testRoot") if err != nil { t.Fatalf("Failed to create root file: %s", err) } @@ -134,7 +134,7 @@ func TestLoadBase64EncodedConfig(t *testing.T) { t.Fatalf("Failed to write to root file: %s", err) } - notb64File, err := ioutil.TempFile(testDir, "testNotb64") + notb64File, err := os.CreateTemp(testDir, "testNotb64") if err != nil { t.Fatalf("Failed to create notb64 file: %s", err) } @@ -142,7 +142,7 @@ func TestLoadBase64EncodedConfig(t *testing.T) { t.Fatalf("Failed to write to notb64 file: %s", err) } - notPEMFile, err := ioutil.TempFile(testDir, "testNotPEM") + notPEMFile, err := os.CreateTemp(testDir, "testNotPEM") if err != nil { t.Fatalf("Failed to create notPEM file: %s", err) } @@ -324,7 +324,7 @@ func TestLoadBase64EncodedConfig(t *testing.T) { tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - tls.TLS_RSA_WITH_AES_128_GCM_SHA256, + tls.TLS_RSA_WITH_AES_128_GCM_SHA256, // #nosec G402 tls.TLS_RSA_WITH_AES_256_GCM_SHA384, }, ClientAuth: tls.RequireAndVerifyClientCert, @@ -339,7 +339,7 @@ func TestLoadBase64EncodedConfig(t *testing.T) { tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - tls.TLS_RSA_WITH_AES_128_GCM_SHA256, + tls.TLS_RSA_WITH_AES_128_GCM_SHA256, // #nosec G402 tls.TLS_RSA_WITH_AES_256_GCM_SHA384, }, ClientAuth: tls.NoClientCert, @@ -416,13 +416,13 @@ func tlsConfigEquals(t *testing.T, cfg1 *tls.Config, cfg2 *tls.Config) { func TestLoadPEMEncodedConfig(t *testing.T) { // setup key/cert files - testDir, err := ioutil.TempDir("", "shiminternal") + testDir, err := os.MkdirTemp("", "shiminternal") if err != nil { t.Fatalf("Failed to test directory: %s", err) } defer os.RemoveAll(testDir) - keyFile, err := ioutil.TempFile(testDir, "testKey") + keyFile, err := os.CreateTemp(testDir, "testKey") if err != nil { t.Fatalf("Failed to create key file: %s", err) } @@ -430,7 +430,7 @@ func TestLoadPEMEncodedConfig(t *testing.T) { t.Fatalf("Failed to write to key file: %s", err) } - certFile, err := ioutil.TempFile(testDir, "testCert") + certFile, err := os.CreateTemp(testDir, "testCert") if err != nil { t.Fatalf("Failed to create cert file: %s", err) } @@ -438,7 +438,7 @@ func TestLoadPEMEncodedConfig(t *testing.T) { t.Fatalf("Failed to write to cert file: %s", err) } - rootFile, err := ioutil.TempFile(testDir, "testRoot") + rootFile, err := os.CreateTemp(testDir, "testRoot") if err != nil { t.Fatalf("Failed to create root file: %s", err) } @@ -446,7 +446,7 @@ func TestLoadPEMEncodedConfig(t *testing.T) { t.Fatalf("Failed to write to root file: %s", err) } - keyFile64, err := ioutil.TempFile(testDir, "testKey64") + keyFile64, err := os.CreateTemp(testDir, "testKey64") if err != nil { t.Fatalf("Failed to create key file: %s", err) } @@ -455,7 +455,7 @@ func TestLoadPEMEncodedConfig(t *testing.T) { t.Fatalf("Failed to write to key file: %s", err) } - certFile64, err := ioutil.TempFile(testDir, "testCert64") + certFile64, err := os.CreateTemp(testDir, "testCert64") if err != nil { t.Fatalf("Failed to create cert file: %s", err) } @@ -607,9 +607,7 @@ func newTLSConnection(t *testing.T, address string, crt, key, rootCert []byte) * dialOpts = append(dialOpts, grpc.WithKeepaliveParams(kap)) - ctx, cancel := context.WithTimeout(context.Background(), (5 * time.Second)) - defer cancel() - conn, err := grpc.DialContext(ctx, address, dialOpts...) + conn, err := grpc.NewClient(address, dialOpts...) assert.NoError(t, err) assert.NotNil(t, conn) @@ -637,7 +635,7 @@ func TestTLSClientWithChaincodeServer(t *testing.T) { tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - tls.TLS_RSA_WITH_AES_128_GCM_SHA256, + tls.TLS_RSA_WITH_AES_128_GCM_SHA256, // #nosec G402 tls.TLS_RSA_WITH_AES_256_GCM_SHA384, }, ClientAuth: tls.RequireAndVerifyClientCert, @@ -701,12 +699,15 @@ func TestTLSClientWithChaincodeServer(t *testing.T) { t.Fatalf("error creating server for test: %v", err) } defer srv.Stop() - go srv.Start() + go func() { + err = srv.Start() + assert.NoError(t, err, "srv.Start") + }() conn := newTLSConnection(t, srv.Listener.Addr().String(), test.clientCert, test.clientKey, test.clientRootCert) assert.NotNil(t, conn) - ccclient := peerpb.NewChaincodeClient(conn) + ccclient := peer.NewChaincodeClient(conn) assert.NotNil(t, ccclient) stream, err := ccclient.Connect(context.Background()) diff --git a/shim/internal/mock/client_stream.go b/shim/internal/mock/client_stream.go index 2ed61512..201cd636 100644 --- a/shim/internal/mock/client_stream.go +++ b/shim/internal/mock/client_stream.go @@ -4,7 +4,7 @@ package mock import ( "sync" - "github.com/hyperledger/fabric-protos-go/peer" + "github.com/hyperledger/fabric-protos-go-apiv2/peer" ) type ClientStream struct { @@ -50,15 +50,16 @@ func (fake *ClientStream) CloseSend() error { ret, specificReturn := fake.closeSendReturnsOnCall[len(fake.closeSendArgsForCall)] fake.closeSendArgsForCall = append(fake.closeSendArgsForCall, struct { }{}) + stub := fake.CloseSendStub + fakeReturns := fake.closeSendReturns fake.recordInvocation("CloseSend", []interface{}{}) fake.closeSendMutex.Unlock() - if fake.CloseSendStub != nil { - return fake.CloseSendStub() + if stub != nil { + return stub() } if specificReturn { return ret.result1 } - fakeReturns := fake.closeSendReturns return fakeReturns.result1 } @@ -102,15 +103,16 @@ func (fake *ClientStream) Recv() (*peer.ChaincodeMessage, error) { ret, specificReturn := fake.recvReturnsOnCall[len(fake.recvArgsForCall)] fake.recvArgsForCall = append(fake.recvArgsForCall, struct { }{}) + stub := fake.RecvStub + fakeReturns := fake.recvReturns fake.recordInvocation("Recv", []interface{}{}) fake.recvMutex.Unlock() - if fake.RecvStub != nil { - return fake.RecvStub() + if stub != nil { + return stub() } if specificReturn { return ret.result1, ret.result2 } - fakeReturns := fake.recvReturns return fakeReturns.result1, fakeReturns.result2 } @@ -158,15 +160,16 @@ func (fake *ClientStream) Send(arg1 *peer.ChaincodeMessage) error { fake.sendArgsForCall = append(fake.sendArgsForCall, struct { arg1 *peer.ChaincodeMessage }{arg1}) + stub := fake.SendStub + fakeReturns := fake.sendReturns fake.recordInvocation("Send", []interface{}{arg1}) fake.sendMutex.Unlock() - if fake.SendStub != nil { - return fake.SendStub(arg1) + if stub != nil { + return stub(arg1) } if specificReturn { return ret.result1 } - fakeReturns := fake.sendReturns return fakeReturns.result1 } diff --git a/shim/internal/mock/peer_chaincode_stream.go b/shim/internal/mock/peer_chaincode_stream.go index 8e12e863..d5a7f015 100644 --- a/shim/internal/mock/peer_chaincode_stream.go +++ b/shim/internal/mock/peer_chaincode_stream.go @@ -4,7 +4,7 @@ package mock import ( "sync" - "github.com/hyperledger/fabric-protos-go/peer" + "github.com/hyperledger/fabric-protos-go-apiv2/peer" ) type PeerChaincodeStream struct { @@ -40,15 +40,16 @@ func (fake *PeerChaincodeStream) Recv() (*peer.ChaincodeMessage, error) { ret, specificReturn := fake.recvReturnsOnCall[len(fake.recvArgsForCall)] fake.recvArgsForCall = append(fake.recvArgsForCall, struct { }{}) + stub := fake.RecvStub + fakeReturns := fake.recvReturns fake.recordInvocation("Recv", []interface{}{}) fake.recvMutex.Unlock() - if fake.RecvStub != nil { - return fake.RecvStub() + if stub != nil { + return stub() } if specificReturn { return ret.result1, ret.result2 } - fakeReturns := fake.recvReturns return fakeReturns.result1, fakeReturns.result2 } @@ -96,15 +97,16 @@ func (fake *PeerChaincodeStream) Send(arg1 *peer.ChaincodeMessage) error { fake.sendArgsForCall = append(fake.sendArgsForCall, struct { arg1 *peer.ChaincodeMessage }{arg1}) + stub := fake.SendStub + fakeReturns := fake.sendReturns fake.recordInvocation("Send", []interface{}{arg1}) fake.sendMutex.Unlock() - if fake.SendStub != nil { - return fake.SendStub(arg1) + if stub != nil { + return stub(arg1) } if specificReturn { return ret.result1 } - fakeReturns := fake.sendReturns return fakeReturns.result1 } diff --git a/shim/internal/server_test.go b/shim/internal/server_test.go index ab4c4eef..03d0e855 100644 --- a/shim/internal/server_test.go +++ b/shim/internal/server_test.go @@ -11,7 +11,7 @@ import ( "testing" "time" - "github.com/hyperledger/fabric-chaincode-go/shim/internal" + "github.com/hyperledger/fabric-chaincode-go/v2/shim/internal" "github.com/stretchr/testify/assert" "google.golang.org/grpc/keepalive" @@ -23,7 +23,7 @@ func TestBadServer(t *testing.T) { assert.NotNil(t, err) assert.Contains(t, err.Error(), "nil listener") - l, err := net.Listen("tcp", ":0") + l, err := net.Listen("tcp", ":0") // #nosec G102 assert.NotNil(t, l) assert.Nil(t, err) srv = &internal.Server{Listener: l} diff --git a/shim/response.go b/shim/response.go index abe62866..f56b4f68 100644 --- a/shim/response.go +++ b/shim/response.go @@ -4,7 +4,7 @@ package shim import ( - pb "github.com/hyperledger/fabric-protos-go/peer" + "github.com/hyperledger/fabric-protos-go-apiv2/peer" ) const ( @@ -20,16 +20,16 @@ const ( ) // Success ... -func Success(payload []byte) pb.Response { - return pb.Response{ +func Success(payload []byte) *peer.Response { + return &peer.Response{ Status: OK, Payload: payload, } } // Error ... -func Error(msg string) pb.Response { - return pb.Response{ +func Error(msg string) *peer.Response { + return &peer.Response{ Status: ERROR, Message: msg, } diff --git a/shim/shim.go b/shim/shim.go index fee7c221..f75839d8 100644 --- a/shim/shim.go +++ b/shim/shim.go @@ -13,9 +13,9 @@ import ( "os" "unicode/utf8" - "github.com/golang/protobuf/proto" - "github.com/hyperledger/fabric-chaincode-go/shim/internal" - peerpb "github.com/hyperledger/fabric-protos-go/peer" + "github.com/hyperledger/fabric-chaincode-go/v2/shim/internal" + "github.com/hyperledger/fabric-protos-go-apiv2/peer" + "google.golang.org/protobuf/proto" ) const ( @@ -85,7 +85,7 @@ func StartInProc(chaincodename string, stream ClientStream, cc Chaincode) error // this is the chat stream resulting from the chaincode-as-client model where the chaincode initiates connection func chaincodeAsClientChat(chaincodename string, stream ClientStream, cc Chaincode) error { - defer stream.CloseSend() + defer stream.CloseSend() //nolint:Errcheck return chatWithPeer(chaincodename, stream, cc) } @@ -95,21 +95,21 @@ func chatWithPeer(chaincodename string, stream PeerChaincodeStream, cc Chaincode handler := newChaincodeHandler(stream, cc) // Send the ChaincodeID during register. - chaincodeID := &peerpb.ChaincodeID{Name: chaincodename} + chaincodeID := &peer.ChaincodeID{Name: chaincodename} payload, err := proto.Marshal(chaincodeID) if err != nil { return fmt.Errorf("error marshalling chaincodeID during chaincode registration: %s", err) } // Register on the stream - if err = handler.serialSend(&peerpb.ChaincodeMessage{Type: peerpb.ChaincodeMessage_REGISTER, Payload: payload}); err != nil { + if err = handler.serialSend(&peer.ChaincodeMessage{Type: peer.ChaincodeMessage_REGISTER, Payload: payload}); err != nil { return fmt.Errorf("error sending chaincode REGISTER: %s", err) } // holds return values from gRPC Recv below type recvMsg struct { - msg *peerpb.ChaincodeMessage + msg *peer.ChaincodeMessage err error } msgAvail := make(chan *recvMsg, 1) diff --git a/shim/shim_test.go b/shim/shim_test.go index 44183f74..d64c1bc9 100644 --- a/shim/shim_test.go +++ b/shim/shim_test.go @@ -9,12 +9,20 @@ import ( "os" "testing" - "github.com/hyperledger/fabric-chaincode-go/shim/internal/mock" - peerpb "github.com/hyperledger/fabric-protos-go/peer" + "github.com/hyperledger/fabric-chaincode-go/v2/shim/internal/mock" + "github.com/hyperledger/fabric-protos-go-apiv2/peer" "github.com/stretchr/testify/assert" ) +// MockQueryIteratorInterface allows a chaincode to iterate over a set of +// key/value pairs returned by range query. +// TODO: Once the execute query and history query are implemented in MockStub, +// we need to update this interface +type MockQueryIteratorInterface interface { + StateQueryIteratorInterface +} + func TestStart(t *testing.T) { var tests = []struct { @@ -52,7 +60,7 @@ func TestStart(t *testing.T) { "CORE_PEER_TLS_ENABLED": "false", }, peerAddress: "127.0.0.1:12345", - expectedErr: `connection error: desc = "transport: error while dialing: dial tcp 127.0.0.1:12345: connect: connection refused"`, + expectedErr: `rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing: dial tcp 127.0.0.1:12345: connect: connection refused"`, }, { name: "Chat - Nil Message", @@ -106,8 +114,8 @@ func TestStart(t *testing.T) { stream := &mock.ClientStream{} stream.RecvReturnsOnCall( 0, - &peerpb.ChaincodeMessage{ - Type: peerpb.ChaincodeMessage_READY, + &peer.ChaincodeMessage{ + Type: peer.ChaincodeMessage_READY, Txid: "txid", }, nil, diff --git a/shim/stub.go b/shim/stub.go index b2ebbd3b..4e3fbe7a 100644 --- a/shim/stub.go +++ b/shim/stub.go @@ -11,11 +11,11 @@ import ( "os" "unicode/utf8" - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/ptypes/timestamp" - "github.com/hyperledger/fabric-protos-go/common" - "github.com/hyperledger/fabric-protos-go/ledger/queryresult" - pb "github.com/hyperledger/fabric-protos-go/peer" + "github.com/hyperledger/fabric-protos-go-apiv2/common" + "github.com/hyperledger/fabric-protos-go-apiv2/ledger/queryresult" + "github.com/hyperledger/fabric-protos-go-apiv2/peer" + "google.golang.org/protobuf/proto" + "google.golang.org/protobuf/types/known/timestamppb" ) // ChaincodeStub is an object passed to chaincode for shim side handling of @@ -23,11 +23,11 @@ import ( type ChaincodeStub struct { TxID string ChannelID string - chaincodeEvent *pb.ChaincodeEvent + chaincodeEvent *peer.ChaincodeEvent args [][]byte handler *Handler - signedProposal *pb.SignedProposal - proposal *pb.Proposal + signedProposal *peer.SignedProposal + proposal *peer.Proposal validationParameterMetakey string // Additional fields extracted from the signedProposal @@ -40,7 +40,7 @@ type ChaincodeStub struct { // ChaincodeInvocation functionality -func newChaincodeStub(handler *Handler, channelID, txid string, input *pb.ChaincodeInput, signedProposal *pb.SignedProposal) (*ChaincodeStub, error) { +func newChaincodeStub(handler *Handler, channelID, txid string, input *peer.ChaincodeInput, signedProposal *peer.SignedProposal) (*ChaincodeStub, error) { stub := &ChaincodeStub{ TxID: txid, ChannelID: channelID, @@ -48,7 +48,7 @@ func newChaincodeStub(handler *Handler, channelID, txid string, input *pb.Chainc handler: handler, signedProposal: signedProposal, decorations: input.Decorations, - validationParameterMetakey: pb.MetaDataKeys_VALIDATION_PARAMETER.String(), + validationParameterMetakey: peer.MetaDataKeys_VALIDATION_PARAMETER.String(), } // TODO: sanity check: verify that every call to init with a nil @@ -57,7 +57,7 @@ func newChaincodeStub(handler *Handler, channelID, txid string, input *pb.Chainc if signedProposal != nil { var err error - stub.proposal = &pb.Proposal{} + stub.proposal = &peer.Proposal{} err = proto.Unmarshal(signedProposal.ProposalBytes, stub.proposal) if err != nil { @@ -100,8 +100,8 @@ func newChaincodeStub(handler *Handler, channelID, txid string, input *pb.Chainc } stub.creator = shdr.GetCreator() - // extract transient data from proposal payload - payload := &pb.ChaincodeProposalPayload{} + // extract trasient data from proposal payload + payload := &peer.ChaincodeProposalPayload{} if err := proto.Unmarshal(stub.proposal.GetPayload(), payload); err != nil { return nil, fmt.Errorf("failed to extract proposal payload: %s", err) } @@ -148,7 +148,7 @@ func GetMSPID() (string, error) { // ------------- Call Chaincode functions --------------- // InvokeChaincode documentation can be found in interfaces.go -func (s *ChaincodeStub) InvokeChaincode(chaincodeName string, args [][]byte, channel string) pb.Response { +func (s *ChaincodeStub) InvokeChaincode(chaincodeName string, args [][]byte, channel string) *peer.Response { // Internally we handle chaincode name as a composite name if channel != "" { chaincodeName = chaincodeName + "/" + channel @@ -192,7 +192,7 @@ func (s *ChaincodeStub) PutState(key string, value []byte) error { return s.handler.handlePutState(collection, key, value, s.ChannelID, s.TxID) } -func (s *ChaincodeStub) createStateQueryIterator(response *pb.QueryResponse) *StateQueryIterator { +func (s *ChaincodeStub) createStateQueryIterator(response *peer.QueryResponse) *StateQueryIterator { return &StateQueryIterator{ CommonIterator: &CommonIterator{ handler: s.handler, @@ -343,7 +343,7 @@ type CommonIterator struct { handler *Handler channelID string txid string - response *pb.QueryResponse + response *peer.QueryResponse currentLoc int } @@ -372,8 +372,8 @@ const ( HistoryQueryResult ) -func createQueryResponseMetadata(metadataBytes []byte) (*pb.QueryResponseMetadata, error) { - metadata := &pb.QueryResponseMetadata{} +func createQueryResponseMetadata(metadataBytes []byte) (*peer.QueryResponseMetadata, error) { + metadata := &peer.QueryResponseMetadata{} err := proto.Unmarshal(metadataBytes, metadata) if err != nil { return nil, err @@ -383,7 +383,7 @@ func createQueryResponseMetadata(metadataBytes []byte) (*pb.QueryResponseMetadat } func (s *ChaincodeStub) handleGetStateByRange(collection, startKey, endKey string, - metadata []byte) (StateQueryIteratorInterface, *pb.QueryResponseMetadata, error) { + metadata []byte) (StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) { response, err := s.handler.handleGetStateByRange(collection, startKey, endKey, metadata, s.ChannelID, s.TxID) if err != nil { @@ -400,7 +400,7 @@ func (s *ChaincodeStub) handleGetStateByRange(collection, startKey, endKey strin } func (s *ChaincodeStub) handleGetQueryResult(collection, query string, - metadata []byte) (StateQueryIteratorInterface, *pb.QueryResponseMetadata, error) { + metadata []byte) (StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) { response, err := s.handler.handleGetQueryResult(collection, query, metadata, s.ChannelID, s.TxID) if err != nil { @@ -493,7 +493,7 @@ func validateCompositeKeyAttribute(str string) error { // To ensure that simple keys do not go into composite key namespace, // we validate simplekey to check whether the key starts with 0x00 (which -// is the namespace for compositeKey). This helps in avoiding simple/composite +// is the namespace for compositeKey). This helps in avoding simple/composite // key collisions. func validateSimpleKeys(simpleKeys ...string) error { for _, key := range simpleKeys { @@ -519,7 +519,7 @@ func (s *ChaincodeStub) GetStateByPartialCompositeKey(objectType string, attribu func createQueryMetadata(pageSize int32, bookmark string) ([]byte, error) { // Construct the QueryMetadata with a page size and a bookmark needed for pagination - metadata := &pb.QueryMetadata{PageSize: pageSize, Bookmark: bookmark} + metadata := &peer.QueryMetadata{PageSize: pageSize, Bookmark: bookmark} metadataBytes, err := proto.Marshal(metadata) if err != nil { return nil, err @@ -529,7 +529,7 @@ func createQueryMetadata(pageSize int32, bookmark string) ([]byte, error) { // GetStateByRangeWithPagination ... func (s *ChaincodeStub) GetStateByRangeWithPagination(startKey, endKey string, pageSize int32, - bookmark string) (StateQueryIteratorInterface, *pb.QueryResponseMetadata, error) { + bookmark string) (StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) { if startKey == "" { startKey = emptyKeySubstitute @@ -550,7 +550,7 @@ func (s *ChaincodeStub) GetStateByRangeWithPagination(startKey, endKey string, p // GetStateByPartialCompositeKeyWithPagination ... func (s *ChaincodeStub) GetStateByPartialCompositeKeyWithPagination(objectType string, keys []string, - pageSize int32, bookmark string) (StateQueryIteratorInterface, *pb.QueryResponseMetadata, error) { + pageSize int32, bookmark string) (StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) { collection := "" @@ -568,7 +568,7 @@ func (s *ChaincodeStub) GetStateByPartialCompositeKeyWithPagination(objectType s // GetQueryResultWithPagination ... func (s *ChaincodeStub) GetQueryResultWithPagination(query string, pageSize int32, - bookmark string) (StateQueryIteratorInterface, *pb.QueryResponseMetadata, error) { + bookmark string) (StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) { // Access public data by setting the collection to empty string collection := "" @@ -609,7 +609,7 @@ func (iter *CommonIterator) HasNext() bool { // or KeyModification depending on the result type (i.e., state (range/execute) // query, history query). Note that queryResult is an empty golang // interface that can hold values of any type. -func (iter *CommonIterator) getResultFromBytes(queryResultBytes *pb.QueryResultBytes, +func (iter *CommonIterator) getResultFromBytes(queryResultBytes *peer.QueryResultBytes, rType resultType) (queryResult, error) { if rType == StateQueryResult { @@ -718,7 +718,7 @@ func (s *ChaincodeStub) GetBinding() ([]byte, error) { } // GetSignedProposal documentation can be found in interfaces.go -func (s *ChaincodeStub) GetSignedProposal() (*pb.SignedProposal, error) { +func (s *ChaincodeStub) GetSignedProposal() (*peer.SignedProposal, error) { return s.signedProposal, nil } @@ -733,7 +733,7 @@ func (s *ChaincodeStub) GetArgsSlice() ([]byte, error) { } // GetTxTimestamp documentation can be found in interfaces.go -func (s *ChaincodeStub) GetTxTimestamp() (*timestamp.Timestamp, error) { +func (s *ChaincodeStub) GetTxTimestamp() (*timestamppb.Timestamp, error) { hdr := &common.Header{} if err := proto.Unmarshal(s.proposal.Header, hdr); err != nil { return nil, fmt.Errorf("error unmarshaling Header: %s", err) @@ -754,6 +754,6 @@ func (s *ChaincodeStub) SetEvent(name string, payload []byte) error { if name == "" { return errors.New("event name can not be empty string") } - s.chaincodeEvent = &pb.ChaincodeEvent{EventName: name, Payload: payload} + s.chaincodeEvent = &peer.ChaincodeEvent{EventName: name, Payload: payload} return nil } diff --git a/shim/stub_test.go b/shim/stub_test.go index 3a50000b..5f41d6e8 100644 --- a/shim/stub_test.go +++ b/shim/stub_test.go @@ -9,15 +9,15 @@ import ( "os" "testing" - "github.com/golang/protobuf/proto" - "github.com/hyperledger/fabric-chaincode-go/shim/internal/mock" - "github.com/hyperledger/fabric-protos-go/common" - "github.com/hyperledger/fabric-protos-go/ledger/queryresult" - peerpb "github.com/hyperledger/fabric-protos-go/peer" - - "github.com/golang/protobuf/ptypes" - "github.com/golang/protobuf/ptypes/timestamp" + "github.com/hyperledger/fabric-chaincode-go/v2/shim/internal/mock" + "github.com/hyperledger/fabric-protos-go-apiv2/common" + "github.com/hyperledger/fabric-protos-go-apiv2/ledger/queryresult" + "github.com/hyperledger/fabric-protos-go-apiv2/peer" + "google.golang.org/protobuf/proto" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + "google.golang.org/protobuf/types/known/timestamppb" ) func toChaincodeArgs(args ...string) [][]byte { @@ -28,6 +28,11 @@ func toChaincodeArgs(args ...string) [][]byte { return ccArgs } +// requireProtoEqual ensures an expected protobuf message matches an actual message +func requireProtoEqual(t *testing.T, expected proto.Message, actual proto.Message) { + require.True(t, proto.Equal(expected, actual), "Expected %v, got %v", expected, actual) +} + func TestNewChaincodeStub(t *testing.T) { expectedArgs := toChaincodeArgs("function", "arg1", "arg2") expectedDecorations := map[string][]byte{"decoration-key": []byte("decoration-value")} @@ -35,8 +40,8 @@ func TestNewChaincodeStub(t *testing.T) { expectedTransient := map[string][]byte{"key": []byte("value")} expectedEpoch := uint64(999) - validSignedProposal := &peerpb.SignedProposal{ - ProposalBytes: marshalOrPanic(&peerpb.Proposal{ + validSignedProposal := &peer.SignedProposal{ + ProposalBytes: marshalOrPanic(&peer.Proposal{ Header: marshalOrPanic(&common.Header{ ChannelHeader: marshalOrPanic(&common.ChannelHeader{ Type: int32(common.HeaderType_ENDORSER_TRANSACTION), @@ -46,7 +51,7 @@ func TestNewChaincodeStub(t *testing.T) { Creator: expectedCreator, }), }), - Payload: marshalOrPanic(&peerpb.ChaincodeProposalPayload{ + Payload: marshalOrPanic(&peer.ChaincodeProposalPayload{ Input: []byte("chaincode-proposal-input"), TransientMap: expectedTransient, }), @@ -54,26 +59,26 @@ func TestNewChaincodeStub(t *testing.T) { } tests := []struct { - signedProposal *peerpb.SignedProposal + signedProposal *peer.SignedProposal expectedErr string }{ {signedProposal: nil}, - {signedProposal: proto.Clone(validSignedProposal).(*peerpb.SignedProposal)}, + {signedProposal: proto.Clone(validSignedProposal).(*peer.SignedProposal)}, { - signedProposal: &peerpb.SignedProposal{ProposalBytes: []byte("garbage")}, + signedProposal: &peer.SignedProposal{ProposalBytes: []byte("garbage")}, expectedErr: "failed to extract Proposal from SignedProposal", }, { - signedProposal: &peerpb.SignedProposal{}, + signedProposal: &peer.SignedProposal{}, expectedErr: "failed to extract Proposal fields: proposal header is nil", }, { - signedProposal: &peerpb.SignedProposal{}, + signedProposal: &peer.SignedProposal{}, expectedErr: "failed to extract Proposal fields: proposal header is nil", }, { - signedProposal: &peerpb.SignedProposal{ - ProposalBytes: marshalOrPanic(&peerpb.Proposal{ + signedProposal: &peer.SignedProposal{ + ProposalBytes: marshalOrPanic(&peer.Proposal{ Header: marshalOrPanic(&common.Header{ ChannelHeader: marshalOrPanic(&common.ChannelHeader{ Type: int32(common.HeaderType_CONFIG_UPDATE), @@ -91,7 +96,7 @@ func TestNewChaincodeStub(t *testing.T) { &Handler{}, "channel-id", "transaction-id", - &peerpb.ChaincodeInput{Args: expectedArgs[:], Decorations: expectedDecorations}, + &peer.ChaincodeInput{Args: expectedArgs[:], Decorations: expectedDecorations}, tt.signedProposal, ) if tt.expectedErr != "" { @@ -116,7 +121,7 @@ func TestNewChaincodeStub(t *testing.T) { continue } - prop := &peerpb.Proposal{} + prop := &peer.Proposal{} err = proto.Unmarshal(tt.signedProposal.ProposalBytes, prop) assert.NoError(t, err) assert.Equal(t, prop, stub.proposal) @@ -141,7 +146,7 @@ func TestChaincodeStubSetEvent(t *testing.T) { stub = &ChaincodeStub{} err = stub.SetEvent("name", []byte("payload")) assert.NoError(t, err) - assert.Equal(t, &peerpb.ChaincodeEvent{EventName: "name", Payload: []byte("payload")}, stub.chaincodeEvent) + assert.Equal(t, &peer.ChaincodeEvent{EventName: "name", Payload: []byte("payload")}, stub.chaincodeEvent) } func TestChaincodeStubAccessors(t *testing.T) { @@ -186,22 +191,22 @@ func TestChaincodeStubAccessors(t *testing.T) { assert.NoError(t, err) assert.Equal(t, []byte("binding"), binding) - stub = &ChaincodeStub{signedProposal: &peerpb.SignedProposal{ProposalBytes: []byte("proposal-bytes")}} + stub = &ChaincodeStub{signedProposal: &peer.SignedProposal{ProposalBytes: []byte("proposal-bytes")}} sp, err := stub.GetSignedProposal() assert.NoError(t, err) - assert.Equal(t, &peerpb.SignedProposal{ProposalBytes: []byte("proposal-bytes")}, sp) + assert.Equal(t, &peer.SignedProposal{ProposalBytes: []byte("proposal-bytes")}, sp) } func TestChaincodeStubGetTxTimestamp(t *testing.T) { - now := ptypes.TimestampNow() + now := timestamppb.Now() tests := []struct { - proposal *peerpb.Proposal - ts *timestamp.Timestamp + proposal *peer.Proposal + ts *timestamppb.Timestamp expectedErr string }{ { ts: now, - proposal: &peerpb.Proposal{ + proposal: &peer.Proposal{ Header: marshalOrPanic(&common.Header{ ChannelHeader: marshalOrPanic(&common.ChannelHeader{ Timestamp: now, @@ -210,7 +215,7 @@ func TestChaincodeStubGetTxTimestamp(t *testing.T) { }, }, { - proposal: &peerpb.Proposal{ + proposal: &peer.Proposal{ Header: marshalOrPanic(&common.Header{ ChannelHeader: []byte("garbage-channel-header"), }), @@ -218,7 +223,7 @@ func TestChaincodeStubGetTxTimestamp(t *testing.T) { expectedErr: "error unmarshaling ChannelHeader", }, { - proposal: &peerpb.Proposal{Header: []byte("garbage-header")}, + proposal: &peer.Proposal{Header: []byte("garbage-header")}, expectedErr: "error unmarshaling Header", }, } @@ -252,13 +257,13 @@ func TestGetMSPID(t *testing.T) { func TestChaincodeStubHandlers(t *testing.T) { var tests = []struct { name string - resType peerpb.ChaincodeMessage_Type + resType peer.ChaincodeMessage_Type payload []byte testFunc func(*ChaincodeStub, *Handler, *testing.T, []byte) }{ { name: "Simple Response", - resType: peerpb.ChaincodeMessage_RESPONSE, + resType: peer.ChaincodeMessage_RESPONSE, payload: []byte("myvalue"), testFunc: func(s *ChaincodeStub, h *Handler, t *testing.T, payload []byte) { resp, err := s.GetState("key") @@ -316,10 +321,10 @@ func TestChaincodeStubHandlers(t *testing.T) { }, { name: "ValidationParameter", - resType: peerpb.ChaincodeMessage_RESPONSE, + resType: peer.ChaincodeMessage_RESPONSE, payload: marshalOrPanic( - &peerpb.StateMetadataResult{ - Entries: []*peerpb.StateMetadata{ + &peer.StateMetadataResult{ + Entries: []*peer.StateMetadata{ { Metakey: "mkey", Value: []byte("metavalue"), @@ -343,12 +348,12 @@ func TestChaincodeStubHandlers(t *testing.T) { }, { name: "InvokeChaincode", - resType: peerpb.ChaincodeMessage_RESPONSE, + resType: peer.ChaincodeMessage_RESPONSE, payload: marshalOrPanic( - &peerpb.ChaincodeMessage{ - Type: peerpb.ChaincodeMessage_COMPLETED, + &peer.ChaincodeMessage{ + Type: peer.ChaincodeMessage_COMPLETED, Payload: marshalOrPanic( - &peerpb.Response{ + &peer.Response{ Status: OK, Payload: []byte("invokechaincode"), }, @@ -362,10 +367,10 @@ func TestChaincodeStubHandlers(t *testing.T) { }, { name: "QueryResponse", - resType: peerpb.ChaincodeMessage_RESPONSE, + resType: peer.ChaincodeMessage_RESPONSE, payload: marshalOrPanic( - &peerpb.QueryResponse{ - Results: []*peerpb.QueryResultBytes{ + &peer.QueryResponse{ + Results: []*peer.QueryResultBytes{ { ResultBytes: marshalOrPanic( &queryresult.KV{ @@ -376,7 +381,7 @@ func TestChaincodeStubHandlers(t *testing.T) { }, }, Metadata: marshalOrPanic( - &peerpb.QueryResponseMetadata{ + &peer.QueryResponseMetadata{ Bookmark: "book", FetchedRecordsCount: 1, }, @@ -399,7 +404,7 @@ func TestChaincodeStubHandlers(t *testing.T) { if err != nil { t.Fatalf("Unexpected error for GetQueryResult: %s", err) } - assert.Equal(t, expectedResult, kv) + requireProtoEqual(t, expectedResult, kv) sqi, err = s.GetPrivateDataQueryResult("col", "query") if err != nil { @@ -409,7 +414,7 @@ func TestChaincodeStubHandlers(t *testing.T) { if err != nil { t.Fatalf("Unexpected error for GetPrivateDataQueryResult: %s", err) } - assert.Equal(t, expectedResult, kv) + requireProtoEqual(t, expectedResult, kv) _, err = s.GetPrivateDataQueryResult("", "query") assert.EqualError(t, err, "collection must not be an empty string") @@ -423,23 +428,24 @@ func TestChaincodeStubHandlers(t *testing.T) { if err != nil { t.Fatalf("Unexpected error for GetStateByRange: %s", err) } - assert.Equal(t, expectedResult, kv) + requireProtoEqual(t, expectedResult, kv) // second result assert.True(t, sqi.HasNext()) kv, err = sqi.Next() if err != nil { t.Fatalf("Unexpected error for GetStateByRange: %s", err) } - assert.Equal(t, expectedResult, kv) + requireProtoEqual(t, expectedResult, kv) err = sqi.Close() assert.NoError(t, err) sqi, qrm, err := s.GetStateByRangeWithPagination("", "end", 1, "book") + assert.NoError(t, err) kv, err = sqi.Next() if err != nil { t.Fatalf("Unexpected error for GetStateByRangeWithPagination: %s", err) } - assert.Equal(t, expectedResult, kv) + requireProtoEqual(t, expectedResult, kv) assert.Equal(t, "book", qrm.GetBookmark()) assert.Equal(t, int32(1), qrm.GetFetchedRecordsCount()) @@ -451,24 +457,26 @@ func TestChaincodeStubHandlers(t *testing.T) { if err != nil { t.Fatalf("Unexpected error for GetPrivateDataByRange: %s", err) } - assert.Equal(t, expectedResult, kv) + requireProtoEqual(t, expectedResult, kv) _, err = s.GetPrivateDataByRange("", "", "end") assert.EqualError(t, err, "collection must not be an empty string") sqi, err = s.GetStateByPartialCompositeKey("object", []string{"attr1", "attr2"}) + assert.NoError(t, err) kv, err = sqi.Next() if err != nil { t.Fatalf("Unexpected error for GetStateByPartialCompositeKey: %s", err) } - assert.Equal(t, expectedResult, kv) + requireProtoEqual(t, expectedResult, kv) sqi, err = s.GetPrivateDataByPartialCompositeKey("col", "object", []string{"attr1", "attr2"}) + assert.NoError(t, err) kv, err = sqi.Next() if err != nil { t.Fatalf("Unexpected error for GetPrivateDataByPartialCompositeKey: %s", err) } - assert.Equal(t, expectedResult, kv) + requireProtoEqual(t, expectedResult, kv) _, err = s.GetPrivateDataByPartialCompositeKey("", "object", []string{"attr1", "attr2"}) assert.EqualError(t, err, "collection must not be an empty string") @@ -479,30 +487,32 @@ func TestChaincodeStubHandlers(t *testing.T) { 1, "book", ) + assert.NoError(t, err) kv, err = sqi.Next() if err != nil { t.Fatalf("Unexpected error for GetStateByPartialCompositeKeyWithPagination: %s", err) } - assert.Equal(t, expectedResult, kv) + requireProtoEqual(t, expectedResult, kv) assert.Equal(t, "book", qrm.GetBookmark()) assert.Equal(t, int32(1), qrm.GetFetchedRecordsCount()) sqi, qrm, err = s.GetQueryResultWithPagination("query", 1, "book") + assert.NoError(t, err) kv, err = sqi.Next() if err != nil { t.Fatalf("Unexpected error forGetQueryResultWithPagination: %s", err) } - assert.Equal(t, expectedResult, kv) + requireProtoEqual(t, expectedResult, kv) assert.Equal(t, "book", qrm.GetBookmark()) assert.Equal(t, int32(1), qrm.GetFetchedRecordsCount()) }, }, { name: "GetHistoryForKey", - resType: peerpb.ChaincodeMessage_RESPONSE, + resType: peer.ChaincodeMessage_RESPONSE, payload: marshalOrPanic( - &peerpb.QueryResponse{ - Results: []*peerpb.QueryResultBytes{ + &peer.QueryResponse{ + Results: []*peer.QueryResultBytes{ { ResultBytes: marshalOrPanic( &queryresult.KeyModification{ @@ -528,13 +538,13 @@ func TestChaincodeStubHandlers(t *testing.T) { if err != nil { t.Fatalf("Unexpected error for GetPrivateDataByRangee: %s", err) } - assert.Equal(t, expectedResult, km) + requireProtoEqual(t, expectedResult, km) assert.False(t, hqi.HasNext()) }, }, { name: "Error Conditions", - resType: peerpb.ChaincodeMessage_ERROR, + resType: peer.ChaincodeMessage_ERROR, payload: []byte("error"), testFunc: func(s *ChaincodeStub, h *Handler, t *testing.T, payload []byte) { _, err := s.GetState("key") @@ -578,7 +588,7 @@ func TestChaincodeStubHandlers(t *testing.T) { handler := &Handler{ cc: &mockChaincode{}, - responseChannels: map[string]chan peerpb.ChaincodeMessage{}, + responseChannels: map[string]chan *peer.ChaincodeMessage{}, state: ready, } stub := &ChaincodeStub{ @@ -588,16 +598,17 @@ func TestChaincodeStubHandlers(t *testing.T) { validationParameterMetakey: "mkey", } chatStream := &mock.PeerChaincodeStream{} - chatStream.SendStub = func(msg *peerpb.ChaincodeMessage) error { + chatStream.SendStub = func(msg *peer.ChaincodeMessage) error { go func() { - handler.handleResponse( - &peerpb.ChaincodeMessage{ + err := handler.handleResponse( + &peer.ChaincodeMessage{ Type: test.resType, ChannelId: msg.GetChannelId(), Txid: msg.GetTxid(), Payload: test.payload, }, ) + assert.NoError(t, err, "handleResponse") }() return nil } diff --git a/shimtest/mock/chaincode.go b/shimtest/mock/chaincode.go deleted file mode 100644 index 6ad21a10..00000000 --- a/shimtest/mock/chaincode.go +++ /dev/null @@ -1,182 +0,0 @@ -// Code generated by counterfeiter. DO NOT EDIT. -package mock - -import ( - "sync" - - "github.com/hyperledger/fabric-chaincode-go/shim" - "github.com/hyperledger/fabric-protos-go/peer" -) - -type Chaincode struct { - InitStub func(shim.ChaincodeStubInterface) peer.Response - initMutex sync.RWMutex - initArgsForCall []struct { - arg1 shim.ChaincodeStubInterface - } - initReturns struct { - result1 peer.Response - } - initReturnsOnCall map[int]struct { - result1 peer.Response - } - InvokeStub func(shim.ChaincodeStubInterface) peer.Response - invokeMutex sync.RWMutex - invokeArgsForCall []struct { - arg1 shim.ChaincodeStubInterface - } - invokeReturns struct { - result1 peer.Response - } - invokeReturnsOnCall map[int]struct { - result1 peer.Response - } - invocations map[string][][]interface{} - invocationsMutex sync.RWMutex -} - -func (fake *Chaincode) Init(arg1 shim.ChaincodeStubInterface) peer.Response { - fake.initMutex.Lock() - ret, specificReturn := fake.initReturnsOnCall[len(fake.initArgsForCall)] - fake.initArgsForCall = append(fake.initArgsForCall, struct { - arg1 shim.ChaincodeStubInterface - }{arg1}) - fake.recordInvocation("Init", []interface{}{arg1}) - fake.initMutex.Unlock() - if fake.InitStub != nil { - return fake.InitStub(arg1) - } - if specificReturn { - return ret.result1 - } - fakeReturns := fake.initReturns - return fakeReturns.result1 -} - -func (fake *Chaincode) InitCallCount() int { - fake.initMutex.RLock() - defer fake.initMutex.RUnlock() - return len(fake.initArgsForCall) -} - -func (fake *Chaincode) InitCalls(stub func(shim.ChaincodeStubInterface) peer.Response) { - fake.initMutex.Lock() - defer fake.initMutex.Unlock() - fake.InitStub = stub -} - -func (fake *Chaincode) InitArgsForCall(i int) shim.ChaincodeStubInterface { - fake.initMutex.RLock() - defer fake.initMutex.RUnlock() - argsForCall := fake.initArgsForCall[i] - return argsForCall.arg1 -} - -func (fake *Chaincode) InitReturns(result1 peer.Response) { - fake.initMutex.Lock() - defer fake.initMutex.Unlock() - fake.InitStub = nil - fake.initReturns = struct { - result1 peer.Response - }{result1} -} - -func (fake *Chaincode) InitReturnsOnCall(i int, result1 peer.Response) { - fake.initMutex.Lock() - defer fake.initMutex.Unlock() - fake.InitStub = nil - if fake.initReturnsOnCall == nil { - fake.initReturnsOnCall = make(map[int]struct { - result1 peer.Response - }) - } - fake.initReturnsOnCall[i] = struct { - result1 peer.Response - }{result1} -} - -func (fake *Chaincode) Invoke(arg1 shim.ChaincodeStubInterface) peer.Response { - fake.invokeMutex.Lock() - ret, specificReturn := fake.invokeReturnsOnCall[len(fake.invokeArgsForCall)] - fake.invokeArgsForCall = append(fake.invokeArgsForCall, struct { - arg1 shim.ChaincodeStubInterface - }{arg1}) - fake.recordInvocation("Invoke", []interface{}{arg1}) - fake.invokeMutex.Unlock() - if fake.InvokeStub != nil { - return fake.InvokeStub(arg1) - } - if specificReturn { - return ret.result1 - } - fakeReturns := fake.invokeReturns - return fakeReturns.result1 -} - -func (fake *Chaincode) InvokeCallCount() int { - fake.invokeMutex.RLock() - defer fake.invokeMutex.RUnlock() - return len(fake.invokeArgsForCall) -} - -func (fake *Chaincode) InvokeCalls(stub func(shim.ChaincodeStubInterface) peer.Response) { - fake.invokeMutex.Lock() - defer fake.invokeMutex.Unlock() - fake.InvokeStub = stub -} - -func (fake *Chaincode) InvokeArgsForCall(i int) shim.ChaincodeStubInterface { - fake.invokeMutex.RLock() - defer fake.invokeMutex.RUnlock() - argsForCall := fake.invokeArgsForCall[i] - return argsForCall.arg1 -} - -func (fake *Chaincode) InvokeReturns(result1 peer.Response) { - fake.invokeMutex.Lock() - defer fake.invokeMutex.Unlock() - fake.InvokeStub = nil - fake.invokeReturns = struct { - result1 peer.Response - }{result1} -} - -func (fake *Chaincode) InvokeReturnsOnCall(i int, result1 peer.Response) { - fake.invokeMutex.Lock() - defer fake.invokeMutex.Unlock() - fake.InvokeStub = nil - if fake.invokeReturnsOnCall == nil { - fake.invokeReturnsOnCall = make(map[int]struct { - result1 peer.Response - }) - } - fake.invokeReturnsOnCall[i] = struct { - result1 peer.Response - }{result1} -} - -func (fake *Chaincode) Invocations() map[string][][]interface{} { - fake.invocationsMutex.RLock() - defer fake.invocationsMutex.RUnlock() - fake.initMutex.RLock() - defer fake.initMutex.RUnlock() - fake.invokeMutex.RLock() - defer fake.invokeMutex.RUnlock() - copiedInvocations := map[string][][]interface{}{} - for key, value := range fake.invocations { - copiedInvocations[key] = value - } - return copiedInvocations -} - -func (fake *Chaincode) recordInvocation(key string, args []interface{}) { - fake.invocationsMutex.Lock() - defer fake.invocationsMutex.Unlock() - if fake.invocations == nil { - fake.invocations = map[string][][]interface{}{} - } - if fake.invocations[key] == nil { - fake.invocations[key] = [][]interface{}{} - } - fake.invocations[key] = append(fake.invocations[key], args) -} diff --git a/shimtest/mockstub.go b/shimtest/mockstub.go deleted file mode 100644 index 7a86e95a..00000000 --- a/shimtest/mockstub.go +++ /dev/null @@ -1,636 +0,0 @@ -// Copyright the Hyperledger Fabric contributors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Package shimtest provides a mock of the ChaincodeStubInterface for -// unit testing chaincode. -// -// Deprecated: ShimTest will be removed in a future release. -// Future development should make use of the ChaincodeStub Interface -// for generating mocks -package shimtest - -import ( - "container/list" - "errors" - "fmt" - "strings" - "unicode/utf8" - - "github.com/golang/protobuf/proto" - "github.com/golang/protobuf/ptypes" - "github.com/golang/protobuf/ptypes/timestamp" - "github.com/hyperledger/fabric-chaincode-go/shim" - "github.com/hyperledger/fabric-protos-go/ledger/queryresult" - pb "github.com/hyperledger/fabric-protos-go/peer" -) - -const ( - minUnicodeRuneValue = 0 //U+0000 - compositeKeyNamespace = "\x00" -) - -// MockStub is an implementation of ChaincodeStubInterface for unit testing chaincode. -// Use this instead of ChaincodeStub in your chaincode's unit test calls to Init or Invoke. -type MockStub struct { - // arguments the stub was called with - args [][]byte - - // transientMap - TransientMap map[string][]byte - // A pointer back to the chaincode that will invoke this, set by constructor. - // If a peer calls this stub, the chaincode will be invoked from here. - cc shim.Chaincode - - // A nice name that can be used for logging - Name string - - // State keeps name value pairs - State map[string][]byte - - // Keys stores the list of mapped values in lexical order - Keys *list.List - - // registered list of other MockStub chaincodes that can be called from this MockStub - Invokables map[string]*MockStub - - // stores a transaction uuid while being Invoked / Deployed - // TODO if a chaincode uses recursion this may need to be a stack of TxIDs or possibly a reference counting map - TxID string - - TxTimestamp *timestamp.Timestamp - - // mocked signedProposal - signedProposal *pb.SignedProposal - - // stores a channel ID of the proposal - ChannelID string - - PvtState map[string]map[string][]byte - - // stores per-key endorsement policy, first map index is the collection, second map index is the key - EndorsementPolicies map[string]map[string][]byte - - // channel to store ChaincodeEvents - ChaincodeEventsChannel chan *pb.ChaincodeEvent - - Creator []byte - - Decorations map[string][]byte -} - -// GetTxID ... -func (stub *MockStub) GetTxID() string { - return stub.TxID -} - -// GetChannelID ... -func (stub *MockStub) GetChannelID() string { - return stub.ChannelID -} - -// GetArgs ... -func (stub *MockStub) GetArgs() [][]byte { - return stub.args -} - -// GetStringArgs ... -func (stub *MockStub) GetStringArgs() []string { - args := stub.GetArgs() - strargs := make([]string, 0, len(args)) - for _, barg := range args { - strargs = append(strargs, string(barg)) - } - return strargs -} - -// GetFunctionAndParameters ... -func (stub *MockStub) GetFunctionAndParameters() (function string, params []string) { - allargs := stub.GetStringArgs() - function = "" - params = []string{} - if len(allargs) >= 1 { - function = allargs[0] - params = allargs[1:] - } - return -} - -// MockTransactionStart Used to indicate to a chaincode that it is part of a transaction. -// This is important when chaincodes invoke each other. -// MockStub doesn't support concurrent transactions at present. -func (stub *MockStub) MockTransactionStart(txid string) { - stub.TxID = txid - stub.setSignedProposal(&pb.SignedProposal{}) - stub.setTxTimestamp(ptypes.TimestampNow()) -} - -// MockTransactionEnd End a mocked transaction, clearing the UUID. -func (stub *MockStub) MockTransactionEnd(uuid string) { - stub.signedProposal = nil - stub.TxID = "" -} - -// MockPeerChaincode Register another MockStub chaincode with this MockStub. -// invokableChaincodeName is the name of a chaincode. -// otherStub is a MockStub of the chaincode, already initialized. -// channel is the name of a channel on which another MockStub is called. -func (stub *MockStub) MockPeerChaincode(invokableChaincodeName string, otherStub *MockStub, channel string) { - // Internally we use chaincode name as a composite name - if channel != "" { - invokableChaincodeName = invokableChaincodeName + "/" + channel - } - stub.Invokables[invokableChaincodeName] = otherStub -} - -// MockInit Initialise this chaincode, also starts and ends a transaction. -func (stub *MockStub) MockInit(uuid string, args [][]byte) pb.Response { - stub.args = args - stub.MockTransactionStart(uuid) - res := stub.cc.Init(stub) - stub.MockTransactionEnd(uuid) - return res -} - -// MockInvoke Invoke this chaincode, also starts and ends a transaction. -func (stub *MockStub) MockInvoke(uuid string, args [][]byte) pb.Response { - stub.args = args - stub.MockTransactionStart(uuid) - res := stub.cc.Invoke(stub) - stub.MockTransactionEnd(uuid) - return res -} - -// GetDecorations ... -func (stub *MockStub) GetDecorations() map[string][]byte { - return stub.Decorations -} - -// MockInvokeWithSignedProposal Invoke this chaincode, also starts and ends a transaction. -func (stub *MockStub) MockInvokeWithSignedProposal(uuid string, args [][]byte, sp *pb.SignedProposal) pb.Response { - stub.args = args - stub.MockTransactionStart(uuid) - stub.signedProposal = sp - res := stub.cc.Invoke(stub) - stub.MockTransactionEnd(uuid) - return res -} - -// GetPrivateData ... -func (stub *MockStub) GetPrivateData(collection string, key string) ([]byte, error) { - m, in := stub.PvtState[collection] - - if !in { - return nil, nil - } - - return m[key], nil -} - -// GetPrivateDataHash ... -func (stub *MockStub) GetPrivateDataHash(collection, key string) ([]byte, error) { - return nil, errors.New("Not Implemented") -} - -// PutPrivateData ... -func (stub *MockStub) PutPrivateData(collection string, key string, value []byte) error { - m, in := stub.PvtState[collection] - if !in { - stub.PvtState[collection] = make(map[string][]byte) - m, in = stub.PvtState[collection] - } - - m[key] = value - - return nil -} - -// DelPrivateData ... -func (stub *MockStub) DelPrivateData(collection string, key string) error { - return errors.New("Not Implemented") -} - -// PurgePrivateData ... -func (stub *MockStub) PurgePrivateData(collection string, key string) error { - return errors.New("Not Implemented") -} - -// GetPrivateDataByRange ... -func (stub *MockStub) GetPrivateDataByRange(collection, startKey, endKey string) (shim.StateQueryIteratorInterface, error) { - return nil, errors.New("Not Implemented") -} - -// GetPrivateDataByPartialCompositeKey ... -func (stub *MockStub) GetPrivateDataByPartialCompositeKey(collection, objectType string, attributes []string) (shim.StateQueryIteratorInterface, error) { - return nil, errors.New("Not Implemented") -} - -// GetPrivateDataQueryResult ... -func (stub *MockStub) GetPrivateDataQueryResult(collection, query string) (shim.StateQueryIteratorInterface, error) { - // Not implemented since the mock engine does not have a query engine. - // However, a very simple query engine that supports string matching - // could be implemented to test that the framework supports queries - return nil, errors.New("Not Implemented") -} - -// GetState retrieves the value for a given key from the ledger -func (stub *MockStub) GetState(key string) ([]byte, error) { - value := stub.State[key] - return value, nil -} - -// PutState writes the specified `value` and `key` into the ledger. -func (stub *MockStub) PutState(key string, value []byte) error { - if stub.TxID == "" { - err := errors.New("cannot PutState without a transactions - call stub.MockTransactionStart()?") - return err - } - - // If the value is nil or empty, delete the key - if len(value) == 0 { - return stub.DelState(key) - } - stub.State[key] = value - - // insert key into ordered list of keys - for elem := stub.Keys.Front(); elem != nil; elem = elem.Next() { - elemValue := elem.Value.(string) - comp := strings.Compare(key, elemValue) - if comp < 0 { - // key < elem, insert it before elem - stub.Keys.InsertBefore(key, elem) - break - } else if comp == 0 { - // keys exists, no need to change - break - } else { // comp > 0 - // key > elem, keep looking unless this is the end of the list - if elem.Next() == nil { - stub.Keys.PushBack(key) - break - } - } - } - - // special case for empty Keys list - if stub.Keys.Len() == 0 { - stub.Keys.PushFront(key) - } - - return nil -} - -// DelState removes the specified `key` and its value from the ledger. -func (stub *MockStub) DelState(key string) error { - delete(stub.State, key) - - for elem := stub.Keys.Front(); elem != nil; elem = elem.Next() { - if strings.Compare(key, elem.Value.(string)) == 0 { - stub.Keys.Remove(elem) - } - } - - return nil -} - -// GetStateByRange ... -func (stub *MockStub) GetStateByRange(startKey, endKey string) (shim.StateQueryIteratorInterface, error) { - if err := validateSimpleKeys(startKey, endKey); err != nil { - return nil, err - } - return NewMockStateRangeQueryIterator(stub, startKey, endKey), nil -} - -// To ensure that simple keys do not go into composite key namespace, -// we validate simplekey to check whether the key starts with 0x00 (which -// is the namespace for compositeKey). This helps in avoiding simple/composite -// key collisions. -func validateSimpleKeys(simpleKeys ...string) error { - for _, key := range simpleKeys { - if len(key) > 0 && key[0] == compositeKeyNamespace[0] { - return fmt.Errorf(`first character of the key [%s] contains a null character which is not allowed`, key) - } - } - return nil -} - -// GetQueryResult function can be invoked by a chaincode to perform a -// rich query against state database. Only supported by state database implementations -// that support rich query. The query string is in the syntax of the underlying -// state database. An iterator is returned which can be used to iterate (next) over -// the query result set -func (stub *MockStub) GetQueryResult(query string) (shim.StateQueryIteratorInterface, error) { - // Not implemented since the mock engine does not have a query engine. - // However, a very simple query engine that supports string matching - // could be implemented to test that the framework supports queries - return nil, errors.New("not implemented") -} - -// GetHistoryForKey function can be invoked by a chaincode to return a history of -// key values across time. GetHistoryForKey is intended to be used for read-only queries. -func (stub *MockStub) GetHistoryForKey(key string) (shim.HistoryQueryIteratorInterface, error) { - return nil, errors.New("not implemented") -} - -// GetStateByPartialCompositeKey function can be invoked by a chaincode to query the -// state based on a given partial composite key. This function returns an -// iterator which can be used to iterate over all composite keys whose prefix -// matches the given partial composite key. This function should be used only for -// a partial composite key. For a full composite key, an iter with empty response -// would be returned. -func (stub *MockStub) GetStateByPartialCompositeKey(objectType string, attributes []string) (shim.StateQueryIteratorInterface, error) { - partialCompositeKey, err := stub.CreateCompositeKey(objectType, attributes) - if err != nil { - return nil, err - } - return NewMockStateRangeQueryIterator(stub, partialCompositeKey, partialCompositeKey+string(utf8.MaxRune)), nil -} - -// CreateCompositeKey combines the list of attributes -// to form a composite key. -func (stub *MockStub) CreateCompositeKey(objectType string, attributes []string) (string, error) { - return shim.CreateCompositeKey(objectType, attributes) -} - -// SplitCompositeKey splits the composite key into attributes -// on which the composite key was formed. -func (stub *MockStub) SplitCompositeKey(compositeKey string) (string, []string, error) { - return splitCompositeKey(compositeKey) -} - -func splitCompositeKey(compositeKey string) (string, []string, error) { - componentIndex := 1 - components := []string{} - for i := 1; i < len(compositeKey); i++ { - if compositeKey[i] == minUnicodeRuneValue { - components = append(components, compositeKey[componentIndex:i]) - componentIndex = i + 1 - } - } - return components[0], components[1:], nil -} - -// GetStateByRangeWithPagination ... -func (stub *MockStub) GetStateByRangeWithPagination(startKey, endKey string, pageSize int32, - bookmark string) (shim.StateQueryIteratorInterface, *pb.QueryResponseMetadata, error) { - return nil, nil, nil -} - -// GetStateByPartialCompositeKeyWithPagination ... -func (stub *MockStub) GetStateByPartialCompositeKeyWithPagination(objectType string, keys []string, - pageSize int32, bookmark string) (shim.StateQueryIteratorInterface, *pb.QueryResponseMetadata, error) { - return nil, nil, nil -} - -// GetQueryResultWithPagination ... -func (stub *MockStub) GetQueryResultWithPagination(query string, pageSize int32, - bookmark string) (shim.StateQueryIteratorInterface, *pb.QueryResponseMetadata, error) { - return nil, nil, nil -} - -// InvokeChaincode locally calls the specified chaincode `Invoke`. -// E.g. stub1.InvokeChaincode("othercc", funcArgs, channel) -// Before calling this make sure to create another MockStub stub2, call shim.NewMockStub("othercc", Chaincode) -// and register it with stub1 by calling stub1.MockPeerChaincode("othercc", stub2, channel) -func (stub *MockStub) InvokeChaincode(chaincodeName string, args [][]byte, channel string) pb.Response { - // Internally we use chaincode name as a composite name - if channel != "" { - chaincodeName = chaincodeName + "/" + channel - } - // TODO "args" here should possibly be a serialized pb.ChaincodeInput - otherStub := stub.Invokables[chaincodeName] - // function, strings := getFuncArgs(args) - res := otherStub.MockInvoke(stub.TxID, args) - return res -} - -// GetCreator ... -func (stub *MockStub) GetCreator() ([]byte, error) { - return stub.Creator, nil -} - -// SetTransient set TransientMap to mockStub -func (stub *MockStub) SetTransient(tMap map[string][]byte) error { - if stub.signedProposal == nil { - return fmt.Errorf("signedProposal is not initialized") - } - payloadByte, err := proto.Marshal(&pb.ChaincodeProposalPayload{ - TransientMap: tMap, - }) - if err != nil { - return err - } - proposalByte, err := proto.Marshal(&pb.Proposal{ - Payload: payloadByte, - }) - if err != nil { - return err - } - stub.signedProposal.ProposalBytes = proposalByte - stub.TransientMap = tMap - return nil -} - -// GetTransient ... -func (stub *MockStub) GetTransient() (map[string][]byte, error) { - return stub.TransientMap, nil -} - -// GetBinding Not implemented ... -func (stub *MockStub) GetBinding() ([]byte, error) { - return nil, nil -} - -// GetSignedProposal Not implemented ... -func (stub *MockStub) GetSignedProposal() (*pb.SignedProposal, error) { - return stub.signedProposal, nil -} - -func (stub *MockStub) setSignedProposal(sp *pb.SignedProposal) { - stub.signedProposal = sp -} - -// GetArgsSlice Not implemented ... -func (stub *MockStub) GetArgsSlice() ([]byte, error) { - return nil, nil -} - -func (stub *MockStub) setTxTimestamp(time *timestamp.Timestamp) { - stub.TxTimestamp = time -} - -// GetTxTimestamp ... -func (stub *MockStub) GetTxTimestamp() (*timestamp.Timestamp, error) { - if stub.TxTimestamp == nil { - return nil, errors.New("TxTimestamp not set") - } - return stub.TxTimestamp, nil -} - -// SetEvent ... -func (stub *MockStub) SetEvent(name string, payload []byte) error { - stub.ChaincodeEventsChannel <- &pb.ChaincodeEvent{EventName: name, Payload: payload} - return nil -} - -// SetStateValidationParameter ... -func (stub *MockStub) SetStateValidationParameter(key string, ep []byte) error { - return stub.SetPrivateDataValidationParameter("", key, ep) -} - -// GetStateValidationParameter ... -func (stub *MockStub) GetStateValidationParameter(key string) ([]byte, error) { - return stub.GetPrivateDataValidationParameter("", key) -} - -// SetPrivateDataValidationParameter ... -func (stub *MockStub) SetPrivateDataValidationParameter(collection, key string, ep []byte) error { - m, in := stub.EndorsementPolicies[collection] - if !in { - stub.EndorsementPolicies[collection] = make(map[string][]byte) - m, in = stub.EndorsementPolicies[collection] - } - - m[key] = ep - return nil -} - -// GetPrivateDataValidationParameter ... -func (stub *MockStub) GetPrivateDataValidationParameter(collection, key string) ([]byte, error) { - m, in := stub.EndorsementPolicies[collection] - - if !in { - return nil, nil - } - - return m[key], nil -} - -// NewMockStub Constructor to initialise the internal State map -func NewMockStub(name string, cc shim.Chaincode) *MockStub { - s := new(MockStub) - s.Name = name - s.cc = cc - s.State = make(map[string][]byte) - s.PvtState = make(map[string]map[string][]byte) - s.EndorsementPolicies = make(map[string]map[string][]byte) - s.Invokables = make(map[string]*MockStub) - s.Keys = list.New() - s.ChaincodeEventsChannel = make(chan *pb.ChaincodeEvent, 100) //define large capacity for non-blocking setEvent calls. - s.Decorations = make(map[string][]byte) - - return s -} - -/***************************** - Range Query Iterator -*****************************/ - -// MockStateRangeQueryIterator ... -type MockStateRangeQueryIterator struct { - Closed bool - Stub *MockStub - StartKey string - EndKey string - Current *list.Element -} - -// HasNext returns true if the range query iterator contains additional keys -// and values. -func (iter *MockStateRangeQueryIterator) HasNext() bool { - if iter.Closed { - // previously called Close() - return false - } - - if iter.Current == nil { - return false - } - - current := iter.Current - for current != nil { - // if this is an open-ended query for all keys, return true - if iter.StartKey == "" && iter.EndKey == "" { - return true - } - comp1 := strings.Compare(current.Value.(string), iter.StartKey) - comp2 := strings.Compare(current.Value.(string), iter.EndKey) - if comp1 >= 0 { - if comp2 < 0 { - return true - } - return false - } - current = current.Next() - } - return false -} - -// Next returns the next key and value in the range query iterator. -func (iter *MockStateRangeQueryIterator) Next() (*queryresult.KV, error) { - if iter.Closed == true { - err := errors.New("MockStateRangeQueryIterator.Next() called after Close()") - return nil, err - } - - if iter.HasNext() == false { - err := errors.New("MockStateRangeQueryIterator.Next() called when it does not HaveNext()") - return nil, err - } - - for iter.Current != nil { - comp1 := strings.Compare(iter.Current.Value.(string), iter.StartKey) - comp2 := strings.Compare(iter.Current.Value.(string), iter.EndKey) - // compare to start and end keys. or, if this is an open-ended query for - // all keys, it should always return the key and value - if (comp1 >= 0 && comp2 < 0) || (iter.StartKey == "" && iter.EndKey == "") { - key := iter.Current.Value.(string) - value, err := iter.Stub.GetState(key) - iter.Current = iter.Current.Next() - return &queryresult.KV{Key: key, Value: value}, err - } - iter.Current = iter.Current.Next() - } - err := errors.New("MockStateRangeQueryIterator.Next() went past end of range") - return nil, err -} - -// Close closes the range query iterator. This should be called when done -// reading from the iterator to free up resources. -func (iter *MockStateRangeQueryIterator) Close() error { - if iter.Closed == true { - err := errors.New("MockStateRangeQueryIterator.Close() called after Close()") - return err - } - - iter.Closed = true - return nil -} - -// NewMockStateRangeQueryIterator ... -func NewMockStateRangeQueryIterator(stub *MockStub, startKey string, endKey string) *MockStateRangeQueryIterator { - iter := new(MockStateRangeQueryIterator) - iter.Closed = false - iter.Stub = stub - iter.StartKey = startKey - iter.EndKey = endKey - iter.Current = stub.Keys.Front() - return iter -} - -func getBytes(function string, args []string) [][]byte { - bytes := make([][]byte, 0, len(args)+1) - bytes = append(bytes, []byte(function)) - for _, s := range args { - bytes = append(bytes, []byte(s)) - } - return bytes -} - -func getFuncArgs(bytes [][]byte) (string, []string) { - function := string(bytes[0]) - args := make([]string, len(bytes)-1) - for i := 1; i < len(bytes); i++ { - args[i-1] = string(bytes[i]) - } - return function, args -} diff --git a/shimtest/mockstub_test.go b/shimtest/mockstub_test.go deleted file mode 100644 index 089810b6..00000000 --- a/shimtest/mockstub_test.go +++ /dev/null @@ -1,289 +0,0 @@ -// Copyright the Hyperledger Fabric contributors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package shimtest - -import ( - "encoding/json" - "fmt" - "reflect" - "testing" - - "github.com/hyperledger/fabric-chaincode-go/shim" - "github.com/hyperledger/fabric-chaincode-go/shimtest/mock" - "github.com/stretchr/testify/assert" -) - -//go:generate counterfeiter -o mock/chaincode.go --fake-name Chaincode . chaincode -type chaincode interface { - shim.Chaincode -} - -func TestMockStateRangeQueryIterator(t *testing.T) { - stub := NewMockStub("rangeTest", nil) - stub.MockTransactionStart("init") - stub.PutState("1", []byte{61}) - stub.PutState("0", []byte{62}) - stub.PutState("5", []byte{65}) - stub.PutState("3", []byte{63}) - stub.PutState("4", []byte{64}) - stub.PutState("6", []byte{66}) - stub.MockTransactionEnd("init") - - expectKeys := []string{"3", "4"} - expectValues := [][]byte{{63}, {64}} - - rqi := NewMockStateRangeQueryIterator(stub, "2", "5") - - fmt.Println("Running loop") - for i := 0; i < 2; i++ { - response, err := rqi.Next() - fmt.Println("Loop", i, "got", response.Key, response.Value, err) - if expectKeys[i] != response.Key { - fmt.Println("Expected key", expectKeys[i], "got", response.Key) - t.FailNow() - } - if expectValues[i][0] != response.Value[0] { - fmt.Println("Expected value", expectValues[i], "got", response.Value) - } - } -} - -// TestMockStateRangeQueryIterator_openEnded tests running an open-ended query -// for all keys on the MockStateRangeQueryIterator -func TestMockStateRangeQueryIterator_openEnded(t *testing.T) { - stub := NewMockStub("rangeTest", nil) - stub.MockTransactionStart("init") - stub.PutState("1", []byte{61}) - stub.PutState("0", []byte{62}) - stub.PutState("5", []byte{65}) - stub.PutState("3", []byte{63}) - stub.PutState("4", []byte{64}) - stub.PutState("6", []byte{66}) - stub.MockTransactionEnd("init") - - rqi := NewMockStateRangeQueryIterator(stub, "", "") - - count := 0 - for rqi.HasNext() { - rqi.Next() - count++ - } - - if count != rqi.Stub.Keys.Len() { - t.FailNow() - } -} - -type Marble struct { - ObjectType string `json:"docType"` //docType is used to distinguish the various types of objects in state database - Name string `json:"name"` //the fieldtags are needed to keep case from bouncing around - Color string `json:"color"` - Size int `json:"size"` - Owner string `json:"owner"` -} - -// JSONBytesEqual compares the JSON in two byte slices. -func jsonBytesEqual(expected []byte, actual []byte) bool { - var infExpected, infActual interface{} - if err := json.Unmarshal(expected, &infExpected); err != nil { - return false - } - if err := json.Unmarshal(actual, &infActual); err != nil { - return false - } - return reflect.DeepEqual(infActual, infExpected) -} - -func TestGetStateByPartialCompositeKey(t *testing.T) { - stub := NewMockStub("GetStateByPartialCompositeKeyTest", nil) - stub.MockTransactionStart("init") - - marble1 := &Marble{"marble", "set-1", "red", 5, "tom"} - // Convert marble1 to JSON with Color and Name as composite key - compositeKey1, _ := stub.CreateCompositeKey(marble1.ObjectType, []string{marble1.Name, marble1.Color}) - marbleJSONBytes1, _ := json.Marshal(marble1) - // Add marble1 JSON to state - stub.PutState(compositeKey1, marbleJSONBytes1) - - marble2 := &Marble{"marble", "set-1", "blue", 5, "jerry"} - compositeKey2, _ := stub.CreateCompositeKey(marble2.ObjectType, []string{marble2.Name, marble2.Color}) - marbleJSONBytes2, _ := json.Marshal(marble2) - stub.PutState(compositeKey2, marbleJSONBytes2) - - marble3 := &Marble{"marble", "set-2", "red", 5, "tom-jerry"} - compositeKey3, _ := stub.CreateCompositeKey(marble3.ObjectType, []string{marble3.Name, marble3.Color}) - marbleJSONBytes3, _ := json.Marshal(marble3) - stub.PutState(compositeKey3, marbleJSONBytes3) - - stub.MockTransactionEnd("init") - // should return in sorted order of attributes - expectKeys := []string{compositeKey2, compositeKey1} - expectKeysAttributes := [][]string{{"set-1", "blue"}, {"set-1", "red"}} - expectValues := [][]byte{marbleJSONBytes2, marbleJSONBytes1} - - rqi, _ := stub.GetStateByPartialCompositeKey("marble", []string{"set-1"}) - fmt.Println("Running loop") - for i := 0; i < 2; i++ { - response, err := rqi.Next() - fmt.Println("Loop", i, "got", response.Key, response.Value, err) - if expectKeys[i] != response.Key { - fmt.Println("Expected key", expectKeys[i], "got", response.Key) - t.FailNow() - } - objectType, attributes, _ := stub.SplitCompositeKey(response.Key) - if objectType != "marble" { - fmt.Println("Expected objectType", "marble", "got", objectType) - t.FailNow() - } - fmt.Println(attributes) - for index, attr := range attributes { - if expectKeysAttributes[i][index] != attr { - fmt.Println("Expected keys attribute", expectKeysAttributes[index][i], "got", attr) - t.FailNow() - } - } - if jsonBytesEqual(expectValues[i], response.Value) != true { - fmt.Println("Expected value", expectValues[i], "got", response.Value) - t.FailNow() - } - } -} - -func TestGetStateByPartialCompositeKeyCollision(t *testing.T) { - stub := NewMockStub("GetStateByPartialCompositeKeyCollisionTest", nil) - stub.MockTransactionStart("init") - - vehicle1Bytes := []byte("vehicle1") - compositeKeyVehicle1, _ := stub.CreateCompositeKey("Vehicle", []string{"VIN_1234"}) - stub.PutState(compositeKeyVehicle1, vehicle1Bytes) - - vehicleListing1Bytes := []byte("vehicleListing1") - compositeKeyVehicleListing1, _ := stub.CreateCompositeKey("VehicleListing", []string{"LIST_1234"}) - stub.PutState(compositeKeyVehicleListing1, vehicleListing1Bytes) - - stub.MockTransactionEnd("init") - - // Only the single "Vehicle" object should be returned, not the "VehicleListing" object - rqi, _ := stub.GetStateByPartialCompositeKey("Vehicle", []string{}) - i := 0 - fmt.Println("Running loop") - for rqi.HasNext() { - i++ - response, err := rqi.Next() - fmt.Println("Loop", i, "got", response.Key, response.Value, err) - } - // Only the single "Vehicle" object should be returned, not the "VehicleListing" object - if i != 1 { - fmt.Println("Expected 1, got", i) - t.FailNow() - } -} - -func TestGetTxTimestamp(t *testing.T) { - stub := NewMockStub("GetTxTimestamp", nil) - stub.MockTransactionStart("init") - - timestamp, err := stub.GetTxTimestamp() - if timestamp == nil || err != nil { - t.FailNow() - } - - stub.MockTransactionEnd("init") -} - -// TestPutEmptyState confirms that setting a key value to empty or nil in the mock state deletes the key -// instead of storing an empty key. -func TestPutEmptyState(t *testing.T) { - stub := NewMockStub("FAB-12545", nil) - - // Put an empty and nil state value - stub.MockTransactionStart("1") - err := stub.PutState("empty", []byte{}) - assert.NoError(t, err) - err = stub.PutState("nil", nil) - assert.NoError(t, err) - stub.MockTransactionEnd("1") - - // Confirm both are nil - stub.MockTransactionStart("2") - val, err := stub.GetState("empty") - assert.NoError(t, err) - assert.Nil(t, val) - val, err = stub.GetState("nil") - assert.NoError(t, err) - assert.Nil(t, val) - // Add a value to both empty and nil - err = stub.PutState("empty", []byte{0}) - assert.NoError(t, err) - err = stub.PutState("nil", []byte{0}) - assert.NoError(t, err) - stub.MockTransactionEnd("2") - - // Confirm the value is in both - stub.MockTransactionStart("3") - val, err = stub.GetState("empty") - assert.NoError(t, err) - assert.Equal(t, val, []byte{0}) - val, err = stub.GetState("nil") - assert.NoError(t, err) - assert.Equal(t, val, []byte{0}) - stub.MockTransactionEnd("3") - - // Set both back to empty / nil - stub.MockTransactionStart("4") - err = stub.PutState("empty", []byte{}) - assert.NoError(t, err) - err = stub.PutState("nil", nil) - assert.NoError(t, err) - stub.MockTransactionEnd("4") - - // Confirm both are nil - stub.MockTransactionStart("5") - val, err = stub.GetState("empty") - assert.NoError(t, err) - assert.Nil(t, val) - val, err = stub.GetState("nil") - assert.NoError(t, err) - assert.Nil(t, val) - stub.MockTransactionEnd("5") - -} - -// TestMockMock clearly cheating for coverage... but not. Mock should -// be tucked away under common/mocks package which is not -// included for coverage. Moving mockstub to another package -// will cause upheaval in other code best dealt with separately -// For now, call all the methods to get mock covered in this -// package -func TestMockMock(t *testing.T) { - stub := NewMockStub("MOCKMOCK", &mock.Chaincode{}) - stub.args = [][]byte{[]byte("a"), []byte("b")} - stub.MockInit("id", nil) - stub.GetArgs() - stub.GetStringArgs() - stub.GetFunctionAndParameters() - stub.GetTxID() - stub.GetChannelID() - stub.MockInvoke("id", nil) - stub.MockInvokeWithSignedProposal("id", nil, nil) - stub.DelState("dummy") - stub.GetStateByRange("start", "end") - stub.GetQueryResult("q") - - stub2 := NewMockStub("othercc", &mock.Chaincode{}) - stub2.MockPeerChaincode("othercc", stub2, "mychan") - stub2.InvokeChaincode("othercc", nil, "mychan") - stub2.GetCreator() - stub2.GetTransient() - stub2.GetBinding() - stub2.GetSignedProposal() - stub2.GetArgsSlice() - stub2.SetEvent("e", nil) - stub2.GetHistoryForKey("k") - iter := &MockStateRangeQueryIterator{} - iter.HasNext() - iter.Close() - getBytes("f", []string{"a", "b"}) - getFuncArgs([][]byte{[]byte("a")}) -} diff --git a/v2/.gitignore b/v2/.gitignore deleted file mode 100644 index b51c70b3..00000000 --- a/v2/.gitignore +++ /dev/null @@ -1 +0,0 @@ -cover.out diff --git a/v2/doc.go b/v2/doc.go deleted file mode 100644 index a4ca35e9..00000000 --- a/v2/doc.go +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright the Hyperledger Fabric contributors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Package chaincode contains the code necessary for chaincode to interact -// with a Hyperledger Fabric peer. -package chaincode diff --git a/v2/go.mod b/v2/go.mod deleted file mode 100644 index 7b8ac90e..00000000 --- a/v2/go.mod +++ /dev/null @@ -1,22 +0,0 @@ -module github.com/hyperledger/fabric-chaincode-go/v2 - -go 1.21.0 - -require ( - github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3 - github.com/stretchr/testify v1.9.0 - google.golang.org/grpc v1.64.0 - google.golang.org/protobuf v1.34.1 -) - -require ( - github.com/davecgh/go-spew v1.1.1 // indirect - github.com/kr/text v0.2.0 // indirect - github.com/pmezard/go-difflib v1.0.0 // indirect - golang.org/x/net v0.23.0 // indirect - golang.org/x/sys v0.19.0 // indirect - golang.org/x/text v0.14.0 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 // indirect - gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect - gopkg.in/yaml.v3 v3.0.1 // indirect -) diff --git a/v2/go.sum b/v2/go.sum deleted file mode 100644 index 558c0f4b..00000000 --- a/v2/go.sum +++ /dev/null @@ -1,34 +0,0 @@ -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= -github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3 h1:Xpd6fzG/KjAOHJsq7EQXY2l+qi/y8muxBaY7R6QWABk= -github.com/hyperledger/fabric-protos-go-apiv2 v0.3.3/go.mod h1:2pq0ui6ZWA0cC8J+eCErgnMDCS1kPOEYVY+06ZAK0qE= -github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= -github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= -github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= -github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= -github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= -golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157 h1:Zy9XzmMEflZ/MAaA7vNcoebnRAld7FsPW1EeBB7V0m8= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240528184218-531527333157/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= -google.golang.org/grpc v1.64.0 h1:KH3VH9y/MgNQg1dE7b3XfVK0GsPSIzJwdF617gUSbvY= -google.golang.org/grpc v1.64.0/go.mod h1:oxjF8E3FBnjp+/gVFYdWacaLDx9na1aqy9oovLpxQYg= -google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= -google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= -gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= -gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= -gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/v2/pkg/attrmgr/attrmgr.go b/v2/pkg/attrmgr/attrmgr.go deleted file mode 100644 index 48b686c7..00000000 --- a/v2/pkg/attrmgr/attrmgr.go +++ /dev/null @@ -1,245 +0,0 @@ -// Copyright the Hyperledger Fabric contributors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Package attrmgr contains utilities for managing attributes. -// Attributes are added to an X509 certificate as an extension. -package attrmgr - -import ( - "crypto/x509" - "crypto/x509/pkix" - "encoding/asn1" - "encoding/json" - "errors" - "fmt" - - "github.com/hyperledger/fabric-protos-go-apiv2/msp" - "google.golang.org/protobuf/proto" -) - -var ( - // AttrOID is the ASN.1 object identifier for an attribute extension in an - // X509 certificate - AttrOID = asn1.ObjectIdentifier{1, 2, 3, 4, 5, 6, 7, 8, 1} - // AttrOIDString is the string version of AttrOID - AttrOIDString = "1.2.3.4.5.6.7.8.1" -) - -// Attribute is a name/value pair -type Attribute interface { - // GetName returns the name of the attribute - GetName() string - // GetValue returns the value of the attribute - GetValue() string -} - -// AttributeRequest is a request for an attribute -type AttributeRequest interface { - // GetName returns the name of an attribute - GetName() string - // IsRequired returns true if the attribute is required - IsRequired() bool -} - -// New constructs an attribute manager -func New() *Mgr { return &Mgr{} } - -// Mgr is the attribute manager and is the main object for this package -type Mgr struct{} - -// ProcessAttributeRequestsForCert add attributes to an X509 certificate, given -// attribute requests and attributes. -func (mgr *Mgr) ProcessAttributeRequestsForCert(requests []AttributeRequest, attributes []Attribute, cert *x509.Certificate) error { - attrs, err := mgr.ProcessAttributeRequests(requests, attributes) - if err != nil { - return err - } - return mgr.AddAttributesToCert(attrs, cert) -} - -// ProcessAttributeRequests takes an array of attribute requests and an identity's attributes -// and returns an Attributes object containing the requested attributes. -func (mgr *Mgr) ProcessAttributeRequests(requests []AttributeRequest, attributes []Attribute) (*Attributes, error) { - attrsMap := map[string]string{} - attrs := &Attributes{Attrs: attrsMap} - missingRequiredAttrs := []string{} - // For each of the attribute requests - for _, req := range requests { - // Get the attribute - name := req.GetName() - attr := getAttrByName(name, attributes) - if attr == nil { - if req.IsRequired() { - // Didn't find attribute and it was required; return error below - missingRequiredAttrs = append(missingRequiredAttrs, name) - } - // Skip attribute requests which aren't required - continue - } - attrsMap[name] = attr.GetValue() - } - if len(missingRequiredAttrs) > 0 { - return nil, fmt.Errorf("the following required attributes are missing: %+v", - missingRequiredAttrs) - } - return attrs, nil -} - -// AddAttributesToCert adds public attribute info to an X509 certificate. -func (mgr *Mgr) AddAttributesToCert(attrs *Attributes, cert *x509.Certificate) error { - buf, err := json.Marshal(attrs) - if err != nil { - return fmt.Errorf("failed to marshal attributes: %s", err) - } - ext := pkix.Extension{ - Id: AttrOID, - Critical: false, - Value: buf, - } - cert.Extensions = append(cert.Extensions, ext) - return nil -} - -// GetAttributesFromCert gets the attributes from a certificate. -func (mgr *Mgr) GetAttributesFromCert(cert *x509.Certificate) (*Attributes, error) { - // Get certificate attributes from the certificate if it exists - buf, err := getAttributesFromCert(cert) - if err != nil { - return nil, err - } - // Unmarshal into attributes object - attrs := &Attributes{} - if buf != nil { - err := json.Unmarshal(buf, attrs) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal attributes from certificate: %s", err) - } - } - return attrs, nil -} - -// GetAttributesFromIdemix ... -func (mgr *Mgr) GetAttributesFromIdemix(creator []byte) (*Attributes, error) { - if creator == nil { - return nil, errors.New("creator is nil") - } - - sid := &msp.SerializedIdentity{} - err := proto.Unmarshal(creator, sid) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal transaction invoker's identity: %s", err) - } - idemixID := &msp.SerializedIdemixIdentity{} - err = proto.Unmarshal(sid.IdBytes, idemixID) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal transaction invoker's idemix identity: %s", err) - } - // Unmarshal into attributes object - attrs := &Attributes{ - Attrs: make(map[string]string), - } - - ou := &msp.OrganizationUnit{} - err = proto.Unmarshal(idemixID.Ou, ou) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal transaction invoker's ou: %s", err) - } - attrs.Attrs["ou"] = ou.OrganizationalUnitIdentifier - - role := &msp.MSPRole{} - err = proto.Unmarshal(idemixID.Role, role) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal transaction invoker's role: %s", err) - } - var roleStr string - switch role.Role { - case 0: - roleStr = "member" - case 1: - roleStr = "admin" - case 2: - roleStr = "client" - case 3: - roleStr = "peer" - } - attrs.Attrs["role"] = roleStr - - return attrs, nil -} - -// Attributes contains attribute names and values -type Attributes struct { - Attrs map[string]string `json:"attrs"` -} - -// Names returns the names of the attributes -func (a *Attributes) Names() []string { - i := 0 - names := make([]string, len(a.Attrs)) - for name := range a.Attrs { - names[i] = name - i++ - } - return names -} - -// Contains returns true if the named attribute is found -func (a *Attributes) Contains(name string) bool { - _, ok := a.Attrs[name] - return ok -} - -// Value returns an attribute's value -func (a *Attributes) Value(name string) (string, bool, error) { - attr, ok := a.Attrs[name] - return attr, ok, nil -} - -// True returns nil if the value of attribute 'name' is true; -// otherwise, an appropriate error is returned. -func (a *Attributes) True(name string) error { - val, ok, err := a.Value(name) - if err != nil { - return err - } - if !ok { - return fmt.Errorf("Attribute '%s' was not found", name) - } - if val != "true" { - return fmt.Errorf("Attribute '%s' is not true", name) - } - return nil -} - -// Get the attribute info from a certificate extension, or return nil if not found -func getAttributesFromCert(cert *x509.Certificate) ([]byte, error) { - for _, ext := range cert.Extensions { - if isAttrOID(ext.Id) { - return ext.Value, nil - } - } - return nil, nil -} - -// Is the object ID equal to the attribute info object ID? -func isAttrOID(oid asn1.ObjectIdentifier) bool { - if len(oid) != len(AttrOID) { - return false - } - for idx, val := range oid { - if val != AttrOID[idx] { - return false - } - } - return true -} - -// Get an attribute from 'attrs' by its name, or nil if not found -func getAttrByName(name string, attrs []Attribute) Attribute { - for _, attr := range attrs { - if attr.GetName() == name { - return attr - } - } - return nil -} diff --git a/v2/pkg/attrmgr/attrmgr_test.go b/v2/pkg/attrmgr/attrmgr_test.go deleted file mode 100644 index 4aee51b1..00000000 --- a/v2/pkg/attrmgr/attrmgr_test.go +++ /dev/null @@ -1,125 +0,0 @@ -// Copyright the Hyperledger Fabric contributors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package attrmgr_test - -import ( - "crypto/x509" - "encoding/base64" - "testing" - - "github.com/hyperledger/fabric-chaincode-go/v2/pkg/attrmgr" - "github.com/stretchr/testify/assert" -) - -const creator = `CgxpZGVtaXhNU1BJRDISgQgKIAZM+v2JgGPuCod5T3RGBdeSUGGAgpu1W1TMwOeEn1sJEiCBvWZYvM0Q7Vpz498M1KlsILTZ5jk6pGihIfWaeGV+0RpCCgxpZGVtaXhNU1BJRDISEG9yZzEuZGVwYXJ0bWVudDEaIIQ6XWpZn5NGEMPdfoKXn262cOdbyiKjTLa+4nXEc0wyIg4KDGlkZW1peE1TUElEMirmBgpECiDUyAZaFx3+OBClul07XsuS1Kh6VKxAkp8CYWGylozr5BIgIxzFAuzglE95JvJYbzUo16mYsiLwLUA7KuDK0lgyYogSRAogILB8Pu98YqrMYURrsftwFtHzWQiZtdwQImcNuPhBA1QSIHrgGLSNFqGHXxC5nOqfDqySyfwYEKLaxWyuO0tMqy8xGkQKIEP2aKh/YLIKMc6vqz8kCIAtHON2iC/TFAcTKo0B8gMAEiDHnrLuVSWUZzRe1iwUh2rsK6UMTnlF7nFPXC/NE2EhNSIg9JqjO+vb3iU0YXdbLlh3vCU1b8hkGkFxd1r91B8ZyL0qIFs7ajZtYPU/gc4x8j/95ujxavBM2CY9+aWo0HHMq5AyMiDkDCZAYRico3+k5UMUyOb/dr2EkO/1Hay8jjZpUGazQzogcxIUhnyP/Jkfmce0KTClAwK4EWYjqSsPYJ9OMKI5R+hCIM7tzJGcK324QYiFCwGLCdIRcf4b0iX2q9+RSsCJmVuuSiC/p2ZvXGKN8HeCzJVbGB8qVE1G1/vx0zCNJ/vqMSdKsFIglOQPuVIHAF6kwVE7Fid5Me4bolxJml2h44aoWXR2slZSILyd0LbL0uwUksqzZ10WqwVbuQ2D69E5e5ItB2CVIF99WiD94PNz3TBMERm7ZPouFYRtw/mhnlNh0T+j5w5R8+BXhGJECiAGTPr9iYBj7gqHeU90RgXXklBhgIKbtVtUzMDnhJ9bCRIggb1mWLzNEO1ac+PfDNSpbCC02eY5OqRooSH1mnhlftFqIJc15atDPZQ+S4ARmu375M/8NuYAUXtwFCViRvzWOuf+cogBCiD+DDNQtMlsIChWD1d8KJE6zhxTmhK/hDzSJha2icCe+xIgTqZgV3OKwFTbWuHGN9gTuSTdeOKH0DWJ0mntNKN+aisaIHAgRufFQqOzdncNdRJOPlHvyyR1jWFYSOkJtIG+3Cf/IiAFVOO804jCkELupkkpfrKfi0y+gIIamLPgEoERSq0Em3pnMGUCMQCgFofNfUeO+uc8wNdqOpwt4dHn/8AggYMNwZD7gY2om71ZrCXDpmznw2eSmaHb2K8CMEk0d4Y29f2xBv2XLMsC0JrkiXjEo0YakZn66FACO02lEBku2/aGBKokDLRfofA1d4ABAYoBAA==` - -// TestAttrs tests attributes -func TestAttrs(t *testing.T) { - mgr := attrmgr.New() - attrs := []attrmgr.Attribute{ - &Attribute{Name: "attr1", Value: "val1"}, - &Attribute{Name: "attr2", Value: "val2"}, - &Attribute{Name: "attr3", Value: "val3"}, - &Attribute{Name: "boolAttr", Value: "true"}, - } - reqs := []attrmgr.AttributeRequest{ - &AttributeRequest{Name: "attr1", Require: false}, - &AttributeRequest{Name: "attr2", Require: true}, - &AttributeRequest{Name: "boolAttr", Require: true}, - &AttributeRequest{Name: "noattr1", Require: false}, - } - cert := &x509.Certificate{} - - // Verify that the certificate has no attributes - at, err := mgr.GetAttributesFromCert(cert) - if err != nil { - t.Fatalf("Failed to GetAttributesFromCert: %s", err) - } - numAttrs := len(at.Names()) - assert.True(t, numAttrs == 0, "expecting 0 attributes but found %d", numAttrs) - - // Add attributes to certificate - err = mgr.ProcessAttributeRequestsForCert(reqs, attrs, cert) - if err != nil { - t.Fatalf("Failed to ProcessAttributeRequestsForCert: %s", err) - } - - // Get attributes from the certificate and verify the count is correct - at, err = mgr.GetAttributesFromCert(cert) - if err != nil { - t.Fatalf("Failed to GetAttributesFromCert: %s", err) - } - numAttrs = len(at.Names()) - assert.True(t, numAttrs == 3, "expecting 3 attributes but found %d", numAttrs) - - // Check individual attributes - checkAttr(t, "attr1", "val1", at) - checkAttr(t, "attr2", "val2", at) - checkAttr(t, "attr3", "", at) - checkAttr(t, "noattr1", "", at) - assert.NoError(t, at.True("boolAttr")) - - // Negative test case: add required attributes which don't exist - reqs = []attrmgr.AttributeRequest{ - &AttributeRequest{Name: "noattr1", Require: true}, - } - err = mgr.ProcessAttributeRequestsForCert(reqs, attrs, cert) - assert.Error(t, err) -} - -func TestIdemixAttrs(t *testing.T) { - mgr := attrmgr.New() - - _, err := mgr.GetAttributesFromIdemix(nil) - assert.Error(t, err, "Should fail, if nil passed for creator") - - creatorBytes, err := base64.StdEncoding.DecodeString(creator) - assert.NoError(t, err, "Failed to base64 decode creator string") - - attrs, err := mgr.GetAttributesFromIdemix(creatorBytes) - assert.NoError(t, err, "GetAttributesFromIdemix") - numAttrs := len(attrs.Names()) - assert.True(t, numAttrs == 2, "expecting 2 attributes but found %d", numAttrs) - checkAttr(t, "ou", "org1.department1", attrs) - checkAttr(t, "role", "member", attrs) - checkAttr(t, "id", "", attrs) -} - -func checkAttr(t *testing.T, name, val string, attrs *attrmgr.Attributes) { - v, ok, err := attrs.Value(name) - assert.NoError(t, err) - if val == "" { - assert.False(t, attrs.Contains(name), "contains attribute '%s'", name) - assert.False(t, ok, "attribute '%s' was found", name) - } else { - assert.True(t, attrs.Contains(name), "does not contain attribute '%s'", name) - assert.True(t, ok, "attribute '%s' was not found", name) - assert.True(t, v == val, "incorrect value for '%s'; expected '%s' but found '%s'", name, val, v) - } -} - -type Attribute struct { - Name, Value string -} - -func (a *Attribute) GetName() string { - return a.Name -} - -func (a *Attribute) GetValue() string { - return a.Value -} - -type AttributeRequest struct { - Name string - Require bool -} - -func (ar *AttributeRequest) GetName() string { - return ar.Name -} - -func (ar *AttributeRequest) IsRequired() bool { - return ar.Require -} diff --git a/v2/pkg/cid/README.md b/v2/pkg/cid/README.md deleted file mode 100644 index 6c763ad9..00000000 --- a/v2/pkg/cid/README.md +++ /dev/null @@ -1,235 +0,0 @@ -# Client Identity Chaincode Library - -The client identity chaincode library enables you to write chaincode which -makes access control decisions based on the identity of the client -(i.e. the invoker of the chaincode). In particular, you may make access -control decisions based on any or a combination of the following information associated with -the client: - -* the client identity's MSP (Membership Service Provider) ID -* an attribute associated with the client identity -* an OU (Organizational Unit) value associated with the client identity - -Attributes are simply name and value pairs associated with an identity. -For example, `email=me@gmail.com` indicates an identity has the `email` -attribute with a value of `me@gmail.com`. - -## Using the client identity chaincode library - -This section describes how to use the client identity chaincode library. - -All code samples below assume two things: - -1. The type of the `stub` variable is `ChaincodeStubInterface` as passed - to your chaincode. -2. You have added the following import statement to your chaincode. - - ```golang - import "github.com/hyperledger/fabric-chaincode-go/v2/pkg/cid" - ``` - -### Getting the client's ID - -The following demonstrates how to get an ID for the client which is guaranteed -to be unique within the MSP: - -```golang -id, err := cid.GetID(stub) -``` - -### Getting the MSP ID - -The following demonstrates how to get the MSP ID of the client's identity: - -```golang -mspid, err := cid.GetMSPID(stub) -``` - -### Getting an attribute value - -The following demonstrates how to get the value of the *attr1* attribute: - -```golang -val, ok, err := cid.GetAttributeValue(stub, "attr1") -if err != nil { - // There was an error trying to retrieve the attribute -} -if !ok { - // The client identity does not possess the attribute -} -// Do something with the value of 'val' -``` - -### Asserting an attribute value - -Often all you want to do is to make an access control decision based on the value -of an attribute, i.e. to assert the value of an attribute. For example, the following -will return an error if the client does not have the `myapp.admin` attribute -with a value of `true`: - -```golang -err := cid.AssertAttributeValue(stub, "myapp.admin", "true") -if err != nil { - // Return an error -} -``` - -This is effectively using attributes to implement role-based access control, -or RBAC for short. - -### Checking for a specific OU value - -```golang -found, err := cid.HasOUValue(stub, "myapp.admin") -if err != nil { - // Return an error -} -if !found { - // The client identity is not part of the Organizational Unit - // Return an error -} -``` - -### Getting the client's X509 certificate - -The following demonstrates how to get the X509 certificate of the client, or -nil if the client's identity was not based on an X509 certificate: - -```golang -cert, err := cid.GetX509Certificate(stub) -``` - -Note that both `cert` and `err` may be nil as will be the case if the identity -is not using an X509 certificate. - -### Performing multiple operations more efficiently - -Sometimes you may need to perform multiple operations in order to make an access -decision. For example, the following demonstrates how to grant access to -identities with MSP *org1MSP* and *attr1* OR with MSP *org1MSP* and *attr2*. - -```golang -// Get the Client ID object -id, err := cid.New(stub) -if err != nil { - // Handle error -} -mspid, err := id.GetMSPID() -if err != nil { - // Handle error -} -switch mspid { - case "org1MSP": - err = id.AssertAttributeValue("attr1", "true") - case "org2MSP": - err = id.AssertAttributeValue("attr2", "true") - default: - err = errors.New("Wrong MSP") -} -``` - -Although it is not required, it is more efficient to make the `cid.New` call -to get the ClientID object if you need to perform multiple operations, -as demonstrated above. - -## Adding Attributes to Identities - -This section describes how to add custom attributes to certificates when -using Hyperledger Fabric CA as well as when using an external CA. - -### Managing attributes with Fabric CA - -There are two methods of adding attributes to an enrollment certificate -with fabric-ca: - - 1. When you register an identity, you can specify that an enrollment certificate - issued for the identity should by default contain an attribute. This behavior - can be overridden at enrollment time, but this is useful for establishing - default behavior and, assuming registration occurs outside of your application, - does not require any application change. - - The following shows how to register *user1* with two attributes: - *app1Admin* and *email*. - The ":ecert" suffix causes the *appAdmin* attribute to be inserted into user1's - enrollment certificate by default. The *email* attribute is not added - to the enrollment certificate by default. - - ```bash - fabric-ca-client register --id.name user1 --id.secret user1pw --id.type user --id.affiliation org1 --id.attrs 'app1Admin=true:ecert,email=user1@gmail.com' - ``` - - 2. When you enroll an identity, you may request that one or more attributes - be added to the certificate. - For each attribute requested, you may specify whether the attribute is - optional or not. If it is not optional but does not exist for the identity, - enrollment fails. - - The following shows how to enroll *user1* with the *email* attribute, - without the *app1Admin* attribute and optionally with the *phone* attribute - (if the user possesses *phone* attribute). - - ```bash - fabric-ca-client enroll -u http://user1:user1pw@localhost:7054 --enrollment.attrs "email,phone:opt" - ``` - -#### Attribute format in a certificate - -Attributes are stored inside an X509 certificate as an extension with an -ASN.1 OID (Abstract Syntax Notation Object IDentifier) -of `1.2.3.4.5.6.7.8.1`. The value of the extension is a JSON string of the -form `{"attrs":{: 0 { - s += "," - } - for j, tv := range rdn { - if j > 0 { - s += "+" - } - typeString := tv.Type.String() - typeName, ok := attributeTypeNames[typeString] - if !ok { - derBytes, err := asn1.Marshal(tv.Value) - if err == nil { - s += typeString + "=#" + hex.EncodeToString(derBytes) - continue // No value escaping necessary. - } - typeName = typeString - } - valueString := fmt.Sprint(tv.Value) - escaped := "" - begin := 0 - for idx, c := range valueString { - if (idx == 0 && (c == ' ' || c == '#')) || - (idx == len(valueString)-1 && c == ' ') { - escaped += valueString[begin:idx] - escaped += "\\" + string(c) - begin = idx + 1 - continue - } - switch c { - case ',', '+', '"', '\\', '<', '>', ';': - escaped += valueString[begin:idx] - escaped += "\\" + string(c) - begin = idx + 1 - } - } - escaped += valueString[begin:] - s += typeName + "=" + escaped - } - } - return s -} - -var attributeTypeNames = map[string]string{ - "2.5.4.6": "C", - "2.5.4.10": "O", - "2.5.4.11": "OU", - "2.5.4.3": "CN", - "2.5.4.5": "SERIALNUMBER", - "2.5.4.7": "L", - "2.5.4.8": "ST", - "2.5.4.9": "STREET", - "2.5.4.17": "POSTALCODE", -} diff --git a/v2/pkg/cid/cid_test.go b/v2/pkg/cid/cid_test.go deleted file mode 100644 index 04b9db18..00000000 --- a/v2/pkg/cid/cid_test.go +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright the Hyperledger Fabric contributors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package cid_test - -import ( - "encoding/base64" - "testing" - - "github.com/hyperledger/fabric-chaincode-go/v2/pkg/cid" - "github.com/hyperledger/fabric-protos-go-apiv2/msp" - "github.com/stretchr/testify/assert" - "google.golang.org/protobuf/proto" -) - -const certWithOutAttrs = `-----BEGIN CERTIFICATE----- -MIICXTCCAgSgAwIBAgIUeLy6uQnq8wwyElU/jCKRYz3tJiQwCgYIKoZIzj0EAwIw -eTELMAkGA1UEBhMCVVMxEzARBgNVBAgTCkNhbGlmb3JuaWExFjAUBgNVBAcTDVNh -biBGcmFuY2lzY28xGTAXBgNVBAoTEEludGVybmV0IFdpZGdldHMxDDAKBgNVBAsT -A1dXVzEUMBIGA1UEAxMLZXhhbXBsZS5jb20wHhcNMTcwOTA4MDAxNTAwWhcNMTgw -OTA4MDAxNTAwWjBdMQswCQYDVQQGEwJVUzEXMBUGA1UECBMOTm9ydGggQ2Fyb2xp -bmExFDASBgNVBAoTC0h5cGVybGVkZ2VyMQ8wDQYDVQQLEwZGYWJyaWMxDjAMBgNV -BAMTBWFkbWluMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEFq/90YMuH4tWugHa -oyZtt4Mbwgv6CkBSDfYulVO1CVInw1i/k16DocQ/KSDTeTfgJxrX1Ree1tjpaodG -1wWyM6OBhTCBgjAOBgNVHQ8BAf8EBAMCB4AwDAYDVR0TAQH/BAIwADAdBgNVHQ4E -FgQUhKs/VJ9IWJd+wer6sgsgtZmxZNwwHwYDVR0jBBgwFoAUIUd4i/sLTwYWvpVr -TApzcT8zv/kwIgYDVR0RBBswGYIXQW5pbHMtTWFjQm9vay1Qcm8ubG9jYWwwCgYI -KoZIzj0EAwIDRwAwRAIgCoXaCdU8ZiRKkai0QiXJM/GL5fysLnmG2oZ6XOIdwtsC -IEmCsI8Mhrvx1doTbEOm7kmIrhQwUVDBNXCWX1t3kJVN ------END CERTIFICATE----- -` -const certWithAttrs = `-----BEGIN CERTIFICATE----- -MIIB6TCCAY+gAwIBAgIUHkmY6fRP0ANTvzaBwKCkMZZPUnUwCgYIKoZIzj0EAwIw -GzEZMBcGA1UEAxMQZmFicmljLWNhLXNlcnZlcjAeFw0xNzA5MDgwMzQyMDBaFw0x -ODA5MDgwMzQyMDBaMB4xHDAaBgNVBAMTE015VGVzdFVzZXJXaXRoQXR0cnMwWTAT -BgcqhkjOPQIBBggqhkjOPQMBBwNCAATmB1r3CdWvOOP3opB3DjJnW3CnN8q1ydiR -dzmuA6A2rXKzPIltHvYbbSqISZJubsy8gVL6GYgYXNdu69RzzFF5o4GtMIGqMA4G -A1UdDwEB/wQEAwICBDAMBgNVHRMBAf8EAjAAMB0GA1UdDgQWBBTYKLTAvJJK08OM -VGwIhjMQpo2DrjAfBgNVHSMEGDAWgBTEs/52DeLePPx1+65VhgTwu3/2ATAiBgNV -HREEGzAZghdBbmlscy1NYWNCb29rLVByby5sb2NhbDAmBggqAwQFBgcIAQQaeyJh -dHRycyI6eyJhdHRyMSI6InZhbDEifX0wCgYIKoZIzj0EAwIDSAAwRQIhAPuEqWUp -svTTvBqLR5JeQSctJuz3zaqGRqSs2iW+QB3FAiAIP0mGWKcgSGRMMBvaqaLytBYo -9v3hRt1r8j8vN0pMcg== ------END CERTIFICATE----- -` - -// #nosec G101 -const idemixCred = `CiAGTPr9iYBj7gqHeU90RgXXklBhgIKbtVtUzMDnhJ9bCRIggb1mWLzNEO1ac+PfDNSpbCC02eY5OqRooSH1mnhlftEaQgoMaWRlbWl4TVNQSUQyEhBvcmcxLmRlcGFydG1lbnQxGiCEOl1qWZ+TRhDD3X6Cl59utnDnW8oio0y2vuJ1xHNMMiIOCgxpZGVtaXhNU1BJRDIq5gYKRAog1MgGWhcd/jgQpbpdO17LktSoelSsQJKfAmFhspaM6+QSICMcxQLs4JRPeSbyWG81KNepmLIi8C1AOyrgytJYMmKIEkQKICCwfD7vfGKqzGFEa7H7cBbR81kImbXcECJnDbj4QQNUEiB64Bi0jRahh18QuZzqnw6sksn8GBCi2sVsrjtLTKsvMRpECiBD9miof2CyCjHOr6s/JAiALRzjdogv0xQHEyqNAfIDABIgx56y7lUllGc0XtYsFIdq7CulDE55Re5xT1wvzRNhITUiIPSaozvr294lNGF3Wy5Yd7wlNW/IZBpBcXda/dQfGci9KiBbO2o2bWD1P4HOMfI//ebo8WrwTNgmPfmlqNBxzKuQMjIg5AwmQGEYnKN/pOVDFMjm/3a9hJDv9R2svI42aVBms0M6IHMSFIZ8j/yZH5nHtCkwpQMCuBFmI6krD2CfTjCiOUfoQiDO7cyRnCt9uEGIhQsBiwnSEXH+G9Il9qvfkUrAiZlbrkogv6dmb1xijfB3gsyVWxgfKlRNRtf78dMwjSf76jEnSrBSIJTkD7lSBwBepMFROxYneTHuG6JcSZpdoeOGqFl0drJWUiC8ndC2y9LsFJLKs2ddFqsFW7kNg+vROXuSLQdglSBffVog/eDzc90wTBEZu2T6LhWEbcP5oZ5TYdE/o+cOUfPgV4RiRAogBkz6/YmAY+4Kh3lPdEYF15JQYYCCm7VbVMzA54SfWwkSIIG9Zli8zRDtWnPj3wzUqWwgtNnmOTqkaKEh9Zp4ZX7RaiCXNeWrQz2UPkuAEZrt++TP/DbmAFF7cBQlYkb81jrn/nKIAQog/gwzULTJbCAoVg9XfCiROs4cU5oSv4Q80iYWtonAnvsSIE6mYFdzisBU21rhxjfYE7kk3Xjih9A1idJp7TSjfmorGiBwIEbnxUKjs3Z3DXUSTj5R78skdY1hWEjpCbSBvtwn/yIgBVTjvNOIwpBC7qZJKX6yn4tMvoCCGpiz4BKBEUqtBJt6ZzBlAjEAoBaHzX1HjvrnPMDXajqcLeHR5//AIIGDDcGQ+4GNqJu9Wawlw6Zs58Nnkpmh29ivAjBJNHeGNvX9sQb9lyzLAtCa5Il4xKNGGpGZ+uhQAjtNpRAZLtv2hgSqJAy0X6HwNXeAAQGKAQA=` - -func TestClient(t *testing.T) { - stub, err := getMockStub() - assert.NoError(t, err, "Failed to get mock submitter") - sinfo, err := cid.New(stub) - assert.NoError(t, err, "Error getting submitter of the transaction") - id, err := cid.GetID(stub) - assert.NoError(t, err, "Error getting ID of the submitter of the transaction") - assert.NotEmpty(t, id, "Transaction submitter ID should not be empty") - t.Logf("The client's ID is: %s", id) - cert, err := cid.GetX509Certificate(stub) - assert.NoError(t, err, "Error getting X509 certificate of the submitter of the transaction") - assert.NotNil(t, cert, "Transaction submitter certificate should not be nil") - mspid, err := cid.GetMSPID(stub) - assert.NoError(t, err, "Error getting MSP ID of the submitter of the transaction") - assert.NotEmpty(t, mspid, "Transaction submitter MSP ID should not be empty") - _, found, err := sinfo.GetAttributeValue("foo") - assert.NoError(t, err, "Error getting Unique ID of the submitter of the transaction") - assert.False(t, found, "Attribute 'foo' should not be found in the submitter cert") - err = cid.AssertAttributeValue(stub, "foo", "") - assert.Error(t, err, "AssertAttributeValue should have returned an error with no attribute") - found, err = cid.HasOUValue(stub, "Fabric") - assert.NoError(t, err, "Error getting X509 cert of the submitter of the transaction") - assert.True(t, found) - found, err = cid.HasOUValue(stub, "foo") - assert.NoError(t, err, "HasOUValue") - assert.False(t, found, "OU 'foo' should not be found in the submitter cert") - - stub, err = getMockStubWithAttrs() - assert.NoError(t, err, "Failed to get mock submitter") - sinfo, err = cid.New(stub) - assert.NoError(t, err, "Failed to new client") - attrVal, found, err := sinfo.GetAttributeValue("attr1") - assert.NoError(t, err, "Error getting Unique ID of the submitter of the transaction") - assert.True(t, found, "Attribute 'attr1' should be found in the submitter cert") - assert.Equal(t, attrVal, "val1", "Value of attribute 'attr1' should be 'val1'") - attrVal, found, err = cid.GetAttributeValue(stub, "attr1") - assert.NoError(t, err, "Error getting Unique ID of the submitter of the transaction") - assert.True(t, found, "Attribute 'attr1' should be found in the submitter cert") - assert.Equal(t, attrVal, "val1", "Value of attribute 'attr1' should be 'val1'") - err = cid.AssertAttributeValue(stub, "attr1", "val1") - assert.NoError(t, err, "Error in AssertAttributeValue") - err = cid.AssertAttributeValue(stub, "attr1", "val2") - assert.Error(t, err, "Assert should have failed; value was val1, not val2") - found, err = cid.HasOUValue(stub, "foo") - assert.NoError(t, err, "Error getting X509 cert of the submitter of the transaction") - assert.False(t, found, "HasOUValue") - - // Error case1 - stub, err = getMockStubWithNilCreator() - assert.NoError(t, err, "Failed to get mock submitter") - _, err = cid.New(stub) - assert.Error(t, err, "NewSubmitterInfo should have returned an error when submitter with nil creator is passed") - - // Error case2 - stub, err = getMockStubWithFakeCreator() - assert.NoError(t, err, "Failed to get mock submitter") - _, err = cid.New(stub) - assert.Error(t, err, "NewSubmitterInfo should have returned an error when submitter with fake creator is passed") -} - -func TestIdemix(t *testing.T) { - stub, err := getIdemixMockStubWithAttrs() - assert.NoError(t, err, "Failed to get mock idemix stub") - sinfo, err := cid.New(stub) - assert.NoError(t, err, "Failed to new client") - cert, err := sinfo.GetX509Certificate() - assert.Nil(t, cert, "Idemix can't get x509 type of cert") - assert.NoError(t, err, "Err for this func is nil") - id, err := cid.GetID(stub) - assert.Error(t, err, "Cannot determine identity") - assert.Equal(t, id, "", "Id should be empty when Idemix") - attrVal, found, err := sinfo.GetAttributeValue("ou") - assert.NoError(t, err, "Error getting 'ou' of the submitter of the transaction") - assert.True(t, found, "Attribute 'ou' should be found in the submitter cert") - assert.Equal(t, attrVal, "org1.department1", "Value of attribute 'attr1' should be 'val1'") - attrVal, found, err = sinfo.GetAttributeValue("role") - assert.NoError(t, err, "Error getting 'role' of the submitter of the transaction") - assert.True(t, found, "Attribute 'role' should be found in the submitter cert") - assert.Equal(t, attrVal, "member", "Value of attribute 'attr1' should be 'val1'") - _, found, err = sinfo.GetAttributeValue("id") - assert.NoError(t, err, "GetAttributeValue") - assert.False(t, found, "Attribute 'id' should not be found in the submitter cert") -} - -func getMockStub() (cid.ChaincodeStubInterface, error) { - stub := &mockStub{} - sid := &msp.SerializedIdentity{Mspid: "SampleOrg", - IdBytes: []byte(certWithOutAttrs)} - b, err := proto.Marshal(sid) - if err != nil { - return nil, err - } - stub.creator = b - return stub, nil -} - -func getMockStubWithAttrs() (cid.ChaincodeStubInterface, error) { - stub := &mockStub{} - sid := &msp.SerializedIdentity{Mspid: "SampleOrg", - IdBytes: []byte(certWithAttrs)} - b, err := proto.Marshal(sid) - if err != nil { - return nil, err - } - stub.creator = b - return stub, nil -} - -func getIdemixMockStubWithAttrs() (cid.ChaincodeStubInterface, error) { - stub := &mockStub{} - idBytes, err := base64.StdEncoding.DecodeString(idemixCred) - if err != nil { - return nil, err - } - sid := &msp.SerializedIdentity{Mspid: "idemixOrg", - IdBytes: idBytes, - } - b, err := proto.Marshal(sid) - if err != nil { - return nil, err - } - stub.creator = b - return stub, nil -} - -func getMockStubWithNilCreator() (cid.ChaincodeStubInterface, error) { - c := &mockStub{} - c.creator = nil - return c, nil -} - -func getMockStubWithFakeCreator() (cid.ChaincodeStubInterface, error) { - c := &mockStub{} - c.creator = []byte("foo") - return c, nil -} - -type mockStub struct { - creator []byte -} - -func (s *mockStub) GetCreator() ([]byte, error) { - return s.creator, nil -} diff --git a/v2/pkg/cid/interfaces.go b/v2/pkg/cid/interfaces.go deleted file mode 100644 index 140ffe98..00000000 --- a/v2/pkg/cid/interfaces.go +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright the Hyperledger Fabric contributors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package cid - -import "crypto/x509" - -// ChaincodeStubInterface is used by deployable chaincode apps to get identity -// of the agent (or user) submitting the transaction. -type ChaincodeStubInterface interface { - // GetCreator returns `SignatureHeader.Creator` (e.g. an identity) - // of the `SignedProposal`. This is the identity of the agent (or user) - // submitting the transaction. - GetCreator() ([]byte, error) -} - -// ClientIdentity represents information about the identity that submitted the -// transaction -type ClientIdentity interface { - - // GetID returns the ID associated with the invoking identity. This ID - // is guaranteed to be unique within the MSP. - GetID() (string, error) - - // Return the MSP ID of the client - GetMSPID() (string, error) - - // GetAttributeValue returns the value of the client's attribute named `attrName`. - // If the client possesses the attribute, `found` is true and `value` equals the - // value of the attribute. - // If the client does not possess the attribute, `found` is false and `value` - // equals "". - GetAttributeValue(attrName string) (value string, found bool, err error) - - // AssertAttributeValue verifies that the client has the attribute named `attrName` - // with a value of `attrValue`; otherwise, an error is returned. - AssertAttributeValue(attrName, attrValue string) error - - // GetX509Certificate returns the X509 certificate associated with the client, - // or nil if it was not identified by an X509 certificate. - GetX509Certificate() (*x509.Certificate, error) -} diff --git a/v2/pkg/statebased/interfaces.go b/v2/pkg/statebased/interfaces.go deleted file mode 100644 index 7f410afd..00000000 --- a/v2/pkg/statebased/interfaces.go +++ /dev/null @@ -1,51 +0,0 @@ -// Copyright the Hyperledger Fabric contributors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package statebased - -import "fmt" - -// RoleType of an endorsement policy's identity -type RoleType string - -const ( - // RoleTypeMember identifies an org's member identity - RoleTypeMember = RoleType("MEMBER") - // RoleTypePeer identifies an org's peer identity - RoleTypePeer = RoleType("PEER") -) - -// RoleTypeDoesNotExistError is returned by function AddOrgs of -// KeyEndorsementPolicy if a role type that does not match one -// specified above is passed as an argument. -type RoleTypeDoesNotExistError struct { - RoleType RoleType -} - -func (r *RoleTypeDoesNotExistError) Error() string { - return fmt.Sprintf("role type %s does not exist", r.RoleType) -} - -// KeyEndorsementPolicy provides a set of convenience methods to create and -// modify a state-based endorsement policy. Endorsement policies created by -// this convenience layer will always be a logical AND of ".peer" -// principals for one or more ORGs specified by the caller. -type KeyEndorsementPolicy interface { - // Policy returns the endorsement policy as bytes - Policy() ([]byte, error) - - // AddOrgs adds the specified orgs to the list of orgs that are required - // to endorse. All orgs MSP role types will be set to the role that is - // specified in the first parameter. Among other aspects the desired role - // depends on the channel's configuration: if it supports node OUs, it is - // likely going to be the PEER role, while the MEMBER role is the suited - // one if it does not. - AddOrgs(roleType RoleType, organizations ...string) error - - // DelOrgs deletes the specified channel orgs from the existing key-level endorsement - // policy for this KVS key. - DelOrgs(organizations ...string) - - // ListOrgs returns an array of channel orgs that are required to endorse changes. - ListOrgs() []string -} diff --git a/v2/pkg/statebased/statebasedimpl.go b/v2/pkg/statebased/statebasedimpl.go deleted file mode 100644 index ae21152a..00000000 --- a/v2/pkg/statebased/statebasedimpl.go +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright the Hyperledger Fabric contributors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package statebased - -import ( - "fmt" - "sort" - - "github.com/hyperledger/fabric-protos-go-apiv2/common" - "github.com/hyperledger/fabric-protos-go-apiv2/msp" - "google.golang.org/protobuf/proto" -) - -// stateEP implements the KeyEndorsementPolicy -type stateEP struct { - orgs map[string]msp.MSPRole_MSPRoleType -} - -// NewStateEP constructs a state-based endorsement policy from a given -// serialized EP byte array. If the byte array is empty, a new EP is created. -func NewStateEP(policy []byte) (KeyEndorsementPolicy, error) { - s := &stateEP{orgs: make(map[string]msp.MSPRole_MSPRoleType)} - if policy != nil { - spe := &common.SignaturePolicyEnvelope{} - if err := proto.Unmarshal(policy, spe); err != nil { - return nil, fmt.Errorf("Error unmarshaling to SignaturePolicy: %s", err) - } - - err := s.setMSPIDsFromSP(spe) - if err != nil { - return nil, err - } - } - return s, nil -} - -// Policy returns the endorsement policy as bytes. -func (s *stateEP) Policy() ([]byte, error) { - spe, err := s.policyFromMSPIDs() - if err != nil { - return nil, err - } - spBytes, err := proto.Marshal(spe) - if err != nil { - return nil, err - } - return spBytes, nil -} - -// AddOrgs adds the specified channel orgs to the existing key-level EP. -func (s *stateEP) AddOrgs(role RoleType, neworgs ...string) error { - var mspRole msp.MSPRole_MSPRoleType - switch role { - case RoleTypeMember: - mspRole = msp.MSPRole_MEMBER - case RoleTypePeer: - mspRole = msp.MSPRole_PEER - default: - return &RoleTypeDoesNotExistError{RoleType: role} - } - - // add new orgs - for _, addorg := range neworgs { - s.orgs[addorg] = mspRole - } - - return nil -} - -// DelOrgs delete the specified channel orgs from the existing key-level EP. -func (s *stateEP) DelOrgs(delorgs ...string) { - for _, delorg := range delorgs { - delete(s.orgs, delorg) - } -} - -// ListOrgs returns an array of channel orgs that are required to endorse changes. -func (s *stateEP) ListOrgs() []string { - orgNames := make([]string, 0, len(s.orgs)) - for mspid := range s.orgs { - orgNames = append(orgNames, mspid) - } - return orgNames -} - -func (s *stateEP) setMSPIDsFromSP(sp *common.SignaturePolicyEnvelope) error { - // iterate over the identities in this envelope - for _, identity := range sp.Identities { - // this imlementation only supports the ROLE type - if identity.PrincipalClassification == msp.MSPPrincipal_ROLE { - msprole := &msp.MSPRole{} - err := proto.Unmarshal(identity.Principal, msprole) - if err != nil { - return fmt.Errorf("error unmarshaling msp principal: %s", err) - } - s.orgs[msprole.GetMspIdentifier()] = msprole.GetRole() - } - } - return nil -} - -func (s *stateEP) policyFromMSPIDs() (*common.SignaturePolicyEnvelope, error) { - mspids := s.ListOrgs() - sort.Strings(mspids) - principals := make([]*msp.MSPPrincipal, len(mspids)) - sigspolicy := make([]*common.SignaturePolicy, len(mspids)) - for i, id := range mspids { - principal, err := proto.Marshal( - &msp.MSPRole{ - Role: s.orgs[id], - MspIdentifier: id, - }, - ) - if err != nil { - return nil, err - } - principals[i] = &msp.MSPPrincipal{ - PrincipalClassification: msp.MSPPrincipal_ROLE, - Principal: principal, - } - sigspolicy[i] = &common.SignaturePolicy{ - Type: &common.SignaturePolicy_SignedBy{ - SignedBy: int32(i), - }, - } - } - - // create the policy: it requires exactly 1 signature from all of the principals - p := &common.SignaturePolicyEnvelope{ - Version: 0, - Rule: &common.SignaturePolicy{ - Type: &common.SignaturePolicy_NOutOf_{ - NOutOf: &common.SignaturePolicy_NOutOf{ - N: int32(len(mspids)), - Rules: sigspolicy, - }, - }, - }, - Identities: principals, - } - return p, nil -} diff --git a/v2/pkg/statebased/statebasedimpl_test.go b/v2/pkg/statebased/statebasedimpl_test.go deleted file mode 100644 index fd0e8202..00000000 --- a/v2/pkg/statebased/statebasedimpl_test.go +++ /dev/null @@ -1,113 +0,0 @@ -// Copyright the Hyperledger Fabric contributors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package statebased_test - -import ( - "testing" - - "github.com/hyperledger/fabric-chaincode-go/v2/pkg/statebased" - "github.com/hyperledger/fabric-protos-go-apiv2/common" - "github.com/hyperledger/fabric-protos-go-apiv2/msp" - "github.com/stretchr/testify/assert" - "google.golang.org/protobuf/proto" -) - -func TestAddOrg(t *testing.T) { - // add an org - ep, err := statebased.NewStateEP(nil) - assert.NoError(t, err) - err = ep.AddOrgs(statebased.RoleTypePeer, "Org1") - assert.NoError(t, err) - - // bad role type - err = ep.AddOrgs("unknown", "Org1") - assert.Equal(t, &statebased.RoleTypeDoesNotExistError{RoleType: statebased.RoleType("unknown")}, err) - assert.EqualError(t, err, "role type unknown does not exist") - - epBytes, err := ep.Policy() - assert.NoError(t, err) - expectedEP := signedByMspPeer("Org1", t) - expectedEPBytes, err := proto.Marshal(expectedEP) - assert.NoError(t, err) - assert.Equal(t, expectedEPBytes, epBytes) -} - -func TestListOrgs(t *testing.T) { - expectedEP := signedByMspPeer("Org1", t) - expectedEPBytes, err := proto.Marshal(expectedEP) - assert.NoError(t, err) - - // retrieve the orgs - ep, err := statebased.NewStateEP(expectedEPBytes) - assert.NoError(t, err, "NewStateEP") - orgs := ep.ListOrgs() - assert.Equal(t, []string{"Org1"}, orgs) -} - -func TestDelAddOrg(t *testing.T) { - expectedEP := signedByMspPeer("Org1", t) - expectedEPBytes, err := proto.Marshal(expectedEP) - assert.NoError(t, err) - ep, err := statebased.NewStateEP(expectedEPBytes) - assert.NoError(t, err) - - // retrieve the orgs - orgs := ep.ListOrgs() - assert.Equal(t, []string{"Org1"}, orgs) - - // mod the endorsement policy - err = ep.AddOrgs(statebased.RoleTypePeer, "Org2") - assert.NoError(t, err) - ep.DelOrgs("Org1") - - // check whether what is stored is correct - epBytes, err := ep.Policy() - assert.NoError(t, err) - expectedEP = signedByMspPeer("Org2", t) - expectedEPBytes, err = proto.Marshal(expectedEP) - assert.NoError(t, err) - assert.Equal(t, expectedEPBytes, epBytes) -} - -// SignedByMspPeer creates a SignaturePolicyEnvelope -// requiring 1 signature from any peer of the specified MSP -func signedByMspPeer(mspId string, t *testing.T) *common.SignaturePolicyEnvelope { - // specify the principal: it's a member of the msp we just found - principal, err := proto.Marshal( - &msp.MSPRole{ - Role: msp.MSPRole_PEER, - MspIdentifier: mspId, - }, - ) - if err != nil { - t.Fatalf("failed to marshal principal: %s", err) - } - - // create the policy: it requires exactly 1 signature from the first (and only) principal - p := &common.SignaturePolicyEnvelope{ - Version: 0, - Rule: &common.SignaturePolicy{ - Type: &common.SignaturePolicy_NOutOf_{ - NOutOf: &common.SignaturePolicy_NOutOf{ - N: 1, - Rules: []*common.SignaturePolicy{ - { - Type: &common.SignaturePolicy_SignedBy{ - SignedBy: 0, - }, - }, - }, - }, - }, - }, - Identities: []*msp.MSPPrincipal{ - { - PrincipalClassification: msp.MSPPrincipal_ROLE, - Principal: principal, - }, - }, - } - - return p -} diff --git a/v2/shim/chaincodeserver.go b/v2/shim/chaincodeserver.go deleted file mode 100644 index 455ec4a1..00000000 --- a/v2/shim/chaincodeserver.go +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright the Hyperledger Fabric contributors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package shim - -import ( - "crypto/tls" - "errors" - - "github.com/hyperledger/fabric-chaincode-go/v2/shim/internal" - "github.com/hyperledger/fabric-protos-go-apiv2/peer" - - "google.golang.org/grpc/keepalive" -) - -// TLSProperties passed to ChaincodeServer -type TLSProperties struct { - //Disabled forces default to be TLS enabled - Disabled bool - Key []byte - Cert []byte - // ClientCACerts set if connecting peer should be verified - ClientCACerts []byte -} - -// ChaincodeServer encapsulates basic properties needed for a chaincode server -type ChaincodeServer struct { - // CCID should match chaincode's package name on peer - CCID string - // Addesss is the listen address of the chaincode server - Address string - // CC is the chaincode that handles Init and Invoke - CC Chaincode - // TLSProps is the TLS properties passed to chaincode server - TLSProps TLSProperties - // KaOpts keepalive options, sensible defaults provided if nil - KaOpts *keepalive.ServerParameters -} - -// Connect the bidi stream entry point called by chaincode to register with the Peer. -func (cs *ChaincodeServer) Connect(stream peer.Chaincode_ConnectServer) error { - return chatWithPeer(cs.CCID, stream, cs.CC) -} - -// Start the server -func (cs *ChaincodeServer) Start() error { - if cs.CCID == "" { - return errors.New("ccid must be specified") - } - - if cs.Address == "" { - return errors.New("address must be specified") - } - - if cs.CC == nil { - return errors.New("chaincode must be specified") - } - - var tlsCfg *tls.Config - var err error - if !cs.TLSProps.Disabled { - tlsCfg, err = internal.LoadTLSConfig(true, cs.TLSProps.Key, cs.TLSProps.Cert, cs.TLSProps.ClientCACerts) - if err != nil { - return err - } - } - - // create listener and grpc server - server, err := internal.NewServer(cs.Address, tlsCfg, cs.KaOpts) - if err != nil { - return err - } - - // register the server with grpc ... - peer.RegisterChaincodeServer(server.Server, cs) - - // ... and start - return server.Start() -} diff --git a/v2/shim/handler.go b/v2/shim/handler.go deleted file mode 100644 index a465ae26..00000000 --- a/v2/shim/handler.go +++ /dev/null @@ -1,708 +0,0 @@ -// Copyright the Hyperledger Fabric contributors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package shim - -import ( - "errors" - "fmt" - "sync" - - "github.com/hyperledger/fabric-protos-go-apiv2/peer" - "google.golang.org/protobuf/proto" -) - -type state string - -const ( - created state = "created" // start state - established state = "established" // connection established - ready state = "ready" // ready for requests -) - -// PeerChaincodeStream is the common stream interface for Peer - chaincode communication. -// Both chaincode-as-server and chaincode-as-client patterns need to support this -type PeerChaincodeStream interface { - Send(*peer.ChaincodeMessage) error - Recv() (*peer.ChaincodeMessage, error) -} - -// ClientStream supports the (original) chaincode-as-client interaction pattern -type ClientStream interface { - PeerChaincodeStream - CloseSend() error -} - -// Handler handler implementation for shim side of chaincode. -type Handler struct { - // serialLock is used to prevent concurrent calls to Send on the - // PeerChaincodeStream. This is required by gRPC. - serialLock sync.Mutex - // chatStream is the client used to access the chaincode support server on - // the peer. - chatStream PeerChaincodeStream - - // cc is the chaincode associated with this handler. - cc Chaincode - // state holds the current state of this handler. - state state - - // Multiple queries (and one transaction) with different txids can be executing in parallel for this chaincode - // responseChannels is the channel on which responses are communicated by the shim to the chaincodeStub. - // need lock to protect chaincode from attempting - // concurrent requests to the peer - responseChannelsMutex sync.Mutex - responseChannels map[string]chan *peer.ChaincodeMessage -} - -func shorttxid(txid string) string { - if len(txid) < 8 { - return txid - } - return txid[0:8] -} - -// serialSend serializes calls to Send on the gRPC client. -func (h *Handler) serialSend(msg *peer.ChaincodeMessage) error { - h.serialLock.Lock() - defer h.serialLock.Unlock() - - return h.chatStream.Send(msg) -} - -// serialSendAsync sends the provided message asynchronously in a separate -// goroutine. The result of the send is communicated back to the caller via -// errc. -func (h *Handler) serialSendAsync(msg *peer.ChaincodeMessage, errc chan<- error) { - go func() { - errc <- h.serialSend(msg) - }() -} - -// transactionContextID builds a transaction context identifier by -// concatenating a channel ID and a transaction ID. -func transactionContextID(chainID, txid string) string { - return chainID + txid -} - -func (h *Handler) createResponseChannel(channelID, txid string) (<-chan *peer.ChaincodeMessage, error) { - h.responseChannelsMutex.Lock() - defer h.responseChannelsMutex.Unlock() - - if h.responseChannels == nil { - return nil, fmt.Errorf("[%s] cannot create response channel", shorttxid(txid)) - } - - txCtxID := transactionContextID(channelID, txid) - if h.responseChannels[txCtxID] != nil { - return nil, fmt.Errorf("[%s] channel exists", shorttxid(txCtxID)) - } - - responseChan := make(chan *peer.ChaincodeMessage) - h.responseChannels[txCtxID] = responseChan - return responseChan, nil -} - -func (h *Handler) deleteResponseChannel(channelID, txid string) { - h.responseChannelsMutex.Lock() - defer h.responseChannelsMutex.Unlock() - if h.responseChannels != nil { - txCtxID := transactionContextID(channelID, txid) - delete(h.responseChannels, txCtxID) - } -} - -func (h *Handler) handleResponse(msg *peer.ChaincodeMessage) error { - h.responseChannelsMutex.Lock() - defer h.responseChannelsMutex.Unlock() - - if h.responseChannels == nil { - return fmt.Errorf("[%s] Cannot send message response channel", shorttxid(msg.Txid)) - } - - txCtxID := transactionContextID(msg.ChannelId, msg.Txid) - responseCh := h.responseChannels[txCtxID] - if responseCh == nil { - return fmt.Errorf("[%s] responseChannel does not exist", shorttxid(msg.Txid)) - } - responseCh <- msg - return nil -} - -// sendReceive sends msg to the peer and waits for the response to arrive on -// the provided responseChan. On success, the response message will be -// returned. An error will be returned msg was not successfully sent to the -// peer. -func (h *Handler) sendReceive(msg *peer.ChaincodeMessage, responseChan <-chan *peer.ChaincodeMessage) (*peer.ChaincodeMessage, error) { - err := h.serialSend(msg) - if err != nil { - return &peer.ChaincodeMessage{}, err - } - - outmsg := <-responseChan - return outmsg, nil -} - -// NewChaincodeHandler returns a new instance of the shim side handler. -func newChaincodeHandler(peerChatStream PeerChaincodeStream, chaincode Chaincode) *Handler { - return &Handler{ - chatStream: peerChatStream, - cc: chaincode, - responseChannels: map[string]chan *peer.ChaincodeMessage{}, - state: created, - } -} - -type stubHandlerFunc func(*peer.ChaincodeMessage) (*peer.ChaincodeMessage, error) - -func (h *Handler) handleStubInteraction(handler stubHandlerFunc, msg *peer.ChaincodeMessage, errc chan<- error) { - resp, err := handler(msg) - if err != nil { - resp = &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_ERROR, Payload: []byte(err.Error()), Txid: msg.Txid, ChannelId: msg.ChannelId} - } - h.serialSendAsync(resp, errc) -} - -// handleInit calls the Init function of the associated chaincode. -func (h *Handler) handleInit(msg *peer.ChaincodeMessage) (*peer.ChaincodeMessage, error) { - // Get the function and args from Payload - input := &peer.ChaincodeInput{} - err := proto.Unmarshal(msg.Payload, input) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal input: %s", err) - } - - // Create the ChaincodeStub which the chaincode can use to callback - stub, err := newChaincodeStub(h, msg.ChannelId, msg.Txid, input, msg.Proposal) - if err != nil { - return nil, fmt.Errorf("failed to create new ChaincodeStub: %s", err) - } - - res := h.cc.Init(stub) - if res.Status >= ERROR { - return &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_ERROR, Payload: []byte(res.Message), Txid: msg.Txid, ChaincodeEvent: stub.chaincodeEvent, ChannelId: msg.ChannelId}, nil - } - - resBytes, err := proto.Marshal(res) - if err != nil { - return nil, fmt.Errorf("failed to marshal response: %s", err) - } - - return &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_COMPLETED, Payload: resBytes, Txid: msg.Txid, ChaincodeEvent: stub.chaincodeEvent, ChannelId: stub.ChannelID}, nil -} - -// handleTransaction calls Invoke on the associated chaincode. -func (h *Handler) handleTransaction(msg *peer.ChaincodeMessage) (*peer.ChaincodeMessage, error) { - // Get the function and args from Payload - input := &peer.ChaincodeInput{} - err := proto.Unmarshal(msg.Payload, input) - if err != nil { - return nil, fmt.Errorf("failed to unmarshal input: %s", err) - } - - // Create the ChaincodeStub which the chaincode can use to callback - stub, err := newChaincodeStub(h, msg.ChannelId, msg.Txid, input, msg.Proposal) - if err != nil { - return nil, fmt.Errorf("failed to create new ChaincodeStub: %s", err) - } - - res := h.cc.Invoke(stub) - - // Endorser will handle error contained in Response. - resBytes, err := proto.Marshal(res) - if err != nil { - return nil, fmt.Errorf("failed to marshal response: %s", err) - } - - return &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_COMPLETED, Payload: resBytes, Txid: msg.Txid, ChaincodeEvent: stub.chaincodeEvent, ChannelId: stub.ChannelID}, nil -} - -// callPeerWithChaincodeMsg sends a chaincode message to the peer for the given -// txid and channel and receives the response. -func (h *Handler) callPeerWithChaincodeMsg(msg *peer.ChaincodeMessage, channelID, txid string) (*peer.ChaincodeMessage, error) { - // Create the channel on which to communicate the response from the peer - respChan, err := h.createResponseChannel(channelID, txid) - if err != nil { - return &peer.ChaincodeMessage{}, err - } - defer h.deleteResponseChannel(channelID, txid) - - return h.sendReceive(msg, respChan) -} - -// handleGetState communicates with the peer to fetch the requested state information from the ledger. -func (h *Handler) handleGetState(collection string, key string, channelID string, txid string) ([]byte, error) { - // Construct payload for GET_STATE - payloadBytes := marshalOrPanic(&peer.GetState{Collection: collection, Key: key}) - - msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_GET_STATE, Payload: payloadBytes, Txid: txid, ChannelId: channelID} - responseMsg, err := h.callPeerWithChaincodeMsg(msg, channelID, txid) - if err != nil { - return nil, fmt.Errorf("[%s] error sending %s: %s", shorttxid(txid), peer.ChaincodeMessage_GET_STATE, err) - } - - if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { - // Success response - return responseMsg.Payload, nil - } - if responseMsg.Type == peer.ChaincodeMessage_ERROR { - // Error response - return nil, fmt.Errorf("%s", responseMsg.Payload[:]) - } - - // Incorrect chaincode message received - return nil, fmt.Errorf("[%s] incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR) -} - -func (h *Handler) handleGetPrivateDataHash(collection string, key string, channelID string, txid string) ([]byte, error) { - // Construct payload for GET_PRIVATE_DATA_HASH - payloadBytes := marshalOrPanic(&peer.GetState{Collection: collection, Key: key}) - - msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_GET_PRIVATE_DATA_HASH, Payload: payloadBytes, Txid: txid, ChannelId: channelID} - responseMsg, err := h.callPeerWithChaincodeMsg(msg, channelID, txid) - if err != nil { - return nil, fmt.Errorf("[%s] error sending %s: %s", shorttxid(txid), peer.ChaincodeMessage_GET_PRIVATE_DATA_HASH, err) - } - - if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { - // Success response - return responseMsg.Payload, nil - } - if responseMsg.Type == peer.ChaincodeMessage_ERROR { - // Error response - return nil, fmt.Errorf("%s", responseMsg.Payload[:]) - } - - // Incorrect chaincode message received - return nil, fmt.Errorf("[%s] incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR) -} - -func (h *Handler) handleGetStateMetadata(collection string, key string, channelID string, txID string) (map[string][]byte, error) { - // Construct payload for GET_STATE_METADATA - payloadBytes := marshalOrPanic(&peer.GetStateMetadata{Collection: collection, Key: key}) - - msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_GET_STATE_METADATA, Payload: payloadBytes, Txid: txID, ChannelId: channelID} - responseMsg, err := h.callPeerWithChaincodeMsg(msg, channelID, txID) - if err != nil { - return nil, fmt.Errorf("[%s] error sending %s: %s", shorttxid(txID), peer.ChaincodeMessage_GET_STATE_METADATA, err) - } - - if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { - // Success response - var mdResult peer.StateMetadataResult - err := proto.Unmarshal(responseMsg.Payload, &mdResult) - if err != nil { - return nil, errors.New("could not unmarshal metadata response") - } - metadata := make(map[string][]byte) - for _, md := range mdResult.Entries { - metadata[md.Metakey] = md.Value - } - - return metadata, nil - } - if responseMsg.Type == peer.ChaincodeMessage_ERROR { - // Error response - return nil, fmt.Errorf("%s", responseMsg.Payload[:]) - } - - // Incorrect chaincode message received - return nil, fmt.Errorf("[%s] incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR) -} - -// handlePutState communicates with the peer to put state information into the ledger. -func (h *Handler) handlePutState(collection string, key string, value []byte, channelID string, txid string) error { - // Construct payload for PUT_STATE - payloadBytes := marshalOrPanic(&peer.PutState{Collection: collection, Key: key, Value: value}) - - msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_PUT_STATE, Payload: payloadBytes, Txid: txid, ChannelId: channelID} - - // Execute the request and get response - responseMsg, err := h.callPeerWithChaincodeMsg(msg, channelID, txid) - if err != nil { - return fmt.Errorf("[%s] error sending %s: %s", msg.Txid, peer.ChaincodeMessage_PUT_STATE, err) - } - - if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { - // Success response - return nil - } - - if responseMsg.Type == peer.ChaincodeMessage_ERROR { - // Error response - return fmt.Errorf("%s", responseMsg.Payload[:]) - } - - // Incorrect chaincode message received - return fmt.Errorf("[%s] incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR) -} - -func (h *Handler) handlePutStateMetadataEntry(collection string, key string, metakey string, metadata []byte, channelID string, txID string) error { - // Construct payload for PUT_STATE_METADATA - md := &peer.StateMetadata{Metakey: metakey, Value: metadata} - payloadBytes := marshalOrPanic(&peer.PutStateMetadata{Collection: collection, Key: key, Metadata: md}) - - msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_PUT_STATE_METADATA, Payload: payloadBytes, Txid: txID, ChannelId: channelID} - // Execute the request and get response - responseMsg, err := h.callPeerWithChaincodeMsg(msg, channelID, txID) - if err != nil { - return fmt.Errorf("[%s] error sending %s: %s", msg.Txid, peer.ChaincodeMessage_PUT_STATE_METADATA, err) - } - - if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { - // Success response - return nil - } - - if responseMsg.Type == peer.ChaincodeMessage_ERROR { - // Error response - return fmt.Errorf("%s", responseMsg.Payload[:]) - } - - // Incorrect chaincode message received - return fmt.Errorf("[%s]incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR) -} - -// handleDelState communicates with the peer to delete a key from the state in the ledger. -func (h *Handler) handleDelState(collection string, key string, channelID string, txid string) error { - payloadBytes := marshalOrPanic(&peer.DelState{Collection: collection, Key: key}) - msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_DEL_STATE, Payload: payloadBytes, Txid: txid, ChannelId: channelID} - // Execute the request and get response - responseMsg, err := h.callPeerWithChaincodeMsg(msg, channelID, txid) - if err != nil { - return fmt.Errorf("[%s] error sending %s", shorttxid(msg.Txid), peer.ChaincodeMessage_DEL_STATE) - } - - if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { - // Success response - return nil - } - if responseMsg.Type == peer.ChaincodeMessage_ERROR { - // Error response - return fmt.Errorf("%s", responseMsg.Payload[:]) - } - - // Incorrect chaincode message received - return fmt.Errorf("[%s] incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR) -} - -// handlePurgeState communicates with the peer to purge a state from private data -func (h *Handler) handlePurgeState(collection string, key string, channelID string, txid string) error { - payloadBytes := marshalOrPanic(&peer.DelState{Collection: collection, Key: key}) - msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_PURGE_PRIVATE_DATA, Payload: payloadBytes, Txid: txid, ChannelId: channelID} - // Execute the request and get response - responseMsg, err := h.callPeerWithChaincodeMsg(msg, channelID, txid) - if err != nil { - return fmt.Errorf("[%s] error sending %s", shorttxid(msg.Txid), peer.ChaincodeMessage_DEL_STATE) - } - - if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { - // Success response - return nil - } - if responseMsg.Type == peer.ChaincodeMessage_ERROR { - // Error response - return fmt.Errorf("%s", responseMsg.Payload[:]) - } - - // Incorrect chaincode message received - return fmt.Errorf("[%s] incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR) -} - -func (h *Handler) handleGetStateByRange(collection, startKey, endKey string, metadata []byte, - channelID string, txid string) (*peer.QueryResponse, error) { - // Send GET_STATE_BY_RANGE message to peer chaincode support - payloadBytes := marshalOrPanic(&peer.GetStateByRange{Collection: collection, StartKey: startKey, EndKey: endKey, Metadata: metadata}) - msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_GET_STATE_BY_RANGE, Payload: payloadBytes, Txid: txid, ChannelId: channelID} - responseMsg, err := h.callPeerWithChaincodeMsg(msg, channelID, txid) - if err != nil { - return nil, fmt.Errorf("[%s] error sending %s", shorttxid(msg.Txid), peer.ChaincodeMessage_GET_STATE_BY_RANGE) - } - - if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { - // Success response - rangeQueryResponse := &peer.QueryResponse{} - err = proto.Unmarshal(responseMsg.Payload, rangeQueryResponse) - if err != nil { - return nil, fmt.Errorf("[%s] GetStateByRangeResponse unmarshall error", shorttxid(responseMsg.Txid)) - } - - return rangeQueryResponse, nil - } - if responseMsg.Type == peer.ChaincodeMessage_ERROR { - // Error response - return nil, fmt.Errorf("%s", responseMsg.Payload[:]) - } - - // Incorrect chaincode message received - return nil, fmt.Errorf("incorrect chaincode message %s received. Expecting %s or %s", responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR) -} - -func (h *Handler) handleQueryStateNext(id, channelID, txid string) (*peer.QueryResponse, error) { - // Create the channel on which to communicate the response from validating peer - respChan, err := h.createResponseChannel(channelID, txid) - if err != nil { - return nil, err - } - defer h.deleteResponseChannel(channelID, txid) - - // Send QUERY_STATE_NEXT message to peer chaincode support - payloadBytes := marshalOrPanic(&peer.QueryStateNext{Id: id}) - - msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_QUERY_STATE_NEXT, Payload: payloadBytes, Txid: txid, ChannelId: channelID} - - var responseMsg *peer.ChaincodeMessage - - if responseMsg, err = h.sendReceive(msg, respChan); err != nil { - return nil, fmt.Errorf("[%s] error sending %s", shorttxid(msg.Txid), peer.ChaincodeMessage_QUERY_STATE_NEXT) - } - - if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { - // Success response - queryResponse := &peer.QueryResponse{} - if err = proto.Unmarshal(responseMsg.Payload, queryResponse); err != nil { - return nil, fmt.Errorf("[%s] unmarshal error", shorttxid(responseMsg.Txid)) - } - - return queryResponse, nil - } - if responseMsg.Type == peer.ChaincodeMessage_ERROR { - // Error response - return nil, fmt.Errorf("%s", responseMsg.Payload[:]) - } - - // Incorrect chaincode message received - return nil, fmt.Errorf("incorrect chaincode message %s received. Expecting %s or %s", responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR) -} - -func (h *Handler) handleQueryStateClose(id, channelID, txid string) (*peer.QueryResponse, error) { - // Create the channel on which to communicate the response from validating peer - respChan, err := h.createResponseChannel(channelID, txid) - if err != nil { - return nil, err - } - defer h.deleteResponseChannel(channelID, txid) - - // Send QUERY_STATE_CLOSE message to peer chaincode support - payloadBytes := marshalOrPanic(&peer.QueryStateClose{Id: id}) - - msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_QUERY_STATE_CLOSE, Payload: payloadBytes, Txid: txid, ChannelId: channelID} - - var responseMsg *peer.ChaincodeMessage - - if responseMsg, err = h.sendReceive(msg, respChan); err != nil { - return nil, fmt.Errorf("[%s] error sending %s", shorttxid(msg.Txid), peer.ChaincodeMessage_QUERY_STATE_CLOSE) - } - - if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { - // Success response - queryResponse := &peer.QueryResponse{} - if err = proto.Unmarshal(responseMsg.Payload, queryResponse); err != nil { - return nil, fmt.Errorf("[%s] unmarshal error", shorttxid(responseMsg.Txid)) - } - - return queryResponse, nil - } - if responseMsg.Type == peer.ChaincodeMessage_ERROR { - // Error response - return nil, fmt.Errorf("%s", responseMsg.Payload[:]) - } - - // Incorrect chaincode message received - return nil, fmt.Errorf("incorrect chaincode message %s received. Expecting %s or %s", responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR) -} - -func (h *Handler) handleGetQueryResult(collection string, query string, metadata []byte, - channelID string, txid string) (*peer.QueryResponse, error) { - // Send GET_QUERY_RESULT message to peer chaincode support - payloadBytes := marshalOrPanic(&peer.GetQueryResult{Collection: collection, Query: query, Metadata: metadata}) - msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_GET_QUERY_RESULT, Payload: payloadBytes, Txid: txid, ChannelId: channelID} - responseMsg, err := h.callPeerWithChaincodeMsg(msg, channelID, txid) - if err != nil { - return nil, fmt.Errorf("[%s] error sending %s", shorttxid(msg.Txid), peer.ChaincodeMessage_GET_QUERY_RESULT) - } - - if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { - // Success response - executeQueryResponse := &peer.QueryResponse{} - if err = proto.Unmarshal(responseMsg.Payload, executeQueryResponse); err != nil { - return nil, fmt.Errorf("[%s] unmarshal error", shorttxid(responseMsg.Txid)) - } - - return executeQueryResponse, nil - } - if responseMsg.Type == peer.ChaincodeMessage_ERROR { - // Error response - return nil, fmt.Errorf("%s", responseMsg.Payload[:]) - } - - // Incorrect chaincode message received - return nil, fmt.Errorf("incorrect chaincode message %s received. Expecting %s or %s", responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR) -} - -func (h *Handler) handleGetHistoryForKey(key string, channelID string, txid string) (*peer.QueryResponse, error) { - // Create the channel on which to communicate the response from validating peer - respChan, err := h.createResponseChannel(channelID, txid) - if err != nil { - return nil, err - } - defer h.deleteResponseChannel(channelID, txid) - - // Send GET_HISTORY_FOR_KEY message to peer chaincode support - payloadBytes := marshalOrPanic(&peer.GetHistoryForKey{Key: key}) - - msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_GET_HISTORY_FOR_KEY, Payload: payloadBytes, Txid: txid, ChannelId: channelID} - var responseMsg *peer.ChaincodeMessage - - if responseMsg, err = h.sendReceive(msg, respChan); err != nil { - return nil, fmt.Errorf("[%s] error sending %s", shorttxid(msg.Txid), peer.ChaincodeMessage_GET_HISTORY_FOR_KEY) - } - - if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { - // Success response - getHistoryForKeyResponse := &peer.QueryResponse{} - if err = proto.Unmarshal(responseMsg.Payload, getHistoryForKeyResponse); err != nil { - return nil, fmt.Errorf("[%s] unmarshal error", shorttxid(responseMsg.Txid)) - } - - return getHistoryForKeyResponse, nil - } - if responseMsg.Type == peer.ChaincodeMessage_ERROR { - // Error response - return nil, fmt.Errorf("%s", responseMsg.Payload[:]) - } - - // Incorrect chaincode message received - return nil, fmt.Errorf("incorrect chaincode message %s received. Expecting %s or %s", responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR) -} - -func (h *Handler) createResponse(status int32, payload []byte) *peer.Response { - return &peer.Response{Status: status, Payload: payload} -} - -// handleInvokeChaincode communicates with the peer to invoke another chaincode. -func (h *Handler) handleInvokeChaincode(chaincodeName string, args [][]byte, channelID string, txid string) *peer.Response { - payloadBytes := marshalOrPanic(&peer.ChaincodeSpec{ChaincodeId: &peer.ChaincodeID{Name: chaincodeName}, Input: &peer.ChaincodeInput{Args: args}}) - - // Create the channel on which to communicate the response from validating peer - respChan, err := h.createResponseChannel(channelID, txid) - if err != nil { - return h.createResponse(ERROR, []byte(err.Error())) - } - defer h.deleteResponseChannel(channelID, txid) - - // Send INVOKE_CHAINCODE message to peer chaincode support - msg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_INVOKE_CHAINCODE, Payload: payloadBytes, Txid: txid, ChannelId: channelID} - - var responseMsg *peer.ChaincodeMessage - - if responseMsg, err = h.sendReceive(msg, respChan); err != nil { - errStr := fmt.Sprintf("[%s] error sending %s", shorttxid(msg.Txid), peer.ChaincodeMessage_INVOKE_CHAINCODE) - return h.createResponse(ERROR, []byte(errStr)) - } - - if responseMsg.Type == peer.ChaincodeMessage_RESPONSE { - // Success response - respMsg := &peer.ChaincodeMessage{} - if err := proto.Unmarshal(responseMsg.Payload, respMsg); err != nil { - return h.createResponse(ERROR, []byte(err.Error())) - } - if respMsg.Type == peer.ChaincodeMessage_COMPLETED { - // Success response - res := &peer.Response{} - if err = proto.Unmarshal(respMsg.Payload, res); err != nil { - return h.createResponse(ERROR, []byte(err.Error())) - } - return res - } - return h.createResponse(ERROR, responseMsg.Payload) - } - if responseMsg.Type == peer.ChaincodeMessage_ERROR { - // Error response - return h.createResponse(ERROR, responseMsg.Payload) - } - - // Incorrect chaincode message received - return h.createResponse(ERROR, []byte(fmt.Sprintf("[%s] Incorrect chaincode message %s received. Expecting %s or %s", shorttxid(responseMsg.Txid), responseMsg.Type, peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR))) -} - -// handleReady handles messages received from the peer when the handler is in the "ready" state. -func (h *Handler) handleReady(msg *peer.ChaincodeMessage, errc chan error) error { - switch msg.Type { - case peer.ChaincodeMessage_RESPONSE, peer.ChaincodeMessage_ERROR: - if err := h.handleResponse(msg); err != nil { - return err - } - return nil - - case peer.ChaincodeMessage_INIT: - go h.handleStubInteraction(h.handleInit, msg, errc) - return nil - - case peer.ChaincodeMessage_TRANSACTION: - go h.handleStubInteraction(h.handleTransaction, msg, errc) - return nil - - default: - return fmt.Errorf("[%s] Chaincode h cannot handle message (%s) while in state: %s", msg.Txid, msg.Type, h.state) - } -} - -// handleEstablished handles messages received from the peer when the handler is in the "established" state. -func (h *Handler) handleEstablished(msg *peer.ChaincodeMessage) error { - if msg.Type != peer.ChaincodeMessage_READY { - return fmt.Errorf("[%s] Chaincode h cannot handle message (%s) while in state: %s", msg.Txid, msg.Type, h.state) - } - - h.state = ready - return nil -} - -// hanndleCreated handles messages received from the peer when the handler is in the "created" state. -func (h *Handler) handleCreated(msg *peer.ChaincodeMessage) error { - if msg.Type != peer.ChaincodeMessage_REGISTERED { - return fmt.Errorf("[%s] Chaincode h cannot handle message (%s) while in state: %s", msg.Txid, msg.Type, h.state) - } - - h.state = established - return nil -} - -// handleMessage message handles loop for shim side of chaincode/peer stream. -func (h *Handler) handleMessage(msg *peer.ChaincodeMessage, errc chan error) error { - if msg.Type == peer.ChaincodeMessage_KEEPALIVE { - h.serialSendAsync(msg, errc) - return nil - } - var err error - - switch h.state { - case ready: - err = h.handleReady(msg, errc) - case established: - err = h.handleEstablished(msg) - case created: - err = h.handleCreated(msg) - default: - panic(fmt.Sprintf("invalid handler state: %s", h.state)) - } - - if err != nil { - payload := []byte(err.Error()) - errorMsg := &peer.ChaincodeMessage{Type: peer.ChaincodeMessage_ERROR, Payload: payload, Txid: msg.Txid} - h.serialSend(errorMsg) //nolint:errcheck - return err - } - - return nil -} - -// marshalOrPanic attempts to marshal the provided protobbuf message but will panic -// when marshaling fails instead of returning an error. -func marshalOrPanic(msg proto.Message) []byte { - bytes, err := proto.Marshal(msg) - if err != nil { - panic(fmt.Sprintf("failed to marshal message: %s", err)) - } - return bytes -} diff --git a/v2/shim/handler_test.go b/v2/shim/handler_test.go deleted file mode 100644 index f9d325c0..00000000 --- a/v2/shim/handler_test.go +++ /dev/null @@ -1,299 +0,0 @@ -// Copyright the Hyperledger Fabric contributors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package shim - -import ( - "fmt" - "testing" - - "github.com/hyperledger/fabric-chaincode-go/v2/shim/internal/mock" - "github.com/hyperledger/fabric-protos-go-apiv2/peer" - - "github.com/stretchr/testify/assert" -) - -//go:generate counterfeiter -o internal/mock/peer_chaincode_stream.go --fake-name PeerChaincodeStream . peerChaincodeStream - -//lint:ignore U1000 Required to avoid circular dependency with mock -type peerChaincodeStream interface{ PeerChaincodeStream } - -//go:generate counterfeiter -o internal/mock/client_stream.go --fake-name ClientStream . clientStream - -//lint:ignore U1000 Required to avoid circular dependency with mock -type clientStream interface{ ClientStream } - -type mockChaincode struct { - initCalled bool - invokeCalled bool -} - -func (mcc *mockChaincode) Init(stub ChaincodeStubInterface) *peer.Response { - mcc.initCalled = true - return Success(nil) -} - -func (mcc *mockChaincode) Invoke(stub ChaincodeStubInterface) *peer.Response { - mcc.invokeCalled = true - return Success(nil) -} - -func TestNewHandler_CreatedState(t *testing.T) { - t.Parallel() - - chatStream := &mock.PeerChaincodeStream{} - cc := &mockChaincode{} - - expected := &Handler{ - chatStream: chatStream, - cc: cc, - responseChannels: map[string]chan *peer.ChaincodeMessage{}, - state: created, - } - - handler := newChaincodeHandler(chatStream, cc) - if handler == nil { - t.Fatal("Handler should not be nil") - } - assert.Equal(t, expected, handler) -} - -func TestHandlerState(t *testing.T) { - t.Parallel() - - var tests = []struct { - name string - state state - msg *peer.ChaincodeMessage - expectedErr string - }{ - { - name: "created", - state: created, - msg: &peer.ChaincodeMessage{ - Type: peer.ChaincodeMessage_REGISTERED, - }, - }, - { - name: "wrong message type in created state", - state: created, - msg: &peer.ChaincodeMessage{ - Type: peer.ChaincodeMessage_READY, - }, - expectedErr: fmt.Sprintf("cannot handle message (%s)", peer.ChaincodeMessage_READY), - }, - { - name: "established", - state: established, - msg: &peer.ChaincodeMessage{ - Type: peer.ChaincodeMessage_READY, - }, - }, - { - name: "wrong message type in established state", - state: established, - msg: &peer.ChaincodeMessage{ - Type: peer.ChaincodeMessage_REGISTERED, - }, - expectedErr: fmt.Sprintf("cannot handle message (%s)", peer.ChaincodeMessage_REGISTERED), - }, - { - name: "wrong message type in ready state", - state: ready, - msg: &peer.ChaincodeMessage{ - Type: peer.ChaincodeMessage_REGISTERED, - }, - expectedErr: fmt.Sprintf("cannot handle message (%s)", peer.ChaincodeMessage_REGISTERED), - }, - { - name: "keepalive", - state: established, - msg: &peer.ChaincodeMessage{ - Type: peer.ChaincodeMessage_KEEPALIVE, - }, - }, - } - - for _, test := range tests { - test := test - t.Run(test.name, func(t *testing.T) { - t.Parallel() - handler := &Handler{ - chatStream: &mock.PeerChaincodeStream{}, - cc: &mockChaincode{}, - state: test.state, - } - err := handler.handleMessage(test.msg, nil) - if test.expectedErr != "" { - assert.Contains(t, err.Error(), test.expectedErr) - } else { - assert.NoError(t, err) - } - }) - } -} - -func TestHandleMessage(t *testing.T) { - t.Parallel() - - var tests = []struct { - name string - msg *peer.ChaincodeMessage - msgType peer.ChaincodeMessage_Type - expectedErr string - invokeCalled bool - initCalled bool - }{ - { - name: "INIT", - msg: &peer.ChaincodeMessage{ - Type: peer.ChaincodeMessage_INIT, - }, - msgType: peer.ChaincodeMessage_COMPLETED, - initCalled: true, - invokeCalled: false, - }, - { - name: "INIT with bad payload", - msg: &peer.ChaincodeMessage{ - Type: peer.ChaincodeMessage_INIT, - Payload: []byte{1}, - }, - msgType: peer.ChaincodeMessage_ERROR, - initCalled: false, - invokeCalled: false, - }, - { - name: "INVOKE", - msg: &peer.ChaincodeMessage{ - Type: peer.ChaincodeMessage_TRANSACTION, - }, - msgType: peer.ChaincodeMessage_COMPLETED, - initCalled: false, - invokeCalled: true, - }, - { - name: "INVOKE with bad payload", - msg: &peer.ChaincodeMessage{ - Type: peer.ChaincodeMessage_TRANSACTION, - Payload: []byte{1}, - }, - msgType: peer.ChaincodeMessage_ERROR, - initCalled: false, - invokeCalled: false, - }, - { - name: "RESPONSE with no responseChannel", - msg: &peer.ChaincodeMessage{ - Type: peer.ChaincodeMessage_RESPONSE, - }, - expectedErr: "responseChannel does not exist", - }, - } - - for _, test := range tests { - test := test - t.Run(test.name, func(t *testing.T) { - t.Parallel() - - chatStream := &mock.PeerChaincodeStream{} - cc := &mockChaincode{} - - msgChan := make(chan *peer.ChaincodeMessage) - chatStream.SendStub = func(msg *peer.ChaincodeMessage) error { - go func() { - msgChan <- msg - }() - return nil - } - - // create handler in ready state - handler := &Handler{ - chatStream: chatStream, - cc: cc, - responseChannels: map[string]chan *peer.ChaincodeMessage{}, - state: ready, - } - - err := handler.handleMessage(test.msg, nil) - if test.expectedErr != "" { - assert.Contains(t, err.Error(), test.expectedErr) - } else { - if err != nil { - t.Fatalf("Unexpected error for '%s': %s", test.name, err) - } - resp := <-msgChan - assert.Equal(t, test.msgType, resp.GetType()) - assert.Equal(t, test.initCalled, cc.initCalled) - assert.Equal(t, test.invokeCalled, cc.invokeCalled) - } - }) - } -} - -func TestHandlePeerCalls(t *testing.T) { - payload := []byte("error") - h := &Handler{ - cc: &mockChaincode{}, - responseChannels: map[string]chan *peer.ChaincodeMessage{}, - state: ready, - } - chatStream := &mock.PeerChaincodeStream{} - chatStream.SendStub = func(msg *peer.ChaincodeMessage) error { - go func() { - err := h.handleResponse( - &peer.ChaincodeMessage{ - Type: peer.ChaincodeMessage_ERROR, - ChannelId: msg.GetChannelId(), - Txid: msg.GetTxid(), - Payload: payload, - }, - ) - assert.NoError(t, err, "handleResponse") - }() - return nil - } - h.chatStream = chatStream - - _, err := h.handleQueryStateNext("id", "channel", "txid") - assert.EqualError(t, err, string(payload)) - - _, err = h.handleQueryStateClose("id", "channel", "txid") - assert.EqualError(t, err, string(payload)) - - // force error by removing responseChannels - h.responseChannels = nil - _, err = h.handleGetState("col", "key", "channel", "txid") - assert.Contains(t, err.Error(), "[txid] error sending GET_STATE") - - _, err = h.handleGetPrivateDataHash("col", "key", "channel", "txid") - assert.Contains(t, err.Error(), "[txid] error sending GET_PRIVATE_DATA_HASH") - - _, err = h.handleGetStateMetadata("col", "key", "channel", "txid") - assert.Contains(t, err.Error(), "[txid] error sending GET_STATE_METADATA") - - err = h.handlePutState("col", "key", []byte{}, "channel", "txid") - assert.Contains(t, err.Error(), "[txid] error sending PUT_STATE") - - err = h.handlePutStateMetadataEntry("col", "key", "mkey", []byte{}, "channel", "txid") - assert.Contains(t, err.Error(), "[txid] error sending PUT_STATE_METADATA") - - err = h.handleDelState("col", "key", "channel", "txid") - assert.Contains(t, err.Error(), "[txid] error sending DEL_STATE") - - _, err = h.handleGetStateByRange("col", "start", "end", []byte{}, "channel", "txid") - assert.Contains(t, err.Error(), "[txid] error sending GET_STATE_BY_RANGE") - - _, err = h.handleQueryStateNext("id", "channel", "txid") - assert.Contains(t, err.Error(), "cannot create response channel") - - _, err = h.handleQueryStateClose("id", "channel", "txid") - assert.Contains(t, err.Error(), "cannot create response channel") - - _, err = h.handleGetQueryResult("col", "query", []byte{}, "channel", "txid") - assert.Contains(t, err.Error(), "[txid] error sending GET_QUERY_RESULT") - - _, err = h.handleGetHistoryForKey("key", "channel", "txid") - assert.Contains(t, err.Error(), "cannot create response channel") - -} diff --git a/v2/shim/interfaces.go b/v2/shim/interfaces.go deleted file mode 100644 index 7e6a6431..00000000 --- a/v2/shim/interfaces.go +++ /dev/null @@ -1,405 +0,0 @@ -// Copyright the Hyperledger Fabric contributors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package shim - -import ( - "github.com/hyperledger/fabric-protos-go-apiv2/ledger/queryresult" - "github.com/hyperledger/fabric-protos-go-apiv2/peer" - "google.golang.org/protobuf/types/known/timestamppb" -) - -// Chaincode interface must be implemented by all chaincodes. The fabric runs -// the transactions by calling these functions as specified. -type Chaincode interface { - // Init is called during Instantiate transaction after the chaincode container - // has been established for the first time, allowing the chaincode to - // initialize its internal data - Init(stub ChaincodeStubInterface) *peer.Response - - // Invoke is called to update or query the ledger in a proposal transaction. - // Updated state variables are not committed to the ledger until the - // transaction is committed. - Invoke(stub ChaincodeStubInterface) *peer.Response -} - -// ChaincodeStubInterface is used by deployable chaincode apps to access and -// modify their ledgers -type ChaincodeStubInterface interface { - // GetArgs returns the arguments intended for the chaincode Init and Invoke - // as an array of byte arrays. - GetArgs() [][]byte - - // GetStringArgs returns the arguments intended for the chaincode Init and - // Invoke as a string array. Only use GetStringArgs if the client passes - // arguments intended to be used as strings. - GetStringArgs() []string - - // GetFunctionAndParameters returns the first argument as the function - // name and the rest of the arguments as parameters in a string array. - // Only use GetFunctionAndParameters if the client passes arguments intended - // to be used as strings. - GetFunctionAndParameters() (string, []string) - - // GetArgsSlice returns the arguments intended for the chaincode Init and - // Invoke as a byte array - GetArgsSlice() ([]byte, error) - - // GetTxID returns the tx_id of the transaction proposal, which is unique per - // transaction and per client. See - // https://godoc.org/github.com/hyperledger/fabric-protos-go-apiv2/common#ChannelHeader - // for further details. - GetTxID() string - - // GetChannelID returns the channel the proposal is sent to for chaincode to process. - // This would be the channel_id of the transaction proposal (see - // https://godoc.org/github.com/hyperledger/fabric-protos-go-apiv2/common#ChannelHeader ) - // except where the chaincode is calling another on a different channel. - GetChannelID() string - - // InvokeChaincode locally calls the specified chaincode `Invoke` using the - // same transaction context; that is, chaincode calling chaincode doesn't - // create a new transaction message. - // If the called chaincode is on the same channel, it simply adds the called - // chaincode read set and write set to the calling transaction. - // If the called chaincode is on a different channel, - // only the Response is returned to the calling chaincode; any PutState calls - // from the called chaincode will not have any effect on the ledger; that is, - // the called chaincode on a different channel will not have its read set - // and write set applied to the transaction. Only the calling chaincode's - // read set and write set will be applied to the transaction. Effectively - // the called chaincode on a different channel is a `Query`, which does not - // participate in state validation checks in subsequent commit phase. - // If `channel` is empty, the caller's channel is assumed. - InvokeChaincode(chaincodeName string, args [][]byte, channel string) *peer.Response - - // GetState returns the value of the specified `key` from the - // ledger. Note that GetState doesn't read data from the writeset, which - // has not been committed to the ledger. In other words, GetState doesn't - // consider data modified by PutState that has not been committed. - // If the key does not exist in the state database, (nil, nil) is returned. - GetState(key string) ([]byte, error) - - // PutState puts the specified `key` and `value` into the transaction's - // writeset as a data-write proposal. PutState doesn't effect the ledger - // until the transaction is validated and successfully committed. - // Simple keys must not be an empty string and must not start with a - // null character (0x00) in order to avoid range query collisions with - // composite keys, which internally get prefixed with 0x00 as composite - // key namespace. In addition, if using CouchDB, keys can only contain - // valid UTF-8 strings and cannot begin with an underscore ("_"). - PutState(key string, value []byte) error - - // DelState records the specified `key` to be deleted in the writeset of - // the transaction proposal. The `key` and its value will be deleted from - // the ledger when the transaction is validated and successfully committed. - DelState(key string) error - - // SetStateValidationParameter sets the key-level endorsement policy for `key`. - SetStateValidationParameter(key string, ep []byte) error - - // GetStateValidationParameter retrieves the key-level endorsement policy - // for `key`. Note that this will introduce a read dependency on `key` in - // the transaction's readset. - GetStateValidationParameter(key string) ([]byte, error) - - // GetStateByRange returns a range iterator over a set of keys in the - // ledger. The iterator can be used to iterate over all keys - // between the startKey (inclusive) and endKey (exclusive). - // However, if the number of keys between startKey and endKey is greater than the - // totalQueryLimit (defined in core.yaml), this iterator cannot be used - // to fetch all keys (results will be capped by the totalQueryLimit). - // The keys are returned by the iterator in lexical order. Note - // that startKey and endKey can be empty string, which implies unbounded range - // query on start or end. - // Call Close() on the returned StateQueryIteratorInterface object when done. - // The query is re-executed during validation phase to ensure result set - // has not changed since transaction endorsement (phantom reads detected). - GetStateByRange(startKey, endKey string) (StateQueryIteratorInterface, error) - - // GetStateByRangeWithPagination returns a range iterator over a set of keys in the - // ledger. The iterator can be used to fetch keys between the startKey (inclusive) - // and endKey (exclusive). - // When an empty string is passed as a value to the bookmark argument, the returned - // iterator can be used to fetch the first `pageSize` keys between the startKey - // (inclusive) and endKey (exclusive). - // When the bookmark is a non-emptry string, the iterator can be used to fetch - // the first `pageSize` keys between the bookmark (inclusive) and endKey (exclusive). - // Note that only the bookmark present in a prior page of query results (ResponseMetadata) - // can be used as a value to the bookmark argument. Otherwise, an empty string must - // be passed as bookmark. - // The keys are returned by the iterator in lexical order. Note - // that startKey and endKey can be empty string, which implies unbounded range - // query on start or end. - // Call Close() on the returned StateQueryIteratorInterface object when done. - // This call is only supported in a read only transaction. - GetStateByRangeWithPagination(startKey, endKey string, pageSize int32, - bookmark string) (StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) - - // GetStateByPartialCompositeKey queries the state in the ledger based on - // a given partial composite key. This function returns an iterator - // which can be used to iterate over all composite keys whose prefix matches - // the given partial composite key. However, if the number of matching composite - // keys is greater than the totalQueryLimit (defined in core.yaml), this iterator - // cannot be used to fetch all matching keys (results will be limited by the totalQueryLimit). - // The `objectType` and attributes are expected to have only valid utf8 strings and - // should not contain U+0000 (nil byte) and U+10FFFF (biggest and unallocated code point). - // See related functions SplitCompositeKey and CreateCompositeKey. - // Call Close() on the returned StateQueryIteratorInterface object when done. - // The query is re-executed during validation phase to ensure result set - // has not changed since transaction endorsement (phantom reads detected). This function should be used only for - // a partial composite key. For a full composite key, an iter with empty response - // would be returned. - GetStateByPartialCompositeKey(objectType string, keys []string) (StateQueryIteratorInterface, error) - - // GetStateByPartialCompositeKeyWithPagination queries the state in the ledger based on - // a given partial composite key. This function returns an iterator - // which can be used to iterate over the composite keys whose - // prefix matches the given partial composite key. - // When an empty string is passed as a value to the bookmark argument, the returned - // iterator can be used to fetch the first `pageSize` composite keys whose prefix - // matches the given partial composite key. - // When the bookmark is a non-emptry string, the iterator can be used to fetch - // the first `pageSize` keys between the bookmark (inclusive) and the last matching - // composite key. - // Note that only the bookmark present in a prior page of query result (ResponseMetadata) - // can be used as a value to the bookmark argument. Otherwise, an empty string must - // be passed as bookmark. - // The `objectType` and attributes are expected to have only valid utf8 strings - // and should not contain U+0000 (nil byte) and U+10FFFF (biggest and unallocated - // code point). See related functions SplitCompositeKey and CreateCompositeKey. - // Call Close() on the returned StateQueryIteratorInterface object when done. - // This call is only supported in a read only transaction. This function should be used only for - // a partial composite key. For a full composite key, an iter with empty response - // would be returned. - GetStateByPartialCompositeKeyWithPagination(objectType string, keys []string, - pageSize int32, bookmark string) (StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) - - // CreateCompositeKey combines the given `attributes` to form a composite - // key. The objectType and attributes are expected to have only valid utf8 - // strings and should not contain U+0000 (nil byte) and U+10FFFF - // (biggest and unallocated code point). - // The resulting composite key can be used as the key in PutState(). - CreateCompositeKey(objectType string, attributes []string) (string, error) - - // SplitCompositeKey splits the specified key into attributes on which the - // composite key was formed. Composite keys found during range queries - // or partial composite key queries can therefore be split into their - // composite parts. - SplitCompositeKey(compositeKey string) (string, []string, error) - - // GetQueryResult performs a "rich" query against a state database. It is - // only supported for state databases that support rich query, - // e.g.CouchDB. The query string is in the native syntax - // of the underlying state database. An iterator is returned - // which can be used to iterate over all keys in the query result set. - // However, if the number of keys in the query result set is greater than the - // totalQueryLimit (defined in core.yaml), this iterator cannot be used - // to fetch all keys in the query result set (results will be limited by - // the totalQueryLimit). - // The query is NOT re-executed during validation phase, phantom reads are - // not detected. That is, other committed transactions may have added, - // updated, or removed keys that impact the result set, and this would not - // be detected at validation/commit time. Applications susceptible to this - // should therefore not use GetQueryResult as part of transactions that update - // ledger, and should limit use to read-only chaincode operations. - GetQueryResult(query string) (StateQueryIteratorInterface, error) - - // GetQueryResultWithPagination performs a "rich" query against a state database. - // It is only supported for state databases that support rich query, - // e.g., CouchDB. The query string is in the native syntax - // of the underlying state database. An iterator is returned - // which can be used to iterate over keys in the query result set. - // When an empty string is passed as a value to the bookmark argument, the returned - // iterator can be used to fetch the first `pageSize` of query results. - // When the bookmark is a non-emptry string, the iterator can be used to fetch - // the first `pageSize` keys between the bookmark and the last key in the query result. - // Note that only the bookmark present in a prior page of query results (ResponseMetadata) - // can be used as a value to the bookmark argument. Otherwise, an empty string - // must be passed as bookmark. - // This call is only supported in a read only transaction. - GetQueryResultWithPagination(query string, pageSize int32, - bookmark string) (StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) - - // GetHistoryForKey returns a history of key values across time. - // For each historic key update, the historic value and associated - // transaction id and timestamp are returned. The timestamp is the - // timestamp provided by the client in the proposal header. - // GetHistoryForKey requires peer configuration - // core.ledger.history.enableHistoryDatabase to be true. - // The query is NOT re-executed during validation phase, phantom reads are - // not detected. That is, other committed transactions may have updated - // the key concurrently, impacting the result set, and this would not be - // detected at validation/commit time. Applications susceptible to this - // should therefore not use GetHistoryForKey as part of transactions that - // update ledger, and should limit use to read-only chaincode operations. - // Starting in Fabric v2.0, the GetHistoryForKey chaincode API - // will return results from newest to oldest in terms of ordered transaction - // height (block height and transaction height within block). - // This will allow applications to efficiently iterate through the top results - // to understand recent changes to a key. - GetHistoryForKey(key string) (HistoryQueryIteratorInterface, error) - - // GetPrivateData returns the value of the specified `key` from the specified - // `collection`. Note that GetPrivateData doesn't read data from the - // private writeset, which has not been committed to the `collection`. In - // other words, GetPrivateData doesn't consider data modified by PutPrivateData - // that has not been committed. - GetPrivateData(collection, key string) ([]byte, error) - - // GetPrivateDataHash returns the hash of the value of the specified `key` from the specified - // `collection` - GetPrivateDataHash(collection, key string) ([]byte, error) - - // PutPrivateData puts the specified `key` and `value` into the transaction's - // private writeset. Note that only hash of the private writeset goes into the - // transaction proposal response (which is sent to the client who issued the - // transaction) and the actual private writeset gets temporarily stored in a - // transient store. PutPrivateData doesn't effect the `collection` until the - // transaction is validated and successfully committed. Simple keys must not - // be an empty string and must not start with a null character (0x00) in order - // to avoid range query collisions with composite keys, which internally get - // prefixed with 0x00 as composite key namespace. In addition, if using - // CouchDB, keys can only contain valid UTF-8 strings and cannot begin with an - // an underscore ("_"). - PutPrivateData(collection string, key string, value []byte) error - - // DelPrivateData records the specified `key` to be deleted in the private writeset - // of the transaction. Note that only hash of the private writeset goes into the - // transaction proposal response (which is sent to the client who issued the - // transaction) and the actual private writeset gets temporarily stored in a - // transient store. The `key` and its value will be deleted from the collection - // when the transaction is validated and successfully committed. - DelPrivateData(collection, key string) error - - // PurgePrivateData records the specified `key` to be purged in the private writeset - // of the transaction. Note that only hash of the private writeset goes into the - // transaction proposal response (which is sent to the client who issued the - // transaction) and the actual private writeset gets temporarily stored in a - // transient store. The `key` and its value will be deleted from the collection - // when the transaction is validated and successfully committed, and will - // subsequently be completely removed from the private data store (that maintains - // the historical versions of private writesets) as a background operation. - PurgePrivateData(collection, key string) error - - // SetPrivateDataValidationParameter sets the key-level endorsement policy - // for the private data specified by `key`. - SetPrivateDataValidationParameter(collection, key string, ep []byte) error - - // GetPrivateDataValidationParameter retrieves the key-level endorsement - // policy for the private data specified by `key`. Note that this introduces - // a read dependency on `key` in the transaction's readset. - GetPrivateDataValidationParameter(collection, key string) ([]byte, error) - - // GetPrivateDataByRange returns a range iterator over a set of keys in a - // given private collection. The iterator can be used to iterate over all keys - // between the startKey (inclusive) and endKey (exclusive). - // The keys are returned by the iterator in lexical order. Note - // that startKey and endKey can be empty string, which implies unbounded range - // query on start or end. - // Call Close() on the returned StateQueryIteratorInterface object when done. - // The query is re-executed during validation phase to ensure result set - // has not changed since transaction endorsement (phantom reads detected). - GetPrivateDataByRange(collection, startKey, endKey string) (StateQueryIteratorInterface, error) - - // GetPrivateDataByPartialCompositeKey queries the state in a given private - // collection based on a given partial composite key. This function returns - // an iterator which can be used to iterate over all composite keys whose prefix - // matches the given partial composite key. The `objectType` and attributes are - // expected to have only valid utf8 strings and should not contain - // U+0000 (nil byte) and U+10FFFF (biggest and unallocated code point). - // See related functions SplitCompositeKey and CreateCompositeKey. - // Call Close() on the returned StateQueryIteratorInterface object when done. - // The query is re-executed during validation phase to ensure result set - // has not changed since transaction endorsement (phantom reads detected). This function should be used only for - //a partial composite key. For a full composite key, an iter with empty response - //would be returned. - GetPrivateDataByPartialCompositeKey(collection, objectType string, keys []string) (StateQueryIteratorInterface, error) - - // GetPrivateDataQueryResult performs a "rich" query against a given private - // collection. It is only supported for state databases that support rich query, - // e.g.CouchDB. The query string is in the native syntax - // of the underlying state database. An iterator is returned - // which can be used to iterate (next) over the query result set. - // The query is NOT re-executed during validation phase, phantom reads are - // not detected. That is, other committed transactions may have added, - // updated, or removed keys that impact the result set, and this would not - // be detected at validation/commit time. Applications susceptible to this - // should therefore not use GetPrivateDataQueryResult as part of transactions that update - // ledger, and should limit use to read-only chaincode operations. - GetPrivateDataQueryResult(collection, query string) (StateQueryIteratorInterface, error) - - // GetCreator returns `SignatureHeader.Creator` (e.g. an identity) - // of the `SignedProposal`. This is the identity of the agent (or user) - // submitting the transaction. - GetCreator() ([]byte, error) - - // GetTransient returns the `ChaincodeProposalPayload.Transient` field. - // It is a map that contains data (e.g. cryptographic material) - // that might be used to implement some form of application-level - // confidentiality. The contents of this field, as prescribed by - // `ChaincodeProposalPayload`, are supposed to always - // be omitted from the transaction and excluded from the ledger. - GetTransient() (map[string][]byte, error) - - // GetBinding returns the transaction binding, which is used to enforce a - // link between application data (like those stored in the transient field - // above) to the proposal itself. This is useful to avoid possible replay - // attacks. - GetBinding() ([]byte, error) - - // GetDecorations returns additional data (if applicable) about the proposal - // that originated from the peer. This data is set by the decorators of the - // peer, which append or mutate the chaincode input passed to the chaincode. - GetDecorations() map[string][]byte - - // GetSignedProposal returns the SignedProposal object, which contains all - // data elements part of a transaction proposal. - GetSignedProposal() (*peer.SignedProposal, error) - - // GetTxTimestamp returns the timestamp when the transaction was created. This - // is taken from the transaction ChannelHeader, therefore it will indicate the - // client's timestamp and will have the same value across all endorsers. - GetTxTimestamp() (*timestamppb.Timestamp, error) - - // SetEvent allows the chaincode to set an event on the response to the - // proposal to be included as part of a transaction. The event will be - // available within the transaction in the committed block regardless of the - // validity of the transaction. - // Only a single event can be included in a transaction, and must originate - // from the outer-most invoked chaincode in chaincode-to-chaincode scenarios. - // The marshaled ChaincodeEvent will be available in the transaction's ChaincodeAction.events field. - SetEvent(name string, payload []byte) error -} - -// CommonIteratorInterface allows a chaincode to check whether any more result -// to be fetched from an iterator and close it when done. -type CommonIteratorInterface interface { - // HasNext returns true if the range query iterator contains additional keys - // and values. - HasNext() bool - - // Close closes the iterator. This should be called when done - // reading from the iterator to free up resources. - Close() error -} - -// StateQueryIteratorInterface allows a chaincode to iterate over a set of -// key/value pairs returned by range and execute query. -type StateQueryIteratorInterface interface { - // Inherit HasNext() and Close() - CommonIteratorInterface - - // Next returns the next key and value in the range and execute query iterator. - Next() (*queryresult.KV, error) -} - -// HistoryQueryIteratorInterface allows a chaincode to iterate over a set of -// key/value pairs returned by a history query. -type HistoryQueryIteratorInterface interface { - // Inherit HasNext() and Close() - CommonIteratorInterface - - // Next returns the next key and value in the history query iterator. - Next() (*queryresult.KeyModification, error) -} diff --git a/v2/shim/internal/client.go b/v2/shim/internal/client.go deleted file mode 100644 index 4516375d..00000000 --- a/v2/shim/internal/client.go +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright the Hyperledger Fabric contributors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package internal - -import ( - "context" - "crypto/tls" - "time" - - "github.com/hyperledger/fabric-protos-go-apiv2/peer" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/credentials/insecure" - "google.golang.org/grpc/keepalive" -) - -const ( - dialTimeout = 10 * time.Second - maxRecvMessageSize = 100 * 1024 * 1024 // 100 MiB - maxSendMessageSize = 100 * 1024 * 1024 // 100 MiB -) - -// NewClientConn ... -func NewClientConn( - address string, - tlsConf *tls.Config, - kaOpts keepalive.ClientParameters, -) (*grpc.ClientConn, error) { - - dialOpts := []grpc.DialOption{ - grpc.WithKeepaliveParams(kaOpts), - grpc.WithDefaultCallOptions( - grpc.MaxCallRecvMsgSize(maxRecvMessageSize), - grpc.MaxCallSendMsgSize(maxSendMessageSize), - ), - } - - if tlsConf != nil { - creds := credentials.NewTLS(tlsConf) - dialOpts = append(dialOpts, grpc.WithTransportCredentials(creds)) - } else { - dialOpts = append(dialOpts, grpc.WithTransportCredentials(insecure.NewCredentials())) - } - - return grpc.NewClient(address, dialOpts...) -} - -// NewRegisterClient ... -func NewRegisterClient(conn *grpc.ClientConn) (peer.ChaincodeSupport_RegisterClient, error) { - return peer.NewChaincodeSupportClient(conn).Register(context.Background()) -} diff --git a/v2/shim/internal/client_test.go b/v2/shim/internal/client_test.go deleted file mode 100644 index ff888c5f..00000000 --- a/v2/shim/internal/client_test.go +++ /dev/null @@ -1,134 +0,0 @@ -// Copyright the Hyperledger Fabric contributors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package internal - -import ( - "errors" - "net" - "testing" - "time" - - "github.com/hyperledger/fabric-protos-go-apiv2/peer" - "github.com/stretchr/testify/assert" - "google.golang.org/grpc" - "google.golang.org/grpc/keepalive" -) - -type testServer struct { - receivedMessages chan<- *peer.ChaincodeMessage - sendMessages <-chan *peer.ChaincodeMessage - waitTime time.Duration -} - -func (t *testServer) Register(registerServer peer.ChaincodeSupport_RegisterServer) error { - for { - recv, err := registerServer.Recv() - if err != nil { - return err - } - - select { - case t.receivedMessages <- recv: - case <-time.After(t.waitTime): - return errors.New("failed to capture received message") - } - - select { - case msg, ok := <-t.sendMessages: - if !ok { - return nil - } - if err := registerServer.Send(msg); err != nil { - return err - } - case <-time.After(t.waitTime): - return errors.New("no messages available on send channel") - } - } -} - -func TestMessageSizes(t *testing.T) { - const waitTime = 10 * time.Second - - lis, err := net.Listen("tcp", "127.0.0.1:0") - assert.NoError(t, err, "listen failed") - defer lis.Close() - - sendMessages := make(chan *peer.ChaincodeMessage, 1) - receivedMessages := make(chan *peer.ChaincodeMessage, 1) - testServer := &testServer{ - receivedMessages: receivedMessages, - sendMessages: sendMessages, - waitTime: waitTime, - } - - server := grpc.NewServer( - grpc.MaxSendMsgSize(2*maxSendMessageSize), - grpc.MaxRecvMsgSize(2*maxRecvMessageSize), - ) - peer.RegisterChaincodeSupportServer(server, testServer) - - serveCompleteCh := make(chan error, 1) - go func() { serveCompleteCh <- server.Serve(lis) }() - - client, err := NewClientConn(lis.Addr().String(), nil, keepalive.ClientParameters{}) - assert.NoError(t, err, "failed to create client connection") - - regClient, err := NewRegisterClient(client) - assert.NoError(t, err, "failed to create register client") - - t.Run("acceptable messaages", func(t *testing.T) { - acceptableMessage := &peer.ChaincodeMessage{ - Payload: make([]byte, maxSendMessageSize-100), - } - sendMessages <- acceptableMessage - err = regClient.Send(acceptableMessage) - assert.NoError(t, err, "sending messge below size threshold failed") - - select { - case m := <-receivedMessages: - assert.Len(t, m.Payload, maxSendMessageSize-100) - case <-time.After(waitTime): - t.Fatalf("acceptable message was not received by server") - } - - msg, err := regClient.Recv() - assert.NoError(t, err, "failed to receive message") - assert.Len(t, msg.Payload, maxSendMessageSize-100) - }) - - t.Run("response message is too large", func(t *testing.T) { - sendMessages <- &peer.ChaincodeMessage{ - Payload: make([]byte, maxSendMessageSize+1), - } - err = regClient.Send(&peer.ChaincodeMessage{}) - assert.NoError(t, err, "sending messge below size threshold should succeed") - - select { - case m := <-receivedMessages: - assert.Len(t, m.Payload, 0) - case <-time.After(waitTime): - t.Fatalf("acceptable message was not received by server") - } - - _, err := regClient.Recv() - assert.Error(t, err, "receiving a message that is too large should fail") - }) - - t.Run("sent message is too large", func(t *testing.T) { - tooBig := &peer.ChaincodeMessage{ - Payload: make([]byte, maxSendMessageSize+1), - } - err = regClient.Send(tooBig) - assert.Error(t, err, "sending messge above size threshold should fail") - }) - - err = lis.Close() - assert.NoError(t, err, "close failed") - select { - case <-serveCompleteCh: - case <-time.After(waitTime): - t.Fatal("server shutdown timeout") - } -} diff --git a/v2/shim/internal/config.go b/v2/shim/internal/config.go deleted file mode 100644 index e7c2c4b5..00000000 --- a/v2/shim/internal/config.go +++ /dev/null @@ -1,150 +0,0 @@ -// Copyright the Hyperledger Fabric contributors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package internal - -import ( - "crypto/tls" - "crypto/x509" - "encoding/base64" - "errors" - "fmt" - "os" - "strconv" - "time" - - "google.golang.org/grpc/keepalive" -) - -// Config contains chaincode's configuration -type Config struct { - ChaincodeName string - TLS *tls.Config - KaOpts keepalive.ClientParameters -} - -// LoadConfig loads the chaincode configuration -func LoadConfig() (Config, error) { - var err error - tlsEnabled, err := strconv.ParseBool(os.Getenv("CORE_PEER_TLS_ENABLED")) - if err != nil { - return Config{}, errors.New("'CORE_PEER_TLS_ENABLED' must be set to 'true' or 'false'") - } - - conf := Config{ - ChaincodeName: os.Getenv("CORE_CHAINCODE_ID_NAME"), - // hardcode to match chaincode server - KaOpts: keepalive.ClientParameters{ - Time: 1 * time.Minute, - Timeout: 20 * time.Second, - PermitWithoutStream: true, - }, - } - - if !tlsEnabled { - return conf, nil - } - - var key []byte - path, set := os.LookupEnv("CORE_TLS_CLIENT_KEY_FILE") - if set { - key, err = os.ReadFile(path) - if err != nil { - return Config{}, fmt.Errorf("failed to read private key file: %s", err) - } - } else { - data, err := os.ReadFile(os.Getenv("CORE_TLS_CLIENT_KEY_PATH")) - if err != nil { - return Config{}, fmt.Errorf("failed to read private key file: %s", err) - } - key, err = base64.StdEncoding.DecodeString(string(data)) - if err != nil { - return Config{}, fmt.Errorf("failed to decode private key file: %s", err) - } - } - - var cert []byte - path, set = os.LookupEnv("CORE_TLS_CLIENT_CERT_FILE") - if set { - cert, err = os.ReadFile(path) - if err != nil { - return Config{}, fmt.Errorf("failed to read public key file: %s", err) - } - } else { - data, err := os.ReadFile(os.Getenv("CORE_TLS_CLIENT_CERT_PATH")) - if err != nil { - return Config{}, fmt.Errorf("failed to read public key file: %s", err) - } - cert, err = base64.StdEncoding.DecodeString(string(data)) - if err != nil { - return Config{}, fmt.Errorf("failed to decode public key file: %s", err) - } - } - - root, err := os.ReadFile(os.Getenv("CORE_PEER_TLS_ROOTCERT_FILE")) - if err != nil { - return Config{}, fmt.Errorf("failed to read root cert file: %s", err) - } - - tlscfg, err := LoadTLSConfig(false, key, cert, root) - if err != nil { - return Config{}, err - } - - conf.TLS = tlscfg - - return conf, nil -} - -// LoadTLSConfig loads the TLS configuration for the chaincode -func LoadTLSConfig(isserver bool, key, cert, root []byte) (*tls.Config, error) { - if key == nil { - return nil, fmt.Errorf("key not provided") - } - - if cert == nil { - return nil, fmt.Errorf("cert not provided") - } - - if !isserver && root == nil { - return nil, fmt.Errorf("root cert not provided") - } - - cccert, err := tls.X509KeyPair(cert, key) - if err != nil { - return nil, fmt.Errorf("failed to parse client key pair: %s", err) - } - - var rootCertPool *x509.CertPool - if root != nil { - rootCertPool = x509.NewCertPool() - if ok := rootCertPool.AppendCertsFromPEM(root); !ok { - return nil, errors.New("failed to load root cert file") - } - } - - tlscfg := &tls.Config{ - MinVersion: tls.VersionTLS12, - Certificates: []tls.Certificate{cccert}, - } - - //follow Peer's server default config properties - if isserver { - tlscfg.ClientCAs = rootCertPool - tlscfg.SessionTicketsDisabled = true - tlscfg.CipherSuites = []uint16{tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - tls.TLS_RSA_WITH_AES_128_GCM_SHA256, - tls.TLS_RSA_WITH_AES_256_GCM_SHA384, - } - if rootCertPool != nil { - tlscfg.ClientAuth = tls.RequireAndVerifyClientCert - } - } else { - tlscfg.RootCAs = rootCertPool - } - - return tlscfg, nil -} diff --git a/v2/shim/internal/config_test.go b/v2/shim/internal/config_test.go deleted file mode 100644 index 63586fd8..00000000 --- a/v2/shim/internal/config_test.go +++ /dev/null @@ -1,731 +0,0 @@ -// Copyright the Hyperledger Fabric contributors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package internal_test - -import ( - "context" - "crypto/tls" - "crypto/x509" - "encoding/base64" - "os" - "testing" - "time" - - . "github.com/hyperledger/fabric-chaincode-go/v2/shim/internal" - "github.com/hyperledger/fabric-protos-go-apiv2/peer" - "github.com/stretchr/testify/assert" - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/keepalive" -) - -// TLS tuples for client and server were created -// using cryptogen tool. Of course, any standard tool such as openssl -// could have been used as well -var keyPEM = `-----BEGIN PRIVATE KEY----- -MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgKg8jpiNIB5LXLull -IRoYMsQximSiU7XvGCYLslx4GauhRANCAARBGdslxalpg0dxk9GwVhi+Qw9oKZPE -n1hWPFmusDKtNbDLsHd9k1lU+SWnJKYlg7hmaUvxC1lR2M6KmvAwSUfN ------END PRIVATE KEY----- -` -var certPEM = `-----BEGIN CERTIFICATE----- -MIICaTCCAhCgAwIBAgIQS46wcUDY2nJ2gQ/7fp/ptzAKBggqhkjOPQQDAjB2MQsw -CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy -YW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEfMB0GA1UEAxMWdGxz -Y2Eub3JnMS5leGFtcGxlLmNvbTAeFw0xOTEyMTIwMTA1NTBaFw0yOTEyMDkwMTA1 -NTBaMFoxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH -Ew1TYW4gRnJhbmNpc2NvMR4wHAYDVQQDExVteWNjLm9yZzEuZXhhbXBsZS5jb20w -WTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAARBGdslxalpg0dxk9GwVhi+Qw9oKZPE -n1hWPFmusDKtNbDLsHd9k1lU+SWnJKYlg7hmaUvxC1lR2M6KmvAwSUfNo4GbMIGY -MA4GA1UdDwEB/wQEAwIFoDAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw -DAYDVR0TAQH/BAIwADArBgNVHSMEJDAigCBxQqUF6hEsSgXTc47WT4U58SOdgX8n -8RlMuxFg0wRtjjAsBgNVHREEJTAjghVteWNjLm9yZzEuZXhhbXBsZS5jb22CBG15 -Y2OHBH8AAAEwCgYIKoZIzj0EAwIDRwAwRAIgWgxAuGibD+Da/qCLBryJMDGlyIrx -HV+tI33lEy1B9qoCIEJD4xipI2WYp1sHmK2nxYPcoTb9WLFdNZ6twKZyw9c8 ------END CERTIFICATE----- -` -var rootPEM = `-----BEGIN CERTIFICATE----- -MIICSTCCAe+gAwIBAgIQWpamEC5/D2N5JKS8FEpgTzAKBggqhkjOPQQDAjB2MQsw -CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy -YW5jaXNjbzEZMBcGA1UEChMQb3JnMS5leGFtcGxlLmNvbTEfMB0GA1UEAxMWdGxz -Y2Eub3JnMS5leGFtcGxlLmNvbTAeFw0xOTEyMTIwMTA1NTBaFw0yOTEyMDkwMTA1 -NTBaMHYxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9ybmlhMRYwFAYDVQQH -Ew1TYW4gRnJhbmNpc2NvMRkwFwYDVQQKExBvcmcxLmV4YW1wbGUuY29tMR8wHQYD -VQQDExZ0bHNjYS5vcmcxLmV4YW1wbGUuY29tMFkwEwYHKoZIzj0CAQYIKoZIzj0D -AQcDQgAE2eFjoZkB/ozmheZZ9P05kUXAQAG+j0oTmRr9vX2qJa+tyrbS/i4UKrXo -82dqcDmmL16l2ukBXt7/aBre5WbVEaNfMF0wDgYDVR0PAQH/BAQDAgGmMA8GA1Ud -JQQIMAYGBFUdJQAwDwYDVR0TAQH/BAUwAwEB/zApBgNVHQ4EIgQgcUKlBeoRLEoF -03OO1k+FOfEjnYF/J/EZTLsRYNMEbY4wCgYIKoZIzj0EAwIDSAAwRQIhANmPRnJi -p7amrl9rF5xWtW0rR+y9uSCi6cy/T8bJl1JTAiATHlHcuNhHFeGb+Vl512FC3sGM -bHHlP/A/QkbGqJL4HQ== ------END CERTIFICATE----- -` - -// #nosec G101 -var clientKeyPEM = `-----BEGIN EC PRIVATE KEY----- -MHcCAQEEINVHep4/z6iPa151Ipp4MmCb1l/VKkY3vuMfUQf3LhQboAoGCCqGSM49 -AwEHoUQDQgAEcE6hZ7muszSi5wXIVKPdIuLYPTIxQxj+jekPRfFnJF/RJKM0Nj3T -Bk9spwCHwu1t3REyobjaZcFQk0y32Pje5A== ------END EC PRIVATE KEY----- -` - -var clientCertPEM = `-----BEGIN CERTIFICATE----- -MIICAzCCAaqgAwIBAgIQe/ZUgn+/dH6FGrx+dr/PfjAKBggqhkjOPQQDAjBYMQsw -CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy -YW5jaXNjbzENMAsGA1UEChMET3JnMTENMAsGA1UEAxMET3JnMTAeFw0xODA4MjEw -ODI1MzNaFw0yODA4MTgwODI1MzNaMGgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpD -YWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRUwEwYDVQQKEwxPcmcx -LWNsaWVudDExFTATBgNVBAMTDE9yZzEtY2xpZW50MTBZMBMGByqGSM49AgEGCCqG -SM49AwEHA0IABHBOoWe5rrM0oucFyFSj3SLi2D0yMUMY/o3pD0XxZyRf0SSjNDY9 -0wZPbKcAh8Ltbd0RMqG42mXBUJNMt9j43uSjRjBEMA4GA1UdDwEB/wQEAwIFoDAT -BgNVHSUEDDAKBggrBgEFBQcDAjAMBgNVHRMBAf8EAjAAMA8GA1UdIwQIMAaABAEC -AwQwCgYIKoZIzj0EAwIDRwAwRAIgaK/prRkZS6zctxwBUl2QApUrH7pMmab30Nn9 -ER8f3m0CICBZ9XoxKXEFFcSRpfiA2/vzoOPg76lRXcCklxzGSJYu ------END CERTIFICATE----- -` - -var clientRootPEM = `-----BEGIN CERTIFICATE----- -MIIB8TCCAZegAwIBAgIQUigdJy6IudO7sVOXsKVrtzAKBggqhkjOPQQDAjBYMQsw -CQYDVQQGEwJVUzETMBEGA1UECBMKQ2FsaWZvcm5pYTEWMBQGA1UEBxMNU2FuIEZy -YW5jaXNjbzENMAsGA1UEChMET3JnMTENMAsGA1UEAxMET3JnMTAeFw0xODA4MjEw -ODI1MzNaFw0yODA4MTgwODI1MzNaMFgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpD -YWxpZm9ybmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMQ0wCwYDVQQKEwRPcmcx -MQ0wCwYDVQQDEwRPcmcxMFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEVOI+oAAB -Pl+iRsCcGq81WbXap2L1r432T5gbzUNKYRvVsyFFYmdO8ql8uDi4UxSY64eaeRFT -uxdcsTG7M5K2yaNDMEEwDgYDVR0PAQH/BAQDAgGmMA8GA1UdJQQIMAYGBFUdJQAw -DwYDVR0TAQH/BAUwAwEB/zANBgNVHQ4EBgQEAQIDBDAKBggqhkjOPQQDAgNIADBF -AiEA6U7IRGf+S7e9U2+jSI2eFiBsVEBIi35LgYoKqjELj5oCIAD7DfVMaMHzzjiQ -XIlJQdS/9afDi32qZWZfe3kAUAs0 ------END CERTIFICATE----- -` - -func TestLoadBase64EncodedConfig(t *testing.T) { - // setup key/cert files - testDir, err := os.MkdirTemp("", "shiminternal") - if err != nil { - t.Fatalf("Failed to test directory: %s", err) - } - defer os.RemoveAll(testDir) - - keyFile, err := os.CreateTemp(testDir, "testKey") - if err != nil { - t.Fatalf("Failed to create key file: %s", err) - } - b64Key := base64.StdEncoding.EncodeToString([]byte(keyPEM)) - if _, err := keyFile.WriteString(b64Key); err != nil { - t.Fatalf("Failed to write to key file: %s", err) - } - - certFile, err := os.CreateTemp(testDir, "testCert") - if err != nil { - t.Fatalf("Failed to create cert file: %s", err) - } - b64Cert := base64.StdEncoding.EncodeToString([]byte(certPEM)) - if _, err := certFile.WriteString(b64Cert); err != nil { - t.Fatalf("Failed to write to cert file: %s", err) - } - - rootFile, err := os.CreateTemp(testDir, "testRoot") - if err != nil { - t.Fatalf("Failed to create root file: %s", err) - } - if _, err := rootFile.WriteString(rootPEM); err != nil { - t.Fatalf("Failed to write to root file: %s", err) - } - - notb64File, err := os.CreateTemp(testDir, "testNotb64") - if err != nil { - t.Fatalf("Failed to create notb64 file: %s", err) - } - if _, err := notb64File.WriteString("#####"); err != nil { - t.Fatalf("Failed to write to notb64 file: %s", err) - } - - notPEMFile, err := os.CreateTemp(testDir, "testNotPEM") - if err != nil { - t.Fatalf("Failed to create notPEM file: %s", err) - } - b64 := base64.StdEncoding.EncodeToString([]byte("not pem")) - if _, err := notPEMFile.WriteString(b64); err != nil { - t.Fatalf("Failed to write to notPEM file: %s", err) - } - - defer cleanupEnv() - - // expected TLS config - rootPool := x509.NewCertPool() - rootPool.AppendCertsFromPEM([]byte(rootPEM)) - clientCert, err := tls.X509KeyPair([]byte(certPEM), []byte(keyPEM)) - if err != nil { - t.Fatalf("Failed to load client cert pair: %s", err) - } - - tlsConfig := &tls.Config{ - MinVersion: tls.VersionTLS12, - Certificates: []tls.Certificate{clientCert}, - RootCAs: rootPool, - } - - kaOpts := keepalive.ClientParameters{ - Time: 1 * time.Minute, - Timeout: 20 * time.Second, - PermitWithoutStream: true, - } - - var tests = []struct { - name string - env map[string]string - expected Config - errMsg string - }{ - { - name: "TLS disabled", - env: map[string]string{ - "CORE_CHAINCODE_ID_NAME": "testCC", - "CORE_PEER_TLS_ENABLED": "false", - }, - expected: Config{ - ChaincodeName: "testCC", - KaOpts: kaOpts, - }, - }, - { - name: "TLS Enabled", - env: map[string]string{ - "CORE_CHAINCODE_ID_NAME": "testCC", - "CORE_PEER_TLS_ENABLED": "true", - "CORE_TLS_CLIENT_KEY_PATH": keyFile.Name(), - "CORE_TLS_CLIENT_CERT_PATH": certFile.Name(), - "CORE_PEER_TLS_ROOTCERT_FILE": rootFile.Name(), - }, - expected: Config{ - ChaincodeName: "testCC", - TLS: tlsConfig, - KaOpts: kaOpts, - }, - }, - { - name: "Bad TLS_ENABLED", - env: map[string]string{ - "CORE_PEER_TLS_ENABLED": "nottruthy", - }, - errMsg: "'CORE_PEER_TLS_ENABLED' must be set to 'true' or 'false'", - }, - { - name: "Missing key file", - env: map[string]string{ - "CORE_CHAINCODE_ID_NAME": "testCC", - "CORE_PEER_TLS_ENABLED": "true", - "CORE_TLS_CLIENT_KEY_PATH": "missingkey", - }, - errMsg: "failed to read private key file", - }, - { - name: "Bad key file", - env: map[string]string{ - "CORE_CHAINCODE_ID_NAME": "testCC", - "CORE_PEER_TLS_ENABLED": "true", - "CORE_TLS_CLIENT_KEY_PATH": notb64File.Name(), - }, - errMsg: "failed to decode private key file", - }, - { - name: "Missing cert file", - env: map[string]string{ - "CORE_CHAINCODE_ID_NAME": "testCC", - "CORE_PEER_TLS_ENABLED": "true", - "CORE_TLS_CLIENT_KEY_PATH": keyFile.Name(), - "CORE_TLS_CLIENT_CERT_PATH": "missingkey", - }, - errMsg: "failed to read public key file", - }, - { - name: "Bad cert file", - env: map[string]string{ - "CORE_CHAINCODE_ID_NAME": "testCC", - "CORE_PEER_TLS_ENABLED": "true", - "CORE_TLS_CLIENT_KEY_PATH": keyFile.Name(), - "CORE_TLS_CLIENT_CERT_PATH": notb64File.Name(), - }, - errMsg: "failed to decode public key file", - }, - { - name: "Missing root file", - env: map[string]string{ - "CORE_CHAINCODE_ID_NAME": "testCC", - "CORE_PEER_TLS_ENABLED": "true", - "CORE_TLS_CLIENT_KEY_PATH": keyFile.Name(), - "CORE_TLS_CLIENT_CERT_PATH": certFile.Name(), - "CORE_PEER_TLS_ROOTCERT_FILE": "missingkey", - }, - errMsg: "failed to read root cert file", - }, - { - name: "Bad root file", - env: map[string]string{ - "CORE_CHAINCODE_ID_NAME": "testCC", - "CORE_PEER_TLS_ENABLED": "true", - "CORE_TLS_CLIENT_KEY_PATH": keyFile.Name(), - "CORE_TLS_CLIENT_CERT_PATH": certFile.Name(), - "CORE_PEER_TLS_ROOTCERT_FILE": notb64File.Name(), - }, - errMsg: "failed to load root cert file", - }, - { - name: "Key not PEM", - env: map[string]string{ - "CORE_CHAINCODE_ID_NAME": "testCC", - "CORE_PEER_TLS_ENABLED": "true", - "CORE_TLS_CLIENT_KEY_PATH": notPEMFile.Name(), - "CORE_TLS_CLIENT_CERT_PATH": certFile.Name(), - "CORE_PEER_TLS_ROOTCERT_FILE": rootFile.Name(), - }, - errMsg: "failed to parse client key pair", - }, - { - name: "Cert not PEM", - env: map[string]string{ - "CORE_CHAINCODE_ID_NAME": "testCC", - "CORE_PEER_TLS_ENABLED": "true", - "CORE_TLS_CLIENT_KEY_PATH": keyFile.Name(), - "CORE_TLS_CLIENT_CERT_PATH": notPEMFile.Name(), - "CORE_PEER_TLS_ROOTCERT_FILE": rootFile.Name(), - }, - errMsg: "failed to parse client key pair", - }, - } - - for _, test := range tests { - test := test - t.Run(test.name, func(t *testing.T) { - for k, v := range test.env { - os.Setenv(k, v) - } - conf, err := LoadConfig() - if test.errMsg == "" { - assert.EqualValues(t, test.expected.ChaincodeName, conf.ChaincodeName) - assert.Equal(t, test.expected.KaOpts, conf.KaOpts) - if test.expected.TLS != nil { - tlsConfigEquals(t, test.expected.TLS, conf.TLS) - } - } else { - assert.Contains(t, err.Error(), test.errMsg) - } - }) - } - - tlsServerConfig := &tls.Config{ - MinVersion: tls.VersionTLS12, - Certificates: []tls.Certificate{clientCert}, - ClientCAs: rootPool, - SessionTicketsDisabled: true, - CipherSuites: []uint16{tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - tls.TLS_RSA_WITH_AES_128_GCM_SHA256, // #nosec G402 - tls.TLS_RSA_WITH_AES_256_GCM_SHA384, - }, - ClientAuth: tls.RequireAndVerifyClientCert, - } - - tlsServerNonMutualConfig := &tls.Config{ - MinVersion: tls.VersionTLS12, - Certificates: []tls.Certificate{clientCert}, - RootCAs: nil, - SessionTicketsDisabled: true, - CipherSuites: []uint16{tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - tls.TLS_RSA_WITH_AES_128_GCM_SHA256, // #nosec G402 - tls.TLS_RSA_WITH_AES_256_GCM_SHA384, - }, - ClientAuth: tls.NoClientCert, - } - - // additional tests to differentiate client vs server - var tlsTests = []struct { - name string - issrv bool - key []byte - cert []byte - rootCert []byte - expected *tls.Config - errMsg string - }{ - { - name: "Server TLS", - issrv: true, - key: []byte(keyPEM), - cert: []byte(certPEM), - rootCert: []byte(rootPEM), - expected: tlsServerConfig, - }, - { - name: "Server non-mutual TLS", - issrv: true, - key: []byte(keyPEM), - cert: []byte(certPEM), - rootCert: nil, - expected: tlsServerNonMutualConfig, - }, - { - name: "Server key unspecified", - issrv: true, - key: nil, - cert: []byte(certPEM), - rootCert: []byte(rootPEM), - errMsg: "key not provided", - }, - { - name: "Server cert unspecified", - issrv: true, - key: []byte(keyPEM), - cert: nil, - rootCert: []byte(rootPEM), - errMsg: "cert not provided", - }, - { - name: "Client TLS root CA unspecified", - issrv: false, - key: []byte(keyPEM), - cert: []byte(certPEM), - rootCert: nil, - errMsg: "root cert not provided", - }, - } - - for _, test := range tlsTests { - t.Run(test.name, func(t *testing.T) { - tlsCfg, err := LoadTLSConfig(test.issrv, test.key, test.cert, test.rootCert) - if test.errMsg == "" { - tlsConfigEquals(t, test.expected, tlsCfg) - } else { - assert.Contains(t, err.Error(), test.errMsg) - } - }) - } -} - -func tlsConfigEquals(t *testing.T, cfg1 *tls.Config, cfg2 *tls.Config) { - assert.EqualValues(t, cfg1.MinVersion, cfg2.MinVersion) - assert.EqualValues(t, cfg1.ClientAuth, cfg2.ClientAuth) -} - -func TestLoadPEMEncodedConfig(t *testing.T) { - // setup key/cert files - testDir, err := os.MkdirTemp("", "shiminternal") - if err != nil { - t.Fatalf("Failed to test directory: %s", err) - } - defer os.RemoveAll(testDir) - - keyFile, err := os.CreateTemp(testDir, "testKey") - if err != nil { - t.Fatalf("Failed to create key file: %s", err) - } - if _, err := keyFile.WriteString(keyPEM); err != nil { - t.Fatalf("Failed to write to key file: %s", err) - } - - certFile, err := os.CreateTemp(testDir, "testCert") - if err != nil { - t.Fatalf("Failed to create cert file: %s", err) - } - if _, err := certFile.WriteString(certPEM); err != nil { - t.Fatalf("Failed to write to cert file: %s", err) - } - - rootFile, err := os.CreateTemp(testDir, "testRoot") - if err != nil { - t.Fatalf("Failed to create root file: %s", err) - } - if _, err := rootFile.WriteString(rootPEM); err != nil { - t.Fatalf("Failed to write to root file: %s", err) - } - - keyFile64, err := os.CreateTemp(testDir, "testKey64") - if err != nil { - t.Fatalf("Failed to create key file: %s", err) - } - b64Key := base64.StdEncoding.EncodeToString([]byte(keyPEM)) - if _, err := keyFile64.WriteString(b64Key); err != nil { - t.Fatalf("Failed to write to key file: %s", err) - } - - certFile64, err := os.CreateTemp(testDir, "testCert64") - if err != nil { - t.Fatalf("Failed to create cert file: %s", err) - } - b64Cert := base64.StdEncoding.EncodeToString([]byte(certPEM)) - if _, err := certFile64.WriteString(b64Cert); err != nil { - t.Fatalf("Failed to write to cert file: %s", err) - } - - defer cleanupEnv() - - // expected TLS config - rootPool := x509.NewCertPool() - rootPool.AppendCertsFromPEM([]byte(rootPEM)) - clientCert, err := tls.X509KeyPair([]byte(certPEM), []byte(keyPEM)) - if err != nil { - t.Fatalf("Failed to load client cert pair: %s", err) - } - - tlsConfig := &tls.Config{ - MinVersion: tls.VersionTLS12, - Certificates: []tls.Certificate{clientCert}, - RootCAs: rootPool, - } - - kaOpts := keepalive.ClientParameters{ - Time: 1 * time.Minute, - Timeout: 20 * time.Second, - PermitWithoutStream: true, - } - - var tests = []struct { - name string - env map[string]string - expected Config - errMsg string - }{ - { - name: "TLS Enabled with PEM-encoded variables", - env: map[string]string{ - "CORE_CHAINCODE_ID_NAME": "testCC", - "CORE_PEER_TLS_ENABLED": "true", - "CORE_TLS_CLIENT_KEY_FILE": keyFile.Name(), - "CORE_TLS_CLIENT_CERT_FILE": certFile.Name(), - "CORE_PEER_TLS_ROOTCERT_FILE": rootFile.Name(), - }, - expected: Config{ - ChaincodeName: "testCC", - TLS: tlsConfig, - KaOpts: kaOpts, - }, - }, - { - name: "Client cert uses base64 encoding", - env: map[string]string{ - "CORE_CHAINCODE_ID_NAME": "testCC", - "CORE_PEER_TLS_ENABLED": "true", - "CORE_TLS_CLIENT_KEY_FILE": keyFile.Name(), - "CORE_TLS_CLIENT_CERT_PATH": certFile64.Name(), - "CORE_PEER_TLS_ROOTCERT_FILE": rootFile.Name(), - }, - expected: Config{ - ChaincodeName: "testCC", - TLS: tlsConfig, - KaOpts: kaOpts, - }, - }, - { - name: "Client key uses base64 encoding", - env: map[string]string{ - "CORE_CHAINCODE_ID_NAME": "testCC", - "CORE_PEER_TLS_ENABLED": "true", - "CORE_TLS_CLIENT_KEY_PATH": keyFile64.Name(), - "CORE_TLS_CLIENT_CERT_FILE": certFile.Name(), - "CORE_PEER_TLS_ROOTCERT_FILE": rootFile.Name(), - }, - expected: Config{ - ChaincodeName: "testCC", - TLS: tlsConfig, - KaOpts: kaOpts, - }, - }, - { - name: "Client cert uses base64 encoding with PEM variable", - env: map[string]string{ - "CORE_CHAINCODE_ID_NAME": "testCC", - "CORE_PEER_TLS_ENABLED": "true", - "CORE_TLS_CLIENT_KEY_FILE": keyFile.Name(), - "CORE_TLS_CLIENT_CERT_FILE": certFile64.Name(), - "CORE_PEER_TLS_ROOTCERT_FILE": rootFile.Name(), - }, - errMsg: "failed to parse client key pair", - }, - { - name: "Client key uses base64 encoding with PEM variable", - env: map[string]string{ - "CORE_CHAINCODE_ID_NAME": "testCC", - "CORE_PEER_TLS_ENABLED": "true", - "CORE_TLS_CLIENT_KEY_FILE": keyFile64.Name(), - "CORE_TLS_CLIENT_CERT_FILE": certFile.Name(), - "CORE_PEER_TLS_ROOTCERT_FILE": rootFile.Name(), - }, - errMsg: "failed to parse client key pair", - }, - } - - for _, test := range tests { - test := test - t.Run(test.name, func(t *testing.T) { - for k, v := range test.env { - os.Setenv(k, v) - } - conf, err := LoadConfig() - if test.errMsg == "" { - assert.EqualValues(t, test.expected.ChaincodeName, conf.ChaincodeName) - assert.Equal(t, test.expected.KaOpts, conf.KaOpts) - if test.expected.TLS != nil { - tlsConfigEquals(t, test.expected.TLS, conf.TLS) - } - } else { - assert.Contains(t, err.Error(), test.errMsg) - } - }) - } -} - -func newTLSConnection(t *testing.T, address string, crt, key, rootCert []byte) *grpc.ClientConn { - tlsConfig := &tls.Config{ - MinVersion: tls.VersionTLS12, - } - - tlsConfig.RootCAs = x509.NewCertPool() - tlsConfig.RootCAs.AppendCertsFromPEM(rootCert) - if crt != nil && key != nil { - cert, err := tls.X509KeyPair(crt, key) - assert.NoError(t, err) - assert.NotNil(t, cert) - - tlsConfig.Certificates = append(tlsConfig.Certificates, cert) - } - - var dialOpts []grpc.DialOption - dialOpts = append(dialOpts, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig))) - - kap := keepalive.ClientParameters{ - Time: time.Duration(1) * time.Minute, - Timeout: time.Duration(20) * time.Second, - PermitWithoutStream: true, - } - - dialOpts = append(dialOpts, grpc.WithKeepaliveParams(kap)) - - conn, err := grpc.NewClient(address, dialOpts...) - assert.NoError(t, err) - assert.NotNil(t, conn) - - return conn -} - -func TestTLSClientWithChaincodeServer(t *testing.T) { - rootPool := x509.NewCertPool() - ok := rootPool.AppendCertsFromPEM([]byte(clientRootPEM)) - if !ok { - t.Fatal("failed to create test root cert pool") - } - - cert, err := tls.X509KeyPair([]byte(certPEM), []byte(keyPEM)) - if err != nil { - t.Fatalf("Failed to load client cert pair: %s", err) - } - - tlsServerConfig := &tls.Config{ - MinVersion: tls.VersionTLS12, - Certificates: []tls.Certificate{cert}, - ClientCAs: rootPool, - SessionTicketsDisabled: true, - CipherSuites: []uint16{tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, - tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, - tls.TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384, - tls.TLS_RSA_WITH_AES_128_GCM_SHA256, // #nosec G402 - tls.TLS_RSA_WITH_AES_256_GCM_SHA384, - }, - ClientAuth: tls.RequireAndVerifyClientCert, - } - - // given server is good and expects valid TLS connection, test good and invalid scenarios - var tlsTests = []struct { - name string - issrv bool - clientKey []byte - clientCert []byte - clientRootCert []byte - expected *tls.Config - errMsg string - success bool - address string - }{ - { - name: "Good TLS", - issrv: true, - clientKey: []byte(clientKeyPEM), - clientCert: []byte(clientCertPEM), - clientRootCert: []byte(rootPEM), - success: true, - address: "127.0.0.1:0", - }, - { - name: "Bad server RootCA", - issrv: true, - clientKey: []byte(clientKeyPEM), - clientCert: []byte(clientCertPEM), - clientRootCert: []byte(clientRootPEM), - success: false, - errMsg: "transport: authentication handshake failed: tls: failed to verify certificate: x509: certificate signed by unknown authority", - address: "127.0.0.1:0", - }, - { - name: "Bad client cert", - issrv: true, - clientKey: []byte(keyPEM), - clientCert: []byte(certPEM), - clientRootCert: []byte(rootPEM), - success: false, - errMsg: "rpc error", - address: "127.0.0.1:0", - }, - { - name: "No client cert", - issrv: true, - clientRootCert: []byte(rootPEM), - success: false, - errMsg: "rpc error", - address: "127.0.0.1:0", - }, - } - - for _, test := range tlsTests { - t.Run(test.name, func(t *testing.T) { - srv, err := NewServer(test.address, tlsServerConfig, nil) - if err != nil { - t.Fatalf("error creating server for test: %v", err) - } - defer srv.Stop() - go func() { - err = srv.Start() - assert.NoError(t, err, "srv.Start") - }() - - conn := newTLSConnection(t, srv.Listener.Addr().String(), test.clientCert, test.clientKey, test.clientRootCert) - assert.NotNil(t, conn) - - ccclient := peer.NewChaincodeClient(conn) - assert.NotNil(t, ccclient) - - stream, err := ccclient.Connect(context.Background()) - if test.success { - assert.NoError(t, err) - assert.NotNil(t, stream) - } else { - assert.Error(t, err) - assert.Regexp(t, test.errMsg, err.Error()) - } - }) - } -} - -func cleanupEnv() { - os.Unsetenv("CORE_PEER_TLS_ENABLED") - os.Unsetenv("CORE_TLS_CLIENT_KEY_PATH") - os.Unsetenv("CORE_TLS_CLIENT_CERT_PATH") - os.Unsetenv("CORE_PEER_TLS_ROOTCERT_FILE") - os.Unsetenv("CORE_CHAINCODE_ID_NAME") -} diff --git a/v2/shim/internal/mock/client_stream.go b/v2/shim/internal/mock/client_stream.go deleted file mode 100644 index 201cd636..00000000 --- a/v2/shim/internal/mock/client_stream.go +++ /dev/null @@ -1,244 +0,0 @@ -// Code generated by counterfeiter. DO NOT EDIT. -package mock - -import ( - "sync" - - "github.com/hyperledger/fabric-protos-go-apiv2/peer" -) - -type ClientStream struct { - CloseSendStub func() error - closeSendMutex sync.RWMutex - closeSendArgsForCall []struct { - } - closeSendReturns struct { - result1 error - } - closeSendReturnsOnCall map[int]struct { - result1 error - } - RecvStub func() (*peer.ChaincodeMessage, error) - recvMutex sync.RWMutex - recvArgsForCall []struct { - } - recvReturns struct { - result1 *peer.ChaincodeMessage - result2 error - } - recvReturnsOnCall map[int]struct { - result1 *peer.ChaincodeMessage - result2 error - } - SendStub func(*peer.ChaincodeMessage) error - sendMutex sync.RWMutex - sendArgsForCall []struct { - arg1 *peer.ChaincodeMessage - } - sendReturns struct { - result1 error - } - sendReturnsOnCall map[int]struct { - result1 error - } - invocations map[string][][]interface{} - invocationsMutex sync.RWMutex -} - -func (fake *ClientStream) CloseSend() error { - fake.closeSendMutex.Lock() - ret, specificReturn := fake.closeSendReturnsOnCall[len(fake.closeSendArgsForCall)] - fake.closeSendArgsForCall = append(fake.closeSendArgsForCall, struct { - }{}) - stub := fake.CloseSendStub - fakeReturns := fake.closeSendReturns - fake.recordInvocation("CloseSend", []interface{}{}) - fake.closeSendMutex.Unlock() - if stub != nil { - return stub() - } - if specificReturn { - return ret.result1 - } - return fakeReturns.result1 -} - -func (fake *ClientStream) CloseSendCallCount() int { - fake.closeSendMutex.RLock() - defer fake.closeSendMutex.RUnlock() - return len(fake.closeSendArgsForCall) -} - -func (fake *ClientStream) CloseSendCalls(stub func() error) { - fake.closeSendMutex.Lock() - defer fake.closeSendMutex.Unlock() - fake.CloseSendStub = stub -} - -func (fake *ClientStream) CloseSendReturns(result1 error) { - fake.closeSendMutex.Lock() - defer fake.closeSendMutex.Unlock() - fake.CloseSendStub = nil - fake.closeSendReturns = struct { - result1 error - }{result1} -} - -func (fake *ClientStream) CloseSendReturnsOnCall(i int, result1 error) { - fake.closeSendMutex.Lock() - defer fake.closeSendMutex.Unlock() - fake.CloseSendStub = nil - if fake.closeSendReturnsOnCall == nil { - fake.closeSendReturnsOnCall = make(map[int]struct { - result1 error - }) - } - fake.closeSendReturnsOnCall[i] = struct { - result1 error - }{result1} -} - -func (fake *ClientStream) Recv() (*peer.ChaincodeMessage, error) { - fake.recvMutex.Lock() - ret, specificReturn := fake.recvReturnsOnCall[len(fake.recvArgsForCall)] - fake.recvArgsForCall = append(fake.recvArgsForCall, struct { - }{}) - stub := fake.RecvStub - fakeReturns := fake.recvReturns - fake.recordInvocation("Recv", []interface{}{}) - fake.recvMutex.Unlock() - if stub != nil { - return stub() - } - if specificReturn { - return ret.result1, ret.result2 - } - return fakeReturns.result1, fakeReturns.result2 -} - -func (fake *ClientStream) RecvCallCount() int { - fake.recvMutex.RLock() - defer fake.recvMutex.RUnlock() - return len(fake.recvArgsForCall) -} - -func (fake *ClientStream) RecvCalls(stub func() (*peer.ChaincodeMessage, error)) { - fake.recvMutex.Lock() - defer fake.recvMutex.Unlock() - fake.RecvStub = stub -} - -func (fake *ClientStream) RecvReturns(result1 *peer.ChaincodeMessage, result2 error) { - fake.recvMutex.Lock() - defer fake.recvMutex.Unlock() - fake.RecvStub = nil - fake.recvReturns = struct { - result1 *peer.ChaincodeMessage - result2 error - }{result1, result2} -} - -func (fake *ClientStream) RecvReturnsOnCall(i int, result1 *peer.ChaincodeMessage, result2 error) { - fake.recvMutex.Lock() - defer fake.recvMutex.Unlock() - fake.RecvStub = nil - if fake.recvReturnsOnCall == nil { - fake.recvReturnsOnCall = make(map[int]struct { - result1 *peer.ChaincodeMessage - result2 error - }) - } - fake.recvReturnsOnCall[i] = struct { - result1 *peer.ChaincodeMessage - result2 error - }{result1, result2} -} - -func (fake *ClientStream) Send(arg1 *peer.ChaincodeMessage) error { - fake.sendMutex.Lock() - ret, specificReturn := fake.sendReturnsOnCall[len(fake.sendArgsForCall)] - fake.sendArgsForCall = append(fake.sendArgsForCall, struct { - arg1 *peer.ChaincodeMessage - }{arg1}) - stub := fake.SendStub - fakeReturns := fake.sendReturns - fake.recordInvocation("Send", []interface{}{arg1}) - fake.sendMutex.Unlock() - if stub != nil { - return stub(arg1) - } - if specificReturn { - return ret.result1 - } - return fakeReturns.result1 -} - -func (fake *ClientStream) SendCallCount() int { - fake.sendMutex.RLock() - defer fake.sendMutex.RUnlock() - return len(fake.sendArgsForCall) -} - -func (fake *ClientStream) SendCalls(stub func(*peer.ChaincodeMessage) error) { - fake.sendMutex.Lock() - defer fake.sendMutex.Unlock() - fake.SendStub = stub -} - -func (fake *ClientStream) SendArgsForCall(i int) *peer.ChaincodeMessage { - fake.sendMutex.RLock() - defer fake.sendMutex.RUnlock() - argsForCall := fake.sendArgsForCall[i] - return argsForCall.arg1 -} - -func (fake *ClientStream) SendReturns(result1 error) { - fake.sendMutex.Lock() - defer fake.sendMutex.Unlock() - fake.SendStub = nil - fake.sendReturns = struct { - result1 error - }{result1} -} - -func (fake *ClientStream) SendReturnsOnCall(i int, result1 error) { - fake.sendMutex.Lock() - defer fake.sendMutex.Unlock() - fake.SendStub = nil - if fake.sendReturnsOnCall == nil { - fake.sendReturnsOnCall = make(map[int]struct { - result1 error - }) - } - fake.sendReturnsOnCall[i] = struct { - result1 error - }{result1} -} - -func (fake *ClientStream) Invocations() map[string][][]interface{} { - fake.invocationsMutex.RLock() - defer fake.invocationsMutex.RUnlock() - fake.closeSendMutex.RLock() - defer fake.closeSendMutex.RUnlock() - fake.recvMutex.RLock() - defer fake.recvMutex.RUnlock() - fake.sendMutex.RLock() - defer fake.sendMutex.RUnlock() - copiedInvocations := map[string][][]interface{}{} - for key, value := range fake.invocations { - copiedInvocations[key] = value - } - return copiedInvocations -} - -func (fake *ClientStream) recordInvocation(key string, args []interface{}) { - fake.invocationsMutex.Lock() - defer fake.invocationsMutex.Unlock() - if fake.invocations == nil { - fake.invocations = map[string][][]interface{}{} - } - if fake.invocations[key] == nil { - fake.invocations[key] = [][]interface{}{} - } - fake.invocations[key] = append(fake.invocations[key], args) -} diff --git a/v2/shim/internal/mock/peer_chaincode_stream.go b/v2/shim/internal/mock/peer_chaincode_stream.go deleted file mode 100644 index d5a7f015..00000000 --- a/v2/shim/internal/mock/peer_chaincode_stream.go +++ /dev/null @@ -1,179 +0,0 @@ -// Code generated by counterfeiter. DO NOT EDIT. -package mock - -import ( - "sync" - - "github.com/hyperledger/fabric-protos-go-apiv2/peer" -) - -type PeerChaincodeStream struct { - RecvStub func() (*peer.ChaincodeMessage, error) - recvMutex sync.RWMutex - recvArgsForCall []struct { - } - recvReturns struct { - result1 *peer.ChaincodeMessage - result2 error - } - recvReturnsOnCall map[int]struct { - result1 *peer.ChaincodeMessage - result2 error - } - SendStub func(*peer.ChaincodeMessage) error - sendMutex sync.RWMutex - sendArgsForCall []struct { - arg1 *peer.ChaincodeMessage - } - sendReturns struct { - result1 error - } - sendReturnsOnCall map[int]struct { - result1 error - } - invocations map[string][][]interface{} - invocationsMutex sync.RWMutex -} - -func (fake *PeerChaincodeStream) Recv() (*peer.ChaincodeMessage, error) { - fake.recvMutex.Lock() - ret, specificReturn := fake.recvReturnsOnCall[len(fake.recvArgsForCall)] - fake.recvArgsForCall = append(fake.recvArgsForCall, struct { - }{}) - stub := fake.RecvStub - fakeReturns := fake.recvReturns - fake.recordInvocation("Recv", []interface{}{}) - fake.recvMutex.Unlock() - if stub != nil { - return stub() - } - if specificReturn { - return ret.result1, ret.result2 - } - return fakeReturns.result1, fakeReturns.result2 -} - -func (fake *PeerChaincodeStream) RecvCallCount() int { - fake.recvMutex.RLock() - defer fake.recvMutex.RUnlock() - return len(fake.recvArgsForCall) -} - -func (fake *PeerChaincodeStream) RecvCalls(stub func() (*peer.ChaincodeMessage, error)) { - fake.recvMutex.Lock() - defer fake.recvMutex.Unlock() - fake.RecvStub = stub -} - -func (fake *PeerChaincodeStream) RecvReturns(result1 *peer.ChaincodeMessage, result2 error) { - fake.recvMutex.Lock() - defer fake.recvMutex.Unlock() - fake.RecvStub = nil - fake.recvReturns = struct { - result1 *peer.ChaincodeMessage - result2 error - }{result1, result2} -} - -func (fake *PeerChaincodeStream) RecvReturnsOnCall(i int, result1 *peer.ChaincodeMessage, result2 error) { - fake.recvMutex.Lock() - defer fake.recvMutex.Unlock() - fake.RecvStub = nil - if fake.recvReturnsOnCall == nil { - fake.recvReturnsOnCall = make(map[int]struct { - result1 *peer.ChaincodeMessage - result2 error - }) - } - fake.recvReturnsOnCall[i] = struct { - result1 *peer.ChaincodeMessage - result2 error - }{result1, result2} -} - -func (fake *PeerChaincodeStream) Send(arg1 *peer.ChaincodeMessage) error { - fake.sendMutex.Lock() - ret, specificReturn := fake.sendReturnsOnCall[len(fake.sendArgsForCall)] - fake.sendArgsForCall = append(fake.sendArgsForCall, struct { - arg1 *peer.ChaincodeMessage - }{arg1}) - stub := fake.SendStub - fakeReturns := fake.sendReturns - fake.recordInvocation("Send", []interface{}{arg1}) - fake.sendMutex.Unlock() - if stub != nil { - return stub(arg1) - } - if specificReturn { - return ret.result1 - } - return fakeReturns.result1 -} - -func (fake *PeerChaincodeStream) SendCallCount() int { - fake.sendMutex.RLock() - defer fake.sendMutex.RUnlock() - return len(fake.sendArgsForCall) -} - -func (fake *PeerChaincodeStream) SendCalls(stub func(*peer.ChaincodeMessage) error) { - fake.sendMutex.Lock() - defer fake.sendMutex.Unlock() - fake.SendStub = stub -} - -func (fake *PeerChaincodeStream) SendArgsForCall(i int) *peer.ChaincodeMessage { - fake.sendMutex.RLock() - defer fake.sendMutex.RUnlock() - argsForCall := fake.sendArgsForCall[i] - return argsForCall.arg1 -} - -func (fake *PeerChaincodeStream) SendReturns(result1 error) { - fake.sendMutex.Lock() - defer fake.sendMutex.Unlock() - fake.SendStub = nil - fake.sendReturns = struct { - result1 error - }{result1} -} - -func (fake *PeerChaincodeStream) SendReturnsOnCall(i int, result1 error) { - fake.sendMutex.Lock() - defer fake.sendMutex.Unlock() - fake.SendStub = nil - if fake.sendReturnsOnCall == nil { - fake.sendReturnsOnCall = make(map[int]struct { - result1 error - }) - } - fake.sendReturnsOnCall[i] = struct { - result1 error - }{result1} -} - -func (fake *PeerChaincodeStream) Invocations() map[string][][]interface{} { - fake.invocationsMutex.RLock() - defer fake.invocationsMutex.RUnlock() - fake.recvMutex.RLock() - defer fake.recvMutex.RUnlock() - fake.sendMutex.RLock() - defer fake.sendMutex.RUnlock() - copiedInvocations := map[string][][]interface{}{} - for key, value := range fake.invocations { - copiedInvocations[key] = value - } - return copiedInvocations -} - -func (fake *PeerChaincodeStream) recordInvocation(key string, args []interface{}) { - fake.invocationsMutex.Lock() - defer fake.invocationsMutex.Unlock() - if fake.invocations == nil { - fake.invocations = map[string][][]interface{}{} - } - if fake.invocations[key] == nil { - fake.invocations[key] = [][]interface{}{} - } - fake.invocations[key] = append(fake.invocations[key], args) -} diff --git a/v2/shim/internal/server.go b/v2/shim/internal/server.go deleted file mode 100644 index 89a31fc1..00000000 --- a/v2/shim/internal/server.go +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright the Hyperledger Fabric contributors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package internal - -import ( - "crypto/tls" - "errors" - "net" - "time" - - "google.golang.org/grpc" - "google.golang.org/grpc/credentials" - "google.golang.org/grpc/keepalive" -) - -const ( - serverInterval = time.Duration(2) * time.Hour // 2 hours - gRPC default - serverTimeout = time.Duration(20) * time.Second // 20 sec - gRPC default - serverMinInterval = time.Duration(1) * time.Minute - connectionTimeout = 5 * time.Second -) - -// Server abstracts grpc service properties -type Server struct { - Listener net.Listener - Server *grpc.Server -} - -// Start the server -func (s *Server) Start() error { - if s.Listener == nil { - return errors.New("nil listener") - } - - if s.Server == nil { - return errors.New("nil server") - } - - return s.Server.Serve(s.Listener) -} - -// Stop the server -func (s *Server) Stop() { - if s.Server != nil { - s.Server.Stop() - } -} - -// NewServer creates a new implementation of a GRPC Server given a -// listen address -func NewServer( - address string, - tlsConf *tls.Config, - srvKaOpts *keepalive.ServerParameters, -) (*Server, error) { - if address == "" { - return nil, errors.New("server listen address not provided") - } - - //create our listener - listener, err := net.Listen("tcp", address) - if err != nil { - return nil, err - } - - //set up server options for keepalive and TLS - var serverOpts []grpc.ServerOption - - if srvKaOpts != nil { - serverOpts = append(serverOpts, grpc.KeepaliveParams(*srvKaOpts)) - } else { - serverKeepAliveParameters := keepalive.ServerParameters{ - Time: 1 * time.Minute, - Timeout: 20 * time.Second, - } - serverOpts = append(serverOpts, grpc.KeepaliveParams(serverKeepAliveParameters)) - } - - if tlsConf != nil { - serverOpts = append(serverOpts, grpc.Creds(credentials.NewTLS(tlsConf))) - } - - // Default properties follow - let's start simple and stick with defaults for now. - // These match Fabric peer side properties. We can expose these as user properties - // if needed - - // set max send and recv msg sizes - serverOpts = append(serverOpts, grpc.MaxSendMsgSize(maxSendMessageSize)) - serverOpts = append(serverOpts, grpc.MaxRecvMsgSize(maxRecvMessageSize)) - - //set enforcement policy - kep := keepalive.EnforcementPolicy{ - MinTime: serverMinInterval, - // allow keepalive w/o rpc - PermitWithoutStream: true, - } - serverOpts = append(serverOpts, grpc.KeepaliveEnforcementPolicy(kep)) - - //set default connection timeout - serverOpts = append(serverOpts, grpc.ConnectionTimeout(connectionTimeout)) - - server := grpc.NewServer(serverOpts...) - - return &Server{Listener: listener, Server: server}, nil -} diff --git a/v2/shim/internal/server_test.go b/v2/shim/internal/server_test.go deleted file mode 100644 index 03d0e855..00000000 --- a/v2/shim/internal/server_test.go +++ /dev/null @@ -1,59 +0,0 @@ -/* -Copyright State Street Corp. All Rights Reserved. - -SPDX-License-Identifier: Apache-2.0 -*/ - -package internal_test - -import ( - "net" - "testing" - "time" - - "github.com/hyperledger/fabric-chaincode-go/v2/shim/internal" - - "github.com/stretchr/testify/assert" - "google.golang.org/grpc/keepalive" -) - -func TestBadServer(t *testing.T) { - srv := &internal.Server{} - err := srv.Start() - assert.NotNil(t, err) - assert.Contains(t, err.Error(), "nil listener") - - l, err := net.Listen("tcp", ":0") // #nosec G102 - assert.NotNil(t, l) - assert.Nil(t, err) - srv = &internal.Server{Listener: l} - err = srv.Start() - assert.NotNil(t, err) - assert.Contains(t, err.Error(), "nil server") -} - -func TestServerAddressNotProvided(t *testing.T) { - kaOpts := &keepalive.ServerParameters{ - Time: 1 * time.Minute, - Timeout: 20 * time.Second, - } - srv, err := internal.NewServer("", nil, kaOpts) - assert.Nil(t, srv) - assert.NotNil(t, err, "server listen address not provided") -} - -func TestBadServerAddress(t *testing.T) { - kaOpts := &keepalive.ServerParameters{ - Time: 1 * time.Minute, - Timeout: 20 * time.Second, - } - srv, err := internal.NewServer("__badhost__:0", nil, kaOpts) - assert.Nil(t, srv) - assert.NotNil(t, err) - assert.Contains(t, err.Error(), "listen tcp: lookup __badhost__") - - srv, err = internal.NewServer("host", nil, kaOpts) - assert.Nil(t, srv) - assert.NotNil(t, err) - assert.Contains(t, err.Error(), "listen tcp: address host: missing port in address") -} diff --git a/v2/shim/response.go b/v2/shim/response.go deleted file mode 100644 index f56b4f68..00000000 --- a/v2/shim/response.go +++ /dev/null @@ -1,36 +0,0 @@ -// Copyright the Hyperledger Fabric contributors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package shim - -import ( - "github.com/hyperledger/fabric-protos-go-apiv2/peer" -) - -const ( - // OK constant - status code less than 400, endorser will endorse it. - // OK means init or invoke successfully. - OK = 200 - - // ERRORTHRESHOLD constant - status code greater than or equal to 400 will be considered an error and rejected by endorser. - ERRORTHRESHOLD = 400 - - // ERROR constant - default error value - ERROR = 500 -) - -// Success ... -func Success(payload []byte) *peer.Response { - return &peer.Response{ - Status: OK, - Payload: payload, - } -} - -// Error ... -func Error(msg string) *peer.Response { - return &peer.Response{ - Status: ERROR, - Message: msg, - } -} diff --git a/v2/shim/shim.go b/v2/shim/shim.go deleted file mode 100644 index f75839d8..00000000 --- a/v2/shim/shim.go +++ /dev/null @@ -1,153 +0,0 @@ -// Copyright the Hyperledger Fabric contributors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -// Package shim provides APIs for the chaincode to access its state -// variables, transaction context and call other chaincodes. -package shim - -import ( - "errors" - "flag" - "fmt" - "io" - "os" - "unicode/utf8" - - "github.com/hyperledger/fabric-chaincode-go/v2/shim/internal" - "github.com/hyperledger/fabric-protos-go-apiv2/peer" - "google.golang.org/protobuf/proto" -) - -const ( - minUnicodeRuneValue = 0 //U+0000 - maxUnicodeRuneValue = utf8.MaxRune //U+10FFFF - maximum (and unallocated) code point - compositeKeyNamespace = "\x00" - emptyKeySubstitute = "\x01" -) - -// peer as server -var peerAddress = flag.String("peer.address", "", "peer address") - -// this separates the chaincode stream interface establishment -// so we can replace it with a mock peer stream -type peerStreamGetter func(name string) (ClientStream, error) - -// UTs to setup mock peer stream getter -var streamGetter peerStreamGetter - -// the non-mock user CC stream establishment func -func userChaincodeStreamGetter(name string) (ClientStream, error) { - if *peerAddress == "" { - return nil, errors.New("flag 'peer.address' must be set") - } - - conf, err := internal.LoadConfig() - if err != nil { - return nil, err - } - - conn, err := internal.NewClientConn(*peerAddress, conf.TLS, conf.KaOpts) - if err != nil { - return nil, err - } - - return internal.NewRegisterClient(conn) -} - -// Start chaincodes -func Start(cc Chaincode) error { - flag.Parse() - chaincodename := os.Getenv("CORE_CHAINCODE_ID_NAME") - if chaincodename == "" { - return errors.New("'CORE_CHAINCODE_ID_NAME' must be set") - } - - //mock stream not set up ... get real stream - if streamGetter == nil { - streamGetter = userChaincodeStreamGetter - } - - stream, err := streamGetter(chaincodename) - if err != nil { - return err - } - - err = chaincodeAsClientChat(chaincodename, stream, cc) - - return err -} - -// StartInProc is an entry point for system chaincodes bootstrap. It is not an -// API for chaincodes. -func StartInProc(chaincodename string, stream ClientStream, cc Chaincode) error { - return chaincodeAsClientChat(chaincodename, stream, cc) -} - -// this is the chat stream resulting from the chaincode-as-client model where the chaincode initiates connection -func chaincodeAsClientChat(chaincodename string, stream ClientStream, cc Chaincode) error { - defer stream.CloseSend() //nolint:Errcheck - return chatWithPeer(chaincodename, stream, cc) -} - -// chat stream for peer-chaincode interactions post connection -func chatWithPeer(chaincodename string, stream PeerChaincodeStream, cc Chaincode) error { - // Create the shim handler responsible for all control logic - handler := newChaincodeHandler(stream, cc) - - // Send the ChaincodeID during register. - chaincodeID := &peer.ChaincodeID{Name: chaincodename} - payload, err := proto.Marshal(chaincodeID) - if err != nil { - return fmt.Errorf("error marshalling chaincodeID during chaincode registration: %s", err) - } - - // Register on the stream - if err = handler.serialSend(&peer.ChaincodeMessage{Type: peer.ChaincodeMessage_REGISTER, Payload: payload}); err != nil { - return fmt.Errorf("error sending chaincode REGISTER: %s", err) - - } - - // holds return values from gRPC Recv below - type recvMsg struct { - msg *peer.ChaincodeMessage - err error - } - msgAvail := make(chan *recvMsg, 1) - errc := make(chan error) - - receiveMessage := func() { - in, err := stream.Recv() - msgAvail <- &recvMsg{in, err} - } - - go receiveMessage() - for { - select { - case rmsg := <-msgAvail: - switch { - case rmsg.err == io.EOF: - return errors.New("received EOF, ending chaincode stream") - case rmsg.err != nil: - err := fmt.Errorf("receive failed: %s", rmsg.err) - return err - case rmsg.msg == nil: - err := errors.New("received nil message, ending chaincode stream") - return err - default: - err := handler.handleMessage(rmsg.msg, errc) - if err != nil { - err = fmt.Errorf("error handling message: %s", err) - return err - } - - go receiveMessage() - } - - case sendErr := <-errc: - if sendErr != nil { - err := fmt.Errorf("error sending: %s", sendErr) - return err - } - } - } -} diff --git a/v2/shim/shim_test.go b/v2/shim/shim_test.go deleted file mode 100644 index d64c1bc9..00000000 --- a/v2/shim/shim_test.go +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright the Hyperledger Fabric contributors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package shim - -import ( - "errors" - "io" - "os" - "testing" - - "github.com/hyperledger/fabric-chaincode-go/v2/shim/internal/mock" - "github.com/hyperledger/fabric-protos-go-apiv2/peer" - - "github.com/stretchr/testify/assert" -) - -// MockQueryIteratorInterface allows a chaincode to iterate over a set of -// key/value pairs returned by range query. -// TODO: Once the execute query and history query are implemented in MockStub, -// we need to update this interface -type MockQueryIteratorInterface interface { - StateQueryIteratorInterface -} - -func TestStart(t *testing.T) { - - var tests = []struct { - name string - envVars map[string]string - peerAddress string - chaincodeAddress string - streamGetter func(name string) (ClientStream, error) - cc Chaincode - expectedErr string - }{ - { - name: "Missing Chaincode ID", - expectedErr: "'CORE_CHAINCODE_ID_NAME' must be set", - }, - { - name: "Missing Peer Address", - envVars: map[string]string{ - "CORE_CHAINCODE_ID_NAME": "cc", - }, - expectedErr: "flag 'peer.address' must be set", - }, - { - name: "TLS Not Set", - envVars: map[string]string{ - "CORE_CHAINCODE_ID_NAME": "cc", - }, - peerAddress: "127.0.0.1:12345", - expectedErr: "'CORE_PEER_TLS_ENABLED' must be set to 'true' or 'false'", - }, - { - name: "Connection Error", - envVars: map[string]string{ - "CORE_CHAINCODE_ID_NAME": "cc", - "CORE_PEER_TLS_ENABLED": "false", - }, - peerAddress: "127.0.0.1:12345", - expectedErr: `rpc error: code = Unavailable desc = connection error: desc = "transport: Error while dialing: dial tcp 127.0.0.1:12345: connect: connection refused"`, - }, - { - name: "Chat - Nil Message", - envVars: map[string]string{ - "CORE_CHAINCODE_ID_NAME": "cc", - "CORE_PEER_TLS_ENABLED": "false", - }, - peerAddress: "127.0.0.1:12345", - streamGetter: func(name string) (ClientStream, error) { - stream := &mock.ClientStream{} - return stream, nil - }, - expectedErr: "received nil message, ending chaincode stream", - }, - { - name: "Chat - EOF", - envVars: map[string]string{ - "CORE_CHAINCODE_ID_NAME": "cc", - "CORE_PEER_TLS_ENABLED": "false", - }, - peerAddress: "127.0.0.1:12345", - streamGetter: func(name string) (ClientStream, error) { - stream := &mock.ClientStream{} - stream.RecvReturns(nil, io.EOF) - return stream, nil - }, - expectedErr: "received EOF, ending chaincode stream", - }, - { - name: "Chat - Recv Error", - envVars: map[string]string{ - "CORE_CHAINCODE_ID_NAME": "cc", - "CORE_PEER_TLS_ENABLED": "false", - }, - peerAddress: "127.0.0.1:12345", - streamGetter: func(name string) (ClientStream, error) { - stream := &mock.ClientStream{} - stream.RecvReturns(nil, errors.New("recvError")) - return stream, nil - }, - expectedErr: "receive failed: recvError", - }, - { - name: "Chat - Not Ready", - envVars: map[string]string{ - "CORE_CHAINCODE_ID_NAME": "cc", - "CORE_PEER_TLS_ENABLED": "false", - }, - peerAddress: "127.0.0.1:12345", - streamGetter: func(name string) (ClientStream, error) { - stream := &mock.ClientStream{} - stream.RecvReturnsOnCall( - 0, - &peer.ChaincodeMessage{ - Type: peer.ChaincodeMessage_READY, - Txid: "txid", - }, - nil, - ) - return stream, nil - }, - expectedErr: "error handling message: [txid] Chaincode h cannot handle message (READY) while in state: created", - }, - } - - for _, test := range tests { - test := test - t.Run(test.name, func(t *testing.T) { - for k, v := range test.envVars { - os.Setenv(k, v) - defer os.Unsetenv(k) - } - peerAddress = &test.peerAddress - streamGetter = test.streamGetter - err := Start(test.cc) - assert.EqualError(t, err, test.expectedErr) - }) - } - -} - -func TestChaincodeServerStart(t *testing.T) { - - var tests = []struct { - name string - ccsrv ChaincodeServer - streamGetter func(name string) (ClientStream, error) - expectedErr string - containsErr string - }{ - { - name: "Missing Chaincode ID", - ccsrv: ChaincodeServer{}, - expectedErr: "ccid must be specified", - }, - { - name: "Missing Peer Address", - ccsrv: ChaincodeServer{CCID: "cc"}, - expectedErr: "address must be specified", - }, - { - name: "Missing Peer Address and Chaincode Address", - ccsrv: ChaincodeServer{CCID: "cc", Address: "127.0.0.1:12345"}, - expectedErr: "chaincode must be specified", - }, - { - name: "Badly formed chaincode server address", - ccsrv: ChaincodeServer{CCID: "cc", Address: "127.0.0.1", CC: &mockChaincode{}, TLSProps: TLSProperties{Disabled: true}}, - expectedErr: "listen tcp: address 127.0.0.1: missing port in address", - }, - { - name: "Bad host in chaincode server address", - ccsrv: ChaincodeServer{CCID: "cc", Address: "__badhost__:12345", CC: &mockChaincode{}, TLSProps: TLSProperties{Disabled: true}}, - containsErr: "listen tcp: lookup __badhost__", - }, - // Basic TLS tests, path tests - { - name: "TLS enabled but key path not provided", - ccsrv: ChaincodeServer{CCID: "cc", Address: "host:12345", CC: &mockChaincode{}, TLSProps: TLSProperties{Disabled: false}}, - containsErr: "key not provided", - }, - { - name: "TLS enabled but cert path not provided", - ccsrv: ChaincodeServer{CCID: "cc", Address: "host:12345", CC: &mockChaincode{}, TLSProps: TLSProperties{Disabled: false, Key: []byte("key")}}, - containsErr: "cert not provided", - }, - } - - for _, test := range tests { - test := test - t.Run(test.name, func(t *testing.T) { - err := test.ccsrv.Start() - if test.expectedErr != "" { - assert.EqualError(t, err, test.expectedErr) - } else if test.containsErr != "" { - assert.Contains(t, err.Error(), test.containsErr) - } - }) - } - -} diff --git a/v2/shim/stub.go b/v2/shim/stub.go deleted file mode 100644 index 4e3fbe7a..00000000 --- a/v2/shim/stub.go +++ /dev/null @@ -1,759 +0,0 @@ -// Copyright the Hyperledger Fabric contributors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package shim - -import ( - "crypto/sha256" - "encoding/binary" - "errors" - "fmt" - "os" - "unicode/utf8" - - "github.com/hyperledger/fabric-protos-go-apiv2/common" - "github.com/hyperledger/fabric-protos-go-apiv2/ledger/queryresult" - "github.com/hyperledger/fabric-protos-go-apiv2/peer" - "google.golang.org/protobuf/proto" - "google.golang.org/protobuf/types/known/timestamppb" -) - -// ChaincodeStub is an object passed to chaincode for shim side handling of -// APIs. -type ChaincodeStub struct { - TxID string - ChannelID string - chaincodeEvent *peer.ChaincodeEvent - args [][]byte - handler *Handler - signedProposal *peer.SignedProposal - proposal *peer.Proposal - validationParameterMetakey string - - // Additional fields extracted from the signedProposal - creator []byte - transient map[string][]byte - binding []byte - - decorations map[string][]byte -} - -// ChaincodeInvocation functionality - -func newChaincodeStub(handler *Handler, channelID, txid string, input *peer.ChaincodeInput, signedProposal *peer.SignedProposal) (*ChaincodeStub, error) { - stub := &ChaincodeStub{ - TxID: txid, - ChannelID: channelID, - args: input.Args, - handler: handler, - signedProposal: signedProposal, - decorations: input.Decorations, - validationParameterMetakey: peer.MetaDataKeys_VALIDATION_PARAMETER.String(), - } - - // TODO: sanity check: verify that every call to init with a nil - // signedProposal is a legitimate one, meaning it is an internal call - // to system chaincodes. - if signedProposal != nil { - var err error - - stub.proposal = &peer.Proposal{} - err = proto.Unmarshal(signedProposal.ProposalBytes, stub.proposal) - if err != nil { - - return nil, fmt.Errorf("failed to extract Proposal from SignedProposal: %s", err) - } - - // check for header - if len(stub.proposal.GetHeader()) == 0 { - return nil, errors.New("failed to extract Proposal fields: proposal header is nil") - } - - // Extract creator, transient, binding... - hdr := &common.Header{} - if err := proto.Unmarshal(stub.proposal.GetHeader(), hdr); err != nil { - return nil, fmt.Errorf("failed to extract proposal header: %s", err) - } - - // extract and validate channel header - chdr := &common.ChannelHeader{} - if err := proto.Unmarshal(hdr.ChannelHeader, chdr); err != nil { - return nil, fmt.Errorf("failed to extract channel header: %s", err) - } - validTypes := map[common.HeaderType]bool{ - common.HeaderType_ENDORSER_TRANSACTION: true, - common.HeaderType_CONFIG: true, - } - if !validTypes[common.HeaderType(chdr.GetType())] { - return nil, fmt.Errorf( - "invalid channel header type. Expected %s or %s, received %s", - common.HeaderType_ENDORSER_TRANSACTION, - common.HeaderType_CONFIG, - common.HeaderType(chdr.GetType()), - ) - } - - // extract creator from signature header - shdr := &common.SignatureHeader{} - if err := proto.Unmarshal(hdr.GetSignatureHeader(), shdr); err != nil { - return nil, fmt.Errorf("failed to extract signature header: %s", err) - } - stub.creator = shdr.GetCreator() - - // extract trasient data from proposal payload - payload := &peer.ChaincodeProposalPayload{} - if err := proto.Unmarshal(stub.proposal.GetPayload(), payload); err != nil { - return nil, fmt.Errorf("failed to extract proposal payload: %s", err) - } - stub.transient = payload.GetTransientMap() - - // compute the proposal binding from the nonce, creator and epoch - epoch := make([]byte, 8) - binary.LittleEndian.PutUint64(epoch, chdr.GetEpoch()) - digest := sha256.Sum256(append(append(shdr.GetNonce(), stub.creator...), epoch...)) - stub.binding = digest[:] - - } - - return stub, nil -} - -// GetTxID returns the transaction ID for the proposal -func (s *ChaincodeStub) GetTxID() string { - return s.TxID -} - -// GetChannelID returns the channel for the proposal -func (s *ChaincodeStub) GetChannelID() string { - return s.ChannelID -} - -// GetDecorations ... -func (s *ChaincodeStub) GetDecorations() map[string][]byte { - return s.decorations -} - -// GetMSPID returns the local mspid of the peer by checking the CORE_PEER_LOCALMSPID -// env var and returns an error if the env var is not set -func GetMSPID() (string, error) { - mspid := os.Getenv("CORE_PEER_LOCALMSPID") - - if mspid == "" { - return "", errors.New("'CORE_PEER_LOCALMSPID' is not set") - } - - return mspid, nil -} - -// ------------- Call Chaincode functions --------------- - -// InvokeChaincode documentation can be found in interfaces.go -func (s *ChaincodeStub) InvokeChaincode(chaincodeName string, args [][]byte, channel string) *peer.Response { - // Internally we handle chaincode name as a composite name - if channel != "" { - chaincodeName = chaincodeName + "/" + channel - } - return s.handler.handleInvokeChaincode(chaincodeName, args, s.ChannelID, s.TxID) -} - -// --------- State functions ---------- - -// GetState documentation can be found in interfaces.go -func (s *ChaincodeStub) GetState(key string) ([]byte, error) { - // Access public data by setting the collection to empty string - collection := "" - return s.handler.handleGetState(collection, key, s.ChannelID, s.TxID) -} - -// SetStateValidationParameter documentation can be found in interfaces.go -func (s *ChaincodeStub) SetStateValidationParameter(key string, ep []byte) error { - return s.handler.handlePutStateMetadataEntry("", key, s.validationParameterMetakey, ep, s.ChannelID, s.TxID) -} - -// GetStateValidationParameter documentation can be found in interfaces.go -func (s *ChaincodeStub) GetStateValidationParameter(key string) ([]byte, error) { - md, err := s.handler.handleGetStateMetadata("", key, s.ChannelID, s.TxID) - if err != nil { - return nil, err - } - if ep, ok := md[s.validationParameterMetakey]; ok { - return ep, nil - } - return nil, nil -} - -// PutState documentation can be found in interfaces.go -func (s *ChaincodeStub) PutState(key string, value []byte) error { - if key == "" { - return errors.New("key must not be an empty string") - } - // Access public data by setting the collection to empty string - collection := "" - return s.handler.handlePutState(collection, key, value, s.ChannelID, s.TxID) -} - -func (s *ChaincodeStub) createStateQueryIterator(response *peer.QueryResponse) *StateQueryIterator { - return &StateQueryIterator{ - CommonIterator: &CommonIterator{ - handler: s.handler, - channelID: s.ChannelID, - txid: s.TxID, - response: response, - currentLoc: 0, - }, - } -} - -// GetQueryResult documentation can be found in interfaces.go -func (s *ChaincodeStub) GetQueryResult(query string) (StateQueryIteratorInterface, error) { - // Access public data by setting the collection to empty string - collection := "" - // ignore QueryResponseMetadata as it is not applicable for a rich query without pagination - iterator, _, err := s.handleGetQueryResult(collection, query, nil) - - return iterator, err -} - -// DelState documentation can be found in interfaces.go -func (s *ChaincodeStub) DelState(key string) error { - // Access public data by setting the collection to empty string - collection := "" - return s.handler.handleDelState(collection, key, s.ChannelID, s.TxID) -} - -// --------- private state functions --------- - -// GetPrivateData documentation can be found in interfaces.go -func (s *ChaincodeStub) GetPrivateData(collection string, key string) ([]byte, error) { - if collection == "" { - return nil, fmt.Errorf("collection must not be an empty string") - } - return s.handler.handleGetState(collection, key, s.ChannelID, s.TxID) -} - -// GetPrivateDataHash documentation can be found in interfaces.go -func (s *ChaincodeStub) GetPrivateDataHash(collection string, key string) ([]byte, error) { - if collection == "" { - return nil, fmt.Errorf("collection must not be an empty string") - } - return s.handler.handleGetPrivateDataHash(collection, key, s.ChannelID, s.TxID) -} - -// PutPrivateData documentation can be found in interfaces.go -func (s *ChaincodeStub) PutPrivateData(collection string, key string, value []byte) error { - if collection == "" { - return fmt.Errorf("collection must not be an empty string") - } - if key == "" { - return fmt.Errorf("key must not be an empty string") - } - return s.handler.handlePutState(collection, key, value, s.ChannelID, s.TxID) -} - -// DelPrivateData documentation can be found in interfaces.go -func (s *ChaincodeStub) DelPrivateData(collection string, key string) error { - if collection == "" { - return fmt.Errorf("collection must not be an empty string") - } - return s.handler.handleDelState(collection, key, s.ChannelID, s.TxID) -} - -// PurgePrivateData documentation can be found in interfaces.go -func (s *ChaincodeStub) PurgePrivateData(collection string, key string) error { - if collection == "" { - return fmt.Errorf("collection must not be an empty string") - } - return s.handler.handlePurgeState(collection, key, s.ChannelID, s.TxID) -} - -// GetPrivateDataByRange documentation can be found in interfaces.go -func (s *ChaincodeStub) GetPrivateDataByRange(collection, startKey, endKey string) (StateQueryIteratorInterface, error) { - if collection == "" { - return nil, fmt.Errorf("collection must not be an empty string") - } - if startKey == "" { - startKey = emptyKeySubstitute - } - if err := validateSimpleKeys(startKey, endKey); err != nil { - return nil, err - } - // ignore QueryResponseMetadata as it is not applicable for a range query without pagination - iterator, _, err := s.handleGetStateByRange(collection, startKey, endKey, nil) - - return iterator, err -} - -func (s *ChaincodeStub) createRangeKeysForPartialCompositeKey(objectType string, attributes []string) (string, string, error) { - partialCompositeKey, err := s.CreateCompositeKey(objectType, attributes) - if err != nil { - return "", "", err - } - startKey := partialCompositeKey - endKey := partialCompositeKey + string(maxUnicodeRuneValue) - - return startKey, endKey, nil -} - -// GetPrivateDataByPartialCompositeKey documentation can be found in interfaces.go -func (s *ChaincodeStub) GetPrivateDataByPartialCompositeKey(collection, objectType string, attributes []string) (StateQueryIteratorInterface, error) { - if collection == "" { - return nil, fmt.Errorf("collection must not be an empty string") - } - - startKey, endKey, err := s.createRangeKeysForPartialCompositeKey(objectType, attributes) - if err != nil { - return nil, err - } - // ignore QueryResponseMetadata as it is not applicable for a partial composite key query without pagination - iterator, _, err := s.handleGetStateByRange(collection, startKey, endKey, nil) - - return iterator, err -} - -// GetPrivateDataQueryResult documentation can be found in interfaces.go -func (s *ChaincodeStub) GetPrivateDataQueryResult(collection, query string) (StateQueryIteratorInterface, error) { - if collection == "" { - return nil, fmt.Errorf("collection must not be an empty string") - } - // ignore QueryResponseMetadata as it is not applicable for a range query without pagination - iterator, _, err := s.handleGetQueryResult(collection, query, nil) - - return iterator, err -} - -// GetPrivateDataValidationParameter documentation can be found in interfaces.go -func (s *ChaincodeStub) GetPrivateDataValidationParameter(collection, key string) ([]byte, error) { - md, err := s.handler.handleGetStateMetadata(collection, key, s.ChannelID, s.TxID) - if err != nil { - return nil, err - } - if ep, ok := md[s.validationParameterMetakey]; ok { - return ep, nil - } - return nil, nil -} - -// SetPrivateDataValidationParameter documentation can be found in interfaces.go -func (s *ChaincodeStub) SetPrivateDataValidationParameter(collection, key string, ep []byte) error { - return s.handler.handlePutStateMetadataEntry(collection, key, s.validationParameterMetakey, ep, s.ChannelID, s.TxID) -} - -// CommonIterator documentation can be found in interfaces.go -type CommonIterator struct { - handler *Handler - channelID string - txid string - response *peer.QueryResponse - currentLoc int -} - -// StateQueryIterator documentation can be found in interfaces.go -type StateQueryIterator struct { - *CommonIterator -} - -// HistoryQueryIterator documentation can be found in interfaces.go -type HistoryQueryIterator struct { - *CommonIterator -} - -// General interface for supporting different types of query results. -// Actual types differ for different queries -type queryResult interface{} - -type resultType uint8 - -// TODO: Document constants -/* - Constants ... -*/ -const ( - StateQueryResult resultType = iota + 1 - HistoryQueryResult -) - -func createQueryResponseMetadata(metadataBytes []byte) (*peer.QueryResponseMetadata, error) { - metadata := &peer.QueryResponseMetadata{} - err := proto.Unmarshal(metadataBytes, metadata) - if err != nil { - return nil, err - } - - return metadata, nil -} - -func (s *ChaincodeStub) handleGetStateByRange(collection, startKey, endKey string, - metadata []byte) (StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) { - - response, err := s.handler.handleGetStateByRange(collection, startKey, endKey, metadata, s.ChannelID, s.TxID) - if err != nil { - return nil, nil, err - } - - iterator := s.createStateQueryIterator(response) - responseMetadata, err := createQueryResponseMetadata(response.Metadata) - if err != nil { - return nil, nil, err - } - - return iterator, responseMetadata, nil -} - -func (s *ChaincodeStub) handleGetQueryResult(collection, query string, - metadata []byte) (StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) { - - response, err := s.handler.handleGetQueryResult(collection, query, metadata, s.ChannelID, s.TxID) - if err != nil { - return nil, nil, err - } - - iterator := s.createStateQueryIterator(response) - responseMetadata, err := createQueryResponseMetadata(response.Metadata) - if err != nil { - return nil, nil, err - } - - return iterator, responseMetadata, nil -} - -// GetStateByRange documentation can be found in interfaces.go -func (s *ChaincodeStub) GetStateByRange(startKey, endKey string) (StateQueryIteratorInterface, error) { - if startKey == "" { - startKey = emptyKeySubstitute - } - if err := validateSimpleKeys(startKey, endKey); err != nil { - return nil, err - } - collection := "" - - // ignore QueryResponseMetadata as it is not applicable for a range query without pagination - iterator, _, err := s.handleGetStateByRange(collection, startKey, endKey, nil) - - return iterator, err -} - -// GetHistoryForKey documentation can be found in interfaces.go -func (s *ChaincodeStub) GetHistoryForKey(key string) (HistoryQueryIteratorInterface, error) { - response, err := s.handler.handleGetHistoryForKey(key, s.ChannelID, s.TxID) - if err != nil { - return nil, err - } - return &HistoryQueryIterator{CommonIterator: &CommonIterator{s.handler, s.ChannelID, s.TxID, response, 0}}, nil -} - -// CreateCompositeKey documentation can be found in interfaces.go -func (s *ChaincodeStub) CreateCompositeKey(objectType string, attributes []string) (string, error) { - return CreateCompositeKey(objectType, attributes) -} - -// SplitCompositeKey documentation can be found in interfaces.go -func (s *ChaincodeStub) SplitCompositeKey(compositeKey string) (string, []string, error) { - return splitCompositeKey(compositeKey) -} - -// CreateCompositeKey ... -func CreateCompositeKey(objectType string, attributes []string) (string, error) { - if err := validateCompositeKeyAttribute(objectType); err != nil { - return "", err - } - ck := compositeKeyNamespace + objectType + string(rune(minUnicodeRuneValue)) - for _, att := range attributes { - if err := validateCompositeKeyAttribute(att); err != nil { - return "", err - } - ck += att + string(rune(minUnicodeRuneValue)) - } - return ck, nil -} - -func splitCompositeKey(compositeKey string) (string, []string, error) { - componentIndex := 1 - components := []string{} - for i := 1; i < len(compositeKey); i++ { - if compositeKey[i] == minUnicodeRuneValue { - components = append(components, compositeKey[componentIndex:i]) - componentIndex = i + 1 - } - } - return components[0], components[1:], nil -} - -func validateCompositeKeyAttribute(str string) error { - if !utf8.ValidString(str) { - return fmt.Errorf("not a valid utf8 string: [%x]", str) - } - for index, runeValue := range str { - if runeValue == minUnicodeRuneValue || runeValue == maxUnicodeRuneValue { - return fmt.Errorf(`input contains unicode %#U starting at position [%d]. %#U and %#U are not allowed in the input attribute of a composite key`, - runeValue, index, minUnicodeRuneValue, maxUnicodeRuneValue) - } - } - return nil -} - -// To ensure that simple keys do not go into composite key namespace, -// we validate simplekey to check whether the key starts with 0x00 (which -// is the namespace for compositeKey). This helps in avoding simple/composite -// key collisions. -func validateSimpleKeys(simpleKeys ...string) error { - for _, key := range simpleKeys { - if len(key) > 0 && key[0] == compositeKeyNamespace[0] { - return fmt.Errorf(`first character of the key [%s] contains a null character which is not allowed`, key) - } - } - return nil -} - -// GetStateByPartialCompositeKey documentation can be found in interfaces.go -func (s *ChaincodeStub) GetStateByPartialCompositeKey(objectType string, attributes []string) (StateQueryIteratorInterface, error) { - collection := "" - startKey, endKey, err := s.createRangeKeysForPartialCompositeKey(objectType, attributes) - if err != nil { - return nil, err - } - // ignore QueryResponseMetadata as it is not applicable for a partial composite key query without pagination - iterator, _, err := s.handleGetStateByRange(collection, startKey, endKey, nil) - - return iterator, err -} - -func createQueryMetadata(pageSize int32, bookmark string) ([]byte, error) { - // Construct the QueryMetadata with a page size and a bookmark needed for pagination - metadata := &peer.QueryMetadata{PageSize: pageSize, Bookmark: bookmark} - metadataBytes, err := proto.Marshal(metadata) - if err != nil { - return nil, err - } - return metadataBytes, nil -} - -// GetStateByRangeWithPagination ... -func (s *ChaincodeStub) GetStateByRangeWithPagination(startKey, endKey string, pageSize int32, - bookmark string) (StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) { - - if startKey == "" { - startKey = emptyKeySubstitute - } - if err := validateSimpleKeys(startKey, endKey); err != nil { - return nil, nil, err - } - - collection := "" - - metadata, err := createQueryMetadata(pageSize, bookmark) - if err != nil { - return nil, nil, err - } - - return s.handleGetStateByRange(collection, startKey, endKey, metadata) -} - -// GetStateByPartialCompositeKeyWithPagination ... -func (s *ChaincodeStub) GetStateByPartialCompositeKeyWithPagination(objectType string, keys []string, - pageSize int32, bookmark string) (StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) { - - collection := "" - - metadata, err := createQueryMetadata(pageSize, bookmark) - if err != nil { - return nil, nil, err - } - - startKey, endKey, err := s.createRangeKeysForPartialCompositeKey(objectType, keys) - if err != nil { - return nil, nil, err - } - return s.handleGetStateByRange(collection, startKey, endKey, metadata) -} - -// GetQueryResultWithPagination ... -func (s *ChaincodeStub) GetQueryResultWithPagination(query string, pageSize int32, - bookmark string) (StateQueryIteratorInterface, *peer.QueryResponseMetadata, error) { - // Access public data by setting the collection to empty string - collection := "" - - metadata, err := createQueryMetadata(pageSize, bookmark) - if err != nil { - return nil, nil, err - } - return s.handleGetQueryResult(collection, query, metadata) -} - -// Next ... -func (iter *StateQueryIterator) Next() (*queryresult.KV, error) { - result, err := iter.nextResult(StateQueryResult) - if err != nil { - return nil, err - } - return result.(*queryresult.KV), err -} - -// Next ... -func (iter *HistoryQueryIterator) Next() (*queryresult.KeyModification, error) { - result, err := iter.nextResult(HistoryQueryResult) - if err != nil { - return nil, err - } - return result.(*queryresult.KeyModification), err -} - -// HasNext documentation can be found in interfaces.go -func (iter *CommonIterator) HasNext() bool { - if iter.currentLoc < len(iter.response.Results) || iter.response.HasMore { - return true - } - return false -} - -// getResultsFromBytes deserializes QueryResult and return either a KV struct -// or KeyModification depending on the result type (i.e., state (range/execute) -// query, history query). Note that queryResult is an empty golang -// interface that can hold values of any type. -func (iter *CommonIterator) getResultFromBytes(queryResultBytes *peer.QueryResultBytes, - rType resultType) (queryResult, error) { - - if rType == StateQueryResult { - stateQueryResult := &queryresult.KV{} - if err := proto.Unmarshal(queryResultBytes.ResultBytes, stateQueryResult); err != nil { - return nil, fmt.Errorf("error unmarshaling result from bytes: %s", err) - } - return stateQueryResult, nil - - } else if rType == HistoryQueryResult { - historyQueryResult := &queryresult.KeyModification{} - if err := proto.Unmarshal(queryResultBytes.ResultBytes, historyQueryResult); err != nil { - return nil, err - } - return historyQueryResult, nil - } - return nil, errors.New("wrong result type") -} - -func (iter *CommonIterator) fetchNextQueryResult() error { - response, err := iter.handler.handleQueryStateNext(iter.response.Id, iter.channelID, iter.txid) - if err != nil { - return err - } - iter.currentLoc = 0 - iter.response = response - return nil -} - -// nextResult returns the next QueryResult (i.e., either a KV struct or KeyModification) -// from the state or history query iterator. Note that queryResult is an -// empty golang interface that can hold values of any type. -func (iter *CommonIterator) nextResult(rType resultType) (queryResult, error) { - if iter.currentLoc < len(iter.response.Results) { - // On valid access of an element from cached results - queryResult, err := iter.getResultFromBytes(iter.response.Results[iter.currentLoc], rType) - if err != nil { - return nil, err - } - iter.currentLoc++ - - if iter.currentLoc == len(iter.response.Results) && iter.response.HasMore { - // On access of last item, pre-fetch to update HasMore flag - if err = iter.fetchNextQueryResult(); err != nil { - return nil, err - } - } - - return queryResult, err - } else if !iter.response.HasMore { - // On call to Next() without check of HasMore - return nil, errors.New("no such key") - } - - // should not fall through here - // case: no cached results but HasMore is true. - return nil, errors.New("invalid iterator state") -} - -// Close documentation can be found in interfaces.go -func (iter *CommonIterator) Close() error { - _, err := iter.handler.handleQueryStateClose(iter.response.Id, iter.channelID, iter.txid) - return err -} - -// GetArgs documentation can be found in interfaces.go -func (s *ChaincodeStub) GetArgs() [][]byte { - return s.args -} - -// GetStringArgs documentation can be found in interfaces.go -func (s *ChaincodeStub) GetStringArgs() []string { - args := s.GetArgs() - strargs := make([]string, 0, len(args)) - for _, barg := range args { - strargs = append(strargs, string(barg)) - } - return strargs -} - -// GetFunctionAndParameters documentation can be found in interfaces.go -func (s *ChaincodeStub) GetFunctionAndParameters() (function string, params []string) { - allargs := s.GetStringArgs() - function = "" - params = []string{} - if len(allargs) >= 1 { - function = allargs[0] - params = allargs[1:] - } - return -} - -// GetCreator documentation can be found in interfaces.go -func (s *ChaincodeStub) GetCreator() ([]byte, error) { - return s.creator, nil -} - -// GetTransient documentation can be found in interfaces.go -func (s *ChaincodeStub) GetTransient() (map[string][]byte, error) { - return s.transient, nil -} - -// GetBinding documentation can be found in interfaces.go -func (s *ChaincodeStub) GetBinding() ([]byte, error) { - return s.binding, nil -} - -// GetSignedProposal documentation can be found in interfaces.go -func (s *ChaincodeStub) GetSignedProposal() (*peer.SignedProposal, error) { - return s.signedProposal, nil -} - -// GetArgsSlice documentation can be found in interfaces.go -func (s *ChaincodeStub) GetArgsSlice() ([]byte, error) { - args := s.GetArgs() - res := []byte{} - for _, barg := range args { - res = append(res, barg...) - } - return res, nil -} - -// GetTxTimestamp documentation can be found in interfaces.go -func (s *ChaincodeStub) GetTxTimestamp() (*timestamppb.Timestamp, error) { - hdr := &common.Header{} - if err := proto.Unmarshal(s.proposal.Header, hdr); err != nil { - return nil, fmt.Errorf("error unmarshaling Header: %s", err) - } - - chdr := &common.ChannelHeader{} - if err := proto.Unmarshal(hdr.ChannelHeader, chdr); err != nil { - return nil, fmt.Errorf("error unmarshaling ChannelHeader: %s", err) - } - - return chdr.GetTimestamp(), nil -} - -// ------------- ChaincodeEvent API ---------------------- - -// SetEvent documentation can be found in interfaces.go -func (s *ChaincodeStub) SetEvent(name string, payload []byte) error { - if name == "" { - return errors.New("event name can not be empty string") - } - s.chaincodeEvent = &peer.ChaincodeEvent{EventName: name, Payload: payload} - return nil -} diff --git a/v2/shim/stub_test.go b/v2/shim/stub_test.go deleted file mode 100644 index 5f41d6e8..00000000 --- a/v2/shim/stub_test.go +++ /dev/null @@ -1,619 +0,0 @@ -// Copyright the Hyperledger Fabric contributors. All rights reserved. -// SPDX-License-Identifier: Apache-2.0 - -package shim - -import ( - "crypto/sha256" - "encoding/binary" - "os" - "testing" - - "github.com/hyperledger/fabric-chaincode-go/v2/shim/internal/mock" - "github.com/hyperledger/fabric-protos-go-apiv2/common" - "github.com/hyperledger/fabric-protos-go-apiv2/ledger/queryresult" - "github.com/hyperledger/fabric-protos-go-apiv2/peer" - "google.golang.org/protobuf/proto" - - "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/require" - "google.golang.org/protobuf/types/known/timestamppb" -) - -func toChaincodeArgs(args ...string) [][]byte { - ccArgs := make([][]byte, len(args)) - for i, a := range args { - ccArgs[i] = []byte(a) - } - return ccArgs -} - -// requireProtoEqual ensures an expected protobuf message matches an actual message -func requireProtoEqual(t *testing.T, expected proto.Message, actual proto.Message) { - require.True(t, proto.Equal(expected, actual), "Expected %v, got %v", expected, actual) -} - -func TestNewChaincodeStub(t *testing.T) { - expectedArgs := toChaincodeArgs("function", "arg1", "arg2") - expectedDecorations := map[string][]byte{"decoration-key": []byte("decoration-value")} - expectedCreator := []byte("signature-header-creator") - expectedTransient := map[string][]byte{"key": []byte("value")} - expectedEpoch := uint64(999) - - validSignedProposal := &peer.SignedProposal{ - ProposalBytes: marshalOrPanic(&peer.Proposal{ - Header: marshalOrPanic(&common.Header{ - ChannelHeader: marshalOrPanic(&common.ChannelHeader{ - Type: int32(common.HeaderType_ENDORSER_TRANSACTION), - Epoch: expectedEpoch, - }), - SignatureHeader: marshalOrPanic(&common.SignatureHeader{ - Creator: expectedCreator, - }), - }), - Payload: marshalOrPanic(&peer.ChaincodeProposalPayload{ - Input: []byte("chaincode-proposal-input"), - TransientMap: expectedTransient, - }), - }), - } - - tests := []struct { - signedProposal *peer.SignedProposal - expectedErr string - }{ - {signedProposal: nil}, - {signedProposal: proto.Clone(validSignedProposal).(*peer.SignedProposal)}, - { - signedProposal: &peer.SignedProposal{ProposalBytes: []byte("garbage")}, - expectedErr: "failed to extract Proposal from SignedProposal", - }, - { - signedProposal: &peer.SignedProposal{}, - expectedErr: "failed to extract Proposal fields: proposal header is nil", - }, - { - signedProposal: &peer.SignedProposal{}, - expectedErr: "failed to extract Proposal fields: proposal header is nil", - }, - { - signedProposal: &peer.SignedProposal{ - ProposalBytes: marshalOrPanic(&peer.Proposal{ - Header: marshalOrPanic(&common.Header{ - ChannelHeader: marshalOrPanic(&common.ChannelHeader{ - Type: int32(common.HeaderType_CONFIG_UPDATE), - Epoch: expectedEpoch, - }), - }), - }), - }, - expectedErr: "invalid channel header type. Expected ENDORSER_TRANSACTION or CONFIG, received CONFIG_UPDATE", - }, - } - - for _, tt := range tests { - stub, err := newChaincodeStub( - &Handler{}, - "channel-id", - "transaction-id", - &peer.ChaincodeInput{Args: expectedArgs[:], Decorations: expectedDecorations}, - tt.signedProposal, - ) - if tt.expectedErr != "" { - assert.Error(t, err) - assert.ErrorContains(t, err, tt.expectedErr) - continue - } - assert.NoError(t, err) - assert.NotNil(t, stub) - - assert.Equal(t, &Handler{}, stub.handler, "expected empty handler") - assert.Equal(t, "channel-id", stub.ChannelID) - assert.Equal(t, "transaction-id", stub.TxID) - assert.Equal(t, expectedArgs, stub.args) - assert.Equal(t, expectedDecorations, stub.decorations) - assert.Equal(t, "VALIDATION_PARAMETER", stub.validationParameterMetakey) - if tt.signedProposal == nil { - assert.Nil(t, stub.proposal, "expected nil proposal") - assert.Nil(t, stub.creator, "expected nil creator") - assert.Nil(t, stub.transient, "expected nil transient") - assert.Nil(t, stub.binding, "expected nil binding") - continue - } - - prop := &peer.Proposal{} - err = proto.Unmarshal(tt.signedProposal.ProposalBytes, prop) - assert.NoError(t, err) - assert.Equal(t, prop, stub.proposal) - - assert.Equal(t, expectedCreator, stub.creator) - assert.Equal(t, expectedTransient, stub.transient) - - epoch := make([]byte, 8) - binary.LittleEndian.PutUint64(epoch, expectedEpoch) - shdr := &common.SignatureHeader{} - digest := sha256.Sum256(append(append(shdr.GetNonce(), expectedCreator...), epoch...)) - assert.Equal(t, digest[:], stub.binding) - } -} - -func TestChaincodeStubSetEvent(t *testing.T) { - stub := &ChaincodeStub{} - err := stub.SetEvent("", []byte("event payload")) - assert.EqualError(t, err, "event name can not be empty string") - assert.Nil(t, stub.chaincodeEvent) - - stub = &ChaincodeStub{} - err = stub.SetEvent("name", []byte("payload")) - assert.NoError(t, err) - assert.Equal(t, &peer.ChaincodeEvent{EventName: "name", Payload: []byte("payload")}, stub.chaincodeEvent) -} - -func TestChaincodeStubAccessors(t *testing.T) { - stub := &ChaincodeStub{TxID: "transaction-id"} - assert.Equal(t, "transaction-id", stub.GetTxID()) - - stub = &ChaincodeStub{ChannelID: "channel-id"} - assert.Equal(t, "channel-id", stub.GetChannelID()) - - stub = &ChaincodeStub{decorations: map[string][]byte{"key": []byte("value")}} - assert.Equal(t, map[string][]byte{"key": []byte("value")}, stub.GetDecorations()) - - stub = &ChaincodeStub{args: [][]byte{[]byte("function"), []byte("arg1"), []byte("arg2")}} - assert.Equal(t, [][]byte{[]byte("function"), []byte("arg1"), []byte("arg2")}, stub.GetArgs()) - assert.Equal(t, []string{"function", "arg1", "arg2"}, stub.GetStringArgs()) - - f, a := stub.GetFunctionAndParameters() - assert.Equal(t, "function", f) - assert.Equal(t, []string{"arg1", "arg2"}, a) - - as, err := stub.GetArgsSlice() - assert.NoError(t, err) - assert.Equal(t, []byte("functionarg1arg2"), as) - - stub = &ChaincodeStub{} - f, a = stub.GetFunctionAndParameters() - assert.Equal(t, "", f) - assert.Empty(t, a) - - stub = &ChaincodeStub{creator: []byte("creator")} - creator, err := stub.GetCreator() - assert.NoError(t, err) - assert.Equal(t, []byte("creator"), creator) - - stub = &ChaincodeStub{transient: map[string][]byte{"key": []byte("value")}} - transient, err := stub.GetTransient() - assert.NoError(t, err) - assert.Equal(t, map[string][]byte{"key": []byte("value")}, transient) - - stub = &ChaincodeStub{binding: []byte("binding")} - binding, err := stub.GetBinding() - assert.NoError(t, err) - assert.Equal(t, []byte("binding"), binding) - - stub = &ChaincodeStub{signedProposal: &peer.SignedProposal{ProposalBytes: []byte("proposal-bytes")}} - sp, err := stub.GetSignedProposal() - assert.NoError(t, err) - assert.Equal(t, &peer.SignedProposal{ProposalBytes: []byte("proposal-bytes")}, sp) -} - -func TestChaincodeStubGetTxTimestamp(t *testing.T) { - now := timestamppb.Now() - tests := []struct { - proposal *peer.Proposal - ts *timestamppb.Timestamp - expectedErr string - }{ - { - ts: now, - proposal: &peer.Proposal{ - Header: marshalOrPanic(&common.Header{ - ChannelHeader: marshalOrPanic(&common.ChannelHeader{ - Timestamp: now, - }), - }), - }, - }, - { - proposal: &peer.Proposal{ - Header: marshalOrPanic(&common.Header{ - ChannelHeader: []byte("garbage-channel-header"), - }), - }, - expectedErr: "error unmarshaling ChannelHeader", - }, - { - proposal: &peer.Proposal{Header: []byte("garbage-header")}, - expectedErr: "error unmarshaling Header", - }, - } - - for _, tt := range tests { - stub := &ChaincodeStub{proposal: tt.proposal} - ts, err := stub.GetTxTimestamp() - if tt.expectedErr != "" { - assert.ErrorContains(t, err, tt.expectedErr) - continue - } - - assert.NoError(t, err) - assert.True(t, proto.Equal(ts, tt.ts)) - } -} - -func TestGetMSPID(t *testing.T) { - _, err := GetMSPID() - assert.EqualError(t, err, "'CORE_PEER_LOCALMSPID' is not set") - - os.Setenv("CORE_PEER_LOCALMSPID", "mspid") - - mspid, err := GetMSPID() - assert.NoError(t, err) - assert.Equal(t, "mspid", mspid) - - os.Unsetenv("CORE_PEER_LOCALMSPID") -} - -func TestChaincodeStubHandlers(t *testing.T) { - var tests = []struct { - name string - resType peer.ChaincodeMessage_Type - payload []byte - testFunc func(*ChaincodeStub, *Handler, *testing.T, []byte) - }{ - { - name: "Simple Response", - resType: peer.ChaincodeMessage_RESPONSE, - payload: []byte("myvalue"), - testFunc: func(s *ChaincodeStub, h *Handler, t *testing.T, payload []byte) { - resp, err := s.GetState("key") - if err != nil { - t.Fatalf("Unexpected error for GetState: %s", err) - } - assert.Equal(t, payload, resp) - - resp, err = s.GetPrivateData("col", "key") - if err != nil { - t.Fatalf("Unexpected error for GetState: %s", err) - } - assert.Equal(t, payload, resp) - _, err = s.GetPrivateData("", "key") - assert.EqualError(t, err, "collection must not be an empty string") - - resp, err = s.GetPrivateDataHash("col", "key") - if err != nil { - t.Fatalf("Unexpected error for GetPrivateDataHash: %s", err) - } - assert.Equal(t, payload, resp) - _, err = s.GetPrivateDataHash("", "key") - assert.EqualError(t, err, "collection must not be an empty string") - - err = s.PutState("key", payload) - assert.NoError(t, err) - - err = s.PutPrivateData("col", "key", payload) - assert.NoError(t, err) - err = s.PutPrivateData("", "key", payload) - assert.EqualError(t, err, "collection must not be an empty string") - err = s.PutPrivateData("col", "", payload) - assert.EqualError(t, err, "key must not be an empty string") - - err = s.SetStateValidationParameter("key", payload) - assert.NoError(t, err) - - err = s.SetPrivateDataValidationParameter("col", "key", payload) - assert.NoError(t, err) - - err = s.DelState("key") - assert.NoError(t, err) - - err = s.DelPrivateData("col", "key") - assert.NoError(t, err) - err = s.DelPrivateData("", "key") - assert.EqualError(t, err, "collection must not be an empty string") - - err = s.PurgePrivateData("col", "key") - assert.NoError(t, err) - err = s.PurgePrivateData("", "key") - assert.EqualError(t, err, "collection must not be an empty string") - - }, - }, - { - name: "ValidationParameter", - resType: peer.ChaincodeMessage_RESPONSE, - payload: marshalOrPanic( - &peer.StateMetadataResult{ - Entries: []*peer.StateMetadata{ - { - Metakey: "mkey", - Value: []byte("metavalue"), - }, - }, - }, - ), - testFunc: func(s *ChaincodeStub, h *Handler, t *testing.T, payload []byte) { - resp, err := s.GetStateValidationParameter("key") - if err != nil { - t.Fatalf("Unexpected error for GetStateValidationParameter: %s", err) - } - assert.Equal(t, []byte("metavalue"), resp) - - resp, err = s.GetPrivateDataValidationParameter("col", "key") - if err != nil { - t.Fatalf("Unexpected error for GetPrivateDataValidationParameter: %s", err) - } - assert.Equal(t, []byte("metavalue"), resp) - }, - }, - { - name: "InvokeChaincode", - resType: peer.ChaincodeMessage_RESPONSE, - payload: marshalOrPanic( - &peer.ChaincodeMessage{ - Type: peer.ChaincodeMessage_COMPLETED, - Payload: marshalOrPanic( - &peer.Response{ - Status: OK, - Payload: []byte("invokechaincode"), - }, - ), - }, - ), - testFunc: func(s *ChaincodeStub, h *Handler, t *testing.T, payload []byte) { - resp := s.InvokeChaincode("cc", [][]byte{}, "channel") - assert.Equal(t, resp.Payload, []byte("invokechaincode")) - }, - }, - { - name: "QueryResponse", - resType: peer.ChaincodeMessage_RESPONSE, - payload: marshalOrPanic( - &peer.QueryResponse{ - Results: []*peer.QueryResultBytes{ - { - ResultBytes: marshalOrPanic( - &queryresult.KV{ - Key: "querykey", - Value: []byte("queryvalue"), - }, - ), - }, - }, - Metadata: marshalOrPanic( - &peer.QueryResponseMetadata{ - Bookmark: "book", - FetchedRecordsCount: 1, - }, - ), - HasMore: true, - }, - ), - testFunc: func(s *ChaincodeStub, h *Handler, t *testing.T, payload []byte) { - expectedResult := &queryresult.KV{ - Key: "querykey", - Value: []byte("queryvalue"), - } - - // stub stuff - sqi, err := s.GetQueryResult("query") - if err != nil { - t.Fatalf("Unexpected error for GetQueryResult: %s", err) - } - kv, err := sqi.Next() - if err != nil { - t.Fatalf("Unexpected error for GetQueryResult: %s", err) - } - requireProtoEqual(t, expectedResult, kv) - - sqi, err = s.GetPrivateDataQueryResult("col", "query") - if err != nil { - t.Fatalf("Unexpected error for GetPrivateDataQueryResult: %s", err) - } - kv, err = sqi.Next() - if err != nil { - t.Fatalf("Unexpected error for GetPrivateDataQueryResult: %s", err) - } - requireProtoEqual(t, expectedResult, kv) - - _, err = s.GetPrivateDataQueryResult("", "query") - assert.EqualError(t, err, "collection must not be an empty string") - - sqi, err = s.GetStateByRange("", "end") - if err != nil { - t.Fatalf("Unexpected error for GetStateByRange: %s", err) - } - // first result - kv, err = sqi.Next() - if err != nil { - t.Fatalf("Unexpected error for GetStateByRange: %s", err) - } - requireProtoEqual(t, expectedResult, kv) - // second result - assert.True(t, sqi.HasNext()) - kv, err = sqi.Next() - if err != nil { - t.Fatalf("Unexpected error for GetStateByRange: %s", err) - } - requireProtoEqual(t, expectedResult, kv) - err = sqi.Close() - assert.NoError(t, err) - - sqi, qrm, err := s.GetStateByRangeWithPagination("", "end", 1, "book") - assert.NoError(t, err) - kv, err = sqi.Next() - if err != nil { - t.Fatalf("Unexpected error for GetStateByRangeWithPagination: %s", err) - } - requireProtoEqual(t, expectedResult, kv) - assert.Equal(t, "book", qrm.GetBookmark()) - assert.Equal(t, int32(1), qrm.GetFetchedRecordsCount()) - - sqi, err = s.GetPrivateDataByRange("col", "", "end") - if err != nil { - t.Fatalf("Unexpected error for GetPrivateDataByRange: %s", err) - } - kv, err = sqi.Next() - if err != nil { - t.Fatalf("Unexpected error for GetPrivateDataByRange: %s", err) - } - requireProtoEqual(t, expectedResult, kv) - - _, err = s.GetPrivateDataByRange("", "", "end") - assert.EqualError(t, err, "collection must not be an empty string") - - sqi, err = s.GetStateByPartialCompositeKey("object", []string{"attr1", "attr2"}) - assert.NoError(t, err) - kv, err = sqi.Next() - if err != nil { - t.Fatalf("Unexpected error for GetStateByPartialCompositeKey: %s", err) - } - requireProtoEqual(t, expectedResult, kv) - - sqi, err = s.GetPrivateDataByPartialCompositeKey("col", "object", []string{"attr1", "attr2"}) - assert.NoError(t, err) - kv, err = sqi.Next() - if err != nil { - t.Fatalf("Unexpected error for GetPrivateDataByPartialCompositeKey: %s", err) - } - requireProtoEqual(t, expectedResult, kv) - - _, err = s.GetPrivateDataByPartialCompositeKey("", "object", []string{"attr1", "attr2"}) - assert.EqualError(t, err, "collection must not be an empty string") - - sqi, qrm, err = s.GetStateByPartialCompositeKeyWithPagination( - "object", - []string{"key1", "key2"}, - 1, - "book", - ) - assert.NoError(t, err) - kv, err = sqi.Next() - if err != nil { - t.Fatalf("Unexpected error for GetStateByPartialCompositeKeyWithPagination: %s", err) - } - requireProtoEqual(t, expectedResult, kv) - assert.Equal(t, "book", qrm.GetBookmark()) - assert.Equal(t, int32(1), qrm.GetFetchedRecordsCount()) - - sqi, qrm, err = s.GetQueryResultWithPagination("query", 1, "book") - assert.NoError(t, err) - kv, err = sqi.Next() - if err != nil { - t.Fatalf("Unexpected error forGetQueryResultWithPagination: %s", err) - } - requireProtoEqual(t, expectedResult, kv) - assert.Equal(t, "book", qrm.GetBookmark()) - assert.Equal(t, int32(1), qrm.GetFetchedRecordsCount()) - }, - }, - { - name: "GetHistoryForKey", - resType: peer.ChaincodeMessage_RESPONSE, - payload: marshalOrPanic( - &peer.QueryResponse{ - Results: []*peer.QueryResultBytes{ - { - ResultBytes: marshalOrPanic( - &queryresult.KeyModification{ - TxId: "txid", - Value: []byte("historyforkey"), - }, - ), - }, - }, - HasMore: false, - }, - ), - testFunc: func(s *ChaincodeStub, h *Handler, t *testing.T, payload []byte) { - expectedResult := &queryresult.KeyModification{ - TxId: "txid", - Value: []byte("historyforkey"), - } - hqi, err := s.GetHistoryForKey("key") - if err != nil { - t.Fatalf("Unexpected error for GetHistoryForKey: %s", err) - } - km, err := hqi.Next() - if err != nil { - t.Fatalf("Unexpected error for GetPrivateDataByRangee: %s", err) - } - requireProtoEqual(t, expectedResult, km) - assert.False(t, hqi.HasNext()) - }, - }, - { - name: "Error Conditions", - resType: peer.ChaincodeMessage_ERROR, - payload: []byte("error"), - testFunc: func(s *ChaincodeStub, h *Handler, t *testing.T, payload []byte) { - _, err := s.GetState("key") - assert.EqualError(t, err, string(payload)) - - _, err = s.GetPrivateDataHash("col", "key") - assert.EqualError(t, err, string(payload)) - - _, err = s.GetStateValidationParameter("key") - assert.EqualError(t, err, string(payload)) - - err = s.PutState("key", payload) - assert.EqualError(t, err, string(payload)) - - err = s.SetPrivateDataValidationParameter("col", "key", payload) - assert.EqualError(t, err, string(payload)) - - err = s.DelState("key") - assert.EqualError(t, err, string(payload)) - - _, err = s.GetStateByRange("start", "end") - assert.EqualError(t, err, string(payload)) - - _, err = s.GetQueryResult("query") - assert.EqualError(t, err, string(payload)) - - _, err = s.GetHistoryForKey("key") - assert.EqualError(t, err, string(payload)) - - resp := s.InvokeChaincode("cc", [][]byte{}, "channel") - assert.Equal(t, payload, resp.GetPayload()) - - }, - }, - } - - for _, test := range tests { - test := test - t.Run(test.name, func(t *testing.T) { - t.Parallel() - - handler := &Handler{ - cc: &mockChaincode{}, - responseChannels: map[string]chan *peer.ChaincodeMessage{}, - state: ready, - } - stub := &ChaincodeStub{ - ChannelID: "channel", - TxID: "txid", - handler: handler, - validationParameterMetakey: "mkey", - } - chatStream := &mock.PeerChaincodeStream{} - chatStream.SendStub = func(msg *peer.ChaincodeMessage) error { - go func() { - err := handler.handleResponse( - &peer.ChaincodeMessage{ - Type: test.resType, - ChannelId: msg.GetChannelId(), - Txid: msg.GetTxid(), - Payload: test.payload, - }, - ) - assert.NoError(t, err, "handleResponse") - }() - return nil - } - handler.chatStream = chatStream - test.testFunc(stub, handler, t, test.payload) - }) - } -}