mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-23 01:55:10 +00:00
Merge branch 'main' into prometheus-grafana
This commit is contained in:
commit
28fd5f40f6
211 changed files with 30713 additions and 989 deletions
7
.gitignore
vendored
7
.gitignore
vendored
|
|
@ -5,7 +5,7 @@
|
|||
# Vim file artifacts
|
||||
.*.sw*
|
||||
# installed platform-specific binaries
|
||||
/bin
|
||||
bin
|
||||
/config
|
||||
.DS_Store
|
||||
.project
|
||||
|
|
@ -19,3 +19,8 @@ node_modules/
|
|||
# Ignore Gradle build output directory
|
||||
build
|
||||
package-lock.json
|
||||
external-chaincode/
|
||||
|
||||
# Eclipse
|
||||
.classpath
|
||||
.settings
|
||||
|
|
|
|||
|
|
@ -6,10 +6,8 @@ fabric-samples uses a non-author code review policy, requiring a single approval
|
|||
| Name | GitHub | Chat | email |
|
||||
|---------------------------|------------------|----------------|-------------------------------------|
|
||||
| Arnaud Le Hors | lehors | lehors | lehors@us.ibm.com |
|
||||
| Bret Harrison | harrisob | bretharrison | harrisob@us.ibm.com |
|
||||
| Chris Ferris | christo4ferris | cbf | chris.ferris@gmail.com |
|
||||
| Dave Enyeart | denyeart | dave.enyeart | enyeart@us.ibm.com |
|
||||
| Gari Singh | mastersingh24 | mastersingh24 | gari.r.singh@gmail.com |
|
||||
| Josh Kneubuhl | jkneubuh | jkneubuhl | jkneubuh@us.ibm.com |
|
||||
| Matthew B White | mbwhite | mbwhite | whitemat@uk.ibm.com |
|
||||
| Nikhil Gupta | nikhil550 | negupta | nikhilg550@gmail.com |
|
||||
|
||||
|
|
|
|||
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
# Hyperledger Fabric Samples
|
||||
|
||||
[](https://dev.azure.com/Hyperledger/Fabric-Samples/_build/latest?definitionId=28&branchName=main)
|
||||
|
||||
You can use Fabric samples to get started working with Hyperledger Fabric, explore important Fabric features, and learn how to build applications that can interact with blockchain networks using the Fabric SDKs. To learn more about Hyperledger Fabric, visit the [Fabric documentation](https://hyperledger-fabric.readthedocs.io/en/latest).
|
||||
|
||||
## Getting started with the Fabric samples
|
||||
|
|
@ -15,6 +17,13 @@ Organization peers and an ordering service node. You can use it on your local ma
|
|||
You can also use it to deploy and test your own Fabric chaincodes and applications. To get started, see
|
||||
the [test network tutorial](https://hyperledger-fabric.readthedocs.io/en/latest/test_network.html).
|
||||
|
||||
The [Kubernetes Test Network](test-network-k8s) sample builds upon the Compose network, constructing a Fabric
|
||||
network with peer, orderer, and CA infrastructure nodes running on Kubernetes. In addition to providing a sample
|
||||
Kubernetes guide, the Kube test network can be used as a platform to author and debug _cloud ready_ Fabric Client
|
||||
applications on a development or CI workstation.
|
||||
|
||||
|
||||
|
||||
## Asset transfer samples and tutorials
|
||||
|
||||
The asset transfer series provides a series of sample smart contracts and applications to demonstrate how to store and transfer assets using Hyperledger Fabric.
|
||||
|
|
|
|||
|
|
@ -2,7 +2,4 @@ module github.com/hyperledger/fabric-samples/asset-transfer-abac/chaincode-go
|
|||
|
||||
go 1.15
|
||||
|
||||
require (
|
||||
github.com/hyperledger/fabric-contract-api-go v1.1.1
|
||||
golang.org/x/tools v0.1.0 // indirect
|
||||
)
|
||||
require github.com/hyperledger/fabric-contract-api-go v1.1.1
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
|
|||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cucumber/godog v0.8.0/go.mod h1:Cp3tEV1LRAyH/RuCThcxHS/+9ORZ+FMzPva2AZ5Ki+A=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||
|
|
@ -34,6 +35,7 @@ github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b
|
|||
github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
|
||||
github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
|
||||
github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
|
|
@ -55,9 +57,11 @@ github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0L
|
|||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
|
|
@ -66,6 +70,7 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN
|
|||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
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.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
|
||||
|
|
@ -84,6 +89,7 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH
|
|||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
|
||||
|
|
@ -93,28 +99,19 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:
|
|||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
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=
|
||||
|
|
@ -123,27 +120,14 @@ golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c h1:KfpJVdWhuRqNk4XVXzjXf2KAV4TBEP77SYdFGjeGuIE=
|
||||
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
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-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
|
||||
|
|
@ -152,6 +136,7 @@ google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
|
|||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
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-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
|
|||
276
asset-transfer-basic/application-gateway-go/assetTransfer.go
Executable file
276
asset-transfer-basic/application-gateway-go/assetTransfer.go
Executable file
|
|
@ -0,0 +1,276 @@
|
|||
/*
|
||||
Copyright 2021 IBM All Rights Reserved.
|
||||
|
||||
SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/x509"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/hyperledger/fabric-gateway/pkg/client"
|
||||
"github.com/hyperledger/fabric-gateway/pkg/identity"
|
||||
gwproto "github.com/hyperledger/fabric-protos-go/gateway"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials"
|
||||
"google.golang.org/grpc/status"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"path"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
mspID = "Org1MSP"
|
||||
cryptoPath = "../../test-network/organizations/peerOrganizations/org1.example.com"
|
||||
certPath = cryptoPath + "/users/User1@org1.example.com/msp/signcerts/cert.pem"
|
||||
keyPath = cryptoPath + "/users/User1@org1.example.com/msp/keystore/"
|
||||
tlsCertPath = cryptoPath + "/peers/peer0.org1.example.com/tls/ca.crt"
|
||||
peerEndpoint = "localhost:7051"
|
||||
gatewayPeer = "peer0.org1.example.com"
|
||||
channelName = "mychannel"
|
||||
chaincodeName = "basic"
|
||||
)
|
||||
|
||||
var now = time.Now()
|
||||
var assetId = fmt.Sprintf("asset%d", now.Unix()*1e3+int64(now.Nanosecond())/1e6)
|
||||
|
||||
func main() {
|
||||
log.Println("============ application-golang starts ============")
|
||||
|
||||
// The gRPC client connection should be shared by all Gateway connections to this endpoint
|
||||
clientConnection := newGrpcConnection()
|
||||
defer clientConnection.Close()
|
||||
|
||||
id := newIdentity()
|
||||
sign := newSign()
|
||||
|
||||
// Create a Gateway connection for a specific client identity
|
||||
gateway, err := client.Connect(
|
||||
id,
|
||||
client.WithSign(sign),
|
||||
client.WithClientConnection(clientConnection),
|
||||
// Default timeouts for different gRPC calls
|
||||
client.WithEvaluateTimeout(5*time.Second),
|
||||
client.WithEndorseTimeout(15*time.Second),
|
||||
client.WithSubmitTimeout(5*time.Second),
|
||||
client.WithCommitStatusTimeout(1*time.Minute),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
defer gateway.Close()
|
||||
|
||||
network := gateway.GetNetwork(channelName)
|
||||
contract := network.GetContract(chaincodeName)
|
||||
|
||||
fmt.Println("initLedger:")
|
||||
initLedger(contract)
|
||||
|
||||
fmt.Println("getAllAssets:")
|
||||
getAllAssets(contract)
|
||||
|
||||
fmt.Println("createAsset:")
|
||||
createAsset(contract)
|
||||
|
||||
fmt.Println("readAssetByID:")
|
||||
readAssetByID(contract)
|
||||
|
||||
fmt.Println("transferAssetAsync:")
|
||||
transferAssetAsync(contract)
|
||||
|
||||
fmt.Println("exampleErrorHandling:")
|
||||
exampleErrorHandling(contract)
|
||||
|
||||
log.Println("============ application-golang ends ============")
|
||||
}
|
||||
|
||||
// newGrpcConnection creates a gRPC connection to the Gateway server.
|
||||
func newGrpcConnection() *grpc.ClientConn {
|
||||
certificate, err := loadCertificate(tlsCertPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
certPool := x509.NewCertPool()
|
||||
certPool.AddCert(certificate)
|
||||
transportCredentials := credentials.NewClientTLSFromCert(certPool, gatewayPeer)
|
||||
|
||||
connection, err := grpc.Dial(peerEndpoint, grpc.WithTransportCredentials(transportCredentials))
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to create gRPC connection: %w", err))
|
||||
}
|
||||
|
||||
return connection
|
||||
}
|
||||
|
||||
// newIdentity creates a client identity for this Gateway connection using an X.509 certificate.
|
||||
func newIdentity() *identity.X509Identity {
|
||||
certificate, err := loadCertificate(certPath)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
id, err := identity.NewX509Identity(mspID, certificate)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return id
|
||||
}
|
||||
|
||||
func loadCertificate(filename string) (*x509.Certificate, error) {
|
||||
certificatePEM, err := ioutil.ReadFile(filename)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read certificate file: %w", err)
|
||||
}
|
||||
return identity.CertificateFromPEM(certificatePEM)
|
||||
}
|
||||
|
||||
// newSign creates a function that generates a digital signature from a message digest using a private key.
|
||||
func newSign() identity.Sign {
|
||||
files, err := ioutil.ReadDir(keyPath)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to read private key directory: %w", err))
|
||||
}
|
||||
privateKeyPEM, err := ioutil.ReadFile(path.Join(keyPath, files[0].Name()))
|
||||
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to read private key file: %w", err))
|
||||
}
|
||||
|
||||
privateKey, err := identity.PrivateKeyFromPEM(privateKeyPEM)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
sign, err := identity.NewPrivateKeySign(privateKey)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return sign
|
||||
}
|
||||
|
||||
/*
|
||||
This type of transaction would typically only be run once by an application the first time it was started after its
|
||||
initial deployment. A new version of the chaincode deployed later would likely not need to run an "init" function.
|
||||
*/
|
||||
func initLedger(contract *client.Contract) {
|
||||
fmt.Printf("Submit Transaction: InitLedger, function creates the initial set of assets on the ledger \n")
|
||||
|
||||
_, err := contract.SubmitTransaction("InitLedger")
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to submit transaction: %w", err))
|
||||
}
|
||||
|
||||
fmt.Printf("*** Transaction committed successfully\n")
|
||||
}
|
||||
|
||||
// Evaluate a transaction to query ledger state.
|
||||
func getAllAssets(contract *client.Contract) {
|
||||
fmt.Println("Evaluate Transaction: GetAllAssets, function returns all the current assets on the ledger")
|
||||
|
||||
evaluateResult, err := contract.EvaluateTransaction("GetAllAssets")
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to evaluate transaction: %w", err))
|
||||
}
|
||||
result := formatJSON(evaluateResult)
|
||||
|
||||
fmt.Printf("*** Result:%s\n", result)
|
||||
}
|
||||
|
||||
// Submit a transaction synchronously, blocking until it has been committed to the ledger.
|
||||
func createAsset(contract *client.Contract) {
|
||||
fmt.Printf("Submit Transaction: CreateAsset, creates new asset with ID, Color, Size, Owner and AppraisedValue arguments \n")
|
||||
|
||||
_, err := contract.SubmitTransaction("CreateAsset", assetId, "yellow", "5", "Tom", "1300")
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to submit transaction: %w", err))
|
||||
}
|
||||
|
||||
fmt.Printf("*** Transaction committed successfully\n")
|
||||
}
|
||||
|
||||
// Evaluate a transaction by assetID to query ledger state.
|
||||
func readAssetByID(contract *client.Contract) {
|
||||
fmt.Printf("Evaluate Transaction: ReadAsset, function returns asset attributes\n")
|
||||
|
||||
evaluateResult, err := contract.EvaluateTransaction("ReadAsset", assetId)
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to evaluate transaction: %w", err))
|
||||
}
|
||||
result := formatJSON(evaluateResult)
|
||||
|
||||
fmt.Printf("*** Result:%s\n", result)
|
||||
}
|
||||
|
||||
/*
|
||||
Submit transaction asynchronously, blocking until the transaction has been sent to the orderer, and allowing
|
||||
this thread to process the chaincode response (e.g. update a UI) without waiting for the commit notification
|
||||
*/
|
||||
func transferAssetAsync(contract *client.Contract) {
|
||||
fmt.Printf("Async Submit Transaction: TransferAsset, updates existing asset owner'\n")
|
||||
|
||||
submitResult, commit, err := contract.SubmitAsync("TransferAsset", client.WithArguments(assetId, "Mark"))
|
||||
if err != nil {
|
||||
panic(fmt.Errorf("failed to submit transaction asynchronously: %w", err))
|
||||
}
|
||||
|
||||
fmt.Printf("Successfully submitted transaction to transfer ownership from %s to Mark. \n", string(submitResult))
|
||||
fmt.Println("Waiting for transaction commit.")
|
||||
|
||||
if status, err := commit.Status(); err != nil {
|
||||
panic(fmt.Errorf("failed to get commit status: %w", err))
|
||||
} else if !status.Successful {
|
||||
panic(fmt.Errorf("transaction %s failed to commit with status: %d", status.TransactionID, int32(status.Code)))
|
||||
}
|
||||
|
||||
fmt.Printf("*** Transaction committed successfully\n")
|
||||
}
|
||||
|
||||
// Submit transaction, passing in the wrong number of arguments ,expected to throw an error containing details of any error responses from the smart contract.
|
||||
func exampleErrorHandling(contract *client.Contract) {
|
||||
fmt.Println("Submit Transaction: UpdateAsset asset70, asset70 does not exist and should return an error")
|
||||
|
||||
_, err := contract.SubmitTransaction("UpdateAsset")
|
||||
if err != nil {
|
||||
switch err := err.(type) {
|
||||
case *client.EndorseError:
|
||||
fmt.Printf("Endorse error with gRPC status %v: %s\n", status.Code(err), err)
|
||||
case *client.SubmitError:
|
||||
fmt.Printf("Submit error with gRPC status %v: %s\n", status.Code(err), err)
|
||||
case *client.CommitStatusError:
|
||||
if errors.Is(err, context.DeadlineExceeded) {
|
||||
fmt.Printf("Timeout waiting for transaction %s commit status: %s", err.TransactionID, err)
|
||||
} else {
|
||||
fmt.Printf("Error obtaining commit status with gRPC status %v: %s\n", status.Code(err), err)
|
||||
}
|
||||
case *client.CommitError:
|
||||
fmt.Printf("Transaction %s failed to commit with status %d: %s\n", err.TransactionID, int32(err.Code), err)
|
||||
}
|
||||
/*
|
||||
Any error that originates from a peer or orderer node external to the gateway will have its details
|
||||
embedded within the gRPC status error. The following code shows how to extract that.
|
||||
*/
|
||||
statusErr := status.Convert(err)
|
||||
for _, detail := range statusErr.Details() {
|
||||
errDetail := detail.(*gwproto.ErrorDetail)
|
||||
fmt.Printf("Error from endpoint: %s, mspId: %s, message: %s\n", errDetail.Address, errDetail.MspId, errDetail.Message)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Format JSON data
|
||||
func formatJSON(data []byte) string {
|
||||
var prettyJSON bytes.Buffer
|
||||
if err := json.Indent(&prettyJSON, data, " ", ""); err != nil {
|
||||
panic(fmt.Errorf("failed to parse JSON: %w", err))
|
||||
}
|
||||
return prettyJSON.String()
|
||||
}
|
||||
11
asset-transfer-basic/application-gateway-go/go.mod
Normal file
11
asset-transfer-basic/application-gateway-go/go.mod
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
module assetTransfer
|
||||
|
||||
go 1.16
|
||||
|
||||
require (
|
||||
github.com/hyperledger/fabric-gateway v1.0.0
|
||||
github.com/hyperledger/fabric-protos-go v0.0.0-20211118165945-23d738fc3553
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f // indirect
|
||||
golang.org/x/text v0.3.7 // indirect
|
||||
google.golang.org/grpc v1.42.0
|
||||
)
|
||||
488
asset-transfer-basic/application-gateway-go/go.sum
Normal file
488
asset-transfer-basic/application-gateway-go/go.sum
Normal file
|
|
@ -0,0 +1,488 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
cloud.google.com/go v0.38.0/go.mod h1:990N+gfupTy94rShfmMCWGDn0LpTmnzTp2qbd1dvSRU=
|
||||
cloud.google.com/go v0.44.1/go.mod h1:iSa0KzasP4Uvy3f1mN/7PiObzGgflwredwwASm/v6AU=
|
||||
cloud.google.com/go v0.44.2/go.mod h1:60680Gw3Yr4ikxnPRS/oxxkBccT6SA1yMk63TGekxKY=
|
||||
cloud.google.com/go v0.45.1/go.mod h1:RpBamKRgapWJb87xiFSdk4g1CME7QZg3uwTez+TSTjc=
|
||||
cloud.google.com/go v0.46.3/go.mod h1:a6bKKbmY7er1mI7TEI4lsAkts/mkhTSZK8w33B4RAg0=
|
||||
cloud.google.com/go/bigquery v1.0.1/go.mod h1:i/xbL2UlR5RvWAURpBYZTtm/cXjCha9lbfbpx4poX+o=
|
||||
cloud.google.com/go/datastore v1.0.0/go.mod h1:LXYbyblFSglQ5pkeyhO+Qmw7ukd3C+pD7TKLgZqpHYE=
|
||||
cloud.google.com/go/firestore v1.1.0/go.mod h1:ulACoGHTpvq5r8rxGJ4ddJZBZqakUQqClKRT5SZwBmk=
|
||||
cloud.google.com/go/pubsub v1.0.1/go.mod h1:R0Gpsv3s54REJCy4fxDixWD93lHJMoZTyQ2kNxGRt3I=
|
||||
cloud.google.com/go/storage v1.0.0/go.mod h1:IhtSnM/ZTZV8YYJWCY8RULGVqBDmpoyjwiyrjsg+URw=
|
||||
dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym/WlBOVXweHU+Q+/VP0lqqI8lqeDx9IjBqo=
|
||||
github.com/Knetic/govaluate v3.0.0+incompatible h1:7o6+MAPhYTCF0+fdvoz1xDedhRb4f6s9Tn1Tt7/WTEg=
|
||||
github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU=
|
||||
github.com/Shopify/sarama v1.27.2 h1:1EyY1dsxNDUQEv0O/4TsjosHI2CgB1uo9H/v56xzTxc=
|
||||
github.com/Shopify/sarama v1.27.2/go.mod h1:g5s5osgELxgM+Md9Qni9rzo7Rbt+vvFQI4bt/Mc93II=
|
||||
github.com/Shopify/toxiproxy v2.1.4+incompatible/go.mod h1:OXgGpZ6Cli1/URJOF1DMxUHB2q5Ap20/P/eIdh4G0pI=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY=
|
||||
github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o=
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs=
|
||||
github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c/go.mod h1:MKsuJmJgSg28kpZDP6UIiPt0e0Oz0kqKNGyRaWEPv84=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc=
|
||||
github.com/cespare/xxhash/v2 v2.1.1/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk=
|
||||
github.com/cncf/udpa/go v0.0.0-20210930031921-04548b0d99d4/go.mod h1:6pvJx4me5XPnfI9Z40ddWsdw2W/uZgQLFXToKeRcDiI=
|
||||
github.com/cncf/xds/go v0.0.0-20210805033703-aa0b78936158/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20210922020428-25de7278fc84/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/cncf/xds/go v0.0.0-20211011173535-cb28da3451f1/go.mod h1:eXthEFrGJvWHgFFCl3hGmgk+/aYT6PnTQLykKQRLhEs=
|
||||
github.com/coreos/bbolt v1.3.2/go.mod h1:iRUV2dpdMOn7Bo10OQBFzIJO9kkE559Wcmn+qkEiiKk=
|
||||
github.com/coreos/etcd v3.3.13+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
|
||||
github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
|
||||
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
|
||||
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
|
||||
github.com/cucumber/gherkin-go/v19 v19.0.3/go.mod h1:jY/NP6jUtRSArQQJ5h1FXOUgk5fZK24qtE7vKi776Vw=
|
||||
github.com/cucumber/godog v0.12.1/go.mod h1:u6SD7IXC49dLpPN35kal0oYEjsXZWee4pW6Tm9t5pIc=
|
||||
github.com/cucumber/messages-go/v16 v16.0.0/go.mod h1:EJcyR5Mm5ZuDsKJnT2N9KRnBK30BGjtYotDKpwQ0v6g=
|
||||
github.com/cucumber/messages-go/v16 v16.0.1/go.mod h1:EJcyR5Mm5ZuDsKJnT2N9KRnBK30BGjtYotDKpwQ0v6g=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/dgrijalva/jwt-go v3.2.0+incompatible/go.mod h1:E3ru+11k8xSBh+hMPgOLZmtrrCbhqsmaPHjLKYnJCaQ=
|
||||
github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954/go.mod h1:vAd38F8PWV+bWy6jNmig1y/TA+kYO4g3RSRF0IAv0no=
|
||||
github.com/eapache/go-resiliency v1.2.0 h1:v7g92e/KSN71Rq7vSThKaWIq68fL4YHvWyiUKorFR1Q=
|
||||
github.com/eapache/go-resiliency v1.2.0/go.mod h1:kFI+JgMyC7bLPUVY133qvEBtVayf5mFgVsvEsIPBvNs=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21 h1:YEetp8/yCZMuEPMUDHG0CW/brkkEp8mzqk2+ODEitlw=
|
||||
github.com/eapache/go-xerial-snappy v0.0.0-20180814174437-776d5712da21/go.mod h1:+020luEh2TKB4/GOp8oxxtq0Daoen/Cii55CzbTV6DU=
|
||||
github.com/eapache/queue v1.1.0 h1:YOEu7KNc61ntiQlcEeUIoDTJ2o8mQznoNvUhiigpIqc=
|
||||
github.com/eapache/queue v1.1.0/go.mod h1:6eCeP0CKFpHLu8blIFXhExK/dRa7WDZfr6jVFPTqq+I=
|
||||
github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/go-control-plane v0.9.9-0.20201210154907-fd9021fe5dad/go.mod h1:cXg6YxExXjJnVBQHBLXeUAgxn2UodCpnH306RInaBQk=
|
||||
github.com/envoyproxy/go-control-plane v0.9.10-0.20210907150352-cf90f659a021/go.mod h1:AFq3mo9L8Lqqiid3OhADV3RfLJnjiw63cSpi+fDTRC0=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fortytw2/leaktest v1.3.0/go.mod h1:jDsjWgpAGjm2CA7WthBh/CdZYEPF31XHquHwclZch5g=
|
||||
github.com/frankban/quicktest v1.10.2/go.mod h1:K+q6oSqb0W0Ininfk863uOk1lMy69l/P6txr3mVT54s=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/fsnotify/fsnotify v1.4.9 h1:hsms1Qyu0jgnwNXIxa+/V/PDsU6CfLf6CNO8H7IWoS4=
|
||||
github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1/go.mod h1:vR7hzQXu2zJy9AVAgeJqvqgH9Q5CA+iKCZ2gyEVpxRU=
|
||||
github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
|
||||
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.3.1/go.mod h1:sBzyDLLjw3U8JLTeZvSv8jJB+tU5PVekmnlKIyFUx0Y=
|
||||
github.com/golang/mock v1.6.0 h1:ErTB+efbowRARo13NNdxyJji2egdxLGQhRaY+DUumQc=
|
||||
github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
|
||||
github.com/golang/protobuf v1.5.2 h1:ROPKBNFfQgOUMifHyP+KYbvpjbdoFNs+aK7DXlji0Tw=
|
||||
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
|
||||
github.com/golang/snappy v0.0.1 h1:Qgr9rKW7uDUkrbSmQeiDsGa8SjGyCOGtuasMWwvp2P4=
|
||||
github.com/golang/snappy v0.0.1/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q=
|
||||
github.com/google/btree v0.0.0-20180813153112-4030bb1f1f0c/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/btree v1.0.0/go.mod h1:lNA+9X1NB3Zf8V7Ke586lFgjr2dZNuvo3lPJSGZ5JPQ=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/go-cmp v0.5.5 h1:Khx7svrCpmxxtHBq5j2mp/xVjsi8hQMfNLvJFAlrGgU=
|
||||
github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/google/martian v2.1.0+incompatible/go.mod h1:9I4somxYTbIHy5NJKHRl3wXiIaQGbYVAs8BPL6v8lEs=
|
||||
github.com/google/pprof v0.0.0-20181206194817-3ea8567a2e57/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/pprof v0.0.0-20190515194954-54271f7e092f/go.mod h1:zfwlbNMJ+OItoe0UupaVj+oy1omPYYDuagoSzA8v9mc=
|
||||
github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI=
|
||||
github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
github.com/googleapis/gax-go/v2 v2.0.4/go.mod h1:0Wqv26UfaUD9n4G6kQubkQ+KchISgw+vpHVxEJEs9eg=
|
||||
github.com/googleapis/gax-go/v2 v2.0.5/go.mod h1:DWXyrwAJ9X0FpwwEdw+IPEYBICEFu5mhpdKc/us6bOk=
|
||||
github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
|
||||
github.com/gorilla/websocket v1.4.2/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
|
||||
github.com/grpc-ecosystem/go-grpc-middleware v1.0.0/go.mod h1:FiyG127CGDf3tlThmgyCl78X/SZQqEOJBCDaAfeWzPs=
|
||||
github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY=
|
||||
github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw=
|
||||
github.com/hashicorp/consul/api v1.1.0/go.mod h1:VmuI/Lkw1nC05EYQWNKwWGbkg+FbDBtguAZLlVdkD9Q=
|
||||
github.com/hashicorp/consul/sdk v0.1.1/go.mod h1:VKf9jXwCTEY1QZP2MOLRhb5i/I/ssyNV1vwHyQBF0x8=
|
||||
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
|
||||
github.com/hashicorp/go-cleanhttp v0.5.1/go.mod h1:JpRdi6/HCYpAwUzNwuwqhbovhLtngrth3wmdIIUrZ80=
|
||||
github.com/hashicorp/go-immutable-radix v1.0.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-immutable-radix v1.3.0/go.mod h1:0y9vanUI8NX6FsYoO3zeMjhV/C5i9g4Q3DwcSNZ4P60=
|
||||
github.com/hashicorp/go-memdb v1.3.0/go.mod h1:Mluclgwib3R93Hk5fxEfiRhB+6Dar64wWh71LpNSe3g=
|
||||
github.com/hashicorp/go-msgpack v0.5.3/go.mod h1:ahLV/dePpqEmjfWmKiqvPkv/twdG7iPBM1vqhUKIvfM=
|
||||
github.com/hashicorp/go-multierror v1.0.0/go.mod h1:dHtQlpGsu+cZNNAkkCN/P3hoUDHhCYQXV3UM06sGGrk=
|
||||
github.com/hashicorp/go-rootcerts v1.0.0/go.mod h1:K6zTfqpRlCUIjkwsN4Z+hiSfzSTQa6eBIzfwKfwNnHU=
|
||||
github.com/hashicorp/go-sockaddr v1.0.0/go.mod h1:7Xibr9yA9JjQq1JpNB2Vw7kxv8xerXegt+ozgdvDeDU=
|
||||
github.com/hashicorp/go-syslog v1.0.0/go.mod h1:qPfqrKkXGihmCqbJM2mZgkZGvKG1dFdvsLplgctolz4=
|
||||
github.com/hashicorp/go-uuid v1.0.0/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.1/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-uuid v1.0.2 h1:cfejS+Tpcp13yd5nYHWDI6qVCny6wyX2Mt5SGur2IGE=
|
||||
github.com/hashicorp/go-uuid v1.0.2/go.mod h1:6SBZvOh/SIDV7/2o3Jml5SYk/TvGqwFJ/bN7x4byOro=
|
||||
github.com/hashicorp/go-version v1.0.0 h1:21MVWPKDphxa7ineQQTrCU5brh7OuVVAzGOCnnCPtE8=
|
||||
github.com/hashicorp/go-version v1.0.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA=
|
||||
github.com/hashicorp/go.net v0.0.1/go.mod h1:hjKkEWcCURg++eb33jQU7oqQcI9XDCnUzHA0oac0k90=
|
||||
github.com/hashicorp/golang-lru v0.5.0/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/golang-lru v0.5.4/go.mod h1:iADmTwqILo4mZ8BN3D2Q6+9jd8WM5uGBxy+E8yxSoD4=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO+LraFDTW64=
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hyperledger/fabric v2.1.1+incompatible h1:cYYRv3vVg4kA6DmrixLxwn1nwBEUuYda8DsMwlaMKbY=
|
||||
github.com/hyperledger/fabric v2.1.1+incompatible/go.mod h1:tGFAOCT696D3rG0Vofd2dyWYLySHlh0aQjf7Q1HAju0=
|
||||
github.com/hyperledger/fabric-amcl v0.0.0-20200424173818-327c9e2cf77a h1:JAKZdGuUIjVmES0X31YUD7UqMR2rz/kxLluJuGvsXPk=
|
||||
github.com/hyperledger/fabric-amcl v0.0.0-20200424173818-327c9e2cf77a/go.mod h1:X+DIyUsaTmalOpmpQfIvFZjKHQedrURQ5t4YqquX7lE=
|
||||
github.com/hyperledger/fabric-gateway v1.0.0 h1:bki1JYYdQzRGHFArxtgG4wyH6sbFNbYn3PzpdeDfjdk=
|
||||
github.com/hyperledger/fabric-gateway v1.0.0/go.mod h1:uaRZyC+xzfucPqZIJpesdEsugVvChPhDxZiZmDRSFd4=
|
||||
github.com/hyperledger/fabric-protos-go v0.0.0-20211118165945-23d738fc3553 h1:E9f0v1q4EDfrE+0LdkxVtdYKAZ7PGCaj1bBx45R9yEQ=
|
||||
github.com/hyperledger/fabric-protos-go v0.0.0-20211118165945-23d738fc3553/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/jcmturner/gofork v1.0.0 h1:J7uCkflzTEhUZ64xqKnkDxq3kzc96ajM1Gli5ktUem8=
|
||||
github.com/jcmturner/gofork v1.0.0/go.mod h1:MK8+TM0La+2rjBD4jE12Kj1pCCxK7d2LK/UM3ncEo0o=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/jstemmer/go-junit-report v0.0.0-20190106144839-af01ea7f8024/go.mod h1:6v2b51hI/fHJwM22ozAgKL4VKDeJcHhJFhtBdhmNjmU=
|
||||
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
|
||||
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
|
||||
github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.11.0 h1:wJbzvpYMVGG9iTI9VxpnNZfd4DzMPoCWze3GgSqz8yg=
|
||||
github.com/klauspost/compress v1.11.0/go.mod h1:aoV0uJVorq1K+umq18yTdKaF57EivdYsUV+/s2qKfXs=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
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/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU=
|
||||
github.com/mattn/go-isatty v0.0.3/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/dns v1.0.14/go.mod h1:W1PPwlIAgtquWBMBEV9nkV9Cazfe8ScdGz/Lj7v3Nrg=
|
||||
github.com/miekg/pkcs11 v1.0.3 h1:iMwmD7I5225wv84WxIG/bmxz9AXjWvTWIbM/TYHvWtw=
|
||||
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/mitchellh/cli v1.0.0/go.mod h1:hNIlj7HEI86fIcpObd7a0FcrxTWetlwJDGcceTlRvqc=
|
||||
github.com/mitchellh/go-homedir v1.0.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/go-testing-interface v1.0.0/go.mod h1:kRemZodwjscx+RGhAo8eIhFbs2+BFgRtFPeD/KE+zxI=
|
||||
github.com/mitchellh/gox v0.4.0/go.mod h1:Sd9lOJ0+aimLBi73mGofS1ycjY8lL3uZM3JPS42BGNg=
|
||||
github.com/mitchellh/iochan v1.0.0/go.mod h1:JwYml1nuB7xOzsp52dPpHFffvOCDupsG0QubkSMEySY=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20160808181253-ca63d7c062ee/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.4.0 h1:7ks8ZkOP5/ujthUsT07rNv+nkLXCQWKNHuwzOAesEks=
|
||||
github.com/mitchellh/mapstructure v1.4.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
|
||||
github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U=
|
||||
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
|
||||
github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pierrec/lz4 v2.5.2+incompatible h1:WCjObylUIOlKy/+7Abdn34TLIkXiA4UWUMhxq9m9ZXI=
|
||||
github.com/pierrec/lz4 v2.5.2+incompatible/go.mod h1:pdkljMzZIN41W+lC3N2tnIh5sFi+IEE17M5jbnwPHcY=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
|
||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
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/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.3/go.mod h1:/TN21ttK/J9q6uSwhBd54HahCDft0ttaMvbicHlPoso=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20181113130724-41aa239b4cce/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.0/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20190507164030-5867b95ac084/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/tsdb v0.7.1/go.mod h1:qhTCs0VvXwvX/y3TZrWD7rabWM+ijKTux40TwIPHuXU=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ=
|
||||
github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0/go.mod h1:bCqnVzQkZxMG4s8nGwiZ5l3QUCyqpo9Y+/ZMZ9VjZe4=
|
||||
github.com/rogpeppe/fastuuid v0.0.0-20150106093220-6724a57986af/go.mod h1:XWv6SoW27p1b0cqNHllgS5HIMJraePCO15w5zCzIWYg=
|
||||
github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
|
||||
github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
|
||||
github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
|
||||
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
|
||||
github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA=
|
||||
github.com/soheilhy/cmux v0.1.4/go.mod h1:IM3LyeVVIOuxMH7sFAkER9+bJ4dT7Ms6E4xg4kGIyLM=
|
||||
github.com/spaolacci/murmur3 v0.0.0-20180118202830-f09979ecbc72/go.mod h1:JwIasOWyU6f++ZhiEuf87xNszmSA2myDM2Kzu9HwQUA=
|
||||
github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8=
|
||||
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/cobra v1.1.1/go.mod h1:WnodtKOvamDL/PwE2M4iKs8aMDBZ5Q5klgD3qfVJQMI=
|
||||
github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk=
|
||||
github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.7.0/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/spf13/viper v1.7.1 h1:pM5oEahlgWv/WnHXpgbKz7iLIxRf65tye2Ci+XFK5sk=
|
||||
github.com/spf13/viper v1.7.1/go.mod h1:8WkrPz2fc9jxqZNCJI/76HCieCp4Q8HaLFoCha5qpdg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/stretchr/testify v1.7.0 h1:nwc3DEeHmmLAfoZucVR881uASk0Mfjw8xYJ99tb5CcY=
|
||||
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
|
||||
github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
|
||||
github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw=
|
||||
github.com/sykesm/zap-logfmt v0.0.4 h1:U2WzRvmIWG1wDLCFY3sz8UeEmsdHQjHFNlIdmroVFaI=
|
||||
github.com/sykesm/zap-logfmt v0.0.4/go.mod h1:AuBd9xQjAe3URrWT1BBDk2v2onAZHkZkWRMiYZXiZWA=
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/xdg/scram v0.0.0-20180814205039-7eeb5667e42c/go.mod h1:lB8K/P019DLNhemzwFU4jHLhdvlE6uDZjXFejJXr49I=
|
||||
github.com/xdg/stringprep v1.0.0/go.mod h1:Jhud4/sHMO4oL310DaZAKk9ZaJ08SJfe+sJh0HrGL1Y=
|
||||
github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU=
|
||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.etcd.io/bbolt v1.3.2/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU=
|
||||
go.opencensus.io v0.21.0/go.mod h1:mSImk1erAIZhrmZN+AvHh14ztQfjbGwt4TtuofqLduU=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI=
|
||||
go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE=
|
||||
go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/atomic v1.6.0 h1:Ezj3JGmsOnG1MoRWQkPBsKLe9DwWD9QeXzTRzzldNVk=
|
||||
go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ=
|
||||
go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0=
|
||||
go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4=
|
||||
go.uber.org/multierr v1.5.0 h1:KCa4XfM8CWFCpxXRGok+Q0SS/0XBhMDbHHGABQLvD2A=
|
||||
go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU=
|
||||
go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA=
|
||||
go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q=
|
||||
go.uber.org/zap v1.12.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM=
|
||||
go.uber.org/zap v1.16.0 h1:uFRZXykJGK9lLY4HtgSw44DnIcAM+kRBP7x5m+NpAOM=
|
||||
go.uber.org/zap v1.16.0/go.mod h1:MA8QOfq0BHJwdXa996Y4dYkAqRKB8/1K1QMMZVaNZjQ=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181029021203-45a5f77698d3/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190605123033-f99c8df09eb5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a h1:vclmkQCjlDX5OydZ9wv8rBCcS0QyQY66Mpf/7BZbInM=
|
||||
golang.org/x/crypto v0.0.0-20200820211705-5c72a883971a/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/exp v0.0.0-20190510132918-efd6b22b2522/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8=
|
||||
golang.org/x/exp v0.0.0-20190829153037-c13cbed26979/go.mod h1:86+5VVa7VpoJ4kLfm080zCjGlMRFzhUhsZKEZO7MGek=
|
||||
golang.org/x/exp v0.0.0-20191030013958-a1ab85dbe136/go.mod h1:JXzH8nQsPlswgeRAPE3MuO9GYsAcnJvJ4vnMwN/5qkY=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067/go.mod h1:kZ7UVZpmo3dzQBMxlp+ypCbDeSB+sBbTgSJuh5dn5js=
|
||||
golang.org/x/image v0.0.0-20190802002840-cff245a6509b/go.mod h1:FeLwcggjj3mMvU+oOTbSwawSJRM1uh48EjtB4UJZlP0=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190301231843-5614ed5bae6f/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190409202823-959b441ac422/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190909230951-414d861bb4ac/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mobile v0.0.0-20190312151609-d3739f865fa6/go.mod h1:z+o9i4GpDbdi3rU15maQ/Ox0txvL9dWGYEHz965HBQE=
|
||||
golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc=
|
||||
golang.org/x/mod v0.1.0/go.mod h1:0QHyrYULN0/3qlju5TqG8bIK38QM8yzMo5ekMj3DlcY=
|
||||
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181023162649-9b4f9f5ad519/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181201002055-351d144fa1fc/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190503192946-f4e77d36d62c/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200822124328-c89045814202/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20200904194848-62affa334b73/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f h1:OfiFi4JbukWwe3lzw+xunroH1mnC1e2Gy5cxNJApiSY=
|
||||
golang.org/x/net v0.0.0-20211015210444-4f30a5c0130f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181026203630-95b1ffbd15a5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190507160741-ecd444e8653b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190606165138-5da285871e9c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190624142023-c5567b49c5d0/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.0.0-20211110154304-99a53858aa08 h1:WecRHqgE09JBkh/584XIE6PMz5KKE/vER4izNUi30AQ=
|
||||
golang.org/x/sys v0.0.0-20211110154304-99a53858aa08/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.1-0.20180807135948-17ff2d5776d2/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
|
||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
|
||||
golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190312170243-e65039ee4138/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190506145303-2d16b83fe98c/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190606124116-d0a3d012864b/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190628153133-6cdbf07be9d0/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20190911174233-4f2ddba30aff/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191012152004-8de300cfc20a/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191112195655-aa38f8e97acc/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
|
||||
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/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/api v0.4.0/go.mod h1:8k5glujaEP+g9n7WNsDg8QP6cUVNI86fCNMcbazEtwE=
|
||||
google.golang.org/api v0.7.0/go.mod h1:WtwebWUNSVBH/HAw79HIFXZNqEvBhG+Ra+ax0hx3E3M=
|
||||
google.golang.org/api v0.8.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.9.0/go.mod h1:o4eAsZoiT+ibD93RtjEohWalFOjRDx6CVaqeizhEnKg=
|
||||
google.golang.org/api v0.13.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.5.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/appengine v1.6.1/go.mod h1:i06prIuMbXzDqacNJfV5OdTW448YApPu5ww/cMBSeb0=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190307195333-5fe7a883aa19/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190418145605-e7d98fc518a7/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190801165951-fa694d86fc64/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20190911173649-1774047e7e51/go.mod h1:IbNlFCBrqXvoKpeg0TB2l7cyZUmoaFKYIwrEpbDKLA8=
|
||||
google.golang.org/genproto v0.0.0-20191108220845-16a3f7862a1a/go.mod h1:n3cpQtvxv34hfy77yVDNjmbRyujviMdxYliBSkLhpCc=
|
||||
google.golang.org/genproto v0.0.0-20200513103714-09dca8ec2884/go.mod h1:55QSHmfGQM9UVYDPBsyGGes0y52j32PQ3BqQfXhyH3c=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.33.1/go.mod h1:fr5YgcSWrqhRRxogOsw7RzIpsmvOZ6IcH4kBYTpR3n0=
|
||||
google.golang.org/grpc v1.36.0/go.mod h1:qjiiYl8FncCW8feJPdyg3v6XW24KsRHe+dy9BAGRRjU=
|
||||
google.golang.org/grpc v1.42.0 h1:XT2/MFpuPFsEX2fWh3YQtHkZ+WYZFQRfaUgLZYj/p6A=
|
||||
google.golang.org/grpc v1.42.0/go.mod h1:k+4IHHFw41K8+bbowsex27ge2rCb65oeWqe4jJ590SU=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw=
|
||||
google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
google.golang.org/protobuf v1.27.1 h1:SnqbnDw1V7RiZcXPx5MEeqPv2s79L9i7BJUlG/+RurQ=
|
||||
google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
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-20200902074654-038fdea0a05b/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
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/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno=
|
||||
gopkg.in/ini.v1 v1.51.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k=
|
||||
gopkg.in/jcmturner/aescts.v1 v1.0.1 h1:cVVZBK2b1zY26haWB4vbBiZrfFQnfbTVrE3xZq6hrEw=
|
||||
gopkg.in/jcmturner/aescts.v1 v1.0.1/go.mod h1:nsR8qBOg+OucoIW+WMhB3GspUQXq9XorLnQb9XtvcOo=
|
||||
gopkg.in/jcmturner/dnsutils.v1 v1.0.1 h1:cIuC1OLRGZrld+16ZJvvZxVJeKPsvd5eUIvxfoN5hSM=
|
||||
gopkg.in/jcmturner/dnsutils.v1 v1.0.1/go.mod h1:m3v+5svpVOhtFAP/wSz+yzh4Mc0Fg7eRhxkJMWSIz9Q=
|
||||
gopkg.in/jcmturner/goidentity.v3 v3.0.0/go.mod h1:oG2kH0IvSYNIu80dVAyu/yoefjq1mNfM5bm88whjWx4=
|
||||
gopkg.in/jcmturner/gokrb5.v7 v7.5.0 h1:a9tsXlIDD9SKxotJMK3niV7rPZAJeX2aD/0yg3qlIrg=
|
||||
gopkg.in/jcmturner/gokrb5.v7 v7.5.0/go.mod h1:l8VISx+WGYp+Fp7KRbsiUuXTTOnxIc3Tuvyavf11/WM=
|
||||
gopkg.in/jcmturner/rpc.v1 v1.1.0 h1:QHIUxTX1ISuAv9dD2wJ9HWQVuWDX/Zc0PfeC2tjc4rU=
|
||||
gopkg.in/jcmturner/rpc.v1 v1.1.0/go.mod h1:YIdkC4XfD6GXbzje11McwsDuOlZQSb9W4vfLvuNnlv8=
|
||||
gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo=
|
||||
gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
|
||||
gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776 h1:tQIYjPdBoyREyB9XMu+nnTclpTYkz2zFM+lzLJFO4gQ=
|
||||
gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190418001031-e561f6794a2a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg=
|
||||
rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8=
|
||||
|
|
@ -0,0 +1,45 @@
|
|||
{
|
||||
"env": {
|
||||
"node": true,
|
||||
"es6": true
|
||||
},
|
||||
"root": true,
|
||||
"ignorePatterns": [
|
||||
"dist/"
|
||||
],
|
||||
"extends": [
|
||||
"eslint:recommended"
|
||||
],
|
||||
"rules": {
|
||||
"indent": [
|
||||
"error",
|
||||
4
|
||||
],
|
||||
"quotes": [
|
||||
"error",
|
||||
"single"
|
||||
]
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": [
|
||||
"**/*.ts"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"sourceType": "module",
|
||||
"ecmaFeatures": {
|
||||
"impliedStrict": true
|
||||
}
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@typescript-eslint/eslint-recommended",
|
||||
"plugin:@typescript-eslint/recommended"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
14
asset-transfer-basic/application-gateway-typescript/.gitignore
vendored
Normal file
14
asset-transfer-basic/application-gateway-typescript/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Compiled TypeScript files
|
||||
dist
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
{
|
||||
"name": "asset-transfer-basic",
|
||||
"version": "1.0.0",
|
||||
"description": "Asset Transfer Basic Application implemented in typeScript using fabric-gateway",
|
||||
"main": "dist/index.js",
|
||||
"typings": "dist/index.d.ts",
|
||||
"engines": {
|
||||
"node": ">=14"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"build:watch": "tsc -w",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"prepare": "npm run build",
|
||||
"pretest": "npm run lint",
|
||||
"start": "node dist/app.js"
|
||||
},
|
||||
"engineStrict": true,
|
||||
"author": "Hyperledger",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@hyperledger/fabric-gateway": "^1.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tsconfig/node14": "^1.0.1",
|
||||
"@typescript-eslint/eslint-plugin": "^5.6.0",
|
||||
"@typescript-eslint/parser": "^5.6.0",
|
||||
"eslint": "^8.4.1",
|
||||
"typescript": "~4.5.2"
|
||||
}
|
||||
}
|
||||
207
asset-transfer-basic/application-gateway-typescript/src/app.ts
Normal file
207
asset-transfer-basic/application-gateway-typescript/src/app.ts
Normal file
|
|
@ -0,0 +1,207 @@
|
|||
/*
|
||||
* Copyright IBM Corp. All Rights Reserved.
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import * as grpc from '@grpc/grpc-js';
|
||||
import { connect, Contract, Identity, Signer, signers } from '@hyperledger/fabric-gateway';
|
||||
import * as crypto from 'crypto';
|
||||
import { promises as fs } from 'fs';
|
||||
import * as path from 'path';
|
||||
import { TextDecoder } from 'util';
|
||||
|
||||
const channelName = 'mychannel';
|
||||
const chaincodeName = 'basic';
|
||||
const mspId = 'Org1MSP';
|
||||
|
||||
// Path to crypto materials.
|
||||
const cryptoPath = path.resolve(__dirname, '..', '..', '..', 'test-network', 'organizations', 'peerOrganizations', 'org1.example.com');
|
||||
|
||||
// Path to user private key directory.
|
||||
const keyDirectoryPath = path.resolve(cryptoPath, 'users', 'User1@org1.example.com', 'msp', 'keystore');
|
||||
|
||||
// Path to user certificate.
|
||||
const certPath = path.resolve(cryptoPath, 'users', 'User1@org1.example.com', 'msp', 'signcerts', 'cert.pem');
|
||||
|
||||
// Path to peer tls certificate.
|
||||
const tlsCertPath = path.resolve(cryptoPath, 'peers', 'peer0.org1.example.com', 'tls', 'ca.crt');
|
||||
|
||||
// Gateway peer endpoint.
|
||||
const peerEndpoint = 'localhost:7051';
|
||||
|
||||
const utf8Decoder = new TextDecoder();
|
||||
const assetId = `asset${Date.now()}`;
|
||||
|
||||
async function main(): Promise<void> {
|
||||
// The gRPC client connection should be shared by all Gateway connections to this endpoint.
|
||||
const client = await newGrpcConnection();
|
||||
|
||||
const gateway = connect({
|
||||
client,
|
||||
identity: await newIdentity(),
|
||||
signer: await newSigner(),
|
||||
// Default timeouts for different gRPC calls
|
||||
evaluateOptions: () => {
|
||||
return { deadline: Date.now() + 5000 }; // 5 seconds
|
||||
},
|
||||
endorseOptions: () => {
|
||||
return { deadline: Date.now() + 15000 }; // 15 seconds
|
||||
},
|
||||
submitOptions: () => {
|
||||
return { deadline: Date.now() + 5000 }; // 5 seconds
|
||||
},
|
||||
commitStatusOptions: () => {
|
||||
return { deadline: Date.now() + 60000 }; // 1 minute
|
||||
},
|
||||
});
|
||||
|
||||
try {
|
||||
// Get a network instance representing the channel where the smart contract is deployed.
|
||||
const network = gateway.getNetwork(channelName);
|
||||
|
||||
// Get the smart contract from the network.
|
||||
const contract = network.getContract(chaincodeName);
|
||||
|
||||
// Initialize a set of asset data on the ledger using the chaincode 'InitLedger' function.
|
||||
await initLedger(contract);
|
||||
|
||||
// Return all the current assets on the ledger.
|
||||
await getAllAssets(contract);
|
||||
|
||||
// Create a new asset on the ledger.
|
||||
await createAsset(contract);
|
||||
|
||||
// Update an existing asset asynchronously.
|
||||
await transferAssetAsync(contract);
|
||||
|
||||
// Get the asset details by assetID.
|
||||
await readAssetByID(contract);
|
||||
|
||||
// Update an asset which does not exist.
|
||||
await updateNonExistentAsset(contract)
|
||||
} finally {
|
||||
gateway.close();
|
||||
client.close();
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(error => console.error('******** FAILED to run the application:', error));
|
||||
|
||||
async function newGrpcConnection(): Promise<grpc.Client> {
|
||||
const tlsRootCert = await fs.readFile(tlsCertPath);
|
||||
const tlsCredentials = grpc.credentials.createSsl(tlsRootCert);
|
||||
return new grpc.Client(peerEndpoint, tlsCredentials, {
|
||||
'grpc.ssl_target_name_override': 'peer0.org1.example.com',
|
||||
});
|
||||
}
|
||||
|
||||
async function newIdentity(): Promise<Identity> {
|
||||
const credentials = await fs.readFile(certPath);
|
||||
return { mspId, credentials };
|
||||
}
|
||||
|
||||
async function newSigner(): Promise<Signer> {
|
||||
const files = await fs.readdir(keyDirectoryPath);
|
||||
const keyPath = path.resolve(keyDirectoryPath, files[0]);
|
||||
const privateKeyPem = await fs.readFile(keyPath);
|
||||
const privateKey = crypto.createPrivateKey(privateKeyPem);
|
||||
return signers.newPrivateKeySigner(privateKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* This type of transaction would typically only be run once by an application the first time it was started after its
|
||||
* initial deployment. A new version of the chaincode deployed later would likely not need to run an "init" function.
|
||||
*/
|
||||
async function initLedger(contract: Contract): Promise<void> {
|
||||
console.log('\n--> Submit Transaction: InitLedger, function creates the initial set of assets on the ledger');
|
||||
|
||||
await contract.submitTransaction('InitLedger');
|
||||
|
||||
console.log('*** Transaction committed successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate a transaction to query ledger state.
|
||||
*/
|
||||
async function getAllAssets(contract: Contract): Promise<void> {
|
||||
console.log('\n--> Evaluate Transaction: GetAllAssets, function returns all the current assets on the ledger');
|
||||
|
||||
const resultBytes = await contract.evaluateTransaction('GetAllAssets');
|
||||
|
||||
const resultJson = utf8Decoder.decode(resultBytes);
|
||||
const result = JSON.parse(resultJson);
|
||||
console.log('*** Result:', result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit a transaction synchronously, blocking until it has been committed to the ledger.
|
||||
*/
|
||||
async function createAsset(contract: Contract): Promise<void> {
|
||||
console.log('\n--> Submit Transaction: CreateAsset, creates new asset with ID, Color, Size, Owner and AppraisedValue arguments');
|
||||
|
||||
await contract.submitTransaction(
|
||||
'CreateAsset',
|
||||
assetId,
|
||||
'yellow',
|
||||
'5',
|
||||
'Tom',
|
||||
'1300',
|
||||
);
|
||||
|
||||
console.log('*** Transaction committed successfully');
|
||||
}
|
||||
|
||||
/**
|
||||
* Submit transaction asynchronously, allowing the application to process the smart contract response (e.g. update a UI)
|
||||
* while waiting for the commit notification.
|
||||
*/
|
||||
async function transferAssetAsync(contract: Contract): Promise<void> {
|
||||
console.log('\n--> Async Submit Transaction: TransferAsset, updates existing asset owner');
|
||||
|
||||
const commit = await contract.submitAsync('TransferAsset', {
|
||||
arguments: [assetId, 'Saptha'],
|
||||
});
|
||||
const oldOwner = utf8Decoder.decode(commit.getResult());
|
||||
|
||||
console.log(`*** Successfully submitted transaction to transfer ownership from ${oldOwner} to Saptha`);
|
||||
console.log('*** Waiting for transaction commit');
|
||||
|
||||
const status = await commit.getStatus();
|
||||
if (!status.successful) {
|
||||
throw new Error(`Transaction ${status.transactionId} failed to commit with status code ${status.code}`);
|
||||
}
|
||||
|
||||
console.log('*** Transaction committed successfully');
|
||||
}
|
||||
|
||||
async function readAssetByID(contract: Contract): Promise<void> {
|
||||
console.log('\n--> Evaluate Transaction: ReadAsset, function returns asset attributes');
|
||||
|
||||
const resultBytes = await contract.evaluateTransaction('ReadAsset', assetId);
|
||||
|
||||
const resultJson = utf8Decoder.decode(resultBytes);
|
||||
const result = JSON.parse(resultJson);
|
||||
console.log('*** Result:', result);
|
||||
}
|
||||
|
||||
/**
|
||||
* submitTransaction() will throw an error containing details of any error responses from the smart contract.
|
||||
*/
|
||||
async function updateNonExistentAsset(contract: Contract): Promise<void>{
|
||||
console.log('\n--> Submit Transaction: UpdateAsset asset70, asset70 does not exist and should return an error');
|
||||
|
||||
try {
|
||||
await contract.submitTransaction(
|
||||
'UpdateAsset',
|
||||
'asset70',
|
||||
'blue',
|
||||
'5',
|
||||
'Tomoko',
|
||||
'300',
|
||||
);
|
||||
console.log('******** FAILED to return an error');
|
||||
} catch (error) {
|
||||
console.log('*** Successfully caught the error: \n', error);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
{
|
||||
"extends":"@tsconfig/node14/tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"experimentalDecorators": true,
|
||||
"emitDecoratorMetadata": true,
|
||||
"outDir": "dist",
|
||||
"moduleResolution": "node",
|
||||
"declaration": true,
|
||||
"sourceMap": true,
|
||||
"noImplicitAny": true
|
||||
},
|
||||
"include": [
|
||||
"./src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"./src/**/*.spec.ts"
|
||||
]
|
||||
}
|
||||
|
|
@ -2,7 +2,4 @@ module asset-transfer-basic
|
|||
|
||||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/hyperledger/fabric-sdk-go v1.0.0-rc1
|
||||
golang.org/x/tools v0.1.0 // indirect
|
||||
)
|
||||
require github.com/hyperledger/fabric-sdk-go v1.0.0-rc1
|
||||
|
|
|
|||
|
|
@ -1,15 +1,16 @@
|
|||
bitbucket.org/liamstask/goose v0.0.0-20150115234039-8488cc47d90c/go.mod h1:hSVuE3qU7grINVSwrmzHfpg9k87ALBk+XaualNyUzI4=
|
||||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/GeertJohan/go.incremental v1.0.0/go.mod h1:6fAjUhbVuX1KcMD3c8TEgVUqmo4seqhv0i0kdATSkM0=
|
||||
github.com/GeertJohan/go.rice v1.0.0/go.mod h1:eH6gbSOAUv07dQuZVnBmoDP8mgsM1rtixis4Tib9if0=
|
||||
github.com/Knetic/govaluate v3.0.0+incompatible h1:7o6+MAPhYTCF0+fdvoz1xDedhRb4f6s9Tn1Tt7/WTEg=
|
||||
github.com/Knetic/govaluate v3.0.0+incompatible/go.mod h1:r7JcOSlj0wfOMncg0iLm8Leh48TZaKVeNIfJntJ2wa0=
|
||||
github.com/VividCortex/gohistogram v1.0.0 h1:6+hBz+qvs0JOrrNhhmR7lFxo5sINxBCGXrdtl/UvroE=
|
||||
github.com/VividCortex/gohistogram v1.0.0/go.mod h1:Pf5mBqqDxYaXu3hDrrU+w6nw50o/4+TcAqDqk/vUH7g=
|
||||
github.com/akavel/rsrc v0.8.0/go.mod h1:uLoCtb9J+EyAqh+26kdrTgmzRBFPGOolLWKpdxkKq+c=
|
||||
github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc=
|
||||
github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973 h1:xJ4a3vCFaGF/jqvzLMYoU8P317H5OQ+Via4RmuPwCS0=
|
||||
github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q=
|
||||
github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8=
|
||||
github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
|
||||
|
|
@ -18,8 +19,6 @@ github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA
|
|||
github.com/certifi/gocertifi v0.0.0-20180118203423-deb3ae2ef261/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cloudflare/backoff v0.0.0-20161212185259-647f3cdfc87a/go.mod h1:rzgs2ZOiguV6/NpiDgADjRLPNyZlApIWxKpkT+X8SdY=
|
||||
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e85keuznYcH5rqI438v41pKcBl4ZxQ=
|
||||
github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA=
|
||||
github.com/cloudflare/cfssl v1.4.1 h1:vScfU2DrIUI9VPHBVeeAQ0q5A+9yshO1Gz+3QoUQiKw=
|
||||
github.com/cloudflare/cfssl v1.4.1/go.mod h1:KManx/OJPb5QY+y0+o/898AMcM128sF0bURvoVUSjTo=
|
||||
github.com/cloudflare/go-metrics v0.0.0-20151117154305-6a9aea36fb41/go.mod h1:eaZPlJWD+G9wseg1BuRXlHnjntPMrywMsyxf+LTOdP4=
|
||||
|
|
@ -41,48 +40,35 @@ github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9
|
|||
github.com/go-logfmt/logfmt v0.4.0 h1:MP4Eh7ZCb31lleYCFuwm0oe4/YGak+5l1vA2NOE80nA=
|
||||
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
|
||||
github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w=
|
||||
github.com/go-stack/stack v1.8.0 h1:5SgMzNM5HxrEjV0ww2lTmX6E2Izsfxas4+YHWRs3Lsk=
|
||||
github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY=
|
||||
github.com/gogo/protobuf v1.1.1 h1:72R+M5VuhED/KujmZVcIquuo8mBgX4oVda//DQb3PXo=
|
||||
github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0 h1:28o5sBqPkBsMGnC6b4MvE2TzSr5/AT4c/1fLqVGIwlk=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/google/certificate-transparency-go v0.0.0-20180222191210-5ab67e519c93 h1:qdfmdGwtm13OVx+AxguOWUTbgmXGn2TbdUHipo3chMg=
|
||||
github.com/google/certificate-transparency-go v0.0.0-20180222191210-5ab67e519c93/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
|
||||
github.com/google/certificate-transparency-go v1.0.21 h1:Yf1aXowfZ2nuboBsg7iYGLmwsOARdV86pfH3g95wXmE=
|
||||
github.com/google/certificate-transparency-go v1.0.21/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce h1:xdsDDbiBDQTKASoGEZ+pEmF1OnWuu8AQ9I8iNbHNeno=
|
||||
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI=
|
||||
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
|
||||
github.com/hyperledger/fabric-config v0.0.5 h1:khRkm8U9Ghdg8VmZfptgzCFlCzrka8bPfUkM+/j6Zlg=
|
||||
github.com/hyperledger/fabric-config v0.0.5/go.mod h1:YpITBI/+ZayA3XWY5lF302K7PAsFYjEEPM/zr3hegA8=
|
||||
github.com/hyperledger/fabric-lib-go v1.0.0 h1:UL1w7c9LvHZUSkIvHTDGklxFv2kTeva1QI2emOVc324=
|
||||
github.com/hyperledger/fabric-lib-go v1.0.0/go.mod h1:H362nMlunurmHwkYqR5uHL2UDWbQdbfz74n8kbCFsqc=
|
||||
github.com/hyperledger/fabric-protos-go v0.0.0-20191121202242-f5500d5e3e85 h1:bNgEcCg5NVRWs/T+VUEfhgh5Olx/N4VB+0+ybW+oSuA=
|
||||
github.com/hyperledger/fabric-protos-go v0.0.0-20191121202242-f5500d5e3e85/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
|
||||
github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
|
||||
github.com/hyperledger/fabric-protos-go v0.0.0-20200707132912-fee30f3ccd23 h1:SEbB3yH4ISTGRifDamYXAst36gO2kM855ndMJlsv+pc=
|
||||
github.com/hyperledger/fabric-protos-go v0.0.0-20200707132912-fee30f3ccd23/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
|
||||
github.com/hyperledger/fabric-sdk-go v1.0.0-beta1.0.20200526155846-219a09aadc0f h1:eAkJx0+8PBbfP6xZxVRD2agk9W7oDbqllxO+ERgnKJk=
|
||||
github.com/hyperledger/fabric-sdk-go v1.0.0-beta1.0.20200526155846-219a09aadc0f/go.mod h1:/s224b8NLvOJOCIqBvWd9O6u7GE33iuIOT6OfcTE1OE=
|
||||
github.com/hyperledger/fabric-sdk-go v1.0.0-beta2 h1:FBYygns0Qga+mQ4PXycyTU5m4N9KAZM+Ttf7agiV7M8=
|
||||
github.com/hyperledger/fabric-sdk-go v1.0.0-beta2/go.mod h1:/s224b8NLvOJOCIqBvWd9O6u7GE33iuIOT6OfcTE1OE=
|
||||
github.com/hyperledger/fabric-sdk-go v1.0.0-beta3.0.20201006151309-9c426dcc5096 h1:veml7LmfavSHqF8w8z/PGGlfdXvmx5SstQIH6Nyy87c=
|
||||
github.com/hyperledger/fabric-sdk-go v1.0.0-beta3.0.20201006151309-9c426dcc5096/go.mod h1:qWE9Syfg1KbwNjtILk70bJLilnmCvllIYFCSY/pa1RU=
|
||||
github.com/hyperledger/fabric-sdk-go v1.0.0-rc1 h1:cfDo/5ovUZf2dCz08fznUxxVYEWAT4yKJcAh9b+K9Mk=
|
||||
github.com/hyperledger/fabric-sdk-go v1.0.0-rc1/go.mod h1:qWE9Syfg1KbwNjtILk70bJLilnmCvllIYFCSY/pa1RU=
|
||||
github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI=
|
||||
|
|
@ -97,22 +83,19 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv
|
|||
github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY=
|
||||
github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/kylelemons/go-gypsy v0.0.0-20160905020020-08cad365cd28/go.mod h1:T/T7jsxVqf9k/zYOqbgNAsANsjxTd1Yq3htjDhQ1H0c=
|
||||
github.com/lib/pq v0.0.0-20180201184707-88edab080323/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo=
|
||||
github.com/magiconair/properties v1.7.6 h1:U+1DqNen04MdEPgFiIwdOUiqZ8qPa37xgogX/sd3+54=
|
||||
github.com/magiconair/properties v1.7.6/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/magiconair/properties v1.8.1 h1:ZC2Vc7/ZFkGmsVC9KvOjumD+G5lXy2RtTKyzRKO2BQ4=
|
||||
github.com/magiconair/properties v1.8.1/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mattn/go-sqlite3 v1.10.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1 h1:4hp9jkHxhMHkqkrB3Ix0jegS5sx/RkqARlsWZ6pIwiU=
|
||||
github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0=
|
||||
github.com/miekg/pkcs11 v0.0.0-20190329070431-55f3fac3af27/go.mod h1:WCBAbTOdfhHhz7YXujeZMF7owC4tPb1naKFsgfUISjo=
|
||||
github.com/miekg/pkcs11 v1.0.3/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238 h1:+MZW2uvHgN8kYvksEN3f7eFL2wpzk0GxmlFsMybWc7E=
|
||||
github.com/mitchellh/mapstructure v0.0.0-20180220230111-00c29f56e238/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/mitchellh/mapstructure v1.3.2 h1:mRS76wmkOn3KkKAyXDu42V+6ebnXWIztFSYGN7GeoRg=
|
||||
github.com/mitchellh/mapstructure v1.3.2/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo=
|
||||
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
|
||||
|
|
@ -122,12 +105,11 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
|
|||
github.com/mreiferson/go-httpclient v0.0.0-20160630210159-31f0106b4474/go.mod h1:OQA4XLvDbMgS8P0CevmM4m9Q3Jq4phKUzcocxuGJ5m8=
|
||||
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
|
||||
github.com/nkovacs/streamquote v0.0.0-20170412213628-49af9bddb229/go.mod h1:0aYXnNPJ8l7uZxf45rWW1a/uME32OF0rhiYGNQ2oF2E=
|
||||
github.com/onsi/ginkgo v1.6.0 h1:Ix8l273rp3QzYgXSR+c8d1fTG7UPgYkOSELPhiY/YGw=
|
||||
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
|
||||
github.com/onsi/gomega v1.4.2/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/onsi/gomega v1.9.0 h1:R1uwffexN6Pr340GtYRIdZmAiN4J+iw6WG4wog1DUXg=
|
||||
github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA=
|
||||
github.com/op/go-logging v0.0.0-20160315200505-970db520ece7/go.mod h1:HzydrMdWErDVzsI23lYNej1Htcns9BCg93Dk0bBINWk=
|
||||
github.com/pelletier/go-toml v1.1.0 h1:cmiOvKzEunMsAxyhXSzpL5Q1CRKpVv0KQsnAIcSEVYM=
|
||||
github.com/pelletier/go-toml v1.1.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
github.com/pelletier/go-toml v1.8.0 h1:Keo9qb7iRJs2voHvunFtuuYFsbWeOBh8/P9v/kVMFtw=
|
||||
github.com/pelletier/go-toml v1.8.0/go.mod h1:D6yutnOGMveHEPV7VQOuvI/gXY61bv+9bAOTRnLElKs=
|
||||
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
|
||||
|
|
@ -136,54 +118,36 @@ github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||
github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI=
|
||||
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/prometheus/client_golang v0.8.0 h1:1921Yw9Gc3iSc4VQh3PIoOqgPCZS7G/4xQNVUp8Mda8=
|
||||
github.com/prometheus/client_golang v0.8.0/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw=
|
||||
github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo=
|
||||
github.com/prometheus/client_golang v1.1.0 h1:BQ53HtBmfOitExawJ6LokA4x8ov/z0SYYb0+HxJfRI8=
|
||||
github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910 h1:idejC8f05m9MGOsuEi1ATq9shN03HrxNkD/luQvxCv8=
|
||||
github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo=
|
||||
github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4 h1:gQz4mCbXsO+nc9n1hCxHcGA3Zx3Eo+UHZoInFGUIXNM=
|
||||
github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA=
|
||||
github.com/prometheus/common v0.0.0-20180518154759-7600349dcfe1 h1:osmNoEW2SCW3L7EX0km2LYM8HKpNWRiouxjE3XHkyGc=
|
||||
github.com/prometheus/common v0.0.0-20180518154759-7600349dcfe1/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro=
|
||||
github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4=
|
||||
github.com/prometheus/common v0.6.0 h1:kRhiuYSXR3+uv2IbVbZhUxK5zVD/2pp3Gd2PpvPkpEo=
|
||||
github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc=
|
||||
github.com/prometheus/procfs v0.0.0-20180705121852-ae68e2d4c00f h1:c9M4CCa6g8WURSsbrl3lb/w/G1Z5xZpYvhhjdcVDOkE=
|
||||
github.com/prometheus/procfs v0.0.0-20180705121852-ae68e2d4c00f/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk=
|
||||
github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA=
|
||||
github.com/prometheus/procfs v0.0.3 h1:CTwfnzjQ+8dS6MhHHu4YswVAD99sL2wjPqP+VkURmKE=
|
||||
github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ=
|
||||
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/sirupsen/logrus v1.3.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
|
||||
github.com/spf13/afero v1.1.0 h1:bopulORc2JeYaxfHLvJa5NzxviA9PoWhpiiJkru7Ji4=
|
||||
github.com/spf13/afero v1.1.0/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
github.com/spf13/afero v1.3.1 h1:GPTpEAuNr98px18yNQ66JllNil98wfRZ/5Ukny8FeQA=
|
||||
github.com/spf13/afero v1.3.1/go.mod h1:5KUK8ByomD5Ti5Artl0RtHeI5pTF7MIDuXL3yY520V4=
|
||||
github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg=
|
||||
github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
|
||||
github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng=
|
||||
github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||
github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec h1:2ZXvIUGghLpdTVHR1UfvfrzoVlZaE/yOWC5LueIHZig=
|
||||
github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
|
||||
github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk=
|
||||
github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo=
|
||||
github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4=
|
||||
github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/viper v1.0.2 h1:Ncr3ZIuJn322w2k1qmzXDnkLAdQMlJqBa9kfAH+irso=
|
||||
github.com/spf13/viper v1.0.2/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
|
||||
github.com/spf13/viper v1.1.1 h1:/8JBRFO4eoHu1TmpsLgNBq1CQgRUg4GolYlEFieqJgo=
|
||||
github.com/spf13/viper v1.1.1/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0 h1:TivCn/peBQ7UY8ooIcPgZFpTNSz0Q2U6UrFlUfqbe0Q=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
|
|
@ -193,7 +157,6 @@ github.com/valyala/fasttemplate v1.0.1/go.mod h1:UQGH1tvbgY+Nz5t2n7tXsz52dQxojPU
|
|||
github.com/weppos/publicsuffix-go v0.4.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
|
||||
github.com/weppos/publicsuffix-go v0.5.0 h1:rutRtjBJViU/YjcI5d80t4JAVvDltS6bciJg2K1HrLU=
|
||||
github.com/weppos/publicsuffix-go v0.5.0/go.mod h1:z3LCPQ38eedDQSwmsSRW4Y7t2L8Ln16JPQ02lHAdn5k=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
github.com/ziutek/mymysql v1.5.4/go.mod h1:LMSpPZ6DbqWFxNCHW77HeMg9I646SAhApZ/wKdgO/C0=
|
||||
github.com/zmap/rc2 v0.0.0-20131011165748-24b9757f5521/go.mod h1:3YZ9o3WnatTIZhuOtot4IcUfzoKVjUHqu6WALIyI0nE=
|
||||
github.com/zmap/zcertificate v0.0.0-20180516150559-0e3d58b1bac4/go.mod h1:5iU54tB79AMBcySS0R2XIyZBAVmeHranShAFELYx7is=
|
||||
|
|
@ -202,94 +165,68 @@ github.com/zmap/zcrypto v0.0.0-20190729165852-9051775e6a2e/go.mod h1:w7kd3qXHh8F
|
|||
github.com/zmap/zlint v0.0.0-20190806154020-fd021b4cfbeb h1:vxqkjztXSaPVDc8FQCdHTaejm2x747f6yPbnu1h2xkg=
|
||||
github.com/zmap/zlint v0.0.0-20190806154020-fd021b4cfbeb/go.mod h1:29UiAJNsiVdvTBFCJW8e3q6dcDbOoPkhMgttOSCIMMY=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d h1:1ZiEyfaQIg3Qh0EoqpwAakHVhecoE5wlSg5GjnafJGw=
|
||||
golang.org/x/crypto v0.0.0-20200221231518-2aa609cf4a9d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9 h1:psW17arqaxU48Z5kZ0CQnkZWQJsqcURM6tKiBApRjXI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a h1:oWX7TPOiFAMXLq8o0ikBYfCJVlRHBcsciT5bXOrH628=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190227155943-e225da77a7e6/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a h1:1BGLXjeY4akVXGgbC9HugT3Jv3hCI0z56oJR5vAMgBU=
|
||||
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/sys v0.0.0-20190801041406-cbf593c0f2f3 h1:4y9KwBHBgBNwDbtu44R5o1fdOCQUEXhbk/P4A9WmJq0=
|
||||
golang.org/x/sys v0.0.0-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190425150028-36563e24a262/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135 h1:5Beo0mZN8dRzgrMMkDp0jc8YXQKx9DiJ2k1dkvGsn5A=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7 h1:9zdDQZ7Thm29KFXgAX/+yaf3eVbP7djjWp/dXAppNCc=
|
||||
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-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190327125643-d831d65fe17d h1:XB2jc5XQ9uhizGTS2vWcN01bc4dI6z3C4KY5MQm8SS8=
|
||||
google.golang.org/genproto v0.0.0-20190327125643-d831d65fe17d/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.29.1 h1:EC2SB8S04d2r73uptxphDSUG+kTKVgjRPF+N3xpxRB4=
|
||||
google.golang.org/grpc v1.29.1/go.mod h1:itym6AZVZYACWQqET3MqgPpjcuV5QH3BxFS3IjizoKk=
|
||||
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4=
|
||||
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ=
|
||||
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
|
||||
gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
|
||||
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
|
|||
|
|
@ -6,11 +6,18 @@
|
|||
"node": ">=12",
|
||||
"npm": ">=5"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint *.js",
|
||||
"pretest": "npm run lint"
|
||||
},
|
||||
"engineStrict": true,
|
||||
"author": "Hyperledger",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"fabric-ca-client": "^2.2.4",
|
||||
"fabric-network": "^2.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^7.32.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -161,20 +161,27 @@ func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface,
|
|||
return assetJSON != nil, nil
|
||||
}
|
||||
|
||||
// TransferAsset updates the owner field of asset with given id in world state.
|
||||
func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) error {
|
||||
// TransferAsset updates the owner field of asset with given id in world state, and returns the old owner.
|
||||
func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) (string, error) {
|
||||
asset, err := s.ReadAsset(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
oldOwner := asset.Owner
|
||||
asset.Owner = newOwner
|
||||
|
||||
assetJSON, err := json.Marshal(asset)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
return ctx.GetStub().PutState(id, assetJSON)
|
||||
err = ctx.GetStub().PutState(id, assetJSON)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return oldOwner, nil
|
||||
}
|
||||
|
||||
// GetAllAssets returns all assets found in world state
|
||||
|
|
@ -223,9 +230,9 @@ func main() {
|
|||
}
|
||||
|
||||
server := &shim.ChaincodeServer{
|
||||
CCID: config.CCID,
|
||||
Address: config.Address,
|
||||
CC: chaincode,
|
||||
CCID: config.CCID,
|
||||
Address: config.Address,
|
||||
CC: chaincode,
|
||||
TLSProps: getTLSProperties(),
|
||||
}
|
||||
|
||||
|
|
@ -265,9 +272,9 @@ func getTLSProperties() shim.TLSProperties {
|
|||
}
|
||||
|
||||
return shim.TLSProperties{
|
||||
Disabled: tlsDisabled,
|
||||
Key: keyBytes,
|
||||
Cert: certBytes,
|
||||
Disabled: tlsDisabled,
|
||||
Key: keyBytes,
|
||||
Cert: certBytes,
|
||||
ClientCACerts: clientCACertBytes,
|
||||
}
|
||||
}
|
||||
|
|
@ -284,7 +291,7 @@ func getEnvOrDefault(env, defaultVal string) string {
|
|||
// cannot be parsed!
|
||||
func getBoolOrDefault(value string, defaultVal bool) bool {
|
||||
parsed, err := strconv.ParseBool(value)
|
||||
if err!= nil {
|
||||
if err != nil {
|
||||
return defaultVal
|
||||
}
|
||||
return parsed
|
||||
|
|
|
|||
|
|
@ -139,7 +139,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
|
|||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
|
|||
|
|
@ -13,12 +13,14 @@ type SmartContract struct {
|
|||
}
|
||||
|
||||
// Asset describes basic details of what makes up a simple asset
|
||||
//Insert struct field in alphabetic order => to achieve determinism accross languages
|
||||
// golang keeps the order when marshal to json but doesn't order automatically
|
||||
type Asset struct {
|
||||
AppraisedValue int `json:"AppraisedValue"`
|
||||
Color string `json:"Color"`
|
||||
ID string `json:"ID"`
|
||||
Color string `json:"color"`
|
||||
Size int `json:"size"`
|
||||
Owner string `json:"owner"`
|
||||
AppraisedValue int `json:"appraisedValue"`
|
||||
Owner string `json:"Owner"`
|
||||
Size int `json:"Size"`
|
||||
}
|
||||
|
||||
// InitLedger adds a base set of assets to the ledger
|
||||
|
|
@ -140,20 +142,27 @@ func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface,
|
|||
return assetJSON != nil, nil
|
||||
}
|
||||
|
||||
// TransferAsset updates the owner field of asset with given id in world state.
|
||||
func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) error {
|
||||
// TransferAsset updates the owner field of asset with given id in world state, and returns the old owner.
|
||||
func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) (string, error) {
|
||||
asset, err := s.ReadAsset(ctx, id)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
oldOwner := asset.Owner
|
||||
asset.Owner = newOwner
|
||||
|
||||
assetJSON, err := json.Marshal(asset)
|
||||
if err != nil {
|
||||
return err
|
||||
return "", err
|
||||
}
|
||||
|
||||
return ctx.GetStub().PutState(id, assetJSON)
|
||||
err = ctx.GetStub().PutState(id, assetJSON)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return oldOwner, nil
|
||||
}
|
||||
|
||||
// GetAllAssets returns all assets found in world state
|
||||
|
|
|
|||
|
|
@ -143,11 +143,11 @@ func TestTransferAsset(t *testing.T) {
|
|||
|
||||
chaincodeStub.GetStateReturns(bytes, nil)
|
||||
assetTransfer := chaincode.SmartContract{}
|
||||
err = assetTransfer.TransferAsset(transactionContext, "", "")
|
||||
_, err = assetTransfer.TransferAsset(transactionContext, "", "")
|
||||
require.NoError(t, err)
|
||||
|
||||
chaincodeStub.GetStateReturns(nil, fmt.Errorf("unable to retrieve asset"))
|
||||
err = assetTransfer.TransferAsset(transactionContext, "", "")
|
||||
_, err = assetTransfer.TransferAsset(transactionContext, "", "")
|
||||
require.EqualError(t, err, "failed to read from world state: unable to retrieve asset")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -8,5 +8,4 @@ require (
|
|||
github.com/hyperledger/fabric-contract-api-go v1.1.0
|
||||
github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e
|
||||
github.com/stretchr/testify v1.5.1
|
||||
golang.org/x/tools v0.1.0 // indirect
|
||||
)
|
||||
|
|
|
|||
|
|
@ -99,28 +99,19 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:
|
|||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
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=
|
||||
|
|
@ -129,27 +120,14 @@ golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c h1:KfpJVdWhuRqNk4XVXzjXf2KAV4TBEP77SYdFGjeGuIE=
|
||||
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
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-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
|
||||
|
|
@ -161,7 +139,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
|
|||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
|
|||
31
asset-transfer-basic/chaincode-java/Dockerfile
Executable file
31
asset-transfer-basic/chaincode-java/Dockerfile
Executable file
|
|
@ -0,0 +1,31 @@
|
|||
# the first stage
|
||||
FROM gradle:jdk11 AS GRADLE_BUILD
|
||||
ARG CC_SERVER_PORT
|
||||
|
||||
# copy the build.gradle and src code to the container
|
||||
COPY src/ src/
|
||||
COPY build.gradle ./
|
||||
|
||||
# Build and package our code
|
||||
RUN gradle --no-daemon build shadowJar -x checkstyleMain -x checkstyleTest
|
||||
|
||||
|
||||
# the second stage of our build just needs the compiled files
|
||||
FROM openjdk:11-jre
|
||||
|
||||
# Setup tini to work better handle signals
|
||||
ENV TINI_VERSION v0.19.0
|
||||
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
|
||||
RUN chmod +x /tini
|
||||
|
||||
RUN addgroup --system javauser && useradd -g javauser javauser
|
||||
|
||||
# copy only the artifacts we need from the first stage and discard the rest
|
||||
COPY --chown=javauser:javauser --from=GRADLE_BUILD /home/gradle/build/libs/chaincode.jar /chaincode.jar
|
||||
COPY --chown=javauser:javauser docker/docker-entrypoint.sh /docker-entrypoint.sh
|
||||
|
||||
ENV PORT $CC_SERVER_PORT
|
||||
EXPOSE $CC_SERVER_PORT
|
||||
|
||||
USER javauser
|
||||
ENTRYPOINT [ "/tini", "--", "/docker-entrypoint.sh" ]
|
||||
|
|
@ -14,10 +14,10 @@ version '1.0-SNAPSHOT'
|
|||
|
||||
dependencies {
|
||||
|
||||
implementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.+'
|
||||
implementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.4.1'
|
||||
implementation 'org.json:json:+'
|
||||
implementation 'com.owlike:genson:1.5'
|
||||
testImplementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.+'
|
||||
testImplementation 'org.hyperledger.fabric-chaincode-java:fabric-chaincode-shim:2.4.1'
|
||||
testImplementation 'org.junit.jupiter:junit-jupiter:5.4.2'
|
||||
testImplementation 'org.assertj:assertj-core:3.11.1'
|
||||
testImplementation 'org.mockito:mockito-core:2.+'
|
||||
|
|
|
|||
16
asset-transfer-basic/chaincode-java/docker/docker-entrypoint.sh
Executable file
16
asset-transfer-basic/chaincode-java/docker/docker-entrypoint.sh
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
set -euo pipefail
|
||||
: ${CORE_PEER_TLS_ENABLED:="false"}
|
||||
: ${DEBUG:="false"}
|
||||
|
||||
if [ "${DEBUG,,}" = "true" ]; then
|
||||
java -agentlib:jdwp=transport=dt_socket,server=y,suspend=n,address=0.0.0.0:8000 -jar /chaincode.jar
|
||||
elif [ "${CORE_PEER_TLS_ENABLED,,}" = "true" ]; then
|
||||
java -jar /chaincode.jar # todo
|
||||
else
|
||||
java -jar /chaincode.jar
|
||||
fi
|
||||
|
||||
|
|
@ -7,6 +7,7 @@ package org.hyperledger.fabric.samples.assettransfer;
|
|||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
|
||||
import org.hyperledger.fabric.contract.Context;
|
||||
import org.hyperledger.fabric.contract.ContractInterface;
|
||||
import org.hyperledger.fabric.contract.annotation.Contact;
|
||||
|
|
@ -86,8 +87,9 @@ public final class AssetTransfer implements ContractInterface {
|
|||
}
|
||||
|
||||
Asset asset = new Asset(assetID, color, size, owner, appraisedValue);
|
||||
String assetJSON = genson.serialize(asset);
|
||||
stub.putStringState(assetID, assetJSON);
|
||||
//Use Genson to convert the Asset into string, sort it alphabetically and serialize it into a json string
|
||||
String sortedJson = genson.serialize(asset);
|
||||
stub.putStringState(assetID, sortedJson);
|
||||
|
||||
return asset;
|
||||
}
|
||||
|
|
@ -137,9 +139,9 @@ public final class AssetTransfer implements ContractInterface {
|
|||
}
|
||||
|
||||
Asset newAsset = new Asset(assetID, color, size, owner, appraisedValue);
|
||||
String newAssetJSON = genson.serialize(newAsset);
|
||||
stub.putStringState(assetID, newAssetJSON);
|
||||
|
||||
//Use Genson to convert the Asset into string, sort it alphabetically and serialize it into a json string
|
||||
String sortedJson = genson.serialize(newAsset);
|
||||
stub.putStringState(assetID, sortedJson);
|
||||
return newAsset;
|
||||
}
|
||||
|
||||
|
|
@ -183,10 +185,10 @@ public final class AssetTransfer implements ContractInterface {
|
|||
* @param ctx the transaction context
|
||||
* @param assetID the ID of the asset being transferred
|
||||
* @param newOwner the new owner
|
||||
* @return the updated asset
|
||||
* @return the old owner
|
||||
*/
|
||||
@Transaction(intent = Transaction.TYPE.SUBMIT)
|
||||
public Asset TransferAsset(final Context ctx, final String assetID, final String newOwner) {
|
||||
public String TransferAsset(final Context ctx, final String assetID, final String newOwner) {
|
||||
ChaincodeStub stub = ctx.getStub();
|
||||
String assetJSON = stub.getStringState(assetID);
|
||||
|
||||
|
|
@ -199,10 +201,11 @@ public final class AssetTransfer implements ContractInterface {
|
|||
Asset asset = genson.deserialize(assetJSON, Asset.class);
|
||||
|
||||
Asset newAsset = new Asset(asset.getAssetID(), asset.getColor(), asset.getSize(), newOwner, asset.getAppraisedValue());
|
||||
String newAssetJSON = genson.serialize(newAsset);
|
||||
stub.putStringState(assetID, newAssetJSON);
|
||||
//Use a Genson to conver the Asset into string, sort it alphabetically and serialize it into a json string
|
||||
String sortedJson = genson.serialize(newAsset);
|
||||
stub.putStringState(assetID, sortedJson);
|
||||
|
||||
return newAsset;
|
||||
return asset.getOwner();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -225,8 +228,8 @@ public final class AssetTransfer implements ContractInterface {
|
|||
|
||||
for (KeyValue result: results) {
|
||||
Asset asset = genson.deserialize(result.getStringValue(), Asset.class);
|
||||
System.out.println(asset);
|
||||
queryResults.add(asset);
|
||||
System.out.println(asset.toString());
|
||||
}
|
||||
|
||||
final String response = genson.serialize(queryResults);
|
||||
|
|
|
|||
|
|
@ -224,9 +224,9 @@ public final class AssetTransferTest {
|
|||
when(stub.getStringState("asset1"))
|
||||
.thenReturn("{ \"assetID\": \"asset1\", \"color\": \"blue\", \"size\": 5, \"owner\": \"Tomoko\", \"appraisedValue\": 300 }");
|
||||
|
||||
Asset asset = contract.TransferAsset(ctx, "asset1", "Dr Evil");
|
||||
String oldOwner = contract.TransferAsset(ctx, "asset1", "Dr Evil");
|
||||
|
||||
assertThat(asset).isEqualTo(new Asset("asset1", "blue", 5, "Dr Evil", 300));
|
||||
assertThat(oldOwner).isEqualTo("Tomoko");
|
||||
}
|
||||
|
||||
@Test
|
||||
|
|
|
|||
|
|
@ -6,6 +6,9 @@
|
|||
|
||||
'use strict';
|
||||
|
||||
// Deterministic JSON.stringify()
|
||||
const stringify = require('json-stringify-deterministic');
|
||||
const sortKeysRecursive = require('sort-keys-recursive');
|
||||
const { Contract } = require('fabric-contract-api');
|
||||
|
||||
class AssetTransfer extends Contract {
|
||||
|
|
@ -58,8 +61,11 @@ class AssetTransfer extends Contract {
|
|||
|
||||
for (const asset of assets) {
|
||||
asset.docType = 'asset';
|
||||
await ctx.stub.putState(asset.ID, Buffer.from(JSON.stringify(asset)));
|
||||
console.info(`Asset ${asset.ID} initialized`);
|
||||
// example of how to write to world state deterministically
|
||||
// use convetion of alphabetic order
|
||||
// we insert data in alphabetic order using 'json-stringify-deterministic' and 'sort-keys-recursive'
|
||||
// when retrieving data, in any lang, the order of data will be the same and consequently also the corresonding hash
|
||||
await ctx.stub.putState(asset.ID, Buffer.from(stringify(sortKeysRecursive(asset))));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -77,7 +83,8 @@ class AssetTransfer extends Contract {
|
|||
Owner: owner,
|
||||
AppraisedValue: appraisedValue,
|
||||
};
|
||||
await ctx.stub.putState(id, Buffer.from(JSON.stringify(asset)));
|
||||
//we insert data in alphabetic order using 'json-stringify-deterministic' and 'sort-keys-recursive'
|
||||
await ctx.stub.putState(id, Buffer.from(stringify(sortKeysRecursive(asset))));
|
||||
return JSON.stringify(asset);
|
||||
}
|
||||
|
||||
|
|
@ -105,7 +112,8 @@ class AssetTransfer extends Contract {
|
|||
Owner: owner,
|
||||
AppraisedValue: appraisedValue,
|
||||
};
|
||||
return ctx.stub.putState(id, Buffer.from(JSON.stringify(updatedAsset)));
|
||||
// we insert data in alphabetic order using 'json-stringify-deterministic' and 'sort-keys-recursive'
|
||||
return ctx.stub.putState(id, Buffer.from(stringify(sortKeysRecursive(updatedAsset))));
|
||||
}
|
||||
|
||||
// DeleteAsset deletes an given asset from the world state.
|
||||
|
|
@ -127,8 +135,11 @@ class AssetTransfer extends Contract {
|
|||
async TransferAsset(ctx, id, newOwner) {
|
||||
const assetString = await this.ReadAsset(ctx, id);
|
||||
const asset = JSON.parse(assetString);
|
||||
const oldOwner = asset.Owner;
|
||||
asset.Owner = newOwner;
|
||||
return ctx.stub.putState(id, Buffer.from(JSON.stringify(asset)));
|
||||
// we insert data in alphabetic order using 'json-stringify-deterministic' and 'sort-keys-recursive'
|
||||
ctx.stub.putState(id, Buffer.from(stringify(sortKeysRecursive(asset))));
|
||||
return oldOwner;
|
||||
}
|
||||
|
||||
// GetAllAssets returns all assets found in the world state.
|
||||
|
|
@ -146,7 +157,7 @@ class AssetTransfer extends Contract {
|
|||
console.log(err);
|
||||
record = strValue;
|
||||
}
|
||||
allResults.push({ Key: result.value.key, Record: record });
|
||||
allResults.push(record);
|
||||
result = await iterator.next();
|
||||
}
|
||||
return JSON.stringify(allResults);
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
"npm": ">=5"
|
||||
},
|
||||
"scripts": {
|
||||
"lint": "eslint .",
|
||||
"lint": "eslint *.js */**.js",
|
||||
"pretest": "npm run lint",
|
||||
"test": "nyc mocha --recursive",
|
||||
"start": "fabric-chaincode-node start"
|
||||
|
|
@ -18,7 +18,9 @@
|
|||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"fabric-contract-api": "^2.0.0",
|
||||
"fabric-shim": "^2.0.0"
|
||||
"fabric-shim": "^2.0.0",
|
||||
"json-stringify-deterministic": "^1.0.1",
|
||||
"sort-keys-recursive": "^2.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"chai": "^4.1.2",
|
||||
|
|
|
|||
34
asset-transfer-basic/chaincode-typescript/Dockerfile
Normal file
34
asset-transfer-basic/chaincode-typescript/Dockerfile
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
FROM node:16 AS builder
|
||||
ARG CC_SERVER_PORT
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
|
||||
# Copy node.js source and build, changing owner as well
|
||||
COPY --chown=node:node . /usr/src/app
|
||||
RUN npm ci && npm run package
|
||||
|
||||
|
||||
FROM node:16 AS production
|
||||
|
||||
# Setup tini to work better handle signals
|
||||
ENV TINI_VERSION v0.19.0
|
||||
ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini
|
||||
RUN chmod +x /tini
|
||||
|
||||
|
||||
WORKDIR /usr/src/app
|
||||
COPY --chown=node:node --from=builder /usr/src/app/dist ./dist
|
||||
COPY --chown=node:node --from=builder /usr/src/app/package.json ./
|
||||
COPY --chown=node:node --from=builder /usr/src/app/npm-shrinkwrap.json ./
|
||||
COPY --chown=node:node docker/docker-entrypoint.sh /usr/src/app/docker-entrypoint.sh
|
||||
RUN npm ci --only=production
|
||||
|
||||
ENV PORT $CC_SERVER_PORT
|
||||
EXPOSE $CC_SERVER_PORT
|
||||
ENV NODE_ENV=production
|
||||
|
||||
USER node
|
||||
ENTRYPOINT [ "/tini", "--", "/usr/src/app/docker-entrypoint.sh" ]
|
||||
16
asset-transfer-basic/chaincode-typescript/docker/docker-entrypoint.sh
Executable file
16
asset-transfer-basic/chaincode-typescript/docker/docker-entrypoint.sh
Executable file
|
|
@ -0,0 +1,16 @@
|
|||
#!/usr/bin/env bash
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
set -euo pipefail
|
||||
: ${CORE_PEER_TLS_ENABLED:="false"}
|
||||
: ${DEBUG:="false"}
|
||||
|
||||
if [ "${DEBUG,,}" = "true" ]; then
|
||||
npm run start:server-debug
|
||||
elif [ "${CORE_PEER_TLS_ENABLED,,}" = "true" ]; then
|
||||
npm run start:server
|
||||
else
|
||||
npm run start:server-nontls
|
||||
fi
|
||||
|
||||
5172
asset-transfer-basic/chaincode-typescript/npm-shrinkwrap.json
generated
Normal file
5172
asset-transfer-basic/chaincode-typescript/npm-shrinkwrap.json
generated
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -12,17 +12,24 @@
|
|||
"lint": "tslint -c tslint.json 'src/**/*.ts'",
|
||||
"pretest": "npm run lint",
|
||||
"test": "nyc mocha -r ts-node/register src/**/*.spec.ts",
|
||||
"start": "fabric-chaincode-node start",
|
||||
"start": "set -x && fabric-chaincode-node start",
|
||||
"build": "tsc",
|
||||
"build:watch": "tsc -w",
|
||||
"prepublishOnly": "npm run build"
|
||||
"prepublishOnly": "npm run build",
|
||||
"docker": "docker build -f ./Dockerfile -t asset-transfer-basic .",
|
||||
"package": "npm run build && npm shrinkwrap",
|
||||
"start:server-nontls": "set -x && fabric-chaincode-node server --chaincode-address=$CHAINCODE_SERVER_ADDRESS --chaincode-id=$CHAINCODE_ID",
|
||||
"start:server-debug": "set -x && NODE_OPTIONS='--inspect=0.0.0.0:9229' fabric-chaincode-node server --chaincode-address=$CHAINCODE_SERVER_ADDRESS --chaincode-id=$CHAINCODE_ID",
|
||||
"start:server": "set -x && fabric-chaincode-node server --chaincode-address=$CHAINCODE_SERVER_ADDRESS --chaincode-id=$CHAINCODE_ID --chaincode-tls-key-file=/hyperledger/privatekey.pem --chaincode-tls-client-cacert-file=/hyperledger/rootcert.pem --chaincode-tls-cert-file=/hyperledger/cert.pem"
|
||||
},
|
||||
"engineStrict": true,
|
||||
"author": "Hyperledger",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"fabric-contract-api": "^2.0.0",
|
||||
"fabric-shim": "^2.0.0"
|
||||
"fabric-contract-api": "^2.4.0",
|
||||
"fabric-shim": "^2.4.0",
|
||||
"json-stringify-deterministic": "^1.0.1",
|
||||
"sort-keys-recursive": "^2.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.1.7",
|
||||
|
|
|
|||
|
|
@ -1,8 +1,10 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
// Deterministic JSON.stringify()
|
||||
import {Context, Contract, Info, Returns, Transaction} from 'fabric-contract-api';
|
||||
import stringify from 'json-stringify-deterministic';
|
||||
import sortKeysRecursive from 'sort-keys-recursive';
|
||||
import {Asset} from './asset';
|
||||
|
||||
@Info({title: 'AssetTransfer', description: 'Smart contract for trading assets'})
|
||||
|
|
@ -57,7 +59,11 @@ export class AssetTransferContract extends Contract {
|
|||
|
||||
for (const asset of assets) {
|
||||
asset.docType = 'asset';
|
||||
await ctx.stub.putState(asset.ID, Buffer.from(JSON.stringify(asset)));
|
||||
// example of how to write to world state deterministically
|
||||
// use convetion of alphabetic order
|
||||
// we insert data in alphabetic order using 'json-stringify-deterministic' and 'sort-keys-recursive'
|
||||
// when retrieving data, in any lang, the order of data will be the same and consequently also the corresonding hash
|
||||
await ctx.stub.putState(asset.ID, Buffer.from(stringify(sortKeysRecursive(asset))));
|
||||
console.info(`Asset ${asset.ID} initialized`);
|
||||
}
|
||||
}
|
||||
|
|
@ -77,7 +83,8 @@ export class AssetTransferContract extends Contract {
|
|||
Owner: owner,
|
||||
AppraisedValue: appraisedValue,
|
||||
};
|
||||
await ctx.stub.putState(id, Buffer.from(JSON.stringify(asset)));
|
||||
// we insert data in alphabetic order using 'json-stringify-deterministic' and 'sort-keys-recursive'
|
||||
await ctx.stub.putState(id, Buffer.from(stringify(sortKeysRecursive(asset))));
|
||||
}
|
||||
|
||||
// ReadAsset returns the asset stored in the world state with given id.
|
||||
|
|
@ -106,7 +113,8 @@ export class AssetTransferContract extends Contract {
|
|||
Owner: owner,
|
||||
AppraisedValue: appraisedValue,
|
||||
};
|
||||
return ctx.stub.putState(id, Buffer.from(JSON.stringify(updatedAsset)));
|
||||
// we insert data in alphabetic order using 'json-stringify-deterministic' and 'sort-keys-recursive'
|
||||
return ctx.stub.putState(id, Buffer.from(stringify(sortKeysRecursive(updatedAsset))));
|
||||
}
|
||||
|
||||
// DeleteAsset deletes an given asset from the world state.
|
||||
|
|
@ -127,13 +135,16 @@ export class AssetTransferContract extends Contract {
|
|||
return assetJSON && assetJSON.length > 0;
|
||||
}
|
||||
|
||||
// TransferAsset updates the owner field of asset with given id in the world state.
|
||||
// TransferAsset updates the owner field of asset with given id in the world state, and returns the old owner.
|
||||
@Transaction()
|
||||
public async TransferAsset(ctx: Context, id: string, newOwner: string): Promise<void> {
|
||||
public async TransferAsset(ctx: Context, id: string, newOwner: string): Promise<string> {
|
||||
const assetString = await this.ReadAsset(ctx, id);
|
||||
const asset = JSON.parse(assetString);
|
||||
const oldOwner = asset.Owner;
|
||||
asset.Owner = newOwner;
|
||||
await ctx.stub.putState(id, Buffer.from(JSON.stringify(asset)));
|
||||
// we insert data in alphabetic order using 'json-stringify-deterministic' and 'sort-keys-recursive'
|
||||
await ctx.stub.putState(id, Buffer.from(stringify(sortKeysRecursive(asset))));
|
||||
return oldOwner;
|
||||
}
|
||||
|
||||
// GetAllAssets returns all assets found in the world state.
|
||||
|
|
@ -153,7 +164,7 @@ export class AssetTransferContract extends Contract {
|
|||
console.log(err);
|
||||
record = strValue;
|
||||
}
|
||||
allResults.push({Key: result.value.key, Record: record});
|
||||
allResults.push(record);
|
||||
result = await iterator.next();
|
||||
}
|
||||
return JSON.stringify(allResults);
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@
|
|||
"target": "es2017",
|
||||
"moduleResolution": "node",
|
||||
"module": "commonjs",
|
||||
"esModuleInterop": true,
|
||||
"declaration": true,
|
||||
"sourceMap": true
|
||||
},
|
||||
|
|
|
|||
4
asset-transfer-basic/rest-api-typescript/.dockerignore
Normal file
4
asset-transfer-basic/rest-api-typescript/.dockerignore
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
node_modules
|
||||
npm-debug.log
|
||||
Dockerfile
|
||||
.gitignore
|
||||
23
asset-transfer-basic/rest-api-typescript/.env.sample
Normal file
23
asset-transfer-basic/rest-api-typescript/.env.sample
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
# Sample .env file
|
||||
#
|
||||
# These are the minimum configuration variables required to start the sample
|
||||
#
|
||||
# See src/config.ts for details and for all the available configuration
|
||||
# variables
|
||||
#
|
||||
|
||||
HLF_CONNECTION_PROFILE_ORG1=
|
||||
|
||||
HLF_CERTIFICATE_ORG1=
|
||||
|
||||
HLF_PRIVATE_KEY_ORG1=
|
||||
|
||||
HLF_CONNECTION_PROFILE_ORG2=
|
||||
|
||||
HLF_CERTIFICATE_ORG2=
|
||||
|
||||
HLF_PRIVATE_KEY_ORG2=
|
||||
|
||||
ORG1_APIKEY=
|
||||
|
||||
ORG2_APIKEY=
|
||||
34
asset-transfer-basic/rest-api-typescript/.eslintrc.json
Normal file
34
asset-transfer-basic/rest-api-typescript/.eslintrc.json
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
{
|
||||
"env": {
|
||||
"node": true,
|
||||
"es2021": true
|
||||
},
|
||||
"extends": [
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:prettier/recommended"
|
||||
],
|
||||
"parser": "@typescript-eslint/parser",
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 12,
|
||||
"sourceType": "module"
|
||||
},
|
||||
"plugins": [
|
||||
"@typescript-eslint"
|
||||
],
|
||||
"rules": {
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
"argsIgnorePattern": "^_"
|
||||
}
|
||||
]
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"files": ["src/**/*.ts"],
|
||||
"parserOptions": {
|
||||
"project": ["./tsconfig.json"]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
15
asset-transfer-basic/rest-api-typescript/.gitignore
vendored
Normal file
15
asset-transfer-basic/rest-api-typescript/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
.env
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
|
||||
# Dependency directories
|
||||
node_modules/
|
||||
jspm_packages/
|
||||
|
||||
# Compiled TypeScript files
|
||||
dist
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"singleQuote": true
|
||||
}
|
||||
25
asset-transfer-basic/rest-api-typescript/Dockerfile
Normal file
25
asset-transfer-basic/rest-api-typescript/Dockerfile
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
FROM node:14-alpine3.14 AS build
|
||||
|
||||
RUN apk add --no-cache g++ make python3 dumb-init
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --chown=node:node . /app
|
||||
|
||||
RUN npm ci
|
||||
RUN npm run build
|
||||
RUN npm prune --production
|
||||
|
||||
FROM node:14-alpine3.14
|
||||
ENV NODE_ENV production
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=build /usr/bin/dumb-init /usr/bin/dumb-init
|
||||
COPY --chown=node:node --from=build /app .
|
||||
|
||||
EXPOSE 3000
|
||||
|
||||
USER node
|
||||
|
||||
ENTRYPOINT [ "dumb-init", "--", "npm", "run"]
|
||||
CMD ["start"]
|
||||
186
asset-transfer-basic/rest-api-typescript/README.md
Normal file
186
asset-transfer-basic/rest-api-typescript/README.md
Normal file
|
|
@ -0,0 +1,186 @@
|
|||
# Asset Transfer REST API Sample
|
||||
|
||||
Prototype sample REST server to demonstrate good Fabric Node SDK practices
|
||||
|
||||
The primary aim of this sample is to show how to write a long running client application using the Fabric Node SDK
|
||||
|
||||
The REST API is intended to work with the [basic asset transfer example](https://github.com/hyperledger/fabric-samples/tree/main/asset-transfer-basic)
|
||||
|
||||
To install the basic asset transfer chaincode on a local Fabric network, follow the [Using the Fabric test network](https://hyperledger-fabric.readthedocs.io/en/release-2.4/test_network.html) tutorial
|
||||
|
||||
## Overview
|
||||
|
||||
The sample creates two long lived connections to a Fabric network in order to submit and evaluate transactions using two different identities
|
||||
|
||||
To ensure requests respond quickly enough to avoid timeouts, all submit transactions are queued for processing and will be retried if they fail
|
||||
|
||||
Submit transactions are retried if they fail with any error, except for errors from the smart contract, or duplicate transaction errors
|
||||
|
||||
Alternatively you might prefer to modify the sample to only retry transactions which fail with specific errors instead, for example:
|
||||
- MVCC_READ_CONFLICT
|
||||
- PHANTOM_READ_CONFLICT
|
||||
- ENDORSEMENT_POLICY_FAILURE
|
||||
- CHAINCODE_VERSION_CONFLICT
|
||||
- EXPIRED_CHAINCODE
|
||||
|
||||
See [src/index.ts](src/index.ts) for a description of the sample code structure, and [src/config.ts](src/config.ts) for details of configuring the sample using environment variables.
|
||||
|
||||
## Usage
|
||||
|
||||
To build and start the sample REST server, you'll need to [download and install an LTS version of node](https://nodejs.org/en/download/)
|
||||
|
||||
Clone the `fabric-samples` repository and change to the `fabric-samples/asset-transfer-basic/rest-api-typescript` directory before running the following commands
|
||||
|
||||
**Note:** these instructions should work with the main branch of `fabric-samples`
|
||||
|
||||
Install dependencies
|
||||
|
||||
```shell
|
||||
npm install
|
||||
```
|
||||
|
||||
Build the REST server
|
||||
|
||||
```shell
|
||||
npm run build
|
||||
```
|
||||
|
||||
Create a `.env` file to configure the server for the test network (make sure TEST_NETWORK_HOME is set to the fully qualified `test-network` directory)
|
||||
|
||||
```shell
|
||||
TEST_NETWORK_HOME=$HOME/fabric-samples/test-network npm run generateEnv
|
||||
```
|
||||
|
||||
Start a Redis server (Redis is used to store the queue of submit transactions)
|
||||
|
||||
```shell
|
||||
npm run start:redis
|
||||
```
|
||||
|
||||
Start the sample REST server
|
||||
|
||||
```shell
|
||||
npm run start:dev
|
||||
```
|
||||
|
||||
### Docker image
|
||||
|
||||
Alternatively, run the following commands in the `fabric-rest-sample/asset-transfer-basic/rest-api-typescript` directory to start the sample in a Docker container
|
||||
|
||||
Build the Docker image
|
||||
|
||||
```shell
|
||||
docker build -t fabric-rest-sample .
|
||||
```
|
||||
|
||||
Create a `.env` file to configure the server for the test network (make sure `TEST_NETWORK_HOME` is set to the fully qualified `test-network` directory and `AS_LOCAL_HOST` is set to `false` so that the server works inside the Docker Compose network)
|
||||
|
||||
```shell
|
||||
TEST_NETWORK_HOME=$HOME/fabric-samples/test-network AS_LOCAL_HOST=false npm run generateEnv
|
||||
```
|
||||
|
||||
Start the sample REST server and Redis server
|
||||
|
||||
```shell
|
||||
docker-compose up -d
|
||||
```
|
||||
|
||||
## REST API
|
||||
|
||||
If everything went well, you can now open a new terminal and try out some basic asset transfer REST calls!
|
||||
|
||||
The examples below require a `SAMPLE_APIKEY` environment variable which must be set to an API key from the `.env` file created above.
|
||||
|
||||
For example, to use the ORG1_APIKEY...
|
||||
|
||||
```
|
||||
SAMPLE_APIKEY=$(grep ORG1_APIKEY .env | cut -d '=' -f 2-)
|
||||
```
|
||||
|
||||
### Get all assets...
|
||||
|
||||
```shell
|
||||
curl --header "X-Api-Key: ${SAMPLE_APIKEY}" http://localhost:3000/api/assets
|
||||
```
|
||||
|
||||
You should see all the available assets, for example
|
||||
|
||||
```
|
||||
[{"AppraisedValue":300,"Color":"blue","ID":"asset1","Owner":"Tomoko","Size":5},{"AppraisedValue":400,"Color":"red","ID":"asset2","Owner":"Brad","Size":5},{"AppraisedValue":500,"Color":"green","ID":"asset3","Owner":"Jin Soo","Size":10},{"AppraisedValue":600,"Color":"yellow","ID":"asset4","Owner":"Max","Size":10},{"AppraisedValue":700,"Color":"black","ID":"asset5","Owner":"Adriana","Size":15},{"AppraisedValue":800,"Color":"white","ID":"asset6","Owner":"Michel","Size":15}]
|
||||
```
|
||||
|
||||
### Check whether an asset exists...
|
||||
|
||||
```shell
|
||||
curl --include --header "X-Api-Key: ${SAMPLE_APIKEY}" --request OPTIONS http://localhost:3000/api/assets/asset7
|
||||
```
|
||||
|
||||
### Create an asset...
|
||||
|
||||
```shell
|
||||
curl --include --header "Content-Type: application/json" --header "X-Api-Key: ${SAMPLE_APIKEY}" --request POST --data '{"id":"asset7","color":"red","size":42,"owner":"Jean","appraisedValue":101}' http://localhost:3000/api/assets
|
||||
```
|
||||
|
||||
The response should include a `jobId` which you can use to check the job status in next step
|
||||
|
||||
```
|
||||
{"status":"Accepted","jobId":"1","timestamp":"2021-10-22T16:27:09.426Z"}
|
||||
```
|
||||
|
||||
### Read job status...
|
||||
|
||||
```shell
|
||||
curl --header "X-Api-Key: ${SAMPLE_APIKEY}" http://localhost:3000/api/jobs/__job_id__
|
||||
```
|
||||
|
||||
The response should include a list of `transactionIds` which you can use to check the transaction status in next step, for example
|
||||
|
||||
```
|
||||
{"jobId":"1","transactionIds":["1dd35c2e5d840fec1dccc6e8cfce886c660c103de3e7b93dd774d04f39eef82a"],"transactionPayload":""}
|
||||
```
|
||||
|
||||
There may be more transaction IDs if the job was retried
|
||||
|
||||
### Read transaction status...
|
||||
|
||||
```shell
|
||||
curl --header "X-Api-Key: ${SAMPLE_APIKEY}" http://localhost:3000/api/transactions/__transaction_id__
|
||||
```
|
||||
|
||||
The response will show the validation code of the transaction, for example
|
||||
|
||||
```
|
||||
{"transactionId":"1dd35c2e5d840fec1dccc6e8cfce886c660c103de3e7b93dd774d04f39eef82a","validationCode":"VALID"}
|
||||
```
|
||||
|
||||
Alternatively, you will get a 404 not found response if the transaction was not committed
|
||||
|
||||
### Read an asset...
|
||||
|
||||
```shell
|
||||
curl --header "X-Api-Key: ${SAMPLE_APIKEY}" http://localhost:3000/api/assets/asset7
|
||||
```
|
||||
|
||||
You should see the newly created asset, for example
|
||||
|
||||
```
|
||||
{"AppraisedValue":101,"Color":"red","ID":"asset7","Owner":"Jean","Size":42}
|
||||
```
|
||||
|
||||
### Update an asset...
|
||||
|
||||
```shell
|
||||
curl --include --header "Content-Type: application/json" --header "X-Api-Key: ${SAMPLE_APIKEY}" --request PUT --data '{"id":"asset7","color":"red","size":11,"owner":"Jean","appraisedValue":101}' http://localhost:3000/api/assets/asset7
|
||||
```
|
||||
|
||||
### Transfer an asset...
|
||||
|
||||
```shell
|
||||
curl --include --header "Content-Type: application/json" --header "X-Api-Key: ${SAMPLE_APIKEY}" --request PATCH --data '[{"op":"replace","path":"/owner","value":"Ashleigh"}]' http://localhost:3000/api/assets/asset7
|
||||
```
|
||||
|
||||
### Delete an asset...
|
||||
|
||||
```shell
|
||||
curl --include --header "X-Api-Key: ${SAMPLE_APIKEY}" --request DELETE http://localhost:3000/api/assets/asset7
|
||||
```
|
||||
89
asset-transfer-basic/rest-api-typescript/demo.http
Normal file
89
asset-transfer-basic/rest-api-typescript/demo.http
Normal file
|
|
@ -0,0 +1,89 @@
|
|||
// Demo file for use with REST Client for Visual Studio Code
|
||||
// See https://github.com/Huachao/vscode-restclient
|
||||
//
|
||||
// Edit the values below to match your environment if required
|
||||
@hostname = localhost
|
||||
@port = {{$dotenv PORT}}
|
||||
@baseUrl = http://{{hostname}}:{{port}}
|
||||
@apiUrl = {{baseUrl}}/api
|
||||
@api-key = {{$dotenv ORG1_APIKEY}}
|
||||
|
||||
### Check the server is ready
|
||||
|
||||
GET {{baseUrl}}/ready HTTP/1.1
|
||||
|
||||
### Check the server is still live
|
||||
|
||||
GET {{baseUrl}}/live HTTP/1.1
|
||||
|
||||
### Get all assets
|
||||
|
||||
GET {{apiUrl}}/assets HTTP/1.1
|
||||
X-Api-Key: {{api-key}}
|
||||
|
||||
### Check if asset exists
|
||||
|
||||
OPTIONS {{apiUrl}}/assets/asset7 HTTP/1.1
|
||||
X-Api-Key: {{api-key}}
|
||||
|
||||
### Create asset
|
||||
|
||||
POST {{apiUrl}}/assets HTTP/1.1
|
||||
content-type: application/json
|
||||
X-Api-Key: {{api-key}}
|
||||
|
||||
{
|
||||
"id": "asset7",
|
||||
"color": "red",
|
||||
"size": 42,
|
||||
"owner": "Jean",
|
||||
"appraisedValue": 101
|
||||
}
|
||||
|
||||
### Read job status
|
||||
|
||||
GET {{apiUrl}}/jobs/__job_id__ HTTP/1.1
|
||||
X-Api-Key: {{api-key}}
|
||||
|
||||
### Read transaction status
|
||||
|
||||
GET {{apiUrl}}/transactions/__transaction_id__ HTTP/1.1
|
||||
X-Api-Key: {{api-key}}
|
||||
|
||||
### Read asset
|
||||
|
||||
GET {{apiUrl}}/assets/asset7 HTTP/1.1
|
||||
X-Api-Key: {{api-key}}
|
||||
|
||||
### Update asset
|
||||
|
||||
PUT {{apiUrl}}/assets/asset7 HTTP/1.1
|
||||
content-type: application/json
|
||||
X-Api-Key: {{api-key}}
|
||||
|
||||
{
|
||||
"id": "asset7",
|
||||
"color": "red",
|
||||
"size": 11,
|
||||
"owner": "Jean",
|
||||
"appraisedValue": 101
|
||||
}
|
||||
|
||||
### Transfer asset
|
||||
|
||||
PATCH {{apiUrl}}/assets/asset7 HTTP/1.1
|
||||
content-type: application/json
|
||||
X-Api-Key: {{api-key}}
|
||||
|
||||
[
|
||||
{
|
||||
"op": "replace",
|
||||
"path": "/owner",
|
||||
"value": "Ashleigh"
|
||||
}
|
||||
]
|
||||
|
||||
### Delete asset
|
||||
|
||||
DELETE {{apiUrl}}/assets/asset7 HTTP/1.1
|
||||
X-Api-Key: {{api-key}}
|
||||
24
asset-transfer-basic/rest-api-typescript/docker-compose.yaml
Normal file
24
asset-transfer-basic/rest-api-typescript/docker-compose.yaml
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
version: '3'
|
||||
# Replace network name with the fabric test-network name
|
||||
services:
|
||||
redis:
|
||||
image: 'redis'
|
||||
ports:
|
||||
- 6379:6379
|
||||
networks:
|
||||
- fabric_test
|
||||
|
||||
nodeapp:
|
||||
image: 'fabric-rest-sample'
|
||||
command: ['start:dotenv']
|
||||
ports:
|
||||
- 3000:3000
|
||||
env_file:
|
||||
- ./.env
|
||||
networks:
|
||||
- fabric_test
|
||||
|
||||
|
||||
networks:
|
||||
fabric_test:
|
||||
external: true
|
||||
208
asset-transfer-basic/rest-api-typescript/jest.config.ts
Normal file
208
asset-transfer-basic/rest-api-typescript/jest.config.ts
Normal file
|
|
@ -0,0 +1,208 @@
|
|||
/*
|
||||
* For a detailed explanation regarding each configuration property and type check, visit:
|
||||
* https://jestjs.io/docs/configuration
|
||||
*/
|
||||
|
||||
export default {
|
||||
// All imported modules in your tests should be mocked automatically
|
||||
// automock: false,
|
||||
|
||||
// Stop running tests after `n` failures
|
||||
// bail: 0,
|
||||
|
||||
// The directory where Jest should store its cached dependency information
|
||||
// cacheDirectory: "/private/var/folders/04/rqvxpdk52gvf1_qq9l8gt4d40000gn/T/jest_dx",
|
||||
|
||||
// Automatically clear mock calls and instances between every test
|
||||
clearMocks: true,
|
||||
|
||||
// Indicates whether the coverage information should be collected while executing the test
|
||||
collectCoverage: true,
|
||||
|
||||
// An array of glob patterns indicating a set of files for which coverage information should be collected
|
||||
// collectCoverageFrom: undefined,
|
||||
|
||||
// The directory where Jest should output its coverage files
|
||||
coverageDirectory: 'coverage',
|
||||
|
||||
// An array of regexp pattern strings used to skip coverage collection
|
||||
// coveragePathIgnorePatterns: [
|
||||
// "/node_modules/"
|
||||
// ],
|
||||
|
||||
// Indicates which provider should be used to instrument code for coverage
|
||||
coverageProvider: 'v8',
|
||||
|
||||
// A list of reporter names that Jest uses when writing coverage reports
|
||||
// coverageReporters: [
|
||||
// "json",
|
||||
// "text",
|
||||
// "lcov",
|
||||
// "clover"
|
||||
// ],
|
||||
|
||||
// An object that configures minimum threshold enforcement for coverage results
|
||||
// coverageThreshold: undefined,
|
||||
|
||||
// A path to a custom dependency extractor
|
||||
// dependencyExtractor: undefined,
|
||||
|
||||
// Make calling deprecated APIs throw helpful error messages
|
||||
// errorOnDeprecated: false,
|
||||
|
||||
// Force coverage collection from ignored files using an array of glob patterns
|
||||
// forceCoverageMatch: [],
|
||||
|
||||
// A path to a module which exports an async function that is triggered once before all test suites
|
||||
// globalSetup: undefined,
|
||||
|
||||
// A path to a module which exports an async function that is triggered once after all test suites
|
||||
// globalTeardown: undefined,
|
||||
|
||||
// A set of global variables that need to be available in all test environments
|
||||
// globals: {},
|
||||
|
||||
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
|
||||
// maxWorkers: "50%",
|
||||
|
||||
// An array of directory names to be searched recursively up from the requiring module's location
|
||||
// moduleDirectories: [
|
||||
// "node_modules"
|
||||
// ],
|
||||
|
||||
// An array of file extensions your modules use
|
||||
// moduleFileExtensions: [
|
||||
// "js",
|
||||
// "jsx",
|
||||
// "ts",
|
||||
// "tsx",
|
||||
// "json",
|
||||
// "node"
|
||||
// ],
|
||||
|
||||
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
|
||||
// moduleNameMapper: {},
|
||||
|
||||
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
|
||||
// modulePathIgnorePatterns: [],
|
||||
|
||||
// Activates notifications for test results
|
||||
// notify: false,
|
||||
|
||||
// An enum that specifies notification mode. Requires { notify: true }
|
||||
// notifyMode: "failure-change",
|
||||
|
||||
// A preset that is used as a base for Jest's configuration
|
||||
preset: 'ts-jest',
|
||||
|
||||
// Run tests from one or more projects
|
||||
// projects: undefined,
|
||||
|
||||
// Use this configuration option to add custom reporters to Jest
|
||||
// reporters: undefined,
|
||||
|
||||
// Automatically reset mock state between every test
|
||||
// resetMocks: false,
|
||||
|
||||
// Reset the module registry before running each individual test
|
||||
// resetModules: false,
|
||||
|
||||
// A path to a custom resolver
|
||||
// resolver: undefined,
|
||||
|
||||
// Automatically restore mock state between every test
|
||||
// restoreMocks: false,
|
||||
|
||||
// The root directory that Jest should scan for tests and modules within
|
||||
// rootDir: undefined,
|
||||
|
||||
// A list of paths to directories that Jest should use to search for files in
|
||||
roots: ['<rootDir>/src'],
|
||||
|
||||
// Allows you to use a custom runner instead of Jest's default test runner
|
||||
// runner: "jest-runner",
|
||||
|
||||
// The paths to modules that run some code to configure or set up the testing environment before each test
|
||||
// setupFiles: [],
|
||||
|
||||
// A list of paths to modules that run some code to configure or set up the testing framework before each test
|
||||
// setupFilesAfterEnv: [],
|
||||
|
||||
// The number of seconds after which a test is considered as slow and reported as such in the results.
|
||||
// slowTestThreshold: 5,
|
||||
|
||||
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
|
||||
// snapshotSerializers: [],
|
||||
|
||||
// The test environment that will be used for testing
|
||||
// testEnvironment: "jest-environment-node",
|
||||
|
||||
// Options that will be passed to the testEnvironment
|
||||
// testEnvironmentOptions: {},
|
||||
|
||||
// Adds a location field to test results
|
||||
// testLocationInResults: false,
|
||||
|
||||
// The glob patterns Jest uses to detect test files
|
||||
testMatch: [
|
||||
// "**/__tests__/**/*.[jt]s?(x)",
|
||||
'**/?(*.)+(spec|test).[tj]s?(x)',
|
||||
],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
|
||||
// testPathIgnorePatterns: [
|
||||
// "/node_modules/"
|
||||
// ],
|
||||
|
||||
// The regexp pattern or array of patterns that Jest uses to detect test files
|
||||
// testRegex: [],
|
||||
|
||||
// This option allows the use of a custom results processor
|
||||
// testResultsProcessor: undefined,
|
||||
|
||||
// This option allows use of a custom test runner
|
||||
// testRunner: "jest-circus/runner",
|
||||
|
||||
// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
|
||||
// testURL: "http://localhost",
|
||||
|
||||
// Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
|
||||
// timers: "real",
|
||||
|
||||
// A map from regular expressions to paths to transformers
|
||||
// transform: undefined,
|
||||
|
||||
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
|
||||
// transformIgnorePatterns: [
|
||||
// "/node_modules/",
|
||||
// "\\.pnp\\.[^\\/]+$"
|
||||
// ],
|
||||
|
||||
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
|
||||
// unmockedModulePathPatterns: undefined,
|
||||
|
||||
// Indicates whether each individual test should be reported during the run
|
||||
// verbose: undefined,
|
||||
|
||||
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
|
||||
// watchPathIgnorePatterns: [],
|
||||
|
||||
// Whether to use watchman for file crawling
|
||||
// watchman: true,
|
||||
};
|
||||
|
||||
// Required environment variable values for the config.ts file
|
||||
process.env = Object.assign(process.env, {
|
||||
HLF_CONNECTION_PROFILE_ORG1: '{"name":"mock-profile-org1"}',
|
||||
HLF_CERTIFICATE_ORG1:
|
||||
'"-----BEGIN CERTIFICATE-----\\nMOCK\\n-----END CERTIFICATE-----\\n"',
|
||||
HLF_PRIVATE_KEY_ORG1:
|
||||
'"-----BEGIN PRIVATE KEY-----\\nMOCK\\n-----END PRIVATE KEY-----\\n"',
|
||||
HLF_CONNECTION_PROFILE_ORG2: '{"name":"mock-profile-org2"}',
|
||||
HLF_CERTIFICATE_ORG2:
|
||||
'"-----BEGIN CERTIFICATE-----\\nMOCK\\n-----END CERTIFICATE-----\\n"',
|
||||
HLF_PRIVATE_KEY_ORG2:
|
||||
'"-----BEGIN PRIVATE KEY-----\\nMOCK\\n-----END PRIVATE KEY-----\\n"',
|
||||
ORG1_APIKEY: 'ORG1MOCKAPIKEY',
|
||||
ORG2_APIKEY: 'ORG2MOCKAPIKEY',
|
||||
});
|
||||
6831
asset-transfer-basic/rest-api-typescript/package-lock.json
generated
Normal file
6831
asset-transfer-basic/rest-api-typescript/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
67
asset-transfer-basic/rest-api-typescript/package.json
Normal file
67
asset-transfer-basic/rest-api-typescript/package.json
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
{
|
||||
"name": "asset-transfer-basic",
|
||||
"version": "1.0.0",
|
||||
"description": "Asset Transfer Basic REST API implemented in TypeScript",
|
||||
"main": "dist/index.js",
|
||||
"engines": {
|
||||
"node": ">=12",
|
||||
"npm": ">=5"
|
||||
},
|
||||
"dependencies": {
|
||||
"bullmq": "^1.47.2",
|
||||
"dotenv": "^10.0.0",
|
||||
"env-var": "^7.0.1",
|
||||
"express": "^4.17.1",
|
||||
"express-validator": "^6.12.0",
|
||||
"fabric-network": "^2.2.10",
|
||||
"helmet": "^4.6.0",
|
||||
"http-status-codes": "^2.1.4",
|
||||
"ioredis": "^4.27.8",
|
||||
"passport": "^0.4.1",
|
||||
"passport-headerapikey": "^1.2.2",
|
||||
"pino": "^6.11.3",
|
||||
"pino-http": "^5.5.0",
|
||||
"source-map-support": "^0.5.19"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/express": "^4.17.12",
|
||||
"@types/ioredis": "^4.26.4",
|
||||
"@types/jest": "^26.0.24",
|
||||
"@types/node": "^15.14.7",
|
||||
"@types/passport": "^1.0.7",
|
||||
"@types/pino": "^6.3.8",
|
||||
"@types/pino-http": "^5.4.1",
|
||||
"@types/supertest": "^2.0.11",
|
||||
"@typescript-eslint/eslint-plugin": "^4.28.0",
|
||||
"@typescript-eslint/parser": "^4.28.0",
|
||||
"eslint": "^7.29.0",
|
||||
"eslint-config-prettier": "^8.3.0",
|
||||
"eslint-plugin-prettier": "^3.4.0",
|
||||
"ioredis-mock": "^5.6.0",
|
||||
"jest": "^27.0.6",
|
||||
"jest-mock-extended": "^2.0.2-beta2",
|
||||
"pino-pretty": "^5.0.2",
|
||||
"prettier": "^2.3.1",
|
||||
"rimraf": "^3.0.2",
|
||||
"supertest": "^6.1.4",
|
||||
"ts-jest": "^27.0.4",
|
||||
"ts-node": "^10.1.0",
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"scripts": {
|
||||
"prebuild": "npm run lint",
|
||||
"build": "tsc",
|
||||
"clean": "rimraf ./dist",
|
||||
"format": "prettier --write \"{src,test}/**/*.ts\"",
|
||||
"generateEnv": "./scripts/generateEnv.sh",
|
||||
"lint": "eslint . --ext .ts",
|
||||
"start": "node --require source-map-support/register ./dist",
|
||||
"start:dotenv": "node --require source-map-support/register --require dotenv/config ./dist",
|
||||
"start:dev": "node --require source-map-support/register --require dotenv/config ./dist | pino-pretty",
|
||||
"start:redis": "docker run -p 6379:6379 --name fabric-sample-redis -d redis --maxmemory-policy noeviction",
|
||||
"test": "jest"
|
||||
},
|
||||
"author": "Hyperledger",
|
||||
"license": "Apache-2.0",
|
||||
"private": true
|
||||
}
|
||||
70
asset-transfer-basic/rest-api-typescript/scripts/generateEnv.sh
Executable file
70
asset-transfer-basic/rest-api-typescript/scripts/generateEnv.sh
Executable file
|
|
@ -0,0 +1,70 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
|
||||
${AS_LOCAL_HOST:=true}
|
||||
|
||||
: "${TEST_NETWORK_HOME:=../..}"
|
||||
: "${CONNECTION_PROFILE_FILE_ORG1:=${TEST_NETWORK_HOME}/organizations/peerOrganizations/org1.example.com/connection-org1.json}"
|
||||
: "${CERTIFICATE_FILE_ORG1:=${TEST_NETWORK_HOME}/organizations/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/signcerts/User1@org1.example.com-cert.pem}"
|
||||
: "${PRIVATE_KEY_FILE_ORG1:=${TEST_NETWORK_HOME}/organizations/peerOrganizations/org1.example.com/users/User1@org1.example.com/msp/keystore/priv_sk}"
|
||||
|
||||
: "${CONNECTION_PROFILE_FILE_ORG2:=${TEST_NETWORK_HOME}/organizations/peerOrganizations/org2.example.com/connection-org2.json}"
|
||||
: "${CERTIFICATE_FILE_ORG2:=${TEST_NETWORK_HOME}/organizations/peerOrganizations/org2.example.com/users/User1@org2.example.com/msp/signcerts/User1@org2.example.com-cert.pem}"
|
||||
: "${PRIVATE_KEY_FILE_ORG2:=${TEST_NETWORK_HOME}/organizations/peerOrganizations/org2.example.com/users/User1@org2.example.com/msp/keystore/priv_sk}"
|
||||
|
||||
|
||||
cat << ENV_END > .env
|
||||
# Generated .env file
|
||||
# See src/config.ts for details of all the available configuration variables
|
||||
#
|
||||
|
||||
LOG_LEVEL=info
|
||||
|
||||
PORT=3000
|
||||
|
||||
HLF_CERTIFICATE_ORG1="$(cat ${CERTIFICATE_FILE_ORG1} | sed -e 's/$/\\n/' | tr -d '\r\n')"
|
||||
|
||||
HLF_PRIVATE_KEY_ORG1="$(cat ${PRIVATE_KEY_FILE_ORG1} | sed -e 's/$/\\n/' | tr -d '\r\n')"
|
||||
|
||||
HLF_CERTIFICATE_ORG2="$(cat ${CERTIFICATE_FILE_ORG2} | sed -e 's/$/\\n/' | tr -d '\r\n')"
|
||||
|
||||
HLF_PRIVATE_KEY_ORG2="$(cat ${PRIVATE_KEY_FILE_ORG2} | sed -e 's/$/\\n/' | tr -d '\r\n')"
|
||||
|
||||
REDIS_PORT=6379
|
||||
|
||||
ORG1_APIKEY=$(uuidgen)
|
||||
|
||||
ORG2_APIKEY=$(uuidgen)
|
||||
|
||||
ENV_END
|
||||
|
||||
if [ "${AS_LOCAL_HOST}" = "true" ]; then
|
||||
|
||||
cat << LOCAL_HOST_END >> .env
|
||||
AS_LOCAL_HOST=true
|
||||
|
||||
HLF_CONNECTION_PROFILE_ORG1=$(cat ${CONNECTION_PROFILE_FILE_ORG1} | jq -c .)
|
||||
|
||||
HLF_CONNECTION_PROFILE_ORG2=$(cat ${CONNECTION_PROFILE_FILE_ORG2} | jq -c .)
|
||||
|
||||
REDIS_HOST=localhost
|
||||
|
||||
LOCAL_HOST_END
|
||||
|
||||
elif [ "${AS_LOCAL_HOST}" = "false" ]; then
|
||||
|
||||
cat << WITH_HOSTNAME_END >> .env
|
||||
AS_LOCAL_HOST=false
|
||||
|
||||
HLF_CONNECTION_PROFILE_ORG1=$(cat ${CONNECTION_PROFILE_FILE_ORG1} | jq -c '.peers["peer0.org1.example.com"].url = "grpcs://peer0.org1.example.com:7051" | .certificateAuthorities["ca.org1.example.com"].url = "https://ca.org1.example.com:7054"')
|
||||
|
||||
HLF_CONNECTION_PROFILE_ORG2=$(cat ${CONNECTION_PROFILE_FILE_ORG2} | jq -c '.peers["peer0.org2.example.com"].url = "grpcs://peer0.org2.example.com:9051" | .certificateAuthorities["ca.org2.example.com"].url = "https://ca.org2.example.com:8054"')
|
||||
|
||||
REDIS_HOST=redis
|
||||
|
||||
WITH_HOSTNAME_END
|
||||
|
||||
fi
|
||||
|
|
@ -0,0 +1,738 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { Job, Queue } from 'bullmq';
|
||||
import { Application } from 'express';
|
||||
import { Contract, Transaction } from 'fabric-network';
|
||||
import * as fabricProtos from 'fabric-protos';
|
||||
import { mock, MockProxy } from 'jest-mock-extended';
|
||||
import { mocked } from 'ts-jest/utils';
|
||||
import request from 'supertest';
|
||||
import * as config from '../config';
|
||||
import { createServer } from '../server';
|
||||
|
||||
jest.mock('../config');
|
||||
jest.mock('bullmq');
|
||||
|
||||
const mockAsset1 = {
|
||||
ID: 'asset1',
|
||||
Color: 'blue',
|
||||
Size: 5,
|
||||
Owner: 'Tomoko',
|
||||
AppraisedValue: 300,
|
||||
};
|
||||
const mockAsset1Buffer = Buffer.from(JSON.stringify(mockAsset1));
|
||||
|
||||
const mockAsset2 = {
|
||||
ID: 'asset2',
|
||||
Color: 'red',
|
||||
Size: 5,
|
||||
Owner: 'Brad',
|
||||
AppraisedValue: 400,
|
||||
};
|
||||
|
||||
const mockAllAssetsBuffer = Buffer.from(
|
||||
JSON.stringify([mockAsset1, mockAsset2])
|
||||
);
|
||||
|
||||
// TODO add tests for server errors
|
||||
describe('Asset Transfer Besic REST API', () => {
|
||||
let app: Application;
|
||||
let mockJobQueue: MockProxy<Queue>;
|
||||
|
||||
beforeEach(async () => {
|
||||
app = await createServer();
|
||||
|
||||
const mockJob = mock<Job>();
|
||||
mockJob.id = '1';
|
||||
mockJobQueue = mock<Queue>();
|
||||
mockJobQueue.add.mockResolvedValue(mockJob);
|
||||
app.locals.jobq = mockJobQueue;
|
||||
});
|
||||
|
||||
describe('/ready', () => {
|
||||
it('GET should respond with 200 OK json', async () => {
|
||||
const response = await request(app).get('/ready');
|
||||
expect(response.statusCode).toEqual(200);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
status: 'OK',
|
||||
timestamp: expect.any(String),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('/live', () => {
|
||||
it('GET should respond with 200 OK json', async () => {
|
||||
const mockBlockchainInfoProto =
|
||||
fabricProtos.common.BlockchainInfo.create();
|
||||
mockBlockchainInfoProto.height = 42;
|
||||
const mockBlockchainInfoBuffer = Buffer.from(
|
||||
fabricProtos.common.BlockchainInfo.encode(
|
||||
mockBlockchainInfoProto
|
||||
).finish()
|
||||
);
|
||||
|
||||
const mockOrg1QsccContract = mock<Contract>();
|
||||
mockOrg1QsccContract.evaluateTransaction
|
||||
.calledWith('GetChainInfo')
|
||||
.mockResolvedValue(mockBlockchainInfoBuffer);
|
||||
app.locals[config.mspIdOrg1] = {
|
||||
qsccContract: mockOrg1QsccContract,
|
||||
};
|
||||
|
||||
const mockOrg2QsccContract = mock<Contract>();
|
||||
mockOrg2QsccContract.evaluateTransaction
|
||||
.calledWith('GetChainInfo')
|
||||
.mockResolvedValue(mockBlockchainInfoBuffer);
|
||||
app.locals[config.mspIdOrg2] = {
|
||||
qsccContract: mockOrg2QsccContract,
|
||||
};
|
||||
|
||||
const response = await request(app).get('/live');
|
||||
expect(response.statusCode).toEqual(200);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
status: 'OK',
|
||||
timestamp: expect.any(String),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('/api/assets', () => {
|
||||
let mockGetAllAssetsTransaction: MockProxy<Transaction>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockGetAllAssetsTransaction = mock<Transaction>();
|
||||
const mockBasicContract = mock<Contract>();
|
||||
mockBasicContract.createTransaction
|
||||
.calledWith('GetAllAssets')
|
||||
.mockReturnValue(mockGetAllAssetsTransaction);
|
||||
app.locals[config.mspIdOrg1] = {
|
||||
assetContract: mockBasicContract,
|
||||
};
|
||||
});
|
||||
|
||||
it('GET should respond with 401 unauthorized json when an invalid API key is specified', async () => {
|
||||
const response = await request(app)
|
||||
.get('/api/assets')
|
||||
.set('X-Api-Key', 'NOTTHERIGHTAPIKEY');
|
||||
expect(response.statusCode).toEqual(401);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
reason: 'NO_VALID_APIKEY',
|
||||
status: 'Unauthorized',
|
||||
timestamp: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('GET should respond with an empty json array when there are no assets', async () => {
|
||||
mockGetAllAssetsTransaction.evaluate.mockResolvedValue(Buffer.from(''));
|
||||
|
||||
const response = await request(app)
|
||||
.get('/api/assets')
|
||||
.set('X-Api-Key', 'ORG1MOCKAPIKEY');
|
||||
expect(response.statusCode).toEqual(200);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual([]);
|
||||
});
|
||||
|
||||
it('GET should respond with json array of assets', async () => {
|
||||
mockGetAllAssetsTransaction.evaluate.mockResolvedValue(
|
||||
mockAllAssetsBuffer
|
||||
);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/api/assets')
|
||||
.set('X-Api-Key', 'ORG1MOCKAPIKEY');
|
||||
expect(response.statusCode).toEqual(200);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual([
|
||||
{
|
||||
ID: 'asset1',
|
||||
Color: 'blue',
|
||||
Size: 5,
|
||||
Owner: 'Tomoko',
|
||||
AppraisedValue: 300,
|
||||
},
|
||||
{
|
||||
ID: 'asset2',
|
||||
Color: 'red',
|
||||
Size: 5,
|
||||
Owner: 'Brad',
|
||||
AppraisedValue: 400,
|
||||
},
|
||||
]);
|
||||
});
|
||||
|
||||
it('POST should respond with 401 unauthorized json when an invalid API key is specified', async () => {
|
||||
const response = await request(app)
|
||||
.post('/api/assets')
|
||||
.send({
|
||||
ID: 'asset6',
|
||||
Color: 'white',
|
||||
Size: 15,
|
||||
Owner: 'Michel',
|
||||
AppraisedValue: 800,
|
||||
})
|
||||
.set('X-Api-Key', 'NOTTHERIGHTAPIKEY');
|
||||
expect(response.statusCode).toEqual(401);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
reason: 'NO_VALID_APIKEY',
|
||||
status: 'Unauthorized',
|
||||
timestamp: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('POST should respond with 400 bad request json for invalid asset json', async () => {
|
||||
const response = await request(app)
|
||||
.post('/api/assets')
|
||||
.send({
|
||||
identifier: 'asset3',
|
||||
color: 'red',
|
||||
size: 5,
|
||||
owner: 'Brad',
|
||||
appraisedValue: 400,
|
||||
})
|
||||
.set('X-Api-Key', 'ORG1MOCKAPIKEY');
|
||||
expect(response.statusCode).toEqual(400);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
status: 'Bad Request',
|
||||
reason: 'VALIDATION_ERROR',
|
||||
errors: [
|
||||
{
|
||||
location: 'body',
|
||||
msg: 'must be a string',
|
||||
param: 'id',
|
||||
},
|
||||
],
|
||||
message: 'Invalid request body',
|
||||
timestamp: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('POST should respond with 202 accepted json', async () => {
|
||||
const response = await request(app)
|
||||
.post('/api/assets')
|
||||
.send({
|
||||
id: 'asset3',
|
||||
color: 'red',
|
||||
size: 5,
|
||||
owner: 'Brad',
|
||||
appraisedValue: 400,
|
||||
})
|
||||
.set('X-Api-Key', 'ORG1MOCKAPIKEY');
|
||||
expect(response.statusCode).toEqual(202);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
status: 'Accepted',
|
||||
jobId: '1',
|
||||
timestamp: expect.any(String),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('/api/assets/:id', () => {
|
||||
let mockAssetExistsTransaction: MockProxy<Transaction>;
|
||||
let mockReadAssetTransaction: MockProxy<Transaction>;
|
||||
|
||||
beforeEach(() => {
|
||||
const mockBasicContract = mock<Contract>();
|
||||
|
||||
mockAssetExistsTransaction = mock<Transaction>();
|
||||
mockBasicContract.createTransaction
|
||||
.calledWith('AssetExists')
|
||||
.mockReturnValue(mockAssetExistsTransaction);
|
||||
|
||||
mockReadAssetTransaction = mock<Transaction>();
|
||||
mockBasicContract.createTransaction
|
||||
.calledWith('ReadAsset')
|
||||
.mockReturnValue(mockReadAssetTransaction);
|
||||
|
||||
app.locals[config.mspIdOrg1] = {
|
||||
assetContract: mockBasicContract,
|
||||
};
|
||||
});
|
||||
|
||||
it('OPTIONS should respond with 401 unauthorized json when an invalid API key is specified', async () => {
|
||||
const response = await request(app)
|
||||
.options('/api/assets/asset1')
|
||||
.set('X-Api-Key', 'NOTTHERIGHTAPIKEY');
|
||||
expect(response.statusCode).toEqual(401);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
reason: 'NO_VALID_APIKEY',
|
||||
status: 'Unauthorized',
|
||||
timestamp: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('OPTIONS should respond with 404 not found json without the allow header when there is no asset with the specified ID', async () => {
|
||||
mockAssetExistsTransaction.evaluate
|
||||
.calledWith('asset3')
|
||||
.mockResolvedValue(Buffer.from('false'));
|
||||
|
||||
const response = await request(app)
|
||||
.options('/api/assets/asset3')
|
||||
.set('X-Api-Key', 'ORG1MOCKAPIKEY');
|
||||
expect(response.statusCode).toEqual(404);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.header).not.toHaveProperty('allow');
|
||||
expect(response.body).toEqual({
|
||||
status: 'Not Found',
|
||||
timestamp: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('OPTIONS should respond with 200 OK json with the allow header', async () => {
|
||||
mockAssetExistsTransaction.evaluate
|
||||
.calledWith('asset1')
|
||||
.mockResolvedValue(Buffer.from('true'));
|
||||
|
||||
const response = await request(app)
|
||||
.options('/api/assets/asset1')
|
||||
.set('X-Api-Key', 'ORG1MOCKAPIKEY');
|
||||
expect(response.statusCode).toEqual(200);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.header).toHaveProperty(
|
||||
'allow',
|
||||
'DELETE,GET,OPTIONS,PATCH,PUT'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
status: 'OK',
|
||||
timestamp: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('GET should respond with 401 unauthorized json when an invalid API key is specified', async () => {
|
||||
const response = await request(app)
|
||||
.get('/api/assets/asset1')
|
||||
.set('X-Api-Key', 'NOTTHERIGHTAPIKEY');
|
||||
expect(response.statusCode).toEqual(401);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
reason: 'NO_VALID_APIKEY',
|
||||
status: 'Unauthorized',
|
||||
timestamp: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('GET should respond with 404 not found json when there is no asset with the specified ID', async () => {
|
||||
mockReadAssetTransaction.evaluate
|
||||
.calledWith('asset3')
|
||||
.mockRejectedValue(new Error('the asset asset3 does not exist'));
|
||||
|
||||
const response = await request(app)
|
||||
.get('/api/assets/asset3')
|
||||
.set('X-Api-Key', 'ORG1MOCKAPIKEY');
|
||||
expect(response.statusCode).toEqual(404);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
status: 'Not Found',
|
||||
timestamp: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('GET should respond with the asset json when the asset exists', async () => {
|
||||
mockReadAssetTransaction.evaluate
|
||||
.calledWith('asset1')
|
||||
.mockResolvedValue(mockAsset1Buffer);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/api/assets/asset1')
|
||||
.set('X-Api-Key', 'ORG1MOCKAPIKEY');
|
||||
expect(response.statusCode).toEqual(200);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
ID: 'asset1',
|
||||
Color: 'blue',
|
||||
Size: 5,
|
||||
Owner: 'Tomoko',
|
||||
AppraisedValue: 300,
|
||||
});
|
||||
});
|
||||
|
||||
it('PUT should respond with 401 unauthorized json when an invalid API key is specified', async () => {
|
||||
const response = await request(app)
|
||||
.put('/api/assets/asset1')
|
||||
.send({
|
||||
id: 'asset3',
|
||||
color: 'red',
|
||||
size: 5,
|
||||
owner: 'Brad',
|
||||
appraisedValue: 400,
|
||||
})
|
||||
.set('X-Api-Key', 'NOTTHERIGHTAPIKEY');
|
||||
expect(response.statusCode).toEqual(401);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
reason: 'NO_VALID_APIKEY',
|
||||
status: 'Unauthorized',
|
||||
timestamp: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('PUT should respond with 400 bad request json when IDs do not match', async () => {
|
||||
const response = await request(app)
|
||||
.put('/api/assets/asset1')
|
||||
.send({
|
||||
id: 'asset2',
|
||||
color: 'red',
|
||||
size: 5,
|
||||
owner: 'Brad',
|
||||
appraisedValue: 400,
|
||||
})
|
||||
.set('X-Api-Key', 'ORG1MOCKAPIKEY');
|
||||
expect(response.statusCode).toEqual(400);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
status: 'Bad Request',
|
||||
reason: 'ASSET_ID_MISMATCH',
|
||||
message: 'Asset IDs must match',
|
||||
timestamp: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('PUT should respond with 400 bad request json for invalid asset json', async () => {
|
||||
const response = await request(app)
|
||||
.put('/api/assets/asset1')
|
||||
.send({
|
||||
identifier: 'asset1',
|
||||
color: 'red',
|
||||
size: 5,
|
||||
owner: 'Brad',
|
||||
appraisedValue: 400,
|
||||
})
|
||||
.set('X-Api-Key', 'ORG1MOCKAPIKEY');
|
||||
expect(response.statusCode).toEqual(400);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
status: 'Bad Request',
|
||||
reason: 'VALIDATION_ERROR',
|
||||
errors: [
|
||||
{
|
||||
location: 'body',
|
||||
msg: 'must be a string',
|
||||
param: 'id',
|
||||
},
|
||||
],
|
||||
message: 'Invalid request body',
|
||||
timestamp: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('PUT should respond with 202 accepted json', async () => {
|
||||
const response = await request(app)
|
||||
.put('/api/assets/asset1')
|
||||
.send({
|
||||
id: 'asset1',
|
||||
color: 'red',
|
||||
size: 5,
|
||||
owner: 'Brad',
|
||||
appraisedValue: 400,
|
||||
})
|
||||
.set('X-Api-Key', 'ORG1MOCKAPIKEY');
|
||||
expect(response.statusCode).toEqual(202);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
status: 'Accepted',
|
||||
jobId: '1',
|
||||
timestamp: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('PATCH should respond with 401 unauthorized json when an invalid API key is specified', async () => {
|
||||
const response = await request(app)
|
||||
.patch('/api/assets/asset1')
|
||||
.send([{ op: 'replace', path: '/owner', value: 'Ashleigh' }])
|
||||
.set('X-Api-Key', 'NOTTHERIGHTAPIKEY');
|
||||
expect(response.statusCode).toEqual(401);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
reason: 'NO_VALID_APIKEY',
|
||||
status: 'Unauthorized',
|
||||
timestamp: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('PATCH should respond with 400 bad request json for invalid patch op/path', async () => {
|
||||
const response = await request(app)
|
||||
.patch('/api/assets/asset1')
|
||||
.send([{ op: 'replace', path: '/color', value: 'orange' }])
|
||||
.set('X-Api-Key', 'ORG1MOCKAPIKEY');
|
||||
expect(response.statusCode).toEqual(400);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
status: 'Bad Request',
|
||||
reason: 'VALIDATION_ERROR',
|
||||
errors: [
|
||||
{
|
||||
location: 'body',
|
||||
msg: "path must be '/owner'",
|
||||
param: '[0].path',
|
||||
value: '/color',
|
||||
},
|
||||
],
|
||||
message: 'Invalid request body',
|
||||
timestamp: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('PATCH should respond with 202 accepted json', async () => {
|
||||
const response = await request(app)
|
||||
.patch('/api/assets/asset1')
|
||||
.send([{ op: 'replace', path: '/owner', value: 'Ashleigh' }])
|
||||
.set('X-Api-Key', 'ORG1MOCKAPIKEY');
|
||||
expect(response.statusCode).toEqual(202);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
status: 'Accepted',
|
||||
jobId: '1',
|
||||
timestamp: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('DELETE should respond with 401 unauthorized json when an invalid API key is specified', async () => {
|
||||
const response = await request(app)
|
||||
.delete('/api/assets/asset1')
|
||||
.set('X-Api-Key', 'NOTTHERIGHTAPIKEY');
|
||||
expect(response.statusCode).toEqual(401);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
reason: 'NO_VALID_APIKEY',
|
||||
status: 'Unauthorized',
|
||||
timestamp: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('DELETE should respond with 202 accepted json', async () => {
|
||||
const response = await request(app)
|
||||
.delete('/api/assets/asset1')
|
||||
.set('X-Api-Key', 'ORG1MOCKAPIKEY');
|
||||
expect(response.statusCode).toEqual(202);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
status: 'Accepted',
|
||||
jobId: '1',
|
||||
timestamp: expect.any(String),
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('/api/jobs/:id', () => {
|
||||
it('GET should respond with 401 unauthorized json when an invalid API key is specified', async () => {
|
||||
const response = await request(app)
|
||||
.get('/api/jobs/1')
|
||||
.set('X-Api-Key', 'NOTTHERIGHTAPIKEY');
|
||||
expect(response.statusCode).toEqual(401);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
reason: 'NO_VALID_APIKEY',
|
||||
status: 'Unauthorized',
|
||||
timestamp: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('GET should respond with 404 not found json when there is no job with the specified ID', async () => {
|
||||
mocked(Job.fromId).mockResolvedValue(undefined);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/api/jobs/3')
|
||||
.set('X-Api-Key', 'ORG1MOCKAPIKEY');
|
||||
expect(response.statusCode).toEqual(404);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
status: 'Not Found',
|
||||
timestamp: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('GET should respond with json details for the specified job ID', async () => {
|
||||
const mockJob = mock<Job>();
|
||||
mockJob.id = '2';
|
||||
mockJob.data = {
|
||||
transactionIds: ['txn1', 'txn2'],
|
||||
};
|
||||
mockJob.returnvalue = {
|
||||
transactionError: 'Mock error',
|
||||
transactionPayload: Buffer.from('Mock payload'),
|
||||
};
|
||||
mockJobQueue.getJob.calledWith('2').mockResolvedValue(mockJob);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/api/jobs/2')
|
||||
.set('X-Api-Key', 'ORG1MOCKAPIKEY');
|
||||
expect(response.statusCode).toEqual(200);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
jobId: '2',
|
||||
transactionIds: ['txn1', 'txn2'],
|
||||
transactionError: 'Mock error',
|
||||
transactionPayload: 'Mock payload',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('/api/transactions/:id', () => {
|
||||
let mockGetTransactionByIDTransaction: MockProxy<Transaction>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockGetTransactionByIDTransaction = mock<Transaction>();
|
||||
const mockQsccContract = mock<Contract>();
|
||||
mockQsccContract.createTransaction
|
||||
.calledWith('GetTransactionByID')
|
||||
.mockReturnValue(mockGetTransactionByIDTransaction);
|
||||
app.locals[config.mspIdOrg1] = {
|
||||
qsccContract: mockQsccContract,
|
||||
};
|
||||
});
|
||||
|
||||
it('GET should respond with 401 unauthorized json when an invalid API key is specified', async () => {
|
||||
const response = await request(app)
|
||||
.get('/api/transactions/txn1')
|
||||
.set('X-Api-Key', 'NOTTHERIGHTAPIKEY');
|
||||
expect(response.statusCode).toEqual(401);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
reason: 'NO_VALID_APIKEY',
|
||||
status: 'Unauthorized',
|
||||
timestamp: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('GET should respond with 404 not found json when there is no transaction with the specified ID', async () => {
|
||||
mockGetTransactionByIDTransaction.evaluate
|
||||
.calledWith('mychannel', 'txn3')
|
||||
.mockRejectedValue(
|
||||
new Error(
|
||||
'Failed to get transaction with id txn3, error Entry not found in index'
|
||||
)
|
||||
);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/api/transactions/txn3')
|
||||
.set('X-Api-Key', 'ORG1MOCKAPIKEY');
|
||||
expect(response.statusCode).toEqual(404);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
status: 'Not Found',
|
||||
timestamp: expect.any(String),
|
||||
});
|
||||
});
|
||||
|
||||
it('GET should respond with json details for the specified transaction ID', async () => {
|
||||
const processedTransactionProto =
|
||||
fabricProtos.protos.ProcessedTransaction.create();
|
||||
processedTransactionProto.validationCode =
|
||||
fabricProtos.protos.TxValidationCode.VALID;
|
||||
const processedTransactionBuffer = Buffer.from(
|
||||
fabricProtos.protos.ProcessedTransaction.encode(
|
||||
processedTransactionProto
|
||||
).finish()
|
||||
);
|
||||
mockGetTransactionByIDTransaction.evaluate
|
||||
.calledWith('mychannel', 'txn2')
|
||||
.mockResolvedValue(processedTransactionBuffer);
|
||||
|
||||
const response = await request(app)
|
||||
.get('/api/transactions/txn2')
|
||||
.set('X-Api-Key', 'ORG1MOCKAPIKEY');
|
||||
expect(response.statusCode).toEqual(200);
|
||||
expect(response.header).toHaveProperty(
|
||||
'content-type',
|
||||
'application/json; charset=utf-8'
|
||||
);
|
||||
expect(response.body).toEqual({
|
||||
transactionId: 'txn2',
|
||||
validationCode: 'VALID',
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
348
asset-transfer-basic/rest-api-typescript/src/assets.router.ts
Normal file
348
asset-transfer-basic/rest-api-typescript/src/assets.router.ts
Normal file
|
|
@ -0,0 +1,348 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* This sample is intended to work with the basic asset transfer
|
||||
* chaincode which imposes some constraints on what is possible here.
|
||||
*
|
||||
* For example,
|
||||
* - There is no validation for Asset IDs
|
||||
* - There are no error codes from the chaincode
|
||||
*
|
||||
* To avoid timeouts, long running tasks should be decoupled from HTTP request
|
||||
* processing
|
||||
*
|
||||
* Submit transactions can potentially be very long running, especially if the
|
||||
* transaction fails and needs to be retried one or more times
|
||||
*
|
||||
* To allow requests to respond quickly enough, this sample queues submit
|
||||
* requests for processing asynchronously and immediately returns 202 Accepted
|
||||
*/
|
||||
|
||||
import express, { Request, Response } from 'express';
|
||||
import { body, validationResult } from 'express-validator';
|
||||
import { Contract } from 'fabric-network';
|
||||
import { getReasonPhrase, StatusCodes } from 'http-status-codes';
|
||||
import { Queue } from 'bullmq';
|
||||
import { AssetNotFoundError } from './errors';
|
||||
import { evatuateTransaction } from './fabric';
|
||||
import { addSubmitTransactionJob } from './jobs';
|
||||
import { logger } from './logger';
|
||||
|
||||
const { ACCEPTED, BAD_REQUEST, INTERNAL_SERVER_ERROR, NOT_FOUND, OK } =
|
||||
StatusCodes;
|
||||
|
||||
export const assetsRouter = express.Router();
|
||||
|
||||
assetsRouter.get('/', async (req: Request, res: Response) => {
|
||||
logger.debug('Get all assets request received');
|
||||
|
||||
try {
|
||||
const mspId = req.user as string;
|
||||
const contract = req.app.locals[mspId]?.assetContract as Contract;
|
||||
|
||||
const data = await evatuateTransaction(contract, 'GetAllAssets');
|
||||
let assets = [];
|
||||
if (data.length > 0) {
|
||||
assets = JSON.parse(data.toString());
|
||||
}
|
||||
|
||||
return res.status(OK).json(assets);
|
||||
} catch (err) {
|
||||
logger.error({ err }, 'Error processing get all assets request');
|
||||
return res.status(INTERNAL_SERVER_ERROR).json({
|
||||
status: getReasonPhrase(INTERNAL_SERVER_ERROR),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
assetsRouter.post(
|
||||
'/',
|
||||
body().isObject().withMessage('body must contain an asset object'),
|
||||
body('id', 'must be a string').notEmpty(),
|
||||
body('color', 'must be a string').notEmpty(),
|
||||
body('size', 'must be a number').isNumeric(),
|
||||
body('owner', 'must be a string').notEmpty(),
|
||||
body('appraisedValue', 'must be a number').isNumeric(),
|
||||
async (req: Request, res: Response) => {
|
||||
logger.debug(req.body, 'Create asset request received');
|
||||
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(BAD_REQUEST).json({
|
||||
status: getReasonPhrase(BAD_REQUEST),
|
||||
reason: 'VALIDATION_ERROR',
|
||||
message: 'Invalid request body',
|
||||
timestamp: new Date().toISOString(),
|
||||
errors: errors.array(),
|
||||
});
|
||||
}
|
||||
|
||||
const mspId = req.user as string;
|
||||
const assetId = req.body.id;
|
||||
|
||||
try {
|
||||
const submitQueue = req.app.locals.jobq as Queue;
|
||||
const jobId = await addSubmitTransactionJob(
|
||||
submitQueue,
|
||||
mspId,
|
||||
'CreateAsset',
|
||||
assetId,
|
||||
req.body.color,
|
||||
req.body.size,
|
||||
req.body.owner,
|
||||
req.body.appraisedValue
|
||||
);
|
||||
|
||||
return res.status(ACCEPTED).json({
|
||||
status: getReasonPhrase(ACCEPTED),
|
||||
jobId: jobId,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
{ err },
|
||||
'Error processing create asset request for asset ID %s',
|
||||
assetId
|
||||
);
|
||||
|
||||
return res.status(INTERNAL_SERVER_ERROR).json({
|
||||
status: getReasonPhrase(INTERNAL_SERVER_ERROR),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
assetsRouter.options('/:assetId', async (req: Request, res: Response) => {
|
||||
const assetId = req.params.assetId;
|
||||
logger.debug('Asset options request received for asset ID %s', assetId);
|
||||
|
||||
try {
|
||||
const mspId = req.user as string;
|
||||
const contract = req.app.locals[mspId]?.assetContract as Contract;
|
||||
|
||||
const data = await evatuateTransaction(contract, 'AssetExists', assetId);
|
||||
const exists = data.toString() === 'true';
|
||||
|
||||
if (exists) {
|
||||
return res
|
||||
.status(OK)
|
||||
.set({
|
||||
Allow: 'DELETE,GET,OPTIONS,PATCH,PUT',
|
||||
})
|
||||
.json({
|
||||
status: getReasonPhrase(OK),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
} else {
|
||||
return res.status(NOT_FOUND).json({
|
||||
status: getReasonPhrase(NOT_FOUND),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
{ err },
|
||||
'Error processing asset options request for asset ID %s',
|
||||
assetId
|
||||
);
|
||||
return res.status(INTERNAL_SERVER_ERROR).json({
|
||||
status: getReasonPhrase(INTERNAL_SERVER_ERROR),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
assetsRouter.get('/:assetId', async (req: Request, res: Response) => {
|
||||
const assetId = req.params.assetId;
|
||||
logger.debug('Read asset request received for asset ID %s', assetId);
|
||||
|
||||
try {
|
||||
const mspId = req.user as string;
|
||||
const contract = req.app.locals[mspId]?.assetContract as Contract;
|
||||
|
||||
const data = await evatuateTransaction(contract, 'ReadAsset', assetId);
|
||||
const asset = JSON.parse(data.toString());
|
||||
|
||||
return res.status(OK).json(asset);
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
{ err },
|
||||
'Error processing read asset request for asset ID %s',
|
||||
assetId
|
||||
);
|
||||
|
||||
if (err instanceof AssetNotFoundError) {
|
||||
return res.status(NOT_FOUND).json({
|
||||
status: getReasonPhrase(NOT_FOUND),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(INTERNAL_SERVER_ERROR).json({
|
||||
status: getReasonPhrase(INTERNAL_SERVER_ERROR),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
assetsRouter.put(
|
||||
'/:assetId',
|
||||
body().isObject().withMessage('body must contain an asset object'),
|
||||
body('id', 'must be a string').notEmpty(),
|
||||
body('color', 'must be a string').notEmpty(),
|
||||
body('size', 'must be a number').isNumeric(),
|
||||
body('owner', 'must be a string').notEmpty(),
|
||||
body('appraisedValue', 'must be a number').isNumeric(),
|
||||
async (req: Request, res: Response) => {
|
||||
logger.debug(req.body, 'Update asset request received');
|
||||
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(BAD_REQUEST).json({
|
||||
status: getReasonPhrase(BAD_REQUEST),
|
||||
reason: 'VALIDATION_ERROR',
|
||||
message: 'Invalid request body',
|
||||
timestamp: new Date().toISOString(),
|
||||
errors: errors.array(),
|
||||
});
|
||||
}
|
||||
|
||||
if (req.params.assetId != req.body.id) {
|
||||
return res.status(BAD_REQUEST).json({
|
||||
status: getReasonPhrase(BAD_REQUEST),
|
||||
reason: 'ASSET_ID_MISMATCH',
|
||||
message: 'Asset IDs must match',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
const mspId = req.user as string;
|
||||
const assetId = req.params.assetId;
|
||||
|
||||
try {
|
||||
const submitQueue = req.app.locals.jobq as Queue;
|
||||
const jobId = await addSubmitTransactionJob(
|
||||
submitQueue,
|
||||
mspId,
|
||||
'UpdateAsset',
|
||||
assetId,
|
||||
req.body.color,
|
||||
req.body.size,
|
||||
req.body.owner,
|
||||
req.body.appraisedValue
|
||||
);
|
||||
|
||||
return res.status(ACCEPTED).json({
|
||||
status: getReasonPhrase(ACCEPTED),
|
||||
jobId: jobId,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
{ err },
|
||||
'Error processing update asset request for asset ID %s',
|
||||
assetId
|
||||
);
|
||||
|
||||
return res.status(INTERNAL_SERVER_ERROR).json({
|
||||
status: getReasonPhrase(INTERNAL_SERVER_ERROR),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
assetsRouter.patch(
|
||||
'/:assetId',
|
||||
body()
|
||||
.isArray({
|
||||
min: 1,
|
||||
max: 1,
|
||||
})
|
||||
.withMessage('body must contain an array with a single patch operation'),
|
||||
body('*.op', "operation must be 'replace'").equals('replace'),
|
||||
body('*.path', "path must be '/owner'").equals('/owner'),
|
||||
body('*.value', 'must be a string').isString(),
|
||||
async (req: Request, res: Response) => {
|
||||
logger.debug(req.body, 'Transfer asset request received');
|
||||
|
||||
const errors = validationResult(req);
|
||||
if (!errors.isEmpty()) {
|
||||
return res.status(BAD_REQUEST).json({
|
||||
status: getReasonPhrase(BAD_REQUEST),
|
||||
reason: 'VALIDATION_ERROR',
|
||||
message: 'Invalid request body',
|
||||
timestamp: new Date().toISOString(),
|
||||
errors: errors.array(),
|
||||
});
|
||||
}
|
||||
|
||||
const mspId = req.user as string;
|
||||
const assetId = req.params.assetId;
|
||||
const newOwner = req.body[0].value;
|
||||
|
||||
try {
|
||||
const submitQueue = req.app.locals.jobq as Queue;
|
||||
const jobId = await addSubmitTransactionJob(
|
||||
submitQueue,
|
||||
mspId,
|
||||
'TransferAsset',
|
||||
assetId,
|
||||
newOwner
|
||||
);
|
||||
|
||||
return res.status(ACCEPTED).json({
|
||||
status: getReasonPhrase(ACCEPTED),
|
||||
jobId: jobId,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
{ err },
|
||||
'Error processing update asset request for asset ID %s',
|
||||
req.params.assetId
|
||||
);
|
||||
|
||||
return res.status(INTERNAL_SERVER_ERROR).json({
|
||||
status: getReasonPhrase(INTERNAL_SERVER_ERROR),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
assetsRouter.delete('/:assetId', async (req: Request, res: Response) => {
|
||||
logger.debug(req.body, 'Delete asset request received');
|
||||
|
||||
const mspId = req.user as string;
|
||||
const assetId = req.params.assetId;
|
||||
|
||||
try {
|
||||
const submitQueue = req.app.locals.jobq as Queue;
|
||||
const jobId = await addSubmitTransactionJob(
|
||||
submitQueue,
|
||||
mspId,
|
||||
'DeleteAsset',
|
||||
assetId
|
||||
);
|
||||
|
||||
return res.status(ACCEPTED).json({
|
||||
status: getReasonPhrase(ACCEPTED),
|
||||
jobId: jobId,
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
} catch (err) {
|
||||
logger.error(
|
||||
{ err },
|
||||
'Error processing delete asset request for asset ID %s',
|
||||
assetId
|
||||
);
|
||||
|
||||
return res.status(INTERNAL_SERVER_ERROR).json({
|
||||
status: getReasonPhrase(INTERNAL_SERVER_ERROR),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
});
|
||||
60
asset-transfer-basic/rest-api-typescript/src/auth.ts
Normal file
60
asset-transfer-basic/rest-api-typescript/src/auth.ts
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { logger } from './logger';
|
||||
import passport from 'passport';
|
||||
import { NextFunction, Request, Response } from 'express';
|
||||
import { HeaderAPIKeyStrategy } from 'passport-headerapikey';
|
||||
import { StatusCodes, getReasonPhrase } from 'http-status-codes';
|
||||
import * as config from './config';
|
||||
|
||||
const { UNAUTHORIZED } = StatusCodes;
|
||||
|
||||
export const fabricAPIKeyStrategy: HeaderAPIKeyStrategy =
|
||||
new HeaderAPIKeyStrategy(
|
||||
{ header: 'X-API-Key', prefix: '' },
|
||||
false,
|
||||
function (apikey, done) {
|
||||
logger.debug({ apikey }, 'Checking X-API-Key');
|
||||
if (apikey === config.org1ApiKey) {
|
||||
const user = config.mspIdOrg1;
|
||||
logger.debug('User set to %s', user);
|
||||
done(null, user);
|
||||
} else if (apikey === config.org2ApiKey) {
|
||||
const user = config.mspIdOrg2;
|
||||
logger.debug('User set to %s', user);
|
||||
done(null, user);
|
||||
} else {
|
||||
logger.debug({ apikey }, 'No valid X-API-Key');
|
||||
return done(null, false);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
export const authenticateApiKey = (
|
||||
req: Request,
|
||||
res: Response,
|
||||
next: NextFunction
|
||||
): void => {
|
||||
passport.authenticate(
|
||||
'headerapikey',
|
||||
{ session: false },
|
||||
(err, user, _info) => {
|
||||
if (err) return next(err);
|
||||
if (!user)
|
||||
return res.status(UNAUTHORIZED).json({
|
||||
status: getReasonPhrase(UNAUTHORIZED),
|
||||
reason: 'NO_VALID_APIKEY',
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
|
||||
req.logIn(user, { session: false }, async (err) => {
|
||||
if (err) {
|
||||
return next(err);
|
||||
}
|
||||
return next();
|
||||
});
|
||||
}
|
||||
)(req, res, next);
|
||||
};
|
||||
575
asset-transfer-basic/rest-api-typescript/src/config.spec.ts
Normal file
575
asset-transfer-basic/rest-api-typescript/src/config.spec.ts
Normal file
|
|
@ -0,0 +1,575 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
/* eslint-disable @typescript-eslint/no-var-requires */
|
||||
|
||||
describe('Config values', () => {
|
||||
const ORIGINAL_ENV = process.env;
|
||||
|
||||
beforeEach(async () => {
|
||||
jest.resetModules();
|
||||
process.env = { ...ORIGINAL_ENV };
|
||||
});
|
||||
|
||||
afterAll(() => {
|
||||
process.env = { ...ORIGINAL_ENV };
|
||||
});
|
||||
|
||||
describe('logLevel', () => {
|
||||
it('defaults to "info"', () => {
|
||||
const config = require('./config');
|
||||
expect(config.logLevel).toBe('info');
|
||||
});
|
||||
|
||||
it('can be configured using the "LOG_LEVEL" environment variable', () => {
|
||||
process.env.LOG_LEVEL = 'debug';
|
||||
const config = require('./config');
|
||||
expect(config.logLevel).toBe('debug');
|
||||
});
|
||||
|
||||
it('throws an error when the "LOG_LEVEL" environment variable has an invalid log level', () => {
|
||||
process.env.LOG_LEVEL = 'ludicrous';
|
||||
expect(() => {
|
||||
require('./config');
|
||||
}).toThrow(
|
||||
'env-var: "LOG_LEVEL" should be one of [fatal, error, warn, info, debug, trace, silent]'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('port', () => {
|
||||
it('defaults to "3000"', () => {
|
||||
const config = require('./config');
|
||||
expect(config.port).toBe(3000);
|
||||
});
|
||||
|
||||
it('can be configured using the "PORT" environment variable', () => {
|
||||
process.env.PORT = '8000';
|
||||
const config = require('./config');
|
||||
expect(config.port).toBe(8000);
|
||||
});
|
||||
|
||||
it('throws an error when the "PORT" environment variable has an invalid port number', () => {
|
||||
process.env.PORT = '65536';
|
||||
expect(() => {
|
||||
require('./config');
|
||||
}).toThrow(
|
||||
'env-var: "PORT" cannot assign a port number greater than 65535. An example of a valid value would be: 3000'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('submitJobBackoffType', () => {
|
||||
it('defaults to "fixed"', () => {
|
||||
const config = require('./config');
|
||||
expect(config.submitJobBackoffType).toBe('fixed');
|
||||
});
|
||||
|
||||
it('can be configured using the "SUBMIT_JOB_BACKOFF_TYPE" environment variable', () => {
|
||||
process.env.SUBMIT_JOB_BACKOFF_TYPE = 'exponential';
|
||||
const config = require('./config');
|
||||
expect(config.submitJobBackoffType).toBe('exponential');
|
||||
});
|
||||
|
||||
it('throws an error when the "LOG_LEVEL" environment variable has an invalid log level', () => {
|
||||
process.env.SUBMIT_JOB_BACKOFF_TYPE = 'jitter';
|
||||
expect(() => {
|
||||
require('./config');
|
||||
}).toThrow(
|
||||
'env-var: "SUBMIT_JOB_BACKOFF_TYPE" should be one of [fixed, exponential]'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('submitJobBackoffDelay', () => {
|
||||
it('defaults to "3000"', () => {
|
||||
const config = require('./config');
|
||||
expect(config.submitJobBackoffDelay).toBe(3000);
|
||||
});
|
||||
|
||||
it('can be configured using the "SUBMIT_JOB_BACKOFF_DELAY" environment variable', () => {
|
||||
process.env.SUBMIT_JOB_BACKOFF_DELAY = '9999';
|
||||
const config = require('./config');
|
||||
expect(config.submitJobBackoffDelay).toBe(9999);
|
||||
});
|
||||
|
||||
it('throws an error when the "SUBMIT_JOB_BACKOFF_DELAY" environment variable has an invalid number', () => {
|
||||
process.env.SUBMIT_JOB_BACKOFF_DELAY = 'short';
|
||||
expect(() => {
|
||||
require('./config');
|
||||
}).toThrow(
|
||||
'env-var: "SUBMIT_JOB_BACKOFF_DELAY" should be a valid integer. An example of a valid value would be: 3000'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('submitJobAttempts', () => {
|
||||
it('defaults to "5"', () => {
|
||||
const config = require('./config');
|
||||
expect(config.submitJobAttempts).toBe(5);
|
||||
});
|
||||
|
||||
it('can be configured using the "SUBMIT_JOB_ATTEMPTS" environment variable', () => {
|
||||
process.env.SUBMIT_JOB_ATTEMPTS = '9999';
|
||||
const config = require('./config');
|
||||
expect(config.submitJobAttempts).toBe(9999);
|
||||
});
|
||||
|
||||
it('throws an error when the "SUBMIT_JOB_ATTEMPTS" environment variable has an invalid number', () => {
|
||||
process.env.SUBMIT_JOB_ATTEMPTS = 'lots';
|
||||
expect(() => {
|
||||
require('./config');
|
||||
}).toThrow(
|
||||
'env-var: "SUBMIT_JOB_ATTEMPTS" should be a valid integer. An example of a valid value would be: 5'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('submitJobConcurrency', () => {
|
||||
it('defaults to "5"', () => {
|
||||
const config = require('./config');
|
||||
expect(config.submitJobConcurrency).toBe(5);
|
||||
});
|
||||
|
||||
it('can be configured using the "SUBMIT_JOB_CONCURRENCY" environment variable', () => {
|
||||
process.env.SUBMIT_JOB_CONCURRENCY = '9999';
|
||||
const config = require('./config');
|
||||
expect(config.submitJobConcurrency).toBe(9999);
|
||||
});
|
||||
|
||||
it('throws an error when the "SUBMIT_JOB_CONCURRENCY" environment variable has an invalid number', () => {
|
||||
process.env.SUBMIT_JOB_CONCURRENCY = 'lots';
|
||||
expect(() => {
|
||||
require('./config');
|
||||
}).toThrow(
|
||||
'env-var: "SUBMIT_JOB_CONCURRENCY" should be a valid integer. An example of a valid value would be: 5'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('maxCompletedSubmitJobs', () => {
|
||||
it('defaults to "1000"', () => {
|
||||
const config = require('./config');
|
||||
expect(config.maxCompletedSubmitJobs).toBe(1000);
|
||||
});
|
||||
|
||||
it('can be configured using the "MAX_COMPLETED_SUBMIT_JOBS" environment variable', () => {
|
||||
process.env.MAX_COMPLETED_SUBMIT_JOBS = '9999';
|
||||
const config = require('./config');
|
||||
expect(config.maxCompletedSubmitJobs).toBe(9999);
|
||||
});
|
||||
|
||||
it('throws an error when the "MAX_COMPLETED_SUBMIT_JOBS" environment variable has an invalid number', () => {
|
||||
process.env.MAX_COMPLETED_SUBMIT_JOBS = 'lots';
|
||||
expect(() => {
|
||||
require('./config');
|
||||
}).toThrow(
|
||||
'env-var: "MAX_COMPLETED_SUBMIT_JOBS" should be a valid integer. An example of a valid value would be: 1000'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('maxFailedSubmitJobs', () => {
|
||||
it('defaults to "1000"', () => {
|
||||
const config = require('./config');
|
||||
expect(config.maxFailedSubmitJobs).toBe(1000);
|
||||
});
|
||||
|
||||
it('can be configured using the "MAX_FAILED_SUBMIT_JOBS" environment variable', () => {
|
||||
process.env.MAX_FAILED_SUBMIT_JOBS = '9999';
|
||||
const config = require('./config');
|
||||
expect(config.maxFailedSubmitJobs).toBe(9999);
|
||||
});
|
||||
|
||||
it('throws an error when the "MAX_FAILED_SUBMIT_JOBS" environment variable has an invalid number', () => {
|
||||
process.env.MAX_FAILED_SUBMIT_JOBS = 'lots';
|
||||
expect(() => {
|
||||
require('./config');
|
||||
}).toThrow(
|
||||
'env-var: "MAX_FAILED_SUBMIT_JOBS" should be a valid integer. An example of a valid value would be: 1000'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('submitJobQueueScheduler', () => {
|
||||
it('defaults to "true"', () => {
|
||||
const config = require('./config');
|
||||
expect(config.submitJobQueueScheduler).toBe(true);
|
||||
});
|
||||
|
||||
it('can be configured using the "SUBMIT_JOB_QUEUE_SCHEDULER" environment variable', () => {
|
||||
process.env.SUBMIT_JOB_QUEUE_SCHEDULER = 'false';
|
||||
const config = require('./config');
|
||||
expect(config.submitJobQueueScheduler).toBe(false);
|
||||
});
|
||||
|
||||
it('throws an error when the "SUBMIT_JOB_QUEUE_SCHEDULER" environment variable has an invalid boolean value', () => {
|
||||
process.env.SUBMIT_JOB_QUEUE_SCHEDULER = '11';
|
||||
expect(() => {
|
||||
require('./config');
|
||||
}).toThrow(
|
||||
'env-var: "SUBMIT_JOB_QUEUE_SCHEDULER" should be either "true", "false", "TRUE", or "FALSE". An example of a valid value would be: true'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('asLocalhost', () => {
|
||||
it('defaults to "true"', () => {
|
||||
const config = require('./config');
|
||||
expect(config.asLocalhost).toBe(true);
|
||||
});
|
||||
|
||||
it('can be configured using the "AS_LOCAL_HOST" environment variable', () => {
|
||||
process.env.AS_LOCAL_HOST = 'false';
|
||||
const config = require('./config');
|
||||
expect(config.asLocalhost).toBe(false);
|
||||
});
|
||||
|
||||
it('throws an error when the "AS_LOCAL_HOST" environment variable has an invalid boolean value', () => {
|
||||
process.env.AS_LOCAL_HOST = '11';
|
||||
expect(() => {
|
||||
require('./config');
|
||||
}).toThrow(
|
||||
'env-var: "AS_LOCAL_HOST" should be either "true", "false", "TRUE", or "FALSE". An example of a valid value would be: true'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('mspIdOrg1', () => {
|
||||
it('defaults to "Org1MSP"', () => {
|
||||
const config = require('./config');
|
||||
expect(config.mspIdOrg1).toBe('Org1MSP');
|
||||
});
|
||||
|
||||
it('can be configured using the "HLF_MSP_ID_ORG1" environment variable', () => {
|
||||
process.env.HLF_MSP_ID_ORG1 = 'Test1MSP';
|
||||
const config = require('./config');
|
||||
expect(config.mspIdOrg1).toBe('Test1MSP');
|
||||
});
|
||||
});
|
||||
|
||||
describe('mspIdOrg2', () => {
|
||||
it('defaults to "Org2MSP"', () => {
|
||||
const config = require('./config');
|
||||
expect(config.mspIdOrg2).toBe('Org2MSP');
|
||||
});
|
||||
|
||||
it('can be configured using the "HLF_MSP_ID_ORG2" environment variable', () => {
|
||||
process.env.HLF_MSP_ID_ORG2 = 'Test2MSP';
|
||||
const config = require('./config');
|
||||
expect(config.mspIdOrg2).toBe('Test2MSP');
|
||||
});
|
||||
});
|
||||
|
||||
describe('channelName', () => {
|
||||
it('defaults to "mychannel"', () => {
|
||||
const config = require('./config');
|
||||
expect(config.channelName).toBe('mychannel');
|
||||
});
|
||||
|
||||
it('can be configured using the "HLF_CHANNEL_NAME" environment variable', () => {
|
||||
process.env.HLF_CHANNEL_NAME = 'testchannel';
|
||||
const config = require('./config');
|
||||
expect(config.channelName).toBe('testchannel');
|
||||
});
|
||||
});
|
||||
|
||||
describe('chaincodeName', () => {
|
||||
it('defaults to "basic"', () => {
|
||||
const config = require('./config');
|
||||
expect(config.chaincodeName).toBe('basic');
|
||||
});
|
||||
|
||||
it('can be configured using the "HLF_CHAINCODE_NAME" environment variable', () => {
|
||||
process.env.HLF_CHAINCODE_NAME = 'testcc';
|
||||
const config = require('./config');
|
||||
expect(config.chaincodeName).toBe('testcc');
|
||||
});
|
||||
});
|
||||
|
||||
describe('commitTimeout', () => {
|
||||
it('defaults to "300"', () => {
|
||||
const config = require('./config');
|
||||
expect(config.commitTimeout).toBe(300);
|
||||
});
|
||||
|
||||
it('can be configured using the "HLF_COMMIT_TIMEOUT" environment variable', () => {
|
||||
process.env.HLF_COMMIT_TIMEOUT = '9999';
|
||||
const config = require('./config');
|
||||
expect(config.commitTimeout).toBe(9999);
|
||||
});
|
||||
|
||||
it('throws an error when the "HLF_COMMIT_TIMEOUT" environment variable has an invalid number', () => {
|
||||
process.env.HLF_COMMIT_TIMEOUT = 'short';
|
||||
expect(() => {
|
||||
require('./config');
|
||||
}).toThrow(
|
||||
'env-var: "HLF_COMMIT_TIMEOUT" should be a valid integer. An example of a valid value would be: 300'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('endorseTimeout', () => {
|
||||
it('defaults to "30"', () => {
|
||||
const config = require('./config');
|
||||
expect(config.endorseTimeout).toBe(30);
|
||||
});
|
||||
|
||||
it('can be configured using the "HLF_ENDORSE_TIMEOUT" environment variable', () => {
|
||||
process.env.HLF_ENDORSE_TIMEOUT = '9999';
|
||||
const config = require('./config');
|
||||
expect(config.endorseTimeout).toBe(9999);
|
||||
});
|
||||
|
||||
it('throws an error when the "HLF_ENDORSE_TIMEOUT" environment variable has an invalid number', () => {
|
||||
process.env.HLF_ENDORSE_TIMEOUT = 'short';
|
||||
expect(() => {
|
||||
require('./config');
|
||||
}).toThrow(
|
||||
'env-var: "HLF_ENDORSE_TIMEOUT" should be a valid integer. An example of a valid value would be: 30'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('queryTimeout', () => {
|
||||
it('defaults to "3"', () => {
|
||||
const config = require('./config');
|
||||
expect(config.queryTimeout).toBe(3);
|
||||
});
|
||||
|
||||
it('can be configured using the "HLF_QUERY_TIMEOUT" environment variable', () => {
|
||||
process.env.HLF_QUERY_TIMEOUT = '9999';
|
||||
const config = require('./config');
|
||||
expect(config.queryTimeout).toBe(9999);
|
||||
});
|
||||
|
||||
it('throws an error when the "HLF_QUERY_TIMEOUT" environment variable has an invalid number', () => {
|
||||
process.env.HLF_QUERY_TIMEOUT = 'long';
|
||||
expect(() => {
|
||||
require('./config');
|
||||
}).toThrow(
|
||||
'env-var: "HLF_QUERY_TIMEOUT" should be a valid integer. An example of a valid value would be: 3'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('connectionProfileOrg1', () => {
|
||||
it('throws an error when the "HLF_CONNECTION_PROFILE_ORG1" environment variable is not set', () => {
|
||||
delete process.env.HLF_CONNECTION_PROFILE_ORG1;
|
||||
expect(() => {
|
||||
require('./config');
|
||||
}).toThrow(
|
||||
'env-var: "HLF_CONNECTION_PROFILE_ORG1" is a required variable, but it was not set. An example of a valid value would be: {"name":"test-network-org1","version":"1.0.0","client":{"organization":"Org1" ... }'
|
||||
);
|
||||
});
|
||||
|
||||
it('can be configured using the "HLF_CONNECTION_PROFILE_ORG1" environment variable', () => {
|
||||
process.env.HLF_CONNECTION_PROFILE_ORG1 = '{"name":"test-network-org1"}';
|
||||
const config = require('./config');
|
||||
expect(config.connectionProfileOrg1).toStrictEqual({
|
||||
name: 'test-network-org1',
|
||||
});
|
||||
});
|
||||
|
||||
it('throws an error when the "HLF_CONNECTION_PROFILE_ORG1" environment variable is set to invalid json', () => {
|
||||
process.env.HLF_CONNECTION_PROFILE_ORG1 = 'testing';
|
||||
expect(() => {
|
||||
require('./config');
|
||||
}).toThrow(
|
||||
'env-var: "HLF_CONNECTION_PROFILE_ORG1" should be valid (parseable) JSON. An example of a valid value would be: {"name":"test-network-org1","version":"1.0.0","client":{"organization":"Org1" ... }'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('certificateOrg1', () => {
|
||||
it('throws an error when the "HLF_CERTIFICATE_ORG1" environment variable is not set', () => {
|
||||
delete process.env.HLF_CERTIFICATE_ORG1;
|
||||
expect(() => {
|
||||
require('./config');
|
||||
}).toThrow(
|
||||
'env-var: "HLF_CERTIFICATE_ORG1" is a required variable, but it was not set. An example of a valid value would be: "-----BEGIN CERTIFICATE-----\\n...\\n-----END CERTIFICATE-----\\n"'
|
||||
);
|
||||
});
|
||||
|
||||
it('can be configured using the "HLF_CERTIFICATE_ORG1" environment variable', () => {
|
||||
process.env.HLF_CERTIFICATE_ORG1 = 'ORG1CERT';
|
||||
const config = require('./config');
|
||||
expect(config.certificateOrg1).toBe('ORG1CERT');
|
||||
});
|
||||
});
|
||||
|
||||
describe('privateKeyOrg1', () => {
|
||||
it('throws an error when the "HLF_PRIVATE_KEY_ORG1" environment variable is not set', () => {
|
||||
delete process.env.HLF_PRIVATE_KEY_ORG1;
|
||||
expect(() => {
|
||||
require('./config');
|
||||
}).toThrow(
|
||||
'env-var: "HLF_PRIVATE_KEY_ORG1" is a required variable, but it was not set. An example of a valid value would be: "-----BEGIN PRIVATE KEY-----\\n...\\n-----END PRIVATE KEY-----\\n"'
|
||||
);
|
||||
});
|
||||
|
||||
it('can be configured using the "HLF_PRIVATE_KEY_ORG1" environment variable', () => {
|
||||
process.env.HLF_PRIVATE_KEY_ORG1 = 'ORG1PK';
|
||||
const config = require('./config');
|
||||
expect(config.privateKeyOrg1).toBe('ORG1PK');
|
||||
});
|
||||
});
|
||||
|
||||
describe('connectionProfileOrg2', () => {
|
||||
it('throws an error when the "HLF_CONNECTION_PROFILE_ORG2" environment variable is not set', () => {
|
||||
delete process.env.HLF_CONNECTION_PROFILE_ORG2;
|
||||
expect(() => {
|
||||
require('./config');
|
||||
}).toThrow(
|
||||
'env-var: "HLF_CONNECTION_PROFILE_ORG2" is a required variable, but it was not set. An example of a valid value would be: {"name":"test-network-org2","version":"1.0.0","client":{"organization":"Org2" ... }'
|
||||
);
|
||||
});
|
||||
|
||||
it('can be configured using the "HLF_CONNECTION_PROFILE_ORG2" environment variable', () => {
|
||||
process.env.HLF_CONNECTION_PROFILE_ORG2 = '{"name":"test-network-org2"}';
|
||||
const config = require('./config');
|
||||
expect(config.connectionProfileOrg2).toStrictEqual({
|
||||
name: 'test-network-org2',
|
||||
});
|
||||
});
|
||||
|
||||
it('throws an error when the "HLF_CONNECTION_PROFILE_ORG2" environment variable is set to invalid json', () => {
|
||||
process.env.HLF_CONNECTION_PROFILE_ORG2 = 'testing';
|
||||
expect(() => {
|
||||
require('./config');
|
||||
}).toThrow(
|
||||
'env-var: "HLF_CONNECTION_PROFILE_ORG2" should be valid (parseable) JSON. An example of a valid value would be: {"name":"test-network-org2","version":"1.0.0","client":{"organization":"Org2" ... }'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('certificateOrg2', () => {
|
||||
it('throws an error when the "HLF_CERTIFICATE_ORG2" environment variable is not set', () => {
|
||||
delete process.env.HLF_CERTIFICATE_ORG2;
|
||||
expect(() => {
|
||||
require('./config');
|
||||
}).toThrow(
|
||||
'env-var: "HLF_CERTIFICATE_ORG2" is a required variable, but it was not set. An example of a valid value would be: "-----BEGIN CERTIFICATE-----\\n...\\n-----END CERTIFICATE-----\\n"'
|
||||
);
|
||||
});
|
||||
|
||||
it('can be configured using the "HLF_CERTIFICATE_ORG2" environment variable', () => {
|
||||
process.env.HLF_CERTIFICATE_ORG2 = 'ORG2CERT';
|
||||
const config = require('./config');
|
||||
expect(config.certificateOrg2).toBe('ORG2CERT');
|
||||
});
|
||||
});
|
||||
|
||||
describe('privateKeyOrg2', () => {
|
||||
it('throws an error when the "HLF_PRIVATE_KEY_ORG2" environment variable is not set', () => {
|
||||
delete process.env.HLF_PRIVATE_KEY_ORG2;
|
||||
expect(() => {
|
||||
require('./config');
|
||||
}).toThrow(
|
||||
'env-var: "HLF_PRIVATE_KEY_ORG2" is a required variable, but it was not set. An example of a valid value would be: "-----BEGIN PRIVATE KEY-----\\n...\\n-----END PRIVATE KEY-----\\n"'
|
||||
);
|
||||
});
|
||||
|
||||
it('can be configured using the "HLF_PRIVATE_KEY_ORG2" environment variable', () => {
|
||||
process.env.HLF_PRIVATE_KEY_ORG2 = 'ORG2PK';
|
||||
const config = require('./config');
|
||||
expect(config.privateKeyOrg2).toBe('ORG2PK');
|
||||
});
|
||||
});
|
||||
|
||||
describe('redisHost', () => {
|
||||
it('defaults to "localhost"', () => {
|
||||
const config = require('./config');
|
||||
expect(config.redisHost).toBe('localhost');
|
||||
});
|
||||
|
||||
it('can be configured using the "REDIS_HOST" environment variable', () => {
|
||||
process.env.REDIS_HOST = 'redis.example.org';
|
||||
const config = require('./config');
|
||||
expect(config.redisHost).toBe('redis.example.org');
|
||||
});
|
||||
});
|
||||
|
||||
describe('redisPort', () => {
|
||||
it('defaults to "6379"', () => {
|
||||
const config = require('./config');
|
||||
expect(config.redisPort).toBe(6379);
|
||||
});
|
||||
|
||||
it('can be configured with a valid port number using the "REDIS_PORT" environment variable', () => {
|
||||
process.env.REDIS_PORT = '9736';
|
||||
const config = require('./config');
|
||||
expect(config.redisPort).toBe(9736);
|
||||
});
|
||||
|
||||
it('throws an error when the "REDIS_PORT" environment variable has an invalid port number', () => {
|
||||
process.env.REDIS_PORT = '65536';
|
||||
expect(() => {
|
||||
require('./config');
|
||||
}).toThrow(
|
||||
'env-var: "REDIS_PORT" cannot assign a port number greater than 65535. An example of a valid value would be: 6379'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('redisUsername', () => {
|
||||
it('has no default value', () => {
|
||||
const config = require('./config');
|
||||
expect(config.redisUsername).toBeUndefined();
|
||||
});
|
||||
|
||||
it('can be configured using the "REDIS_USERNAME" environment variable', () => {
|
||||
process.env.REDIS_USERNAME = 'test';
|
||||
const config = require('./config');
|
||||
expect(config.redisUsername).toBe('test');
|
||||
});
|
||||
});
|
||||
|
||||
describe('redisPassword', () => {
|
||||
it('has no default value', () => {
|
||||
const config = require('./config');
|
||||
expect(config.redisPassword).toBeUndefined();
|
||||
});
|
||||
|
||||
it('can be configured using the "REDIS_PASSWORD" environment variable', () => {
|
||||
process.env.REDIS_PASSWORD = 'testpw';
|
||||
const config = require('./config');
|
||||
expect(config.redisPassword).toBe('testpw');
|
||||
});
|
||||
});
|
||||
|
||||
describe('org1ApiKey', () => {
|
||||
it('throws an error when the "ORG1_APIKEY" environment variable is not set', () => {
|
||||
delete process.env.ORG1_APIKEY;
|
||||
expect(() => {
|
||||
require('./config');
|
||||
}).toThrow(
|
||||
'env-var: "ORG1_APIKEY" is a required variable, but it was not set. An example of a valid value would be: 123'
|
||||
);
|
||||
});
|
||||
|
||||
it('can be configured using the "ORG1_APIKEY" environment variable', () => {
|
||||
process.env.ORG1_APIKEY = 'org1ApiKey';
|
||||
const config = require('./config');
|
||||
expect(config.org1ApiKey).toBe('org1ApiKey');
|
||||
});
|
||||
});
|
||||
|
||||
describe('org2ApiKey', () => {
|
||||
it('throws an error when the "ORG1_APIKEY" environment variable is not set', () => {
|
||||
delete process.env.ORG2_APIKEY;
|
||||
expect(() => {
|
||||
require('./config');
|
||||
}).toThrow(
|
||||
'env-var: "ORG2_APIKEY" is a required variable, but it was not set. An example of a valid value would be: 456'
|
||||
);
|
||||
});
|
||||
|
||||
it('can be configured using the "ORG1_APIKEY" environment variable', () => {
|
||||
process.env.ORG2_APIKEY = 'org2ApiKey';
|
||||
const config = require('./config');
|
||||
expect(config.org2ApiKey).toBe('org2ApiKey');
|
||||
});
|
||||
});
|
||||
});
|
||||
285
asset-transfer-basic/rest-api-typescript/src/config.ts
Normal file
285
asset-transfer-basic/rest-api-typescript/src/config.ts
Normal file
|
|
@ -0,0 +1,285 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* The sample REST server can be configured using the environment variables
|
||||
* documented below
|
||||
*
|
||||
* In a local development environment, these variables can be loaded from a
|
||||
* .env file by starting the server with the following command:
|
||||
*
|
||||
* npm start:dev
|
||||
*
|
||||
* The scripts/generateEnv.sh script can be used to generate a suitable .env
|
||||
* file for the Fabric Test Network
|
||||
*/
|
||||
|
||||
import * as env from 'env-var';
|
||||
|
||||
export const ORG1 = 'Org1';
|
||||
export const ORG2 = 'Org2';
|
||||
|
||||
export const JOB_QUEUE_NAME = 'submit';
|
||||
|
||||
/*
|
||||
* Log level for the REST server
|
||||
*/
|
||||
export const logLevel = env
|
||||
.get('LOG_LEVEL')
|
||||
.default('info')
|
||||
.asEnum(['fatal', 'error', 'warn', 'info', 'debug', 'trace', 'silent']);
|
||||
|
||||
/*
|
||||
* The port to start the REST server on
|
||||
*/
|
||||
export const port = env
|
||||
.get('PORT')
|
||||
.default('3000')
|
||||
.example('3000')
|
||||
.asPortNumber();
|
||||
|
||||
/*
|
||||
* The type of backoff to use for retrying failed submit jobs
|
||||
*/
|
||||
export const submitJobBackoffType = env
|
||||
.get('SUBMIT_JOB_BACKOFF_TYPE')
|
||||
.default('fixed')
|
||||
.asEnum(['fixed', 'exponential']);
|
||||
|
||||
/*
|
||||
* Backoff delay for retrying failed submit jobs in milliseconds
|
||||
*/
|
||||
export const submitJobBackoffDelay = env
|
||||
.get('SUBMIT_JOB_BACKOFF_DELAY')
|
||||
.default('3000')
|
||||
.example('3000')
|
||||
.asIntPositive();
|
||||
|
||||
/*
|
||||
* The total number of attempts to try a submit job until it completes
|
||||
*/
|
||||
export const submitJobAttempts = env
|
||||
.get('SUBMIT_JOB_ATTEMPTS')
|
||||
.default('5')
|
||||
.example('5')
|
||||
.asIntPositive();
|
||||
|
||||
/*
|
||||
* The maximum number of submit jobs that can be processed in parallel
|
||||
*/
|
||||
export const submitJobConcurrency = env
|
||||
.get('SUBMIT_JOB_CONCURRENCY')
|
||||
.default('5')
|
||||
.example('5')
|
||||
.asIntPositive();
|
||||
|
||||
/*
|
||||
* The number of completed submit jobs to keep
|
||||
*/
|
||||
export const maxCompletedSubmitJobs = env
|
||||
.get('MAX_COMPLETED_SUBMIT_JOBS')
|
||||
.default('1000')
|
||||
.example('1000')
|
||||
.asIntPositive();
|
||||
|
||||
/*
|
||||
* The number of failed submit jobs to keep
|
||||
*/
|
||||
export const maxFailedSubmitJobs = env
|
||||
.get('MAX_FAILED_SUBMIT_JOBS')
|
||||
.default('1000')
|
||||
.example('1000')
|
||||
.asIntPositive();
|
||||
|
||||
/*
|
||||
* Whether to initialise a scheduler for the submit job queue
|
||||
* There must be at least on queue scheduler to handle retries and you may want
|
||||
* more than one for redundancy
|
||||
*/
|
||||
export const submitJobQueueScheduler = env
|
||||
.get('SUBMIT_JOB_QUEUE_SCHEDULER')
|
||||
.default('true')
|
||||
.example('true')
|
||||
.asBoolStrict();
|
||||
|
||||
/*
|
||||
* Whether to convert discovered host addresses to be 'localhost'
|
||||
* This should be set to 'true' when running a docker composed fabric network on the
|
||||
* local system, e.g. using the test network; otherwise should it should be 'false'
|
||||
*/
|
||||
export const asLocalhost = env
|
||||
.get('AS_LOCAL_HOST')
|
||||
.default('true')
|
||||
.example('true')
|
||||
.asBoolStrict();
|
||||
|
||||
/*
|
||||
* The Org1 MSP ID
|
||||
*/
|
||||
export const mspIdOrg1 = env
|
||||
.get('HLF_MSP_ID_ORG1')
|
||||
.default(`${ORG1}MSP`)
|
||||
.example(`${ORG1}MSP`)
|
||||
.asString();
|
||||
|
||||
/*
|
||||
* The Org2 MSP ID
|
||||
*/
|
||||
export const mspIdOrg2 = env
|
||||
.get('HLF_MSP_ID_ORG2')
|
||||
.default(`${ORG2}MSP`)
|
||||
.example(`${ORG2}MSP`)
|
||||
.asString();
|
||||
|
||||
/*
|
||||
* Name of the channel which the basic asset sample chaincode has been installed on
|
||||
*/
|
||||
export const channelName = env
|
||||
.get('HLF_CHANNEL_NAME')
|
||||
.default('mychannel')
|
||||
.example('mychannel')
|
||||
.asString();
|
||||
|
||||
/*
|
||||
* Name used to install the basic asset sample
|
||||
*/
|
||||
export const chaincodeName = env
|
||||
.get('HLF_CHAINCODE_NAME')
|
||||
.default('basic')
|
||||
.example('basic')
|
||||
.asString();
|
||||
|
||||
/*
|
||||
* The transaction submit timeout in seconds for commit notification to complete
|
||||
*/
|
||||
export const commitTimeout = env
|
||||
.get('HLF_COMMIT_TIMEOUT')
|
||||
.default('300')
|
||||
.example('300')
|
||||
.asIntPositive();
|
||||
|
||||
/*
|
||||
* The transaction submit timeout in seconds for the endorsement to complete
|
||||
*/
|
||||
export const endorseTimeout = env
|
||||
.get('HLF_ENDORSE_TIMEOUT')
|
||||
.default('30')
|
||||
.example('30')
|
||||
.asIntPositive();
|
||||
|
||||
/*
|
||||
* The transaction query timeout in seconds
|
||||
*/
|
||||
export const queryTimeout = env
|
||||
.get('HLF_QUERY_TIMEOUT')
|
||||
.default('3')
|
||||
.example('3')
|
||||
.asIntPositive();
|
||||
|
||||
/*
|
||||
* The Org1 connection profile JSON
|
||||
*/
|
||||
export const connectionProfileOrg1 = env
|
||||
.get('HLF_CONNECTION_PROFILE_ORG1')
|
||||
.required()
|
||||
.example(
|
||||
'{"name":"test-network-org1","version":"1.0.0","client":{"organization":"Org1" ... }'
|
||||
)
|
||||
.asJsonObject() as Record<string, unknown>;
|
||||
|
||||
/*
|
||||
* Certificate for an Org1 identity to evaluate and submit transactions
|
||||
*/
|
||||
export const certificateOrg1 = env
|
||||
.get('HLF_CERTIFICATE_ORG1')
|
||||
.required()
|
||||
.example('"-----BEGIN CERTIFICATE-----\\n...\\n-----END CERTIFICATE-----\\n"')
|
||||
.asString();
|
||||
|
||||
/*
|
||||
* Private key for an Org1 identity to evaluate and submit transactions
|
||||
*/
|
||||
export const privateKeyOrg1 = env
|
||||
.get('HLF_PRIVATE_KEY_ORG1')
|
||||
.required()
|
||||
.example('"-----BEGIN PRIVATE KEY-----\\n...\\n-----END PRIVATE KEY-----\\n"')
|
||||
.asString();
|
||||
|
||||
/*
|
||||
* The Org2 connection profile JSON
|
||||
*/
|
||||
export const connectionProfileOrg2 = env
|
||||
.get('HLF_CONNECTION_PROFILE_ORG2')
|
||||
.required()
|
||||
.example(
|
||||
'{"name":"test-network-org2","version":"1.0.0","client":{"organization":"Org2" ... }'
|
||||
)
|
||||
.asJsonObject() as Record<string, unknown>;
|
||||
|
||||
/*
|
||||
* Certificate for an Org2 identity to evaluate and submit transactions
|
||||
*/
|
||||
export const certificateOrg2 = env
|
||||
.get('HLF_CERTIFICATE_ORG2')
|
||||
.required()
|
||||
.example('"-----BEGIN CERTIFICATE-----\\n...\\n-----END CERTIFICATE-----\\n"')
|
||||
.asString();
|
||||
|
||||
/*
|
||||
* Private key for an Org2 identity to evaluate and submit transactions
|
||||
*/
|
||||
export const privateKeyOrg2 = env
|
||||
.get('HLF_PRIVATE_KEY_ORG2')
|
||||
.required()
|
||||
.example('"-----BEGIN PRIVATE KEY-----\\n...\\n-----END PRIVATE KEY-----\\n"')
|
||||
.asString();
|
||||
|
||||
/*
|
||||
* The host the Redis server is running on
|
||||
*/
|
||||
export const redisHost = env
|
||||
.get('REDIS_HOST')
|
||||
.default('localhost')
|
||||
.example('localhost')
|
||||
.asString();
|
||||
|
||||
/*
|
||||
* The port the Redis server is running on
|
||||
*/
|
||||
export const redisPort = env
|
||||
.get('REDIS_PORT')
|
||||
.default('6379')
|
||||
.example('6379')
|
||||
.asPortNumber();
|
||||
|
||||
/*
|
||||
* Username for the Redis server
|
||||
*/
|
||||
export const redisUsername = env
|
||||
.get('REDIS_USERNAME')
|
||||
.example('fabric')
|
||||
.asString();
|
||||
|
||||
/*
|
||||
* Password for the Redis server
|
||||
*/
|
||||
export const redisPassword = env.get('REDIS_PASSWORD').asString();
|
||||
|
||||
/*
|
||||
* API key for Org1
|
||||
* Specify this API key with the X-Api-Key header to use the Org1 connection profile and credentials
|
||||
*/
|
||||
export const org1ApiKey = env
|
||||
.get('ORG1_APIKEY')
|
||||
.required()
|
||||
.example('123')
|
||||
.asString();
|
||||
|
||||
/*
|
||||
* API key for Org2
|
||||
* Specify this API key with the X-Api-Key header to use the Org2 connection profile and credentials
|
||||
*/
|
||||
export const org2ApiKey = env
|
||||
.get('ORG2_APIKEY')
|
||||
.required()
|
||||
.example('456')
|
||||
.asString();
|
||||
314
asset-transfer-basic/rest-api-typescript/src/errors.spec.ts
Normal file
314
asset-transfer-basic/rest-api-typescript/src/errors.spec.ts
Normal file
|
|
@ -0,0 +1,314 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { TimeoutError, TransactionError } from 'fabric-network';
|
||||
import {
|
||||
AssetExistsError,
|
||||
AssetNotFoundError,
|
||||
TransactionNotFoundError,
|
||||
getRetryAction,
|
||||
handleError,
|
||||
isDuplicateTransactionError,
|
||||
isErrorLike,
|
||||
RetryAction,
|
||||
} from './errors';
|
||||
|
||||
import { mock } from 'jest-mock-extended';
|
||||
|
||||
describe('Errors', () => {
|
||||
describe('isErrorLike', () => {
|
||||
it('returns false for null', () => {
|
||||
expect(isErrorLike(null)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for undefined', () => {
|
||||
expect(isErrorLike(undefined)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for empty object', () => {
|
||||
expect(isErrorLike({})).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for string', () => {
|
||||
expect(isErrorLike('true')).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for non-error object', () => {
|
||||
expect(isErrorLike({ size: 42 })).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for invalid error object', () => {
|
||||
expect(isErrorLike({ name: 'MockError', message: 42 })).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for error like object with invalid stack', () => {
|
||||
expect(
|
||||
isErrorLike({ name: 'MockError', message: 'Fail', stack: false })
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('returns true for error like object', () => {
|
||||
expect(isErrorLike({ name: 'MockError', message: 'Fail' })).toBe(true);
|
||||
});
|
||||
|
||||
it('returns true for new Error', () => {
|
||||
expect(isErrorLike(new Error('Error'))).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isDuplicateTransactionError', () => {
|
||||
it('returns true for a TransactionError with a transaction code of DUPLICATE_TXID', () => {
|
||||
const mockDuplicateTransactionError = mock<TransactionError>();
|
||||
mockDuplicateTransactionError.transactionCode = 'DUPLICATE_TXID';
|
||||
|
||||
expect(isDuplicateTransactionError(mockDuplicateTransactionError)).toBe(
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
it('returns false for a TransactionError without a transaction code of MVCC_READ_CONFLICT', () => {
|
||||
const mockDuplicateTransactionError = mock<TransactionError>();
|
||||
mockDuplicateTransactionError.transactionCode = 'MVCC_READ_CONFLICT';
|
||||
|
||||
expect(isDuplicateTransactionError(mockDuplicateTransactionError)).toBe(
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it('returns true for an error when all endorsement details are duplicate transaction found', () => {
|
||||
const mockDuplicateTransactionError = {
|
||||
errors: [
|
||||
{
|
||||
endorsements: [
|
||||
{
|
||||
details: 'duplicate transaction found',
|
||||
},
|
||||
{
|
||||
details: 'duplicate transaction found',
|
||||
},
|
||||
{
|
||||
details: 'duplicate transaction found',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(isDuplicateTransactionError(mockDuplicateTransactionError)).toBe(
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
it('returns true for an error when at least one endorsement details are duplicate transaction found', () => {
|
||||
const mockDuplicateTransactionError = {
|
||||
errors: [
|
||||
{
|
||||
endorsements: [
|
||||
{
|
||||
details: 'duplicate transaction found',
|
||||
},
|
||||
{
|
||||
details: 'mock endorsement details',
|
||||
},
|
||||
{
|
||||
details: 'mock endorsement details',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(isDuplicateTransactionError(mockDuplicateTransactionError)).toBe(
|
||||
true
|
||||
);
|
||||
});
|
||||
|
||||
it('returns false for an error without duplicate transaction endorsement details', () => {
|
||||
const mockDuplicateTransactionError = {
|
||||
errors: [
|
||||
{
|
||||
endorsements: [
|
||||
{
|
||||
details: 'mock endorsement details',
|
||||
},
|
||||
{
|
||||
details: 'mock endorsement details',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(isDuplicateTransactionError(mockDuplicateTransactionError)).toBe(
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it('returns false for an error without endorsement details', () => {
|
||||
const mockDuplicateTransactionError = {
|
||||
errors: [
|
||||
{
|
||||
rejections: [
|
||||
{
|
||||
details: 'duplicate transaction found',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(isDuplicateTransactionError(mockDuplicateTransactionError)).toBe(
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it('returns false for a basic Error object without endorsement details', () => {
|
||||
expect(
|
||||
isDuplicateTransactionError(new Error('duplicate transaction found'))
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for an undefined error', () => {
|
||||
expect(isDuplicateTransactionError(undefined)).toBe(false);
|
||||
});
|
||||
|
||||
it('returns false for a null error', () => {
|
||||
expect(isDuplicateTransactionError(null)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRetryAction', () => {
|
||||
it('returns RetryAction.None for duplicate transaction errors', () => {
|
||||
const mockDuplicateTransactionError = {
|
||||
errors: [
|
||||
{
|
||||
endorsements: [
|
||||
{
|
||||
details: 'duplicate transaction found',
|
||||
},
|
||||
{
|
||||
details: 'duplicate transaction found',
|
||||
},
|
||||
{
|
||||
details: 'duplicate transaction found',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
expect(getRetryAction(mockDuplicateTransactionError)).toBe(
|
||||
RetryAction.None
|
||||
);
|
||||
});
|
||||
|
||||
it('returns RetryAction.None for a TransactionNotFoundError', () => {
|
||||
const mockTransactionNotFoundError = new TransactionNotFoundError(
|
||||
'Failed to get transaction with id txn, error Entry not found in index',
|
||||
'txn1'
|
||||
);
|
||||
|
||||
expect(getRetryAction(mockTransactionNotFoundError)).toBe(
|
||||
RetryAction.None
|
||||
);
|
||||
});
|
||||
|
||||
it('returns RetryAction.None for an AssetExistsError', () => {
|
||||
const mockAssetExistsError = new AssetExistsError(
|
||||
'The asset MOCK_ASSET already exists',
|
||||
'txn1'
|
||||
);
|
||||
|
||||
expect(getRetryAction(mockAssetExistsError)).toBe(RetryAction.None);
|
||||
});
|
||||
|
||||
it('returns RetryAction.None for an AssetNotFoundError', () => {
|
||||
const mockAssetNotFoundError = new AssetNotFoundError(
|
||||
'the asset MOCK_ASSET does not exist',
|
||||
'txn1'
|
||||
);
|
||||
|
||||
expect(getRetryAction(mockAssetNotFoundError)).toBe(RetryAction.None);
|
||||
});
|
||||
|
||||
it('returns RetryAction.WithExistingTransactionId for a TimeoutError', () => {
|
||||
const mockTimeoutError = new TimeoutError('MOCK TIMEOUT ERROR');
|
||||
|
||||
expect(getRetryAction(mockTimeoutError)).toBe(
|
||||
RetryAction.WithExistingTransactionId
|
||||
);
|
||||
});
|
||||
|
||||
it('returns RetryAction.WithNewTransactionId for an MVCC_READ_CONFLICT TransactionError', () => {
|
||||
const mockTransactionError = mock<TransactionError>();
|
||||
mockTransactionError.transactionCode = 'MVCC_READ_CONFLICT';
|
||||
|
||||
expect(getRetryAction(mockTransactionError)).toBe(
|
||||
RetryAction.WithNewTransactionId
|
||||
);
|
||||
});
|
||||
|
||||
it('returns RetryAction.WithNewTransactionId for an Error', () => {
|
||||
const mockError = new Error('MOCK ERROR');
|
||||
|
||||
expect(getRetryAction(mockError)).toBe(RetryAction.WithNewTransactionId);
|
||||
});
|
||||
|
||||
it('returns RetryAction.WithNewTransactionId for a string error', () => {
|
||||
const mockError = 'MOCK ERROR';
|
||||
|
||||
expect(getRetryAction(mockError)).toBe(RetryAction.WithNewTransactionId);
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleError', () => {
|
||||
it.each([
|
||||
'the asset GOCHAINCODE already exists',
|
||||
'Asset JAVACHAINCODE already exists',
|
||||
'The asset JSCHAINCODE already exists',
|
||||
])(
|
||||
'returns a AssetExistsError for errors with an asset already exists message: %s',
|
||||
(msg) => {
|
||||
expect(handleError('txn1', new Error(msg))).toStrictEqual(
|
||||
new AssetExistsError(msg, 'txn1')
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
it.each([
|
||||
'the asset GOCHAINCODE does not exist',
|
||||
'Asset JAVACHAINCODE does not exist',
|
||||
'The asset JSCHAINCODE does not exist',
|
||||
])(
|
||||
'returns a AssetNotFoundError for errors with an asset does not exist message: %s',
|
||||
(msg) => {
|
||||
expect(handleError('txn1', new Error(msg))).toStrictEqual(
|
||||
new AssetNotFoundError(msg, 'txn1')
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
it.each([
|
||||
'Failed to get transaction with id txn, error Entry not found in index',
|
||||
'Failed to get transaction with id txn, error no such transaction ID [txn] in index',
|
||||
])(
|
||||
'returns a TransactionNotFoundError for errors with a transaction not found message: %s',
|
||||
(msg) => {
|
||||
expect(handleError('txn1', new Error(msg))).toStrictEqual(
|
||||
new TransactionNotFoundError(msg, 'txn1')
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
it('returns the original error for errors with other messages', () => {
|
||||
expect(handleError('txn1', new Error('MOCK ERROR'))).toStrictEqual(
|
||||
new Error('MOCK ERROR')
|
||||
);
|
||||
});
|
||||
|
||||
it('returns the original error for errors of other types', () => {
|
||||
expect(handleError('txn1', 42)).toEqual(42);
|
||||
});
|
||||
});
|
||||
});
|
||||
269
asset-transfer-basic/rest-api-typescript/src/errors.ts
Normal file
269
asset-transfer-basic/rest-api-typescript/src/errors.ts
Normal file
|
|
@ -0,0 +1,269 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* This file contains all the error handling for Fabric transactions, including
|
||||
* whether a transaction should be retried.
|
||||
*/
|
||||
|
||||
import { TimeoutError, TransactionError } from 'fabric-network';
|
||||
import { logger } from './logger';
|
||||
|
||||
/*
|
||||
* Base type for errors from the smart contract.
|
||||
*
|
||||
* These errors will not be retried.
|
||||
*/
|
||||
export class ContractError extends Error {
|
||||
transactionId: string;
|
||||
|
||||
constructor(message: string, transactionId: string) {
|
||||
super(message);
|
||||
Object.setPrototypeOf(this, ContractError.prototype);
|
||||
|
||||
this.name = 'TransactionError';
|
||||
this.transactionId = transactionId;
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Represents the error which occurs when the transaction being submitted or
|
||||
* evaluated is not implemented in a smart contract.
|
||||
*/
|
||||
export class TransactionNotFoundError extends ContractError {
|
||||
constructor(message: string, transactionId: string) {
|
||||
super(message, transactionId);
|
||||
Object.setPrototypeOf(this, TransactionNotFoundError.prototype);
|
||||
|
||||
this.name = 'TransactionNotFoundError';
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Represents the error which occurs in the basic asset transfer smart contract
|
||||
* implementation when an asset already exists.
|
||||
*/
|
||||
export class AssetExistsError extends ContractError {
|
||||
constructor(message: string, transactionId: string) {
|
||||
super(message, transactionId);
|
||||
Object.setPrototypeOf(this, AssetExistsError.prototype);
|
||||
|
||||
this.name = 'AssetExistsError';
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Represents the error which occurs in the basic asset transfer smart contract
|
||||
* implementation when an asset does not exist.
|
||||
*/
|
||||
export class AssetNotFoundError extends ContractError {
|
||||
constructor(message: string, transactionId: string) {
|
||||
super(message, transactionId);
|
||||
Object.setPrototypeOf(this, AssetNotFoundError.prototype);
|
||||
|
||||
this.name = 'AssetNotFoundError';
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Enumeration of possible retry actions.
|
||||
*
|
||||
* WithExistingTransactionId - transactions should be retried using the same
|
||||
* transaction ID to protect against duplicate transactions being committed if
|
||||
* a timeout error occurs
|
||||
*
|
||||
* WithNewTransactionId - transactions which could not be committed due to
|
||||
* other errors require a new transaction ID when retrying
|
||||
*
|
||||
* None - transactions that failed due to a duplicate transaction error, or
|
||||
* errors from the smart contract, should not be retried
|
||||
*/
|
||||
export enum RetryAction {
|
||||
WithExistingTransactionId,
|
||||
WithNewTransactionId,
|
||||
None,
|
||||
}
|
||||
|
||||
/*
|
||||
* Get the required transaction retry action for an error.
|
||||
*
|
||||
* For this sample transactions are considered retriable if they fail with any
|
||||
* error, *except* for duplicate transaction errors, or errors from the smart
|
||||
* contract.
|
||||
*
|
||||
* You might decide to retry transactions which fail with specific errors
|
||||
* instead, for example:
|
||||
* MVCC_READ_CONFLICT
|
||||
* PHANTOM_READ_CONFLICT
|
||||
* ENDORSEMENT_POLICY_FAILURE
|
||||
* CHAINCODE_VERSION_CONFLICT
|
||||
* EXPIRED_CHAINCODE
|
||||
*/
|
||||
export const getRetryAction = (err: unknown): RetryAction => {
|
||||
if (isDuplicateTransactionError(err) || err instanceof ContractError) {
|
||||
return RetryAction.None;
|
||||
} else if (err instanceof TimeoutError) {
|
||||
return RetryAction.WithExistingTransactionId;
|
||||
}
|
||||
|
||||
return RetryAction.WithNewTransactionId;
|
||||
};
|
||||
|
||||
/*
|
||||
* Type guard to make catching unknown errors easier
|
||||
*/
|
||||
export const isErrorLike = (err: unknown): err is Error => {
|
||||
return (
|
||||
err != undefined &&
|
||||
err != null &&
|
||||
typeof (err as Error).name === 'string' &&
|
||||
typeof (err as Error).message === 'string' &&
|
||||
((err as Error).stack === undefined ||
|
||||
typeof (err as Error).stack === 'string')
|
||||
);
|
||||
};
|
||||
|
||||
/*
|
||||
* Checks whether an error was caused by a duplicate transaction.
|
||||
*
|
||||
* This is ...painful.
|
||||
*/
|
||||
export const isDuplicateTransactionError = (err: unknown): boolean => {
|
||||
logger.debug({ err }, 'Checking for duplicate transaction error');
|
||||
|
||||
if (err === undefined || err === null) return false;
|
||||
|
||||
let isDuplicate;
|
||||
if (typeof (err as TransactionError).transactionCode === 'string') {
|
||||
// Checking whether a commit failure is caused by a duplicate transaction
|
||||
// is straightforward because the transaction code should be available
|
||||
isDuplicate =
|
||||
(err as TransactionError).transactionCode === 'DUPLICATE_TXID';
|
||||
} else {
|
||||
// Checking whether an endorsement failure is caused by a duplicate
|
||||
// transaction is only possible by processing error strings, which is not ideal.
|
||||
const endorsementError = err as {
|
||||
errors: { endorsements: { details: string }[] }[];
|
||||
};
|
||||
|
||||
isDuplicate = endorsementError?.errors?.some((err) =>
|
||||
err?.endorsements?.some((endorsement) =>
|
||||
endorsement?.details?.startsWith('duplicate transaction found')
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
return isDuplicate === true;
|
||||
};
|
||||
|
||||
/*
|
||||
* Matches asset already exists error strings from the asset contract
|
||||
*
|
||||
* The regex needs to match the following error messages:
|
||||
* "the asset %s already exists"
|
||||
* "The asset ${id} already exists"
|
||||
* "Asset %s already exists"
|
||||
*/
|
||||
const matchAssetAlreadyExistsMessage = (message: string): string | null => {
|
||||
//
|
||||
const assetAlreadyExistsRegex = /([tT]he )?[aA]sset \w* already exists/g;
|
||||
const assetAlreadyExistsMatch = message.match(assetAlreadyExistsRegex);
|
||||
logger.debug(
|
||||
{ message: message, result: assetAlreadyExistsMatch },
|
||||
'Checking for asset already exists message'
|
||||
);
|
||||
|
||||
if (assetAlreadyExistsMatch !== null) {
|
||||
return assetAlreadyExistsMatch[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/*
|
||||
* Matches asset does not exist error strings from the asset contract
|
||||
*
|
||||
* The regex needs to match the following error messages:
|
||||
* "the asset %s does not exist"
|
||||
* "The asset ${id} does not exist"
|
||||
* "Asset %s does not exist"
|
||||
*/
|
||||
const matchAssetDoesNotExistMessage = (message: string): string | null => {
|
||||
const assetDoesNotExistRegex = /([tT]he )?[aA]sset \w* does not exist/g;
|
||||
const assetDoesNotExistMatch = message.match(assetDoesNotExistRegex);
|
||||
logger.debug(
|
||||
{ message: message, result: assetDoesNotExistMatch },
|
||||
'Checking for asset does not exist message'
|
||||
);
|
||||
|
||||
if (assetDoesNotExistMatch !== null) {
|
||||
return assetDoesNotExistMatch[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/*
|
||||
* Matches transaction does not exist error strings from the contract API
|
||||
*
|
||||
* The regex needs to match the following error messages:
|
||||
* "Failed to get transaction with id %s, error Entry not found in index"
|
||||
* "Failed to get transaction with id %s, error no such transaction ID [%s] in index"
|
||||
*/
|
||||
const matchTransactionDoesNotExistMessage = (
|
||||
message: string
|
||||
): string | null => {
|
||||
const transactionDoesNotExistRegex =
|
||||
/Failed to get transaction with id [^,]*, error (?:(?:Entry not found)|(?:no such transaction ID \[[^\]]*\])) in index/g;
|
||||
const transactionDoesNotExistMatch = message.match(
|
||||
transactionDoesNotExistRegex
|
||||
);
|
||||
logger.debug(
|
||||
{ message: message, result: transactionDoesNotExistMatch },
|
||||
'Checking for transaction does not exist message'
|
||||
);
|
||||
|
||||
if (transactionDoesNotExistMatch !== null) {
|
||||
return transactionDoesNotExistMatch[0];
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
/*
|
||||
* Handles errors from evaluating and submitting transactions.
|
||||
*
|
||||
* Smart contract errors from the the basic asset transfer samples do not use
|
||||
* error codes so matching strings is the only option, which is not ideal.
|
||||
* Note: the error message text is not the same for the Go, Java, and
|
||||
* Javascript implementations of the chaincode!
|
||||
*/
|
||||
export const handleError = (
|
||||
transactionId: string,
|
||||
err: unknown
|
||||
): Error | unknown => {
|
||||
logger.debug({ transactionId: transactionId, err }, 'Processing error');
|
||||
|
||||
if (isErrorLike(err)) {
|
||||
const assetAlreadyExistsMatch = matchAssetAlreadyExistsMessage(err.message);
|
||||
if (assetAlreadyExistsMatch !== null) {
|
||||
return new AssetExistsError(assetAlreadyExistsMatch, transactionId);
|
||||
}
|
||||
|
||||
const assetDoesNotExistMatch = matchAssetDoesNotExistMessage(err.message);
|
||||
if (assetDoesNotExistMatch !== null) {
|
||||
return new AssetNotFoundError(assetDoesNotExistMatch, transactionId);
|
||||
}
|
||||
|
||||
const transactionDoesNotExistMatch = matchTransactionDoesNotExistMessage(
|
||||
err.message
|
||||
);
|
||||
if (transactionDoesNotExistMatch !== null) {
|
||||
return new TransactionNotFoundError(
|
||||
transactionDoesNotExistMatch,
|
||||
transactionId
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return err;
|
||||
};
|
||||
310
asset-transfer-basic/rest-api-typescript/src/fabric.spec.ts
Normal file
310
asset-transfer-basic/rest-api-typescript/src/fabric.spec.ts
Normal file
|
|
@ -0,0 +1,310 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {
|
||||
createGateway,
|
||||
createWallet,
|
||||
getContracts,
|
||||
getNetwork,
|
||||
evatuateTransaction,
|
||||
submitTransaction,
|
||||
getBlockHeight,
|
||||
getTransactionValidationCode,
|
||||
} from './fabric';
|
||||
import * as config from './config';
|
||||
|
||||
import {
|
||||
AssetExistsError,
|
||||
AssetNotFoundError,
|
||||
TransactionNotFoundError,
|
||||
} from './errors';
|
||||
|
||||
import {
|
||||
Contract,
|
||||
Gateway,
|
||||
GatewayOptions,
|
||||
Network,
|
||||
Transaction,
|
||||
Wallet,
|
||||
} from 'fabric-network';
|
||||
|
||||
import * as fabricProtos from 'fabric-protos';
|
||||
|
||||
import { MockProxy, mock } from 'jest-mock-extended';
|
||||
import Long from 'long';
|
||||
|
||||
jest.mock('./config');
|
||||
jest.mock('fabric-network', () => {
|
||||
type FabricNetworkModule = jest.Mocked<typeof import('fabric-network')>;
|
||||
const originalModule: FabricNetworkModule =
|
||||
jest.requireActual('fabric-network');
|
||||
const mockModule: FabricNetworkModule =
|
||||
jest.createMockFromModule('fabric-network');
|
||||
|
||||
return {
|
||||
__esModule: true,
|
||||
...mockModule,
|
||||
Wallets: originalModule.Wallets,
|
||||
};
|
||||
});
|
||||
jest.mock('ioredis', () => require('ioredis-mock/jest'));
|
||||
|
||||
describe('Fabric', () => {
|
||||
describe('createWallet', () => {
|
||||
it('creates a wallet containing identities for both orgs', async () => {
|
||||
const wallet = await createWallet();
|
||||
|
||||
expect(await wallet.list()).toStrictEqual(['Org1MSP', 'Org2MSP']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('createGateway', () => {
|
||||
it('creates a Gateway and connects using the provided arguments', async () => {
|
||||
const connectionProfile = config.connectionProfileOrg1;
|
||||
const identity = config.mspIdOrg1;
|
||||
const mockWallet = mock<Wallet>();
|
||||
|
||||
const gateway = await createGateway(
|
||||
connectionProfile,
|
||||
identity,
|
||||
mockWallet
|
||||
);
|
||||
|
||||
expect(gateway.connect).toBeCalledWith(
|
||||
connectionProfile,
|
||||
expect.objectContaining<GatewayOptions>({
|
||||
wallet: mockWallet,
|
||||
identity,
|
||||
discovery: expect.any(Object),
|
||||
eventHandlerOptions: expect.any(Object),
|
||||
queryHandlerOptions: expect.any(Object),
|
||||
})
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getNetwork', () => {
|
||||
it('gets a Network instance for the required channel from the Gateway', async () => {
|
||||
const mockGateway = mock<Gateway>();
|
||||
|
||||
await getNetwork(mockGateway);
|
||||
|
||||
expect(mockGateway.getNetwork).toHaveBeenCalledWith(config.channelName);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getContracts', () => {
|
||||
it('gets the asset and qscc contracts from the network', async () => {
|
||||
const mockBasicContract = mock<Contract>();
|
||||
const mockSystemContract = mock<Contract>();
|
||||
const mockNetwork = mock<Network>();
|
||||
mockNetwork.getContract
|
||||
.calledWith(config.chaincodeName)
|
||||
.mockReturnValue(mockBasicContract);
|
||||
mockNetwork.getContract
|
||||
.calledWith('qscc')
|
||||
.mockReturnValue(mockSystemContract);
|
||||
|
||||
const contracts = await getContracts(mockNetwork);
|
||||
|
||||
expect(contracts).toStrictEqual({
|
||||
assetContract: mockBasicContract,
|
||||
qsccContract: mockSystemContract,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('evatuateTransaction', () => {
|
||||
const mockPayload = Buffer.from('MOCK PAYLOAD');
|
||||
let mockTransaction: MockProxy<Transaction>;
|
||||
let mockContract: MockProxy<Contract>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockTransaction = mock<Transaction>();
|
||||
mockTransaction.evaluate.mockResolvedValue(mockPayload);
|
||||
mockContract = mock<Contract>();
|
||||
mockContract.createTransaction
|
||||
.calledWith('txn')
|
||||
.mockReturnValue(mockTransaction);
|
||||
});
|
||||
|
||||
it('gets the result of evaluating a transaction', async () => {
|
||||
const result = await evatuateTransaction(
|
||||
mockContract,
|
||||
'txn',
|
||||
'arga',
|
||||
'argb'
|
||||
);
|
||||
expect(result.toString()).toBe(mockPayload.toString());
|
||||
});
|
||||
|
||||
it('throws an AssetExistsError an asset already exists error occurs', async () => {
|
||||
mockTransaction.evaluate.mockRejectedValue(
|
||||
new Error('The asset JSCHAINCODE already exists')
|
||||
);
|
||||
|
||||
await expect(async () => {
|
||||
await evatuateTransaction(mockContract, 'txn', 'arga', 'argb');
|
||||
}).rejects.toThrow(AssetExistsError);
|
||||
});
|
||||
|
||||
it('throws an AssetNotFoundError if an asset does not exist error occurs', async () => {
|
||||
mockTransaction.evaluate.mockRejectedValue(
|
||||
new Error('The asset JSCHAINCODE does not exist')
|
||||
);
|
||||
|
||||
await expect(async () => {
|
||||
await evatuateTransaction(mockContract, 'txn', 'arga', 'argb');
|
||||
}).rejects.toThrow(AssetNotFoundError);
|
||||
});
|
||||
|
||||
it('throws a TransactionNotFoundError if a transaction not found error occurs', async () => {
|
||||
mockTransaction.evaluate.mockRejectedValue(
|
||||
new Error(
|
||||
'Failed to get transaction with id txn, error Entry not found in index'
|
||||
)
|
||||
);
|
||||
|
||||
await expect(async () => {
|
||||
await evatuateTransaction(mockContract, 'txn', 'arga', 'argb');
|
||||
}).rejects.toThrow(TransactionNotFoundError);
|
||||
});
|
||||
|
||||
it('throws an Error for other errors', async () => {
|
||||
mockTransaction.evaluate.mockRejectedValue(new Error('MOCK ERROR'));
|
||||
await expect(async () => {
|
||||
await evatuateTransaction(mockContract, 'txn', 'arga', 'argb');
|
||||
}).rejects.toThrow(Error);
|
||||
});
|
||||
});
|
||||
|
||||
describe('submitTransaction', () => {
|
||||
let mockTransaction: MockProxy<Transaction>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockTransaction = mock<Transaction>();
|
||||
});
|
||||
|
||||
it('gets the result of submitting a transaction', async () => {
|
||||
const mockPayload = Buffer.from('MOCK PAYLOAD');
|
||||
mockTransaction.submit.mockResolvedValue(mockPayload);
|
||||
|
||||
const result = await submitTransaction(
|
||||
mockTransaction,
|
||||
'txn',
|
||||
'arga',
|
||||
'argb'
|
||||
);
|
||||
expect(result.toString()).toBe(mockPayload.toString());
|
||||
});
|
||||
|
||||
it('throws an AssetExistsError an asset already exists error occurs', async () => {
|
||||
mockTransaction.submit.mockRejectedValue(
|
||||
new Error('The asset JSCHAINCODE already exists')
|
||||
);
|
||||
|
||||
await expect(async () => {
|
||||
await submitTransaction(
|
||||
mockTransaction,
|
||||
'mspid',
|
||||
'txn',
|
||||
'arga',
|
||||
'argb'
|
||||
);
|
||||
}).rejects.toThrow(AssetExistsError);
|
||||
});
|
||||
|
||||
it('throws an AssetNotFoundError if an asset does not exist error occurs', async () => {
|
||||
mockTransaction.submit.mockRejectedValue(
|
||||
new Error('The asset JSCHAINCODE does not exist')
|
||||
);
|
||||
|
||||
await expect(async () => {
|
||||
await submitTransaction(
|
||||
mockTransaction,
|
||||
'mspid',
|
||||
'txn',
|
||||
'arga',
|
||||
'argb'
|
||||
);
|
||||
}).rejects.toThrow(AssetNotFoundError);
|
||||
});
|
||||
|
||||
it('throws a TransactionNotFoundError if a transaction not found error occurs', async () => {
|
||||
mockTransaction.submit.mockRejectedValue(
|
||||
new Error(
|
||||
'Failed to get transaction with id txn, error Entry not found in index'
|
||||
)
|
||||
);
|
||||
|
||||
await expect(async () => {
|
||||
await submitTransaction(
|
||||
mockTransaction,
|
||||
'mspid',
|
||||
'txn',
|
||||
'arga',
|
||||
'argb'
|
||||
);
|
||||
}).rejects.toThrow(TransactionNotFoundError);
|
||||
});
|
||||
|
||||
it('throws an Error for other errors', async () => {
|
||||
mockTransaction.submit.mockRejectedValue(new Error('MOCK ERROR'));
|
||||
|
||||
await expect(async () => {
|
||||
await submitTransaction(
|
||||
mockTransaction,
|
||||
'mspid',
|
||||
'txn',
|
||||
'arga',
|
||||
'argb'
|
||||
);
|
||||
}).rejects.toThrow(Error);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getTransactionValidationCode', () => {
|
||||
it('gets the validation code from a processed transaction', async () => {
|
||||
const processedTransactionProto =
|
||||
fabricProtos.protos.ProcessedTransaction.create();
|
||||
processedTransactionProto.validationCode =
|
||||
fabricProtos.protos.TxValidationCode.VALID;
|
||||
const processedTransactionBuffer = Buffer.from(
|
||||
fabricProtos.protos.ProcessedTransaction.encode(
|
||||
processedTransactionProto
|
||||
).finish()
|
||||
);
|
||||
|
||||
const mockTransaction = mock<Transaction>();
|
||||
mockTransaction.evaluate.mockResolvedValue(processedTransactionBuffer);
|
||||
const mockContract = mock<Contract>();
|
||||
mockContract.createTransaction
|
||||
.calledWith('GetTransactionByID')
|
||||
.mockReturnValue(mockTransaction);
|
||||
expect(await getTransactionValidationCode(mockContract, 'txn1')).toBe(
|
||||
'VALID'
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getBlockHeight', () => {
|
||||
it('gets the current block height', async () => {
|
||||
const mockBlockchainInfoProto =
|
||||
fabricProtos.common.BlockchainInfo.create();
|
||||
mockBlockchainInfoProto.height = 42;
|
||||
const mockBlockchainInfoBuffer = Buffer.from(
|
||||
fabricProtos.common.BlockchainInfo.encode(
|
||||
mockBlockchainInfoProto
|
||||
).finish()
|
||||
);
|
||||
const mockContract = mock<Contract>();
|
||||
mockContract.evaluateTransaction
|
||||
.calledWith('GetChainInfo', 'mychannel')
|
||||
.mockResolvedValue(mockBlockchainInfoBuffer);
|
||||
|
||||
const result = (await getBlockHeight(mockContract)) as Long;
|
||||
expect(result.toInt()).toStrictEqual(42);
|
||||
});
|
||||
});
|
||||
});
|
||||
202
asset-transfer-basic/rest-api-typescript/src/fabric.ts
Normal file
202
asset-transfer-basic/rest-api-typescript/src/fabric.ts
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import {
|
||||
Contract,
|
||||
DefaultEventHandlerStrategies,
|
||||
DefaultQueryHandlerStrategies,
|
||||
Gateway,
|
||||
GatewayOptions,
|
||||
Wallets,
|
||||
Network,
|
||||
Transaction,
|
||||
Wallet,
|
||||
} from 'fabric-network';
|
||||
import * as config from './config';
|
||||
import { logger } from './logger';
|
||||
import { handleError } from './errors';
|
||||
import * as protos from 'fabric-protos';
|
||||
|
||||
/*
|
||||
* Creates an in memory wallet to hold credentials for an Org1 and Org2 user
|
||||
*
|
||||
* In this sample there is a single user for each MSP ID to demonstrate how
|
||||
* a client app might submit transactions for different users
|
||||
*
|
||||
* Alternatively a REST server could use its own identity for all transactions,
|
||||
* or it could use credentials supplied in the REST requests
|
||||
*/
|
||||
export const createWallet = async (): Promise<Wallet> => {
|
||||
const wallet = await Wallets.newInMemoryWallet();
|
||||
|
||||
const org1Identity = {
|
||||
credentials: {
|
||||
certificate: config.certificateOrg1,
|
||||
privateKey: config.privateKeyOrg1,
|
||||
},
|
||||
mspId: config.mspIdOrg1,
|
||||
type: 'X.509',
|
||||
};
|
||||
|
||||
await wallet.put(config.mspIdOrg1, org1Identity);
|
||||
|
||||
const org2Identity = {
|
||||
credentials: {
|
||||
certificate: config.certificateOrg2,
|
||||
privateKey: config.privateKeyOrg2,
|
||||
},
|
||||
mspId: config.mspIdOrg2,
|
||||
type: 'X.509',
|
||||
};
|
||||
|
||||
await wallet.put(config.mspIdOrg2, org2Identity);
|
||||
|
||||
return wallet;
|
||||
};
|
||||
|
||||
/*
|
||||
* Create a Gateway connection
|
||||
*
|
||||
* Gateway instances can and should be reused rather than connecting to submit every transaction
|
||||
*/
|
||||
export const createGateway = async (
|
||||
connectionProfile: Record<string, unknown>,
|
||||
identity: string,
|
||||
wallet: Wallet
|
||||
): Promise<Gateway> => {
|
||||
logger.debug({ connectionProfile, identity }, 'Configuring gateway');
|
||||
|
||||
const gateway = new Gateway();
|
||||
|
||||
const options: GatewayOptions = {
|
||||
wallet,
|
||||
identity,
|
||||
discovery: { enabled: true, asLocalhost: config.asLocalhost },
|
||||
eventHandlerOptions: {
|
||||
commitTimeout: config.commitTimeout,
|
||||
endorseTimeout: config.endorseTimeout,
|
||||
strategy: DefaultEventHandlerStrategies.PREFER_MSPID_SCOPE_ANYFORTX,
|
||||
},
|
||||
queryHandlerOptions: {
|
||||
timeout: config.queryTimeout,
|
||||
strategy: DefaultQueryHandlerStrategies.PREFER_MSPID_SCOPE_ROUND_ROBIN,
|
||||
},
|
||||
};
|
||||
|
||||
await gateway.connect(connectionProfile, options);
|
||||
|
||||
return gateway;
|
||||
};
|
||||
|
||||
/*
|
||||
* Get the network which the asset transfer sample chaincode is running on
|
||||
*
|
||||
* In addion to getting the contract, the network will also be used to
|
||||
* start a block event listener
|
||||
*/
|
||||
export const getNetwork = async (gateway: Gateway): Promise<Network> => {
|
||||
const network = await gateway.getNetwork(config.channelName);
|
||||
return network;
|
||||
};
|
||||
|
||||
/*
|
||||
* Get the asset transfer sample contract and the qscc system contract
|
||||
*
|
||||
* The system contract is used for the liveness REST endpoint
|
||||
*/
|
||||
export const getContracts = async (
|
||||
network: Network
|
||||
): Promise<{ assetContract: Contract; qsccContract: Contract }> => {
|
||||
const assetContract = network.getContract(config.chaincodeName);
|
||||
const qsccContract = network.getContract('qscc');
|
||||
return { assetContract, qsccContract };
|
||||
};
|
||||
|
||||
/*
|
||||
* Evaluate a transaction and handle any errors
|
||||
*/
|
||||
export const evatuateTransaction = async (
|
||||
contract: Contract,
|
||||
transactionName: string,
|
||||
...transactionArgs: string[]
|
||||
): Promise<Buffer> => {
|
||||
const transaction = contract.createTransaction(transactionName);
|
||||
const transactionId = transaction.getTransactionId();
|
||||
logger.trace({ transaction }, 'Evaluating transaction');
|
||||
|
||||
try {
|
||||
const payload = await transaction.evaluate(...transactionArgs);
|
||||
logger.trace(
|
||||
{ transactionId: transactionId, payload: payload.toString() },
|
||||
'Evaluate transaction response received'
|
||||
);
|
||||
return payload;
|
||||
} catch (err) {
|
||||
throw handleError(transactionId, err);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Submit a transaction and handle any errors
|
||||
*/
|
||||
export const submitTransaction = async (
|
||||
transaction: Transaction,
|
||||
...transactionArgs: string[]
|
||||
): Promise<Buffer> => {
|
||||
logger.trace({ transaction }, 'Submitting transaction');
|
||||
const txnId = transaction.getTransactionId();
|
||||
|
||||
try {
|
||||
const payload = await transaction.submit(...transactionArgs);
|
||||
logger.trace(
|
||||
{ transactionId: txnId, payload: payload.toString() },
|
||||
'Submit transaction response received'
|
||||
);
|
||||
return payload;
|
||||
} catch (err) {
|
||||
throw handleError(txnId, err);
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Get the validation code of the specified transaction
|
||||
*/
|
||||
export const getTransactionValidationCode = async (
|
||||
qsccContract: Contract,
|
||||
transactionId: string
|
||||
): Promise<string> => {
|
||||
const data = await evatuateTransaction(
|
||||
qsccContract,
|
||||
'GetTransactionByID',
|
||||
config.channelName,
|
||||
transactionId
|
||||
);
|
||||
|
||||
const processedTransaction = protos.protos.ProcessedTransaction.decode(data);
|
||||
const validationCode =
|
||||
protos.protos.TxValidationCode[processedTransaction.validationCode];
|
||||
|
||||
logger.debug({ transactionId }, 'Validation code: %s', validationCode);
|
||||
return validationCode;
|
||||
};
|
||||
|
||||
/*
|
||||
* Get the current block height
|
||||
*
|
||||
* This example of using a system contract is used for the liveness REST
|
||||
* endpoint
|
||||
*/
|
||||
export const getBlockHeight = async (
|
||||
qscc: Contract
|
||||
): Promise<number | Long.Long> => {
|
||||
const data = await qscc.evaluateTransaction(
|
||||
'GetChainInfo',
|
||||
config.channelName
|
||||
);
|
||||
const info = protos.common.BlockchainInfo.decode(data);
|
||||
const blockHeight = info.height;
|
||||
|
||||
logger.debug('Current block height: %d', blockHeight);
|
||||
return blockHeight;
|
||||
};
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import express, { Request, Response } from 'express';
|
||||
import { Contract } from 'fabric-network';
|
||||
import { getReasonPhrase, StatusCodes } from 'http-status-codes';
|
||||
import { getBlockHeight } from './fabric';
|
||||
import { logger } from './logger';
|
||||
import * as config from './config';
|
||||
import { Queue } from 'bullmq';
|
||||
import { getJobCounts } from './jobs';
|
||||
|
||||
const { SERVICE_UNAVAILABLE, OK } = StatusCodes;
|
||||
|
||||
export const healthRouter = express.Router();
|
||||
|
||||
/*
|
||||
* Example of possible health endpoints for use in a cloud environment
|
||||
*/
|
||||
|
||||
healthRouter.get('/ready', (_req, res: Response) =>
|
||||
res.status(OK).json({
|
||||
status: getReasonPhrase(OK),
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
);
|
||||
|
||||
healthRouter.get('/live', async (req: Request, res: Response) => {
|
||||
logger.debug(req.body, 'Liveness request received');
|
||||
|
||||
try {
|
||||
const submitQueue = req.app.locals.jobq as Queue;
|
||||
const qsccOrg1 = req.app.locals[config.mspIdOrg1]?.qsccContract as Contract;
|
||||
const qsccOrg2 = req.app.locals[config.mspIdOrg2]?.qsccContract as Contract;
|
||||
|
||||
await Promise.all([
|
||||
getBlockHeight(qsccOrg1),
|
||||
getBlockHeight(qsccOrg2),
|
||||
getJobCounts(submitQueue),
|
||||
]);
|
||||
} catch (err) {
|
||||
logger.error({ err }, 'Error processing liveness request');
|
||||
|
||||
return res.status(SERVICE_UNAVAILABLE).json({
|
||||
status: getReasonPhrase(SERVICE_UNAVAILABLE),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(OK).json({
|
||||
status: getReasonPhrase(OK),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
});
|
||||
123
asset-transfer-basic/rest-api-typescript/src/index.ts
Normal file
123
asset-transfer-basic/rest-api-typescript/src/index.ts
Normal file
|
|
@ -0,0 +1,123 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* This is the main entrypoint for the sample REST server, which is responsible
|
||||
* for connecting to the Fabric network and setting up a job queue for
|
||||
* processing submit transactions
|
||||
*
|
||||
* You can find more details related to the Fabric aspects of the sample in the
|
||||
* following files:
|
||||
*
|
||||
* - errors.ts
|
||||
* Fabric transaction error handling and retry logic
|
||||
* - fabric.ts
|
||||
* all the sample code which interacts with the Fabric SDK
|
||||
*
|
||||
* The remaining files are related to the REST server aspects of the sample,
|
||||
* rather than Fabric itself:
|
||||
*
|
||||
* - *.router.ts
|
||||
* details of the REST endpoints provided by the sample
|
||||
* - auth.ts
|
||||
* basic API key authentication strategy used for the sample
|
||||
* - config.ts
|
||||
* descriptions of all the available configuration environment variables
|
||||
* - jobs.ts
|
||||
* job queue implementation details
|
||||
* - logger.ts
|
||||
* logging implementation details
|
||||
* - redis.ts
|
||||
* redis implementation details
|
||||
* - server.ts
|
||||
* express server implementation details
|
||||
*/
|
||||
|
||||
import * as config from './config';
|
||||
import {
|
||||
createGateway,
|
||||
createWallet,
|
||||
getContracts,
|
||||
getNetwork,
|
||||
} from './fabric';
|
||||
import {
|
||||
initJobQueue,
|
||||
initJobQueueScheduler,
|
||||
initJobQueueWorker,
|
||||
} from './jobs';
|
||||
import { logger } from './logger';
|
||||
import { createServer } from './server';
|
||||
import { isMaxmemoryPolicyNoeviction } from './redis';
|
||||
import { Queue, QueueScheduler, Worker } from 'bullmq';
|
||||
|
||||
let jobQueue: Queue | undefined;
|
||||
let jobQueueWorker: Worker | undefined;
|
||||
let jobQueueScheduler: QueueScheduler | undefined;
|
||||
|
||||
async function main() {
|
||||
logger.info('Checking Redis config');
|
||||
if (!(await isMaxmemoryPolicyNoeviction())) {
|
||||
throw new Error(
|
||||
'Invalid redis configuration: redis instance must have the setting maxmemory-policy=noeviction'
|
||||
);
|
||||
}
|
||||
|
||||
logger.info('Creating REST server');
|
||||
const app = await createServer();
|
||||
|
||||
logger.info('Connecting to Fabric network with org1 mspid');
|
||||
const wallet = await createWallet();
|
||||
|
||||
const gatewayOrg1 = await createGateway(
|
||||
config.connectionProfileOrg1,
|
||||
config.mspIdOrg1,
|
||||
wallet
|
||||
);
|
||||
const networkOrg1 = await getNetwork(gatewayOrg1);
|
||||
const contractsOrg1 = await getContracts(networkOrg1);
|
||||
|
||||
app.locals[config.mspIdOrg1] = contractsOrg1;
|
||||
|
||||
logger.info('Connecting to Fabric network with org2 mspid');
|
||||
const gatewayOrg2 = await createGateway(
|
||||
config.connectionProfileOrg2,
|
||||
config.mspIdOrg2,
|
||||
wallet
|
||||
);
|
||||
const networkOrg2 = await getNetwork(gatewayOrg2);
|
||||
const contractsOrg2 = await getContracts(networkOrg2);
|
||||
|
||||
app.locals[config.mspIdOrg2] = contractsOrg2;
|
||||
|
||||
logger.info('Initialising submit job queue');
|
||||
jobQueue = initJobQueue();
|
||||
jobQueueWorker = initJobQueueWorker(app);
|
||||
if (config.submitJobQueueScheduler === true) {
|
||||
logger.info('Initialising submit job queue scheduler');
|
||||
jobQueueScheduler = initJobQueueScheduler();
|
||||
}
|
||||
app.locals.jobq = jobQueue;
|
||||
|
||||
logger.info('Starting REST server');
|
||||
app.listen(config.port, () => {
|
||||
logger.info('REST server started on port: %d', config.port);
|
||||
});
|
||||
}
|
||||
|
||||
main().catch(async (err) => {
|
||||
logger.error({ err }, 'Unxepected error');
|
||||
|
||||
if (jobQueueScheduler != undefined) {
|
||||
logger.debug('Closing job queue scheduler');
|
||||
await jobQueueScheduler.close();
|
||||
}
|
||||
|
||||
if (jobQueueWorker != undefined) {
|
||||
logger.debug('Closing job queue worker');
|
||||
await jobQueueWorker.close();
|
||||
}
|
||||
|
||||
if (jobQueue != undefined) {
|
||||
logger.debug('Closing job queue');
|
||||
await jobQueue.close();
|
||||
}
|
||||
});
|
||||
40
asset-transfer-basic/rest-api-typescript/src/jobs.router.ts
Normal file
40
asset-transfer-basic/rest-api-typescript/src/jobs.router.ts
Normal file
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { Queue } from 'bullmq';
|
||||
import express, { Request, Response } from 'express';
|
||||
import { getReasonPhrase, StatusCodes } from 'http-status-codes';
|
||||
import { getJobSummary, JobNotFoundError } from './jobs';
|
||||
import { logger } from './logger';
|
||||
|
||||
const { INTERNAL_SERVER_ERROR, NOT_FOUND, OK } = StatusCodes;
|
||||
|
||||
export const jobsRouter = express.Router();
|
||||
|
||||
jobsRouter.get('/:jobId', async (req: Request, res: Response) => {
|
||||
const jobId = req.params.jobId;
|
||||
logger.debug('Read request received for job ID %s', jobId);
|
||||
|
||||
try {
|
||||
const submitQueue = req.app.locals.jobq as Queue;
|
||||
|
||||
const jobSummary = await getJobSummary(submitQueue, jobId);
|
||||
|
||||
return res.status(OK).json(jobSummary);
|
||||
} catch (err) {
|
||||
logger.error({ err }, 'Error processing read request for job ID %s', jobId);
|
||||
|
||||
if (err instanceof JobNotFoundError) {
|
||||
return res.status(NOT_FOUND).json({
|
||||
status: getReasonPhrase(NOT_FOUND),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
|
||||
return res.status(INTERNAL_SERVER_ERROR).json({
|
||||
status: getReasonPhrase(INTERNAL_SERVER_ERROR),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
});
|
||||
349
asset-transfer-basic/rest-api-typescript/src/jobs.spec.ts
Normal file
349
asset-transfer-basic/rest-api-typescript/src/jobs.spec.ts
Normal file
|
|
@ -0,0 +1,349 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { Job, Queue } from 'bullmq';
|
||||
import {
|
||||
addSubmitTransactionJob,
|
||||
getJobCounts,
|
||||
getJobSummary,
|
||||
processSubmitTransactionJob,
|
||||
JobNotFoundError,
|
||||
updateJobData,
|
||||
} from './jobs';
|
||||
import { Contract, Transaction } from 'fabric-network';
|
||||
import { mock, MockProxy } from 'jest-mock-extended';
|
||||
import { Application } from 'express';
|
||||
|
||||
describe('addSubmitTransactionJob', () => {
|
||||
let mockJob: MockProxy<Job>;
|
||||
let mockQueue: MockProxy<Queue>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockJob = mock<Job>();
|
||||
mockQueue = mock<Queue>();
|
||||
mockQueue.add.mockResolvedValue(mockJob);
|
||||
});
|
||||
|
||||
it('returns the new job ID', async () => {
|
||||
mockJob.id = 'mockJobId';
|
||||
|
||||
const jobid = await addSubmitTransactionJob(
|
||||
mockQueue,
|
||||
'mockMspId',
|
||||
'txn',
|
||||
'arg1',
|
||||
'arg2'
|
||||
);
|
||||
|
||||
expect(jobid).toBe('mockJobId');
|
||||
});
|
||||
|
||||
it('throws an error if there is no job ID', async () => {
|
||||
mockJob.id = undefined;
|
||||
|
||||
await expect(async () => {
|
||||
await addSubmitTransactionJob(
|
||||
mockQueue,
|
||||
'mockMspId',
|
||||
'txn',
|
||||
'arg1',
|
||||
'arg2'
|
||||
);
|
||||
}).rejects.toThrowError('Submit transaction job ID not available');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getJobSummary', () => {
|
||||
let mockQueue: MockProxy<Queue>;
|
||||
let mockJob: MockProxy<Job>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockQueue = mock<Queue>();
|
||||
mockJob = mock<Job>();
|
||||
});
|
||||
|
||||
it('throws a JobNotFoundError if the Job is undefined', async () => {
|
||||
mockQueue.getJob.calledWith('1').mockResolvedValue(undefined);
|
||||
|
||||
await expect(async () => {
|
||||
await getJobSummary(mockQueue, '1');
|
||||
}).rejects.toThrow(JobNotFoundError);
|
||||
});
|
||||
|
||||
it('gets a job summary with transaction payload data', async () => {
|
||||
mockQueue.getJob.calledWith('1').mockResolvedValue(mockJob);
|
||||
mockJob.id = '1';
|
||||
mockJob.data = {
|
||||
transactionIds: ['txn1'],
|
||||
};
|
||||
mockJob.returnvalue = {
|
||||
transactionPayload: Buffer.from('MOCK PAYLOAD'),
|
||||
};
|
||||
|
||||
expect(await getJobSummary(mockQueue, '1')).toStrictEqual({
|
||||
jobId: '1',
|
||||
transactionIds: ['txn1'],
|
||||
transactionError: undefined,
|
||||
transactionPayload: 'MOCK PAYLOAD',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets a job summary with empty transaction payload data', async () => {
|
||||
mockQueue.getJob.calledWith('1').mockResolvedValue(mockJob);
|
||||
mockJob.id = '1';
|
||||
mockJob.data = {
|
||||
transactionIds: ['txn1'],
|
||||
};
|
||||
mockJob.returnvalue = {
|
||||
transactionPayload: Buffer.from(''),
|
||||
};
|
||||
|
||||
expect(await getJobSummary(mockQueue, '1')).toStrictEqual({
|
||||
jobId: '1',
|
||||
transactionIds: ['txn1'],
|
||||
transactionError: undefined,
|
||||
transactionPayload: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets a job summary with a transaction error', async () => {
|
||||
mockQueue.getJob.calledWith('1').mockResolvedValue(mockJob);
|
||||
mockJob.id = '1';
|
||||
mockJob.data = {
|
||||
transactionIds: ['txn1'],
|
||||
};
|
||||
mockJob.returnvalue = {
|
||||
transactionError: 'MOCK ERROR',
|
||||
};
|
||||
|
||||
expect(await getJobSummary(mockQueue, '1')).toStrictEqual({
|
||||
jobId: '1',
|
||||
transactionIds: ['txn1'],
|
||||
transactionError: 'MOCK ERROR',
|
||||
transactionPayload: '',
|
||||
});
|
||||
});
|
||||
|
||||
it('gets a job summary when there is no return value', async () => {
|
||||
mockQueue.getJob.calledWith('1').mockResolvedValue(mockJob);
|
||||
mockJob.id = '1';
|
||||
mockJob.returnvalue = undefined;
|
||||
mockJob.data = {
|
||||
transactionIds: ['txn1'],
|
||||
};
|
||||
|
||||
expect(await getJobSummary(mockQueue, '1')).toStrictEqual({
|
||||
jobId: '1',
|
||||
transactionIds: ['txn1'],
|
||||
transactionError: undefined,
|
||||
transactionPayload: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('gets a job summary when there is no job data', async () => {
|
||||
mockQueue.getJob.calledWith('1').mockResolvedValue(mockJob);
|
||||
mockJob.id = '1';
|
||||
mockJob.data = undefined;
|
||||
mockJob.returnvalue = {
|
||||
transactionPayload: Buffer.from('MOCK PAYLOAD'),
|
||||
};
|
||||
|
||||
expect(await getJobSummary(mockQueue, '1')).toStrictEqual({
|
||||
jobId: '1',
|
||||
transactionIds: [],
|
||||
transactionError: undefined,
|
||||
transactionPayload: 'MOCK PAYLOAD',
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('updateJobData', () => {
|
||||
let mockJob: MockProxy<Job>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockJob = mock<Job>();
|
||||
mockJob.data = {
|
||||
transactionIds: ['txn1'],
|
||||
};
|
||||
});
|
||||
|
||||
it('stores the serialized state in the job data if a transaction is specified', async () => {
|
||||
const mockSavedState = Buffer.from('MOCK SAVED STATE');
|
||||
const mockTransaction = mock<Transaction>();
|
||||
mockTransaction.getTransactionId.mockReturnValue('txn2');
|
||||
mockTransaction.serialize.mockReturnValue(mockSavedState);
|
||||
|
||||
await updateJobData(mockJob, mockTransaction);
|
||||
|
||||
expect(mockJob.update).toBeCalledTimes(1);
|
||||
expect(mockJob.update).toBeCalledWith({
|
||||
transactionIds: ['txn1', 'txn2'],
|
||||
transactionState: mockSavedState,
|
||||
});
|
||||
});
|
||||
|
||||
it('removes the serialized state from the job data if a transaction is not specified', async () => {
|
||||
await updateJobData(mockJob, undefined);
|
||||
|
||||
expect(mockJob.update).toBeCalledTimes(1);
|
||||
expect(mockJob.update).toBeCalledWith({
|
||||
transactionIds: ['txn1'],
|
||||
transactionState: undefined,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('getJobCounts', () => {
|
||||
it('gets job counts from the specified queue', async () => {
|
||||
const mockQueue = mock<Queue>();
|
||||
mockQueue.getJobCounts
|
||||
.calledWith('active', 'completed', 'delayed', 'failed', 'waiting')
|
||||
.mockResolvedValue({
|
||||
active: 1,
|
||||
completed: 2,
|
||||
delayed: 3,
|
||||
failed: 4,
|
||||
waiting: 5,
|
||||
});
|
||||
|
||||
expect(await getJobCounts(mockQueue)).toStrictEqual({
|
||||
active: 1,
|
||||
completed: 2,
|
||||
delayed: 3,
|
||||
failed: 4,
|
||||
waiting: 5,
|
||||
});
|
||||
});
|
||||
|
||||
describe('processSubmitTransactionJob', () => {
|
||||
const mockContracts = new Map<string, Contract>();
|
||||
const mockPayload = Buffer.from('MOCK PAYLOAD');
|
||||
const mockSavedState = Buffer.from('MOCK SAVED STATE');
|
||||
let mockTransaction: MockProxy<Transaction>;
|
||||
let mockContract: MockProxy<Contract>;
|
||||
let mockApplication: MockProxy<Application>;
|
||||
let mockJob: MockProxy<Job>;
|
||||
|
||||
beforeEach(() => {
|
||||
mockTransaction = mock<Transaction>();
|
||||
mockTransaction.getTransactionId.mockReturnValue('mockTransactionId');
|
||||
|
||||
mockContract = mock<Contract>();
|
||||
mockContract.createTransaction
|
||||
.calledWith('txn')
|
||||
.mockReturnValue(mockTransaction);
|
||||
mockContract.deserializeTransaction
|
||||
.calledWith(mockSavedState)
|
||||
.mockReturnValue(mockTransaction);
|
||||
mockContracts.set('mockMspid', mockContract);
|
||||
|
||||
mockApplication = mock<Application>();
|
||||
mockApplication.locals.mockMspid = { assetContract: mockContract };
|
||||
|
||||
mockJob = mock<Job>();
|
||||
});
|
||||
|
||||
it('gets job result with no error or payload if no contract is available for the required mspid', async () => {
|
||||
mockJob.data = {
|
||||
mspid: 'missingMspid',
|
||||
};
|
||||
|
||||
const jobResult = await processSubmitTransactionJob(
|
||||
mockApplication,
|
||||
mockJob
|
||||
);
|
||||
|
||||
expect(jobResult).toStrictEqual({
|
||||
transactionError: undefined,
|
||||
transactionPayload: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('gets a job result containing a payload if the transaction was successful first time', async () => {
|
||||
mockJob.data = {
|
||||
mspid: 'mockMspid',
|
||||
transactionName: 'txn',
|
||||
transactionArgs: ['arg1', 'arg2'],
|
||||
};
|
||||
mockTransaction.submit
|
||||
.calledWith('arg1', 'arg2')
|
||||
.mockResolvedValue(mockPayload);
|
||||
|
||||
const jobResult = await processSubmitTransactionJob(
|
||||
mockApplication,
|
||||
mockJob
|
||||
);
|
||||
|
||||
expect(jobResult).toStrictEqual({
|
||||
transactionError: undefined,
|
||||
transactionPayload: Buffer.from('MOCK PAYLOAD'),
|
||||
});
|
||||
});
|
||||
|
||||
it('gets a job result containing a payload if the transaction was successfully rerun using saved transaction state', async () => {
|
||||
mockJob.data = {
|
||||
mspid: 'mockMspid',
|
||||
transactionName: 'txn',
|
||||
transactionArgs: ['arg1', 'arg2'],
|
||||
transactionState: mockSavedState,
|
||||
};
|
||||
mockTransaction.submit
|
||||
.calledWith('arg1', 'arg2')
|
||||
.mockResolvedValue(mockPayload);
|
||||
|
||||
const jobResult = await processSubmitTransactionJob(
|
||||
mockApplication,
|
||||
mockJob
|
||||
);
|
||||
|
||||
expect(jobResult).toStrictEqual({
|
||||
transactionError: undefined,
|
||||
transactionPayload: Buffer.from('MOCK PAYLOAD'),
|
||||
});
|
||||
});
|
||||
|
||||
it('gets a job result containing an error message if the transaction fails but cannot be retried', async () => {
|
||||
mockJob.data = {
|
||||
mspid: 'mockMspid',
|
||||
transactionName: 'txn',
|
||||
transactionArgs: ['arg1', 'arg2'],
|
||||
transactionState: mockSavedState,
|
||||
};
|
||||
mockTransaction.submit
|
||||
.calledWith('arg1', 'arg2')
|
||||
.mockRejectedValue(
|
||||
new Error(
|
||||
'Failed to get transaction with id txn, error Entry not found in index'
|
||||
)
|
||||
);
|
||||
|
||||
const jobResult = await processSubmitTransactionJob(
|
||||
mockApplication,
|
||||
mockJob
|
||||
);
|
||||
|
||||
expect(jobResult).toStrictEqual({
|
||||
transactionError:
|
||||
'TransactionNotFoundError: Failed to get transaction with id txn, error Entry not found in index',
|
||||
transactionPayload: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('throws an error if the transaction fails but can be retried', async () => {
|
||||
mockJob.data = {
|
||||
mspid: 'mockMspid',
|
||||
transactionName: 'txn',
|
||||
transactionArgs: ['arg1', 'arg2'],
|
||||
transactionState: mockSavedState,
|
||||
};
|
||||
mockTransaction.submit
|
||||
.calledWith('arg1', 'arg2')
|
||||
.mockRejectedValue(new Error('MOCK ERROR'));
|
||||
|
||||
await expect(async () => {
|
||||
await processSubmitTransactionJob(mockApplication, mockJob);
|
||||
}).rejects.toThrow('MOCK ERROR');
|
||||
});
|
||||
});
|
||||
});
|
||||
346
asset-transfer-basic/rest-api-typescript/src/jobs.ts
Normal file
346
asset-transfer-basic/rest-api-typescript/src/jobs.ts
Normal file
|
|
@ -0,0 +1,346 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* This sample uses BullMQ jobs to process submit transactions, which includes
|
||||
* retry support for failing jobs
|
||||
*/
|
||||
|
||||
import { ConnectionOptions, Job, Queue, QueueScheduler, Worker } from 'bullmq';
|
||||
import { Application } from 'express';
|
||||
import { Contract, Transaction } from 'fabric-network';
|
||||
import * as config from './config';
|
||||
import { getRetryAction, RetryAction } from './errors';
|
||||
import { submitTransaction } from './fabric';
|
||||
import { logger } from './logger';
|
||||
|
||||
export type JobData = {
|
||||
mspid: string;
|
||||
transactionName: string;
|
||||
transactionArgs: string[];
|
||||
transactionState?: Buffer;
|
||||
transactionIds: string[];
|
||||
};
|
||||
|
||||
export type JobResult = {
|
||||
transactionPayload?: Buffer;
|
||||
transactionError?: string;
|
||||
};
|
||||
|
||||
export type JobSummary = {
|
||||
jobId: string;
|
||||
transactionIds: string[];
|
||||
transactionPayload?: string;
|
||||
transactionError?: string;
|
||||
};
|
||||
|
||||
export class JobNotFoundError extends Error {
|
||||
jobId: string;
|
||||
|
||||
constructor(message: string, jobId: string) {
|
||||
super(message);
|
||||
Object.setPrototypeOf(this, JobNotFoundError.prototype);
|
||||
|
||||
this.name = 'JobNotFoundError';
|
||||
this.jobId = jobId;
|
||||
}
|
||||
}
|
||||
|
||||
const connection: ConnectionOptions = {
|
||||
port: config.redisPort,
|
||||
host: config.redisHost,
|
||||
username: config.redisUsername,
|
||||
password: config.redisPassword,
|
||||
};
|
||||
|
||||
/*
|
||||
* Set up the queue for submit jobs
|
||||
*/
|
||||
export const initJobQueue = (): Queue => {
|
||||
const submitQueue = new Queue(config.JOB_QUEUE_NAME, {
|
||||
connection,
|
||||
defaultJobOptions: {
|
||||
attempts: config.submitJobAttempts,
|
||||
backoff: {
|
||||
type: config.submitJobBackoffType,
|
||||
delay: config.submitJobBackoffDelay,
|
||||
},
|
||||
removeOnComplete: config.maxCompletedSubmitJobs,
|
||||
removeOnFail: config.maxFailedSubmitJobs,
|
||||
},
|
||||
});
|
||||
|
||||
return submitQueue;
|
||||
};
|
||||
|
||||
/*
|
||||
* Set up a worker to process submit jobs on the queue, using the
|
||||
* processSubmitTransactionJob function below
|
||||
*/
|
||||
export const initJobQueueWorker = (app: Application): Worker => {
|
||||
const worker = new Worker<JobData, JobResult>(
|
||||
config.JOB_QUEUE_NAME,
|
||||
async (job): Promise<JobResult> => {
|
||||
return await processSubmitTransactionJob(app, job);
|
||||
},
|
||||
{ connection, concurrency: config.submitJobConcurrency }
|
||||
);
|
||||
|
||||
worker.on('failed', (job) => {
|
||||
logger.warn({ job }, 'Job failed');
|
||||
});
|
||||
|
||||
// Important: need to handle this error otherwise worker may stop
|
||||
// processing jobs
|
||||
worker.on('error', (err) => {
|
||||
logger.error({ err }, 'Worker error');
|
||||
});
|
||||
|
||||
if (logger.isLevelEnabled('debug')) {
|
||||
worker.on('completed', (job) => {
|
||||
logger.debug({ job }, 'Job completed');
|
||||
});
|
||||
}
|
||||
|
||||
return worker;
|
||||
};
|
||||
|
||||
/*
|
||||
* Process a submit transaction request from the job queue
|
||||
*
|
||||
* The job will be retried if this function throws an error
|
||||
*/
|
||||
export const processSubmitTransactionJob = async (
|
||||
app: Application,
|
||||
job: Job<JobData, JobResult>
|
||||
): Promise<JobResult> => {
|
||||
logger.debug({ jobId: job.id, jobName: job.name }, 'Processing job');
|
||||
|
||||
const contract = app.locals[job.data.mspid]?.assetContract as Contract;
|
||||
if (contract === undefined) {
|
||||
logger.error(
|
||||
{ jobId: job.id, jobName: job.name },
|
||||
'Contract not found for MSP ID %s',
|
||||
job.data.mspid
|
||||
);
|
||||
|
||||
// Retrying will never work without a contract, so give up with an
|
||||
// empty job result
|
||||
return {
|
||||
transactionError: undefined,
|
||||
transactionPayload: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
const args = job.data.transactionArgs;
|
||||
let transaction: Transaction;
|
||||
|
||||
if (job.data.transactionState) {
|
||||
const savedState = job.data.transactionState;
|
||||
logger.debug(
|
||||
{
|
||||
jobId: job.id,
|
||||
jobName: job.name,
|
||||
savedState,
|
||||
},
|
||||
'Reusing previously saved transaction state'
|
||||
);
|
||||
|
||||
transaction = contract.deserializeTransaction(savedState);
|
||||
} else {
|
||||
logger.debug(
|
||||
{
|
||||
jobId: job.id,
|
||||
jobName: job.name,
|
||||
},
|
||||
'Using new transaction'
|
||||
);
|
||||
|
||||
transaction = contract.createTransaction(job.data.transactionName);
|
||||
await updateJobData(job, transaction);
|
||||
}
|
||||
|
||||
logger.debug(
|
||||
{
|
||||
jobId: job.id,
|
||||
jobName: job.name,
|
||||
transactionId: transaction.getTransactionId(),
|
||||
},
|
||||
'Submitting transaction'
|
||||
);
|
||||
|
||||
try {
|
||||
const payload = await submitTransaction(transaction, ...args);
|
||||
|
||||
return {
|
||||
transactionError: undefined,
|
||||
transactionPayload: payload,
|
||||
};
|
||||
} catch (err) {
|
||||
const retryAction = getRetryAction(err);
|
||||
|
||||
if (retryAction === RetryAction.None) {
|
||||
logger.error(
|
||||
{ jobId: job.id, jobName: job.name, err },
|
||||
'Fatal transaction error occurred'
|
||||
);
|
||||
|
||||
// Not retriable so return a job result with the error details
|
||||
return {
|
||||
transactionError: `${err}`,
|
||||
transactionPayload: undefined,
|
||||
};
|
||||
}
|
||||
|
||||
logger.warn(
|
||||
{ jobId: job.id, jobName: job.name, err },
|
||||
'Retryable transaction error occurred'
|
||||
);
|
||||
|
||||
if (retryAction === RetryAction.WithNewTransactionId) {
|
||||
logger.debug(
|
||||
{ jobId: job.id, jobName: job.name },
|
||||
'Clearing saved transaction state'
|
||||
);
|
||||
await updateJobData(job, undefined);
|
||||
}
|
||||
|
||||
// Rethrow the error to keep retrying
|
||||
throw err;
|
||||
}
|
||||
};
|
||||
|
||||
/*
|
||||
* Set up a scheduler for the submit job queue
|
||||
*
|
||||
* This manages stalled and delayed jobs and is required for retries with backoff
|
||||
*/
|
||||
export const initJobQueueScheduler = (): QueueScheduler => {
|
||||
const queueScheduler = new QueueScheduler(config.JOB_QUEUE_NAME, {
|
||||
connection,
|
||||
});
|
||||
|
||||
queueScheduler.on('failed', (jobId, failedReason) => {
|
||||
logger.error({ jobId, failedReason }, 'Queue sceduler failure');
|
||||
});
|
||||
|
||||
return queueScheduler;
|
||||
};
|
||||
|
||||
/*
|
||||
* Helper to add a new submit transaction job to the queue
|
||||
*/
|
||||
export const addSubmitTransactionJob = async (
|
||||
submitQueue: Queue<JobData, JobResult>,
|
||||
mspid: string,
|
||||
transactionName: string,
|
||||
...transactionArgs: string[]
|
||||
): Promise<string> => {
|
||||
const jobName = `submit ${transactionName} transaction`;
|
||||
const job = await submitQueue.add(jobName, {
|
||||
mspid,
|
||||
transactionName,
|
||||
transactionArgs: transactionArgs,
|
||||
transactionIds: [],
|
||||
});
|
||||
|
||||
if (job?.id === undefined) {
|
||||
throw new Error('Submit transaction job ID not available');
|
||||
}
|
||||
|
||||
return job.id;
|
||||
};
|
||||
|
||||
/*
|
||||
* Helper to update the data for an existing job
|
||||
*/
|
||||
export const updateJobData = async (
|
||||
job: Job<JobData, JobResult>,
|
||||
transaction: Transaction | undefined
|
||||
): Promise<void> => {
|
||||
const newData = { ...job.data };
|
||||
|
||||
if (transaction != undefined) {
|
||||
const transationIds = ([] as string[]).concat(
|
||||
newData.transactionIds,
|
||||
transaction.getTransactionId()
|
||||
);
|
||||
newData.transactionIds = transationIds;
|
||||
|
||||
newData.transactionState = transaction.serialize();
|
||||
} else {
|
||||
newData.transactionState = undefined;
|
||||
}
|
||||
|
||||
await job.update(newData);
|
||||
};
|
||||
|
||||
/*
|
||||
* Gets a job summary
|
||||
*
|
||||
* This function is used for the jobs REST endpoint
|
||||
*/
|
||||
export const getJobSummary = async (
|
||||
queue: Queue,
|
||||
jobId: string
|
||||
): Promise<JobSummary> => {
|
||||
const job: Job<JobData, JobResult> | undefined = await queue.getJob(jobId);
|
||||
logger.debug({ job }, 'Got job');
|
||||
|
||||
if (!(job && job.id != undefined)) {
|
||||
throw new JobNotFoundError(`Job ${jobId} not found`, jobId);
|
||||
}
|
||||
|
||||
let transactionIds: string[];
|
||||
if (job.data && job.data.transactionIds) {
|
||||
transactionIds = job.data.transactionIds;
|
||||
} else {
|
||||
transactionIds = [];
|
||||
}
|
||||
|
||||
let transactionError;
|
||||
let transactionPayload;
|
||||
const returnValue = job.returnvalue;
|
||||
if (returnValue) {
|
||||
if (returnValue.transactionError) {
|
||||
transactionError = returnValue.transactionError;
|
||||
}
|
||||
|
||||
if (
|
||||
returnValue.transactionPayload &&
|
||||
returnValue.transactionPayload.length > 0
|
||||
) {
|
||||
transactionPayload = returnValue.transactionPayload.toString();
|
||||
} else {
|
||||
transactionPayload = '';
|
||||
}
|
||||
}
|
||||
|
||||
const jobSummary: JobSummary = {
|
||||
jobId: job.id,
|
||||
transactionIds,
|
||||
transactionError,
|
||||
transactionPayload,
|
||||
};
|
||||
|
||||
return jobSummary;
|
||||
};
|
||||
|
||||
/*
|
||||
* Get the current job counts
|
||||
*
|
||||
* This function is used for the liveness REST endpoint
|
||||
*/
|
||||
export const getJobCounts = async (
|
||||
queue: Queue
|
||||
): Promise<{ [index: string]: number }> => {
|
||||
const jobCounts = await queue.getJobCounts(
|
||||
'active',
|
||||
'completed',
|
||||
'delayed',
|
||||
'failed',
|
||||
'waiting'
|
||||
);
|
||||
logger.debug({ jobCounts }, 'Current job counts');
|
||||
|
||||
return jobCounts;
|
||||
};
|
||||
10
asset-transfer-basic/rest-api-typescript/src/logger.ts
Normal file
10
asset-transfer-basic/rest-api-typescript/src/logger.ts
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import pino from 'pino';
|
||||
import * as config from './config';
|
||||
|
||||
export const logger = pino({
|
||||
level: config.logLevel,
|
||||
});
|
||||
34
asset-transfer-basic/rest-api-typescript/src/redis.spec.ts
Normal file
34
asset-transfer-basic/rest-api-typescript/src/redis.spec.ts
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import { isMaxmemoryPolicyNoeviction } from './redis';
|
||||
|
||||
const mockRedisConfig = jest.fn();
|
||||
jest.mock('ioredis', () => {
|
||||
return jest.fn().mockImplementation(() => {
|
||||
return {
|
||||
config: mockRedisConfig,
|
||||
disconnect: jest.fn(),
|
||||
};
|
||||
});
|
||||
});
|
||||
jest.mock('./config');
|
||||
|
||||
describe('Redis', () => {
|
||||
beforeEach(() => {
|
||||
mockRedisConfig.mockClear();
|
||||
});
|
||||
|
||||
describe('isMaxmemoryPolicyNoeviction', () => {
|
||||
it('returns true when the maxmemory-policy is noeviction', async () => {
|
||||
mockRedisConfig.mockReturnValue(['maxmemory-policy', 'noeviction']);
|
||||
expect(await isMaxmemoryPolicyNoeviction()).toBe(true);
|
||||
});
|
||||
|
||||
it('returns false when the maxmemory-policy is not noeviction', async () => {
|
||||
mockRedisConfig.mockReturnValue(['maxmemory-policy', 'allkeys-lru']);
|
||||
expect(await isMaxmemoryPolicyNoeviction()).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
51
asset-transfer-basic/rest-api-typescript/src/redis.ts
Normal file
51
asset-transfer-basic/rest-api-typescript/src/redis.ts
Normal file
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* This sample uses the BullMQ queue system, which is built on top of Redis
|
||||
*/
|
||||
|
||||
import IORedis, { Redis, RedisOptions } from 'ioredis';
|
||||
|
||||
import * as config from './config';
|
||||
import { logger } from './logger';
|
||||
|
||||
/*
|
||||
* Check whether the maxmemory-policy config is set to noeviction
|
||||
*
|
||||
* BullMQ requires this setting in redis
|
||||
* For details, see: https://docs.bullmq.io/guide/connections
|
||||
*/
|
||||
export const isMaxmemoryPolicyNoeviction = async (): Promise<boolean> => {
|
||||
let redis: Redis | undefined;
|
||||
|
||||
const redisOptions: RedisOptions = {
|
||||
port: config.redisPort,
|
||||
host: config.redisHost,
|
||||
username: config.redisUsername,
|
||||
password: config.redisPassword,
|
||||
};
|
||||
|
||||
try {
|
||||
redis = new IORedis(redisOptions);
|
||||
|
||||
const maxmemoryPolicyConfig = await (redis as Redis).config(
|
||||
'GET',
|
||||
'maxmemory-policy'
|
||||
);
|
||||
logger.debug({ maxmemoryPolicyConfig }, 'Got maxmemory-policy config');
|
||||
|
||||
if (
|
||||
maxmemoryPolicyConfig.length == 2 &&
|
||||
'maxmemory-policy' === maxmemoryPolicyConfig[0] &&
|
||||
'noeviction' === maxmemoryPolicyConfig[1]
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
} finally {
|
||||
if (redis != undefined) {
|
||||
redis.disconnect();
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
86
asset-transfer-basic/rest-api-typescript/src/server.ts
Normal file
86
asset-transfer-basic/rest-api-typescript/src/server.ts
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import express, { Application, NextFunction, Request, Response } from 'express';
|
||||
import helmet from 'helmet';
|
||||
import { getReasonPhrase, StatusCodes } from 'http-status-codes';
|
||||
import passport from 'passport';
|
||||
import pinoMiddleware from 'pino-http';
|
||||
import { assetsRouter } from './assets.router';
|
||||
import { authenticateApiKey, fabricAPIKeyStrategy } from './auth';
|
||||
import { healthRouter } from './health.router';
|
||||
import { jobsRouter } from './jobs.router';
|
||||
import { logger } from './logger';
|
||||
import { transactionsRouter } from './transactions.router';
|
||||
|
||||
const { BAD_REQUEST, INTERNAL_SERVER_ERROR, NOT_FOUND } = StatusCodes;
|
||||
|
||||
export const createServer = async (): Promise<Application> => {
|
||||
const app = express();
|
||||
|
||||
app.use(
|
||||
pinoMiddleware({
|
||||
logger,
|
||||
customLogLevel: function customLogLevel(res, err) {
|
||||
if (
|
||||
res.statusCode >= BAD_REQUEST &&
|
||||
res.statusCode < INTERNAL_SERVER_ERROR
|
||||
) {
|
||||
return 'warn';
|
||||
}
|
||||
|
||||
if (res.statusCode >= INTERNAL_SERVER_ERROR || err) {
|
||||
return 'error';
|
||||
}
|
||||
|
||||
return 'debug';
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
|
||||
//define passport startegy
|
||||
passport.use(fabricAPIKeyStrategy);
|
||||
|
||||
//initialize passport js
|
||||
app.use(passport.initialize());
|
||||
|
||||
if (process.env.NODE_ENV === 'development') {
|
||||
// TBC
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'test') {
|
||||
// TBC
|
||||
}
|
||||
|
||||
if (process.env.NODE_ENV === 'production') {
|
||||
app.use(helmet());
|
||||
}
|
||||
|
||||
app.use('/', healthRouter);
|
||||
app.use('/api/assets', authenticateApiKey, assetsRouter);
|
||||
app.use('/api/jobs', authenticateApiKey, jobsRouter);
|
||||
app.use('/api/transactions', authenticateApiKey, transactionsRouter);
|
||||
|
||||
// For everything else
|
||||
app.use((_req, res) =>
|
||||
res.status(NOT_FOUND).json({
|
||||
status: getReasonPhrase(NOT_FOUND),
|
||||
timestamp: new Date().toISOString(),
|
||||
})
|
||||
);
|
||||
|
||||
// Print API errors
|
||||
app.use((err: Error, _req: Request, res: Response, _next: NextFunction) => {
|
||||
logger.error(err);
|
||||
return res.status(INTERNAL_SERVER_ERROR).json({
|
||||
status: getReasonPhrase(INTERNAL_SERVER_ERROR),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
});
|
||||
|
||||
return app;
|
||||
};
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*/
|
||||
|
||||
import express, { Request, Response } from 'express';
|
||||
import { Contract } from 'fabric-network';
|
||||
import { getReasonPhrase, StatusCodes } from 'http-status-codes';
|
||||
import { getTransactionValidationCode } from './fabric';
|
||||
import { logger } from './logger';
|
||||
import { TransactionNotFoundError } from './errors';
|
||||
|
||||
const { INTERNAL_SERVER_ERROR, NOT_FOUND, OK } = StatusCodes;
|
||||
|
||||
export const transactionsRouter = express.Router();
|
||||
|
||||
transactionsRouter.get(
|
||||
'/:transactionId',
|
||||
async (req: Request, res: Response) => {
|
||||
const mspId = req.user as string;
|
||||
const transactionId = req.params.transactionId;
|
||||
logger.debug('Read request received for transaction ID %s', transactionId);
|
||||
|
||||
try {
|
||||
const qsccContract = req.app.locals[mspId]?.qsccContract as Contract;
|
||||
|
||||
const validationCode = await getTransactionValidationCode(
|
||||
qsccContract,
|
||||
transactionId
|
||||
);
|
||||
|
||||
return res.status(OK).json({
|
||||
transactionId,
|
||||
validationCode,
|
||||
});
|
||||
} catch (err) {
|
||||
if (err instanceof TransactionNotFoundError) {
|
||||
return res.status(NOT_FOUND).json({
|
||||
status: getReasonPhrase(NOT_FOUND),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
} else {
|
||||
logger.error(
|
||||
{ err },
|
||||
'Error processing read request for transaction ID %s',
|
||||
transactionId
|
||||
);
|
||||
|
||||
return res.status(INTERNAL_SERVER_ERROR).json({
|
||||
status: getReasonPhrase(INTERNAL_SERVER_ERROR),
|
||||
timestamp: new Date().toISOString(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
75
asset-transfer-basic/rest-api-typescript/tsconfig.json
Normal file
75
asset-transfer-basic/rest-api-typescript/tsconfig.json
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
/* Visit https://aka.ms/tsconfig.json to read more about this file */
|
||||
|
||||
/* Basic Options */
|
||||
// "incremental": true, /* Enable incremental compilation */
|
||||
"target": "es5", /* Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
|
||||
"module": "commonjs", /* Specify module code generation: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
|
||||
// "lib": [], /* Specify library files to be included in the compilation. */
|
||||
// "allowJs": true, /* Allow javascript files to be compiled. */
|
||||
// "checkJs": true, /* Report errors in .js files. */
|
||||
// "jsx": "preserve", /* Specify JSX code generation: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
|
||||
// "declaration": true, /* Generates corresponding '.d.ts' file. */
|
||||
// "declarationMap": true, /* Generates a sourcemap for each corresponding '.d.ts' file. */
|
||||
"sourceMap": true, /* Generates corresponding '.map' file. */
|
||||
// "outFile": "./", /* Concatenate and emit output to single file. */
|
||||
"outDir": "./dist", /* Redirect output structure to the directory. */
|
||||
// "rootDir": "./", /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
|
||||
// "composite": true, /* Enable project compilation */
|
||||
// "tsBuildInfoFile": "./", /* Specify file to store incremental compilation information */
|
||||
// "removeComments": true, /* Do not emit comments to output. */
|
||||
// "noEmit": true, /* Do not emit outputs. */
|
||||
// "importHelpers": true, /* Import emit helpers from 'tslib'. */
|
||||
// "downlevelIteration": true, /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
|
||||
// "isolatedModules": true, /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
|
||||
|
||||
/* Strict Type-Checking Options */
|
||||
"strict": true, /* Enable all strict type-checking options. */
|
||||
// "noImplicitAny": true, /* Raise error on expressions and declarations with an implied 'any' type. */
|
||||
// "strictNullChecks": true, /* Enable strict null checks. */
|
||||
// "strictFunctionTypes": true, /* Enable strict checking of function types. */
|
||||
// "strictBindCallApply": true, /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
|
||||
// "strictPropertyInitialization": true, /* Enable strict checking of property initialization in classes. */
|
||||
// "noImplicitThis": true, /* Raise error on 'this' expressions with an implied 'any' type. */
|
||||
"alwaysStrict": true, /* Parse in strict mode and emit "use strict" for each source file. */
|
||||
|
||||
/* Additional Checks */
|
||||
// "noUnusedLocals": true, /* Report errors on unused locals. */
|
||||
// "noUnusedParameters": true, /* Report errors on unused parameters. */
|
||||
// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
|
||||
// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
|
||||
// "noUncheckedIndexedAccess": true, /* Include 'undefined' in index signature results */
|
||||
// "noImplicitOverride": true, /* Ensure overriding members in derived classes are marked with an 'override' modifier. */
|
||||
// "noPropertyAccessFromIndexSignature": true, /* Require undeclared properties from index signatures to use element accesses. */
|
||||
|
||||
/* Module Resolution Options */
|
||||
// "moduleResolution": "node", /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
|
||||
// "baseUrl": "./", /* Base directory to resolve non-absolute module names. */
|
||||
// "paths": {}, /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
|
||||
// "rootDirs": [], /* List of root folders whose combined content represents the structure of the project at runtime. */
|
||||
// "typeRoots": [], /* List of folders to include type definitions from. */
|
||||
// "types": [], /* Type declaration files to be included in compilation. */
|
||||
// "allowSyntheticDefaultImports": true, /* Allow default imports from modules with no default export. This does not affect code emit, just typechecking. */
|
||||
"esModuleInterop": true, /* Enables emit interoperability between CommonJS and ES Modules via creation of namespace objects for all imports. Implies 'allowSyntheticDefaultImports'. */
|
||||
// "preserveSymlinks": true, /* Do not resolve the real path of symlinks. */
|
||||
// "allowUmdGlobalAccess": true, /* Allow accessing UMD globals from modules. */
|
||||
|
||||
/* Source Map Options */
|
||||
// "sourceRoot": "", /* Specify the location where debugger should locate TypeScript files instead of source locations. */
|
||||
// "mapRoot": "", /* Specify the location where debugger should locate map files instead of generated locations. */
|
||||
// "inlineSourceMap": true, /* Emit a single file with source maps instead of having a separate file. */
|
||||
// "inlineSources": true, /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
|
||||
|
||||
/* Experimental Options */
|
||||
// "experimentalDecorators": true, /* Enables experimental support for ES7 decorators. */
|
||||
// "emitDecoratorMetadata": true, /* Enables experimental support for emitting type metadata for decorators. */
|
||||
|
||||
/* Advanced Options */
|
||||
"skipLibCheck": true, /* Skip type checking of declaration files. */
|
||||
"forceConsistentCasingInFileNames": true /* Disallow inconsistently-cased references to the same file. */
|
||||
},
|
||||
"include": [
|
||||
"src/"
|
||||
]
|
||||
}
|
||||
|
|
@ -9,8 +9,14 @@
|
|||
"engineStrict": true,
|
||||
"author": "Hyperledger",
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
"lint": "eslint *.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"fabric-ca-client": "^2.2.2",
|
||||
"fabric-network": "^2.2.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^7.32.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -191,7 +191,14 @@ async function main() {
|
|||
|
||||
// Rich Query with Pagination (Only supported if CouchDB is used as state database)
|
||||
console.log('\n--> Evaluate Transaction: QueryAssetsWithPagination, function returns "Tom" assets');
|
||||
result = await contract.evaluateTransaction('QueryAssetsWithPagination', '{"selector":{"docType":"asset","owner":"Tom"}, "use_index":["_design/indexOwnerDoc", "indexOwner"]}', '3', '');
|
||||
result = await contract.evaluateTransaction('QueryAssetsWithPagination', '{"selector":{"docType":"asset","owner":"Tom"}, "use_index":["_design/indexOwnerDoc", "indexOwner"]}', '1', '');
|
||||
console.log(`*** Result: ${prettyJSONString(result.toString())}`);
|
||||
|
||||
// Recover the bookmark from previous query. Normally it will be inside a variable.
|
||||
const resultJson = JSON.parse(result.toString());
|
||||
|
||||
console.log('\n--> Evaluate Transaction: QueryAssetsWithPagination, function returns "Tom" assets next page');
|
||||
result = await contract.evaluateTransaction('QueryAssetsWithPagination', '{"selector":{"docType":"asset","owner":"Tom"}, "use_index":["_design/indexOwnerDoc", "indexOwner"]}', '1', resultJson.ResponseMetadata.Bookmark);
|
||||
console.log(`*** Result: ${prettyJSONString(result.toString())}`);
|
||||
|
||||
console.log('\n--> Submit Transaction: TransferAssetByColor, transfer all yellow assets to new owner(Michel)');
|
||||
|
|
|
|||
|
|
@ -9,8 +9,15 @@
|
|||
"engineStrict": true,
|
||||
"author": "Hyperledger",
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
"run": "node app.js",
|
||||
"lint": "eslint *.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"fabric-ca-client": "^2.2.4",
|
||||
"fabric-network": "^2.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^7.32.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,4 @@ require (
|
|||
github.com/golang/protobuf v1.3.2
|
||||
github.com/hyperledger/fabric-chaincode-go v0.0.0-20200511190512-bcfeb58dd83a
|
||||
github.com/hyperledger/fabric-contract-api-go v1.1.0
|
||||
golang.org/x/tools v0.1.0 // indirect
|
||||
)
|
||||
|
|
|
|||
|
|
@ -100,28 +100,19 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:
|
|||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
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=
|
||||
|
|
@ -130,27 +121,14 @@ golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c h1:KfpJVdWhuRqNk4XVXzjXf2KAV4TBEP77SYdFGjeGuIE=
|
||||
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
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-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@
|
|||
// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["CreateAsset","asset2","red","50","Tom","150"]}'
|
||||
// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["CreateAsset","asset3","blue","70","Tom","200"]}'
|
||||
// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["TransferAsset","asset2","jerry"]}'
|
||||
// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["TransferAssetsBasedOnColor","blue","jerry"]}'
|
||||
// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["TransferAssetByColor","blue","jerry"]}'
|
||||
// peer chaincode invoke -C CHANNEL_NAME -n asset_transfer -c '{"Args":["DeleteAsset","asset1"]}'
|
||||
|
||||
// ==== Query assets ====
|
||||
|
|
@ -62,10 +62,10 @@
|
|||
// curl -i -X POST -H "Content-Type: application/json" -d "{\"index\":{\"fields\":[{\"size\":\"desc\"},{\"docType\":\"desc\"},{\"owner\":\"desc\"}]},\"ddoc\":\"indexSizeSortDoc\", \"name\":\"indexSizeSortDesc\",\"type\":\"json\"}" http://hostname:port/myc1_assets/_index
|
||||
|
||||
// Rich Query with index design doc and index name specified (Only supported if CouchDB is used as state database):
|
||||
// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["QueryAssets","{\"selector\":{\"docType\":\"asset\",\"owner\":\"Tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}"]}'
|
||||
// peer chaincode query -C CHANNEL_NAME -n ledger -c '{"Args":["QueryAssets","{\"selector\":{\"docType\":\"asset\",\"owner\":\"Tom\"}, \"use_index\":[\"_design/indexOwnerDoc\", \"indexOwner\"]}"]}'
|
||||
|
||||
// Rich Query with index design doc specified only (Only supported if CouchDB is used as state database):
|
||||
// peer chaincode query -C CHANNEL_NAME -n asset_transfer -c '{"Args":["QueryAssets","{\"selector\":{\"docType\":{\"$eq\":\"asset\"},\"owner\":{\"$eq\":\"Tom\"},\"size\":{\"$gt\":0}},\"fields\":[\"docType\",\"owner\",\"size\"],\"sort\":[{\"size\":\"desc\"}],\"use_index\":\"_design/indexSizeSortDoc\"}"]}'
|
||||
// peer chaincode query -C CHANNEL_NAME -n ledger -c '{"Args":["QueryAssets","{\"selector\":{\"docType\":{\"$eq\":\"asset\"},\"owner\":{\"$eq\":\"Tom\"},\"size\":{\"$gt\":0}},\"fields\":[\"docType\",\"owner\",\"size\"],\"sort\":[{\"size\":\"desc\"}],\"use_index\":\"_design/indexSizeSortDoc\"}"]}'
|
||||
|
||||
'use strict';
|
||||
|
||||
|
|
@ -186,7 +186,7 @@ class Chaincode extends Contract {
|
|||
return JSON.stringify(results);
|
||||
}
|
||||
|
||||
// TransferAssetBasedOnColor will transfer assets of a given color to a certain new owner.
|
||||
// TransferAssetByColor will transfer assets of a given color to a certain new owner.
|
||||
// Uses a GetStateByPartialCompositeKey (range query) against color~name 'index'.
|
||||
// Committing peers will re-execute range queries to guarantee that result sets are stable
|
||||
// between endorsement time and commit time. The transaction is invalidated by the
|
||||
|
|
@ -262,12 +262,15 @@ class Chaincode extends Contract {
|
|||
async GetAssetsByRangeWithPagination(ctx, startKey, endKey, pageSize, bookmark) {
|
||||
|
||||
const {iterator, metadata} = await ctx.stub.getStateByRangeWithPagination(startKey, endKey, pageSize, bookmark);
|
||||
const results = await this._GetAllResults(iterator, false);
|
||||
let results = {};
|
||||
|
||||
results.results = await this._GetAllResults(iterator, false);
|
||||
|
||||
results.ResponseMetadata = {
|
||||
RecordsCount: metadata.fetched_records_count,
|
||||
RecordsCount: metadata.fetchedRecordsCount,
|
||||
Bookmark: metadata.bookmark,
|
||||
};
|
||||
|
||||
return JSON.stringify(results);
|
||||
}
|
||||
|
||||
|
|
@ -282,10 +285,12 @@ class Chaincode extends Contract {
|
|||
async QueryAssetsWithPagination(ctx, queryString, pageSize, bookmark) {
|
||||
|
||||
const {iterator, metadata} = await ctx.stub.getQueryResultWithPagination(queryString, pageSize, bookmark);
|
||||
const results = await this._GetAllResults(iterator, false);
|
||||
let results = {};
|
||||
|
||||
results.results = await this._GetAllResults(iterator, false);
|
||||
|
||||
results.ResponseMetadata = {
|
||||
RecordsCount: metadata.fetched_records_count,
|
||||
RecordsCount: metadata.fetchedRecordsCount,
|
||||
Bookmark: metadata.bookmark,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -8,12 +8,16 @@
|
|||
"npm": ">=5.3.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "fabric-chaincode-node start"
|
||||
"start": "fabric-chaincode-node start",
|
||||
"lint": "eslint *.js */**.js"
|
||||
},
|
||||
"engine-strict": true,
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"fabric-contract-api": "^2.0.0",
|
||||
"fabric-shim": "^2.0.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^7.32.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3,22 +3,9 @@ module github.com/hyperledger/fabric-samples/asset-transfer-private-data/chainco
|
|||
go 1.14
|
||||
|
||||
require (
|
||||
github.com/go-openapi/jsonreference v0.19.4 // indirect
|
||||
github.com/go-openapi/spec v0.19.8 // indirect
|
||||
github.com/go-openapi/swag v0.19.9 // indirect
|
||||
github.com/gobuffalo/envy v1.9.0 // indirect
|
||||
github.com/gobuffalo/packd v1.0.0 // indirect
|
||||
github.com/golang/protobuf v1.4.2 // indirect
|
||||
github.com/hyperledger/fabric-chaincode-go v0.0.0-20200511190512-bcfeb58dd83a
|
||||
github.com/hyperledger/fabric-contract-api-go v1.1.0
|
||||
github.com/hyperledger/fabric-protos-go v0.0.0-20200707132912-fee30f3ccd23
|
||||
github.com/mailru/easyjson v0.7.1 // indirect
|
||||
github.com/rogpeppe/go-internal v1.6.0 // indirect
|
||||
github.com/stretchr/testify v1.5.1
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect
|
||||
golang.org/x/tools v0.1.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20200721032028-5044d0edf986 // indirect
|
||||
google.golang.org/grpc v1.30.0 // indirect
|
||||
google.golang.org/protobuf v1.25.0
|
||||
gopkg.in/yaml.v2 v2.3.0 // indirect
|
||||
)
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdko
|
|||
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||
github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc=
|
||||
github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE=
|
||||
github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk=
|
||||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
|
|
@ -17,9 +16,7 @@ github.com/cucumber/godog v0.8.0/go.mod h1:Cp3tEV1LRAyH/RuCThcxHS/+9ORZ+FMzPva2A
|
|||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4=
|
||||
github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98=
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||
|
|
@ -27,27 +24,17 @@ github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm7232
|
|||
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
|
||||
github.com/go-openapi/jsonreference v0.19.2 h1:o20suLFB4Ri0tuzpWtyHlh7E7HnkqTNLq6aR6WVNS1w=
|
||||
github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc=
|
||||
github.com/go-openapi/jsonreference v0.19.4 h1:3Vw+rh13uq2JFNxgnMTGE1rnoieU9FmyE1gvnyylsYg=
|
||||
github.com/go-openapi/jsonreference v0.19.4/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE69AqPYEJeo/TWfEeg=
|
||||
github.com/go-openapi/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
|
||||
github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||
github.com/go-openapi/spec v0.19.8 h1:qAdZLh1r6QF/hI/gTq+TJTvsQUodZsM7KLqkAJdiJNg=
|
||||
github.com/go-openapi/spec v0.19.8/go.mod h1:Hm2Jr4jv8G1ciIAo+frC/Ft+rR2kQDh8JHKHb3gWUSk=
|
||||
github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.5 h1:lTz6Ys4CmqqCQmZPBlbQENR1/GucA2bzYTE12Pw4tFY=
|
||||
github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=
|
||||
github.com/go-openapi/swag v0.19.9 h1:1IxuqvBUU3S2Bi4YC7tlP9SJF1gVpCvqN0T2Qof4azE=
|
||||
github.com/go-openapi/swag v0.19.9/go.mod h1:ao+8BpOPyKdpQz3AOJfbeEVpLmWAvlT1IfTe5McPyhY=
|
||||
github.com/go-sql-driver/mysql v1.5.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
|
||||
github.com/gobuffalo/envy v1.7.0 h1:GlXgaiBkmrYMHco6t4j7SacKO4XUjvh5pwXh0f4uxXU=
|
||||
github.com/gobuffalo/envy v1.7.0/go.mod h1:n7DRkBerg/aorDM8kbduw5dN3oXGswK5liaSCx4T5NI=
|
||||
github.com/gobuffalo/envy v1.9.0 h1:eZR0DuEgVLfeIb1zIKt3bT4YovIMf9O9LXQeCZLXpqE=
|
||||
github.com/gobuffalo/envy v1.9.0/go.mod h1:FurDp9+EDPE4aIUS3ZLyD+7/9fpx7YRt/ukY6jIHf0w=
|
||||
github.com/gobuffalo/logger v1.0.0/go.mod h1:2zbswyIUa45I+c+FLXuWl9zSWEiVuthsk8ze5s8JvPs=
|
||||
github.com/gobuffalo/packd v0.3.0 h1:eMwymTkA1uXsqxS0Tpoop3Lc0u3kTfiMBE6nKtQU4g4=
|
||||
github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b36chA3Q=
|
||||
github.com/gobuffalo/packd v1.0.0 h1:6ERZvJHfe24rfFmA9OaoKBdC7+c9sydrytMg8SdFGBM=
|
||||
github.com/gobuffalo/packd v1.0.0/go.mod h1:6VTc4htmJRFB7u1m/4LeMTWjFoYrUiBkU9Fdec9hrhI=
|
||||
github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
|
||||
github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
|
||||
github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
|
||||
|
|
@ -55,17 +42,14 @@ github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekf
|
|||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.2 h1:6nsPYzhq5kReh6QImI3k5qWzO4PEbvbIW2cwSfR/6xs=
|
||||
github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
|
||||
github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8=
|
||||
github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA=
|
||||
github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs=
|
||||
github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w=
|
||||
github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0=
|
||||
github.com/golang/protobuf v1.4.1 h1:ZFgWrT+bLgsYPirOnRfKLYJLvssAegOj/hgyMFdJZe0=
|
||||
github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8=
|
||||
github.com/golang/protobuf v1.4.2 h1:+Z5KGCizgyZCbGh1KZqA0fcLLkwbsjIzS4aV2v7wJX0=
|
||||
github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI=
|
||||
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
|
|
@ -73,14 +57,12 @@ github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/
|
|||
github.com/google/go-cmp v0.5.0 h1:/QaMHBdZ26BB3SSst0Iwl10Epc+xhTquomWX0oZEB6w=
|
||||
github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
|
||||
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
|
||||
github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212 h1:1i4lnpV8BDgKOLi1hgElfBqdHXjXieSuj8629mwBZ8o=
|
||||
github.com/hyperledger/fabric-chaincode-go v0.0.0-20200424173110-d7076418f212/go.mod h1:N7H3sA7Tx4k/YzFq7U0EPdqJtqvM4Kild0JoCc7C0Dc=
|
||||
github.com/hyperledger/fabric-chaincode-go v0.0.0-20200511190512-bcfeb58dd83a h1:KoFw2HnRfW+EItMP0zvUUl1FGzDb/7O0ov7uXZffQok=
|
||||
github.com/hyperledger/fabric-chaincode-go v0.0.0-20200511190512-bcfeb58dd83a/go.mod h1:N7H3sA7Tx4k/YzFq7U0EPdqJtqvM4Kild0JoCc7C0Dc=
|
||||
github.com/hyperledger/fabric-contract-api-go v1.1.0 h1:K9uucl/6eX3NF0/b+CGIiO1IPm1VYQxBkpnVGJur2S4=
|
||||
github.com/hyperledger/fabric-contract-api-go v1.1.0/go.mod h1:nHWt0B45fK53owcFpLtAe8DH0Q5P068mnzkNXMPSL7E=
|
||||
github.com/hyperledger/fabric-protos-go v0.0.0-20190919234611-2a87503ac7c9/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
|
||||
github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e h1:9PS5iezHk/j7XriSlNuSQILyCOfcZ9wZ3/PiucmSE8E=
|
||||
github.com/hyperledger/fabric-protos-go v0.0.0-20200424173316-dd554ba3746e/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
|
||||
github.com/hyperledger/fabric-protos-go v0.0.0-20200707132912-fee30f3ccd23 h1:SEbB3yH4ISTGRifDamYXAst36gO2kM855ndMJlsv+pc=
|
||||
github.com/hyperledger/fabric-protos-go v0.0.0-20200707132912-fee30f3ccd23/go.mod h1:xVYTjK4DtZRBxZ2D9aE4y6AbLaPwue2o/criQyQbVD0=
|
||||
|
|
@ -101,8 +83,6 @@ github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czP
|
|||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e h1:hB2xlXdHp/pmPZq0y3QnmWAArdw9PqbmotexnWx/FU8=
|
||||
github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
github.com/mailru/easyjson v0.7.1 h1:mdxE1MF9o53iCb2Ghj1VfWvh7ZOwHpnVG/xwXrV90U8=
|
||||
github.com/mailru/easyjson v0.7.1/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs=
|
||||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
|
|
@ -112,9 +92,6 @@ github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:
|
|||
github.com/rogpeppe/go-internal v1.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
|
||||
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.3.2/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/rogpeppe/go-internal v1.6.0 h1:IZRgg4sfrDH7nsAD1Y/Nwj+GzIfEwpJSLjCaNC3SbsI=
|
||||
github.com/rogpeppe/go-internal v1.6.0/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc=
|
||||
github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
|
||||
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
|
||||
github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
|
||||
|
|
@ -134,26 +111,19 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
|
|||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=
|
||||
github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
|
|
@ -161,18 +131,12 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
|||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381 h1:VXak5I6aEWmAXeQjA+QSZzlgNrpq9mjcfDemuexIKsU=
|
||||
golang.org/x/net v0.0.0-20200707034311-ab3426394381/go.mod h1:/O7V0waA8r7cgGh81Ro3o1hOxt32SMVPicZroKQ2sZA=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
|
@ -182,60 +146,36 @@ golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666 h1:gVCS+QOncANNPlmlO1AhlU3oxs4V9z+gTtPwIk3p2N8=
|
||||
golang.org/x/sys v0.0.0-20200720211630-cb9d2d5c5666/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c h1:KfpJVdWhuRqNk4XVXzjXf2KAV4TBEP77SYdFGjeGuIE=
|
||||
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
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=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013 h1:+kGHl1aib/qcwaRi1CbqBZ1rk19r85MNUf8HaBghugY=
|
||||
google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo=
|
||||
google.golang.org/genproto v0.0.0-20200721032028-5044d0edf986 h1:10ohwcLf82I55O/aQxYqmWKoOdNbQTYYComeP1KDOS4=
|
||||
google.golang.org/genproto v0.0.0-20200721032028-5044d0edf986/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY=
|
||||
google.golang.org/grpc v1.27.0 h1:rRYRFMVgRv6E0D70Skyfsr28tDXIuuPZyWGMPdMcnXg=
|
||||
google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk=
|
||||
google.golang.org/grpc v1.30.0 h1:M5a8xTlYTxwMn5ZFkwhRabsygDY5G8TYLyQDBxJNAxE=
|
||||
google.golang.org/grpc v1.30.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak=
|
||||
google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8=
|
||||
google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0=
|
||||
google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM=
|
||||
google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE=
|
||||
google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo=
|
||||
google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU=
|
||||
google.golang.org/protobuf v1.24.0 h1:UhZDfRO8JRQru4/+LlLE0BRKGF8L+PICnvYZmx/fEGA=
|
||||
google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4=
|
||||
google.golang.org/protobuf v1.25.0 h1:Ejskq+SyPohKW+1uil0JJMtmHCgJPJ/qWTxr8qp+R4c=
|
||||
google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
|
@ -244,10 +184,7 @@ gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogR
|
|||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU=
|
||||
gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
|
|
|||
|
|
@ -9,8 +9,14 @@
|
|||
"engineStrict": true,
|
||||
"author": "Hyperledger",
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
"lint": "eslint *.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"fabric-ca-client": "^2.2.4",
|
||||
"fabric-network": "^2.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^7.32.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,8 +9,14 @@
|
|||
"engineStrict": true,
|
||||
"author": "Hyperledger",
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
"lint": "eslint *.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"fabric-ca-client": "^2.2.4",
|
||||
"fabric-network": "^2.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^7.32.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,5 +6,4 @@ require (
|
|||
github.com/golang/protobuf v1.3.2
|
||||
github.com/hyperledger/fabric-chaincode-go v0.0.0-20200128192331-2d899240a7ed
|
||||
github.com/hyperledger/fabric-contract-api-go v1.0.0
|
||||
golang.org/x/tools v0.1.0 // indirect
|
||||
)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8Nz
|
|||
github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk=
|
||||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||
|
|
@ -33,6 +34,7 @@ github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wK
|
|||
github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
|
||||
github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
|
||||
github.com/gogo/protobuf v1.2.1/go.mod h1:hp+jE20tsWTFYpLwKvXlhS1hjn+gTNwPg2I6zVXpSg4=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
|
|
@ -55,9 +57,11 @@ github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvW
|
|||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
|
|
@ -66,6 +70,7 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN
|
|||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
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.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
|
||||
|
|
@ -83,6 +88,7 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
|
||||
|
|
@ -92,28 +98,19 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:
|
|||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
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=
|
||||
|
|
@ -122,28 +119,15 @@ golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c h1:KfpJVdWhuRqNk4XVXzjXf2KAV4TBEP77SYdFGjeGuIE=
|
||||
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
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-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
|
||||
|
|
@ -151,6 +135,7 @@ google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoA
|
|||
google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
|
|
|
|||
|
|
@ -9,6 +9,9 @@
|
|||
"engineStrict": true,
|
||||
"author": "Hyperledger",
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
"lint": "eslint *.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"fabric-ca-client": "^2.2.4",
|
||||
"fabric-network": "^2.2.4"
|
||||
|
|
|
|||
|
|
@ -7,7 +7,4 @@ require (
|
|||
github.com/hyperledger/fabric-chaincode-go v0.0.0-20201119163726-f8ef75b17719
|
||||
github.com/hyperledger/fabric-contract-api-go v1.1.1
|
||||
github.com/hyperledger/fabric-protos-go v0.0.0-20210127161553-4f432a78f286
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974 // indirect
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 // indirect
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
|
||||
)
|
||||
|
|
|
|||
|
|
@ -115,15 +115,13 @@ golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnf
|
|||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
|
|
@ -132,22 +130,18 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
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=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
|
||||
|
|
|
|||
|
|
@ -9,8 +9,14 @@
|
|||
"engineStrict": true,
|
||||
"author": "Hyperledger",
|
||||
"license": "Apache-2.0",
|
||||
"scripts": {
|
||||
"lint": "eslint *.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"fabric-ca-client": "^2.2.4",
|
||||
"fabric-network": "^2.2.4"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "^7.32.0"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,5 +5,4 @@ go 1.15
|
|||
require (
|
||||
github.com/hyperledger/fabric-chaincode-go v0.0.0-20200728190242-9b3ae92d8664
|
||||
github.com/hyperledger/fabric-contract-api-go v1.1.0
|
||||
golang.org/x/tools v0.1.0 // indirect
|
||||
)
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
|
|||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cucumber/godog v0.8.0/go.mod h1:Cp3tEV1LRAyH/RuCThcxHS/+9ORZ+FMzPva2AZ5Ki+A=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||
|
|
@ -34,6 +35,7 @@ github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b
|
|||
github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
|
||||
github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
|
||||
github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
|
|
@ -56,9 +58,11 @@ github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0L
|
|||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
|
|
@ -67,6 +71,7 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN
|
|||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
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.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
|
||||
|
|
@ -85,6 +90,7 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH
|
|||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
|
||||
|
|
@ -94,28 +100,19 @@ github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:
|
|||
github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=
|
||||
github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20190621222207-cc06ce4a13d4/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/mod v0.3.0 h1:RM4zey1++hCTbCVQfnWeKs9/IEsaBLA8vTkd0WVtmH4=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297 h1:k7pJ2yAPLPgbskkFdhRCsA77k2fySZ1zf2zCjvQCiIM=
|
||||
golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974 h1:IX6qOQeG5uLjB/hjjwjedwfjND0hgjPMMyO1RoIXQNI=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
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=
|
||||
|
|
@ -124,27 +121,14 @@ golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4 h1:myAQVi0cGEoqQVR5POX+8RR2mrocKqNN1hmeMqhX27k=
|
||||
golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
golang.org/x/tools v0.0.0-20190614205625-5aca471b1d59/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c h1:KfpJVdWhuRqNk4XVXzjXf2KAV4TBEP77SYdFGjeGuIE=
|
||||
golang.org/x/tools v0.0.0-20190624180213-70d37148ca0c/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.1.0 h1:po9/4sTYwZU9lPhi1tOrb4hCv3qrhiQ77LZfGa2OjwY=
|
||||
golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
|
||||
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-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
|
||||
|
|
@ -153,6 +137,7 @@ google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
|
|||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
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-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
|
|||
|
|
@ -56,7 +56,6 @@ github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqx
|
|||
github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0LhBygSwrAsHA=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
|
|
@ -89,7 +88,6 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||
github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
|
||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
|
|
@ -137,12 +135,10 @@ google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoA
|
|||
google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
|
|||
1
chaincode/fabcar/external/go.sum
vendored
1
chaincode/fabcar/external/go.sum
vendored
|
|
@ -139,7 +139,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
|
|||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
|
|||
|
|
@ -139,7 +139,6 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8
|
|||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
|
|||
|
|
@ -5,8 +5,4 @@ go 1.12
|
|||
require (
|
||||
github.com/hyperledger/fabric-chaincode-go v0.0.0-20190823162523-04390e015b85
|
||||
github.com/hyperledger/fabric-protos-go v0.0.0-20190821214336-621b908d5022
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 // indirect
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a // indirect
|
||||
golang.org/x/text v0.3.2 // indirect
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 // indirect
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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=
|
||||
|
|
@ -28,58 +26,33 @@ github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
|||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
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/objx v0.1.0 h1:4G4v2dO3VZwixGIRoQ5Lfboy6nUhCyYzaqnIAPPhYs4=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
|
|
|||
|
|
@ -440,12 +440,17 @@ let Chaincode = class {
|
|||
|
||||
const { iterator, metadata } = await stub.getStateByRangeWithPagination(startKey, endKey, pageSize, bookmark);
|
||||
const getAllResults = thisClass['getAllResults'];
|
||||
const results = await getAllResults(iterator, false);
|
||||
|
||||
let results = {};
|
||||
|
||||
results.results = await getAllResults(iterator, false);
|
||||
|
||||
// use RecordsCount and Bookmark to keep consistency with the go sample
|
||||
results.ResponseMetadata = {
|
||||
RecordsCount: metadata.fetched_records_count,
|
||||
RecordsCount: metadata.fetchedRecordsCount,
|
||||
Bookmark: metadata.bookmark,
|
||||
};
|
||||
|
||||
return Buffer.from(JSON.stringify(results));
|
||||
}
|
||||
|
||||
|
|
@ -467,10 +472,13 @@ let Chaincode = class {
|
|||
|
||||
const { iterator, metadata } = await stub.getQueryResultWithPagination(queryString, pageSize, bookmark);
|
||||
const getAllResults = thisClass['getAllResults'];
|
||||
const results = await getAllResults(iterator, false);
|
||||
let results = {};
|
||||
|
||||
results.results = await getAllResults(iterator, false);
|
||||
|
||||
// use RecordsCount and Bookmark to keep consistency with the go sample
|
||||
results.ResponseMetadata = {
|
||||
RecordsCount: metadata.fetched_records_count,
|
||||
RecordsCount: metadata.fetchedRecordsCount,
|
||||
Bookmark: metadata.bookmark,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee
|
|||
github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
|
||||
github.com/cucumber/godog v0.8.0/go.mod h1:Cp3tEV1LRAyH/RuCThcxHS/+9ORZ+FMzPva2AZ5Ki+A=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
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/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg=
|
||||
|
|
@ -34,6 +35,7 @@ github.com/gobuffalo/packd v0.3.0/go.mod h1:zC7QkmNkYVGKPw4tHpBQ+ml7W/3tIebgeo1b
|
|||
github.com/gobuffalo/packr v1.30.1 h1:hu1fuVR3fXEZR7rXNW3h8rqSML8EVAf6KNm0NKO/wKg=
|
||||
github.com/gobuffalo/packr v1.30.1/go.mod h1:ljMyFO2EcrnzsHsN99cvbq055Y9OhRrIaviy289eRuk=
|
||||
github.com/gobuffalo/packr/v2 v2.5.1/go.mod h1:8f9c96ITobJlPzI44jj+4tHnEKNt0xXWSVlXRN9X1Iw=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b h1:VKtxabqXZkF25pY9ekfRL6a582T4P37/31XEstQ5p58=
|
||||
github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
|
|
@ -55,9 +57,11 @@ github.com/karrick/godirwalk v1.10.12/go.mod h1:RoGL9dQei4vP9ilrpETWE8CLOZ1kiN0L
|
|||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
|
||||
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||
github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs=
|
||||
github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI=
|
||||
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
|
||||
github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=
|
||||
|
|
@ -66,6 +70,7 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN
|
|||
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
|
||||
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
|
||||
github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
|
||||
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.1.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||
github.com/rogpeppe/go-internal v1.3.0 h1:RR9dF3JtopPvtkroDZuVD7qquD0bnHlKSqaQhgwt8yk=
|
||||
|
|
@ -84,6 +89,7 @@ github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoH
|
|||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
|
||||
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=
|
||||
|
|
@ -112,6 +118,7 @@ golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190515120540-06a5c4944438/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
|
|
@ -129,6 +136,7 @@ google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
|
|||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
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-20190902080502-41f04d3bba15 h1:YR8cESwS4TdDjEe65xsg0ogRM/Nc3DYOhEAlW+xobZo=
|
||||
gopkg.in/check.v1 v1.0.0-20190902080502-41f04d3bba15/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
|
|
|
|||
|
|
@ -5,8 +5,4 @@ go 1.12
|
|||
require (
|
||||
github.com/hyperledger/fabric-chaincode-go v0.0.0-20190823162523-04390e015b85
|
||||
github.com/hyperledger/fabric-protos-go v0.0.0-20190821214336-621b908d5022
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 // indirect
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a // indirect
|
||||
golang.org/x/text v0.3.2 // indirect
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 // indirect
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,5 +1,4 @@
|
|||
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
|
||||
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
|
|
@ -31,46 +30,24 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
|
|||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
|
||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092 h1:4QSRKanuywn15aTZvI/mIDEgPQpswuFndXpOj3rKEco=
|
||||
golang.org/x/net v0.0.0-20190522155817-f3200d17e092/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7 h1:fHDIZ2oxGnUZRN6WgWFCbYBjH9uqVPRCUVUDhs0wnbA=
|
||||
golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542 h1:6ZQFf1D2YYDDI7eSwW8adlkkavTB9sw5I24FVtEvNUQ=
|
||||
golang.org/x/sys v0.0.0-20190710143415-6ec70d6a5542/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a h1:aYOabOQFp6Vj6W1F80affTUvO9UxmJRx8K0gsfABByQ=
|
||||
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.2 h1:tW2bmiBqwgJj/UpqtC8EpXEZVYOwU0yG4iWbprSVAcs=
|
||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
golang.org/x/tools v0.0.0-20180221164845-07fd8470d635/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY=
|
||||
golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
|
||||
golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q=
|
||||
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b h1:lohp5blsw53GBXtLyLNaTXPXS9pJ1tiTw61ZHUoE9Qw=
|
||||
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55 h1:gSJIx1SDwno+2ElGhA4+qG2zF97qiUzTM+rQ0klBOcE=
|
||||
google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.23.0 h1:AzbTB6ux+okLTzP8Ru1Xs41C303zdcfEht7MQnYJt5A=
|
||||
google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
|
@ -78,5 +55,4 @@ gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33
|
|||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
|
||||
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
|
|
|
|||
|
|
@ -5,12 +5,13 @@
|
|||
trigger:
|
||||
- main
|
||||
- release-1.4
|
||||
- release-2.2
|
||||
|
||||
variables:
|
||||
FABRIC_VERSION: 2.4
|
||||
GO_BIN: $(Build.Repository.LocalPath)/bin
|
||||
GO_VER: 1.14.6
|
||||
NODE_VER: 12.x
|
||||
GO_VER: 1.16.7
|
||||
NODE_VER: 16.x
|
||||
PATH: $(Build.Repository.LocalPath)/bin:/bin:/usr/bin:/sbin:/usr/sbin:/usr/local/bin:/usr/local/sbin
|
||||
|
||||
jobs:
|
||||
|
|
@ -84,8 +85,6 @@ jobs:
|
|||
inputs:
|
||||
versionSpec: $(NODE_VER)
|
||||
displayName: Install Node.js
|
||||
- script: npm install -g typescript eslint tslint
|
||||
displayName: Install Javascript Linting Deps
|
||||
- script: ./ci/scripts/lint.sh
|
||||
displayName: Lint Code
|
||||
|
||||
|
|
|
|||
|
|
@ -1,3 +1,4 @@
|
|||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
function print() {
|
||||
|
|
@ -7,13 +8,14 @@ function print() {
|
|||
echo -e "${GREEN}${1}${NC}"
|
||||
}
|
||||
|
||||
go install golang.org/x/tools/cmd/goimports@latest
|
||||
|
||||
dirs=("$(find . -name "*-go" -o -name "*-java" -o -name "*-javascript" -o -name "*-typescript")")
|
||||
for dir in $dirs; do
|
||||
if [[ -d $dir ]]; then
|
||||
if [[ -d $dir ]] && [[ ! $dir =~ node_modules ]]; then
|
||||
print "Linting $dir"
|
||||
pushd $dir
|
||||
if [[ "$dir" =~ "-go" ]]; then
|
||||
go get golang.org/x/tools/cmd/goimports
|
||||
print "Running go vet"
|
||||
go vet ./...
|
||||
print "Running gofmt"
|
||||
|
|
@ -29,13 +31,11 @@ for dir in $dirs; do
|
|||
print "The following files contain import errors, please run 'goimports -l -w <path>' to fix these issues:"
|
||||
echo "${output}"
|
||||
fi
|
||||
elif [[ "$dir" =~ "-javascript" ]]; then
|
||||
print "Running ESLint"
|
||||
if [[ "$dir" =~ "chaincode" ]]; then
|
||||
eslint *.js */**.js
|
||||
else
|
||||
eslint *.js
|
||||
fi
|
||||
elif [[ "$dir" =~ "-javascript" || "$dir" =~ "-typescript" ]]; then
|
||||
print "Installing node modules"
|
||||
npm install
|
||||
print "Running Lint"
|
||||
npm run lint
|
||||
elif [[ "$dir" =~ "-java" ]]; then
|
||||
if [[ -f "pom.xml" ]]; then
|
||||
print "Running Maven Build"
|
||||
|
|
@ -44,9 +44,6 @@ for dir in $dirs; do
|
|||
print "Running Gradle Build"
|
||||
./gradlew build
|
||||
fi
|
||||
elif [[ "$dir" =~ "-typescript" ]]; then
|
||||
print "Running TSLint"
|
||||
tslint --project .
|
||||
fi
|
||||
popd
|
||||
fi
|
||||
|
|
|
|||
|
|
@ -63,6 +63,18 @@ node dist/app.js
|
|||
popd
|
||||
stopNetwork
|
||||
|
||||
# Run gateway typescript application
|
||||
createNetwork
|
||||
print "Initializing Typescript gateway application"
|
||||
pushd ../asset-transfer-basic/application-gateway-typescript
|
||||
npm install
|
||||
print "Building app.ts"
|
||||
npm run build
|
||||
print "Running the output app"
|
||||
node dist/app.js
|
||||
popd
|
||||
stopNetwork
|
||||
|
||||
# Run typescript HSM application
|
||||
createNetwork
|
||||
print "Initializing Typescript HSM application"
|
||||
|
|
|
|||
|
|
@ -7,9 +7,9 @@ steps:
|
|||
workingDirectory: fabcar
|
||||
displayName: Start Fabric
|
||||
- task: GoTool@0
|
||||
displayName: 'Use Go 1.14.2'
|
||||
displayName: 'Use Go 1.16.7'
|
||||
inputs:
|
||||
version: '1.14.2'
|
||||
version: '1.16.7'
|
||||
- task: Go@0
|
||||
displayName: 'go build'
|
||||
inputs:
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue