mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-26 11:35:10 +00:00
Fabric auction sample preview
Signed-off-by: NIKHIL E GUPTA <negupta@us.ibm.com>
This commit is contained in:
parent
6d196bd9df
commit
93767a001d
19 changed files with 3282 additions and 0 deletions
432
auction/README.md
Normal file
432
auction/README.md
Normal file
|
|
@ -0,0 +1,432 @@
|
||||||
|
## Auction sample
|
||||||
|
|
||||||
|
The auction sample demonstrates how Hyperledger Fabric can be used to run an auction where bids kept private from other bidders. Instead of displaying the full bid on the public ledger, only a hash of the bid is revealed while bidding is is open. This prevents potential buyers from reading that were already submitted and changing their bids in response. After the bidding period has ended, participants can reveal their bid. The organizations participating in the auction verify that the bid being revealed matches the hash before the full bid is added to the auction. The auction sample smart contract implements a [Dutch auction](https://en.wikipedia.org/wiki/Dutch_auction) for multiple items of the same type. All items are sold at the price of the lowest winning bid.
|
||||||
|
|
||||||
|
A potential seller can use the smart contract to create an auction that sells one or more items. The auction is stoed on the public channel ledger and can be read by all channel members. The auctions created by the smart contract are run in three steps:
|
||||||
|
1. Each auction is created with the status **open**. While the auction is open, bidders can join the auction by adding a hashed bid. Potential buyers store their bids in the implicit private data collection of their organization. After the bid is created, the bidder can add the hash of the bid to the auction. The bid is added to the auction in two steps because the transaction that creates the bid only needs be endorsed by the the peer of the bidders organization, while a transaction that updates the auction may need to be endorsed by multiple organizations. When the bid is added to the auction, the bidders organization is added to he list of organizations that need to endorse any auction updates.
|
||||||
|
2. When the bidding period is over, the auction is **closed** to prevent additional bids from being added to the auction. The auction being closed allows bidders to reveal their full bids, as long as they reveal the same bid that they added to the auction in the previous step. Only bids that have been revealed are eligible to win the auction.
|
||||||
|
3. The auction is **ended** to calculate the winners from the set of revealed bids. All organizations participating in the auction calculate the winning bids and the price that clears the auction. The seller can end the auction only if all organizations endorse the same winners and price.
|
||||||
|
|
||||||
|
Before each organization endorses the transaction that ends the auction, the organization queries their private data collection to check no bidder from their organization has created a bid that is higher than the winning price that has not yet been revealed. If there is a winning bid that has not yet been added to the auction, the organization will withhold their endorsement and prevent the auction from being closed. This prevents the seller from ending the auction prematurely, or colluding with buyers to end the auction at an artificially low price?
|
||||||
|
|
||||||
|
The sample uses several Fabric features that facilitate privacy and security. Bids are stored in the implicit private data collections to prevent bids from being distributed to other peers in the channel. When bidding is closed, the auction smart contract uses the `GetPrivateDataHash()` API to verify that hash of the bids being stored in private data collections match the hash that was added to the auction and the hash of the revealed bid. State based endorsement is used to add the organization of each bidder to the auction endorsement policy. The smart contract uses the `GetClientIdentity.GetID()` API to ensure that only the potential buyer can read their bid from the private state and only seller can close or end the auction.
|
||||||
|
|
||||||
|
This tutorial uses the smart contract to create an auction to sell 100 tickets to an event. Four bidders that belong to two organizations will bid on the tickets.
|
||||||
|
|
||||||
|
## Deploy the chaincode
|
||||||
|
|
||||||
|
You can run the auction smart contract using the Fabric test network. Open a command terminal and navigate to the test network directory:
|
||||||
|
```
|
||||||
|
cd fabric-samples/test-network
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then run the following command to deploy the test network.
|
||||||
|
```
|
||||||
|
./network.sh up createChannel -ca
|
||||||
|
```
|
||||||
|
|
||||||
|
Note that we use the `-ca` flag to deploy the network using certificate authorities. We will use the CA to register and enroll our sellers and buyers.
|
||||||
|
|
||||||
|
Run the following command to deploy the auction smart contract. We will override the default endorsement policy to allow any channel member to create an auction without requiring an endorsement from another organization.
|
||||||
|
```
|
||||||
|
./network.sh deployCC -ccn auction -ccp ../auction/chaincode-go/ -ccep "OR('Org1MSP.peer','Org2MSP.peer')"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Install the application dependencies
|
||||||
|
|
||||||
|
We will interact with the auction smart contract through a set of Node.js applications. Change into the `application-javascript` directory:
|
||||||
|
```
|
||||||
|
cd fabric-samples/auction/application-javascript
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the following command in the `application-javascript` directory to download the application dependencies:
|
||||||
|
```
|
||||||
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
|
## Register and enroll the application identities
|
||||||
|
|
||||||
|
To interact with the network, you will need to enroll the Certificate Authority administrators of Org1 and Org2. You can use the `enrollAdmin.js` program for this task. Run the following command to enroll the Org1 admin:
|
||||||
|
```
|
||||||
|
node enrollAdmin.js org1
|
||||||
|
```
|
||||||
|
You should see the logs of the admin wallet being created on your local file system. Now run the command to enroll the CA admin of Org2:
|
||||||
|
```
|
||||||
|
node enrollAdmin.js org2
|
||||||
|
```
|
||||||
|
|
||||||
|
We can use the CA admins of both organizations to register and enroll the identities the seller that will create the auction and the bidders who will try to purchase the tickets.
|
||||||
|
|
||||||
|
Run the following command to register and enroll the seller identity that will create the auction. The seller will belong to Org1.
|
||||||
|
```
|
||||||
|
node registerEnrollUser.js org1 seller
|
||||||
|
```
|
||||||
|
|
||||||
|
You should see the logs of the seller wallet being created as well. Run the following commands to register 2 bidders from Org1 and another 2 bidders from Org2:
|
||||||
|
```
|
||||||
|
node registerEnrollUser.js org1 bidder1
|
||||||
|
node registerEnrollUser.js org1 bidder2
|
||||||
|
node registerEnrollUser.js org2 bidder3
|
||||||
|
node registerEnrollUser.js org2 bidder4
|
||||||
|
```
|
||||||
|
|
||||||
|
## Create the auction
|
||||||
|
|
||||||
|
The seller from Org1 would like to create an auction to sell 100 tickets to an event. Run the following command to use the seller wallet to run the `createAuction.js` program. The application will submit a transaction to the network creating the auction on the channel ledger. The organization and identity name are passed to the application to use the wallet that was created by the`registerEnrollUser.js` application. The seller needs to provide the auctionID, the item to be sold, and the quantity to be sold to create the auction:
|
||||||
|
```
|
||||||
|
node createAuction.js org1 seller auction1 tickets 100
|
||||||
|
```
|
||||||
|
|
||||||
|
After the transaction is complete, the `createAuction.js` application will query the auction stored in the public channel ledger:
|
||||||
|
```
|
||||||
|
*** Result: Auction: {
|
||||||
|
"objectType": "auction",
|
||||||
|
"ID": "tickets",
|
||||||
|
"seller": "eDUwOTo6Q049c2VsbGVyLE9VPWNsaWVudCtPVT1vcmcxK09VPWRlcGFydG1lbnQxOjpDTj1jYS5vcmcxLmV4YW1wbGUuY29tLE89b3JnMS5leGFtcGxlLmNvbSxMPUR1cmhhbSxTVD1Ob3J0aCBDYXJvbGluYSxDPVVT",
|
||||||
|
"quantity": 100,
|
||||||
|
"bidingOrgs": [
|
||||||
|
"Org1MSP"
|
||||||
|
],
|
||||||
|
"privateBids": {},
|
||||||
|
"revealedBids": {},
|
||||||
|
"winners": [],
|
||||||
|
"price": 0,
|
||||||
|
"status": "open"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
The smart contract uses the `GetClientIdentity().GetID()` API to read identity that creates the auction and defines that identity as the auction `"seller"`. You can see the seller information by decoding the `"seller"` string out of base64 format:
|
||||||
|
|
||||||
|
```
|
||||||
|
echo eDUwOTo6Q049c2VsbGVyLE9VPWNsaWVudCtPVT1vcmcxK09VPWRlcGFydG1lbnQxOjpDTj1jYS5vcmcxLmV4YW1wbGUuY29tLE89b3JnMS5leGFtcGxlLmNvbSxMPUR1cmhhbSxTVD1Ob3J0aCBDYXJvbGluYSxDPVVT | base64 --decode
|
||||||
|
```
|
||||||
|
|
||||||
|
The result is the common name and issuer of the seller's certificate:
|
||||||
|
```
|
||||||
|
x509::CN=org1admin,OU=admin,O=Hyperledger,ST=North Carolina,C=US::CN=ca.org1.example.com,O=org1.example.com,L=Durham,ST=North Carolina,C=USn
|
||||||
|
```
|
||||||
|
|
||||||
|
## Bid on the auction
|
||||||
|
|
||||||
|
We can now use the bidder identities to bid for the auction.
|
||||||
|
|
||||||
|
### Bid as bidder1
|
||||||
|
|
||||||
|
Bidder1 will enter a bid for 50 tickets for 70 dollars.
|
||||||
|
```
|
||||||
|
node bid.js org1 bidder1 auction1 50 80
|
||||||
|
```
|
||||||
|
|
||||||
|
The application will query the bid after it is created:
|
||||||
|
```
|
||||||
|
*** Result: Bid: {
|
||||||
|
"objectType": "bid",
|
||||||
|
"quantity": 50,
|
||||||
|
"price": 80,
|
||||||
|
"org": "Org1MSP",
|
||||||
|
"buyer": "eDUwOTo6Q049YmlkZGVyMSxPVT1jbGllbnQrT1U9b3JnMStPVT1kZXBhcnRtZW50MTo6Q049Y2Eub3JnMS5leGFtcGxlLmNvbSxPPW9yZzEuZXhhbXBsZS5jb20sTD1EdXJoYW0sU1Q9Tm9ydGggQ2Fyb2xpbmEsQz1VUw=="
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Each bid is stored in the implicit data collection of their respective organizations. You can use the `queryBid.js` program to read a bid that is stored in a peers private data collection. The `"buyer"` identity in the bid is the certificate information from the user that created the bid. Only this identity will be able can query the bid from private state or reveal the bid during the auction.
|
||||||
|
|
||||||
|
The `bid.js` application also prints the bidID:
|
||||||
|
```
|
||||||
|
*** Result ***SAVE THIS VALUE*** BidID: 68d3de99032700d92b2d753c79d5aff2ca79378b169efb6a70ca3e0ea43acb1b
|
||||||
|
```
|
||||||
|
|
||||||
|
The BidID acts as the unique identifier of the bid. This ID allows you to query the bid and add the bid to the auction. Save the bidID from the query result as an environment variable in your terminal:
|
||||||
|
```
|
||||||
|
export BIDDER1_BID_ID=68d3de99032700d92b2d753c79d5aff2ca79378b169efb6a70ca3e0ea43acb1b
|
||||||
|
```
|
||||||
|
This value will be different for each transaction, so you will need to use the value returned by your application.
|
||||||
|
|
||||||
|
After the bid is created in private state, you can add the bid to the auction. Run the following command to add the bid that was just created to the auction:
|
||||||
|
```
|
||||||
|
node addBid.js org1 bidder1 auction1 $BIDDER1_BID_ID
|
||||||
|
```
|
||||||
|
|
||||||
|
The hash of bid will be added to the list private bids in that have been added to `auction1`. Storing the hash in the public auction allows users to accurately reveal the bid after bidding is closed. After the bid is added, the application will query the auction to verify that it was updated:
|
||||||
|
```
|
||||||
|
*** Result: Auction: {
|
||||||
|
"objectType": "auction",
|
||||||
|
"ID": "tickets",
|
||||||
|
"seller": "eDUwOTo6Q049c2VsbGVyLE9VPWNsaWVudCtPVT1vcmcxK09VPWRlcGFydG1lbnQxOjpDTj1jYS5vcmcxLmV4YW1wbGUuY29tLE89b3JnMS5leGFtcGxlLmNvbSxMPUR1cmhhbSxTVD1Ob3J0aCBDYXJvbGluYSxDPVVT",
|
||||||
|
"quantity": 100,
|
||||||
|
"bidingOrgs": [
|
||||||
|
"Org1MSP"
|
||||||
|
],
|
||||||
|
"privateBids": {
|
||||||
|
"\u0000bid\u0000auction1\u000068d3de99032700d92b2d753c79d5aff2ca79378b169efb6a70ca3e0ea43acb1b\u0000": {
|
||||||
|
"org": "Org1MSP",
|
||||||
|
"hash": "584dbad2269a44afb42bdbc7a7a4c08a7cd50deece1eb3fc38d4e49b9342f270"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"revealedBids": {},
|
||||||
|
"winners": [],
|
||||||
|
"price": 0,
|
||||||
|
"status": "open"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bid as bidder2
|
||||||
|
|
||||||
|
Let's submit another bid. Bidder2 would like to purchase 40 tickets for 50 dollars.
|
||||||
|
```
|
||||||
|
node bid.js org1 bidder2 auction1 40 50
|
||||||
|
```
|
||||||
|
|
||||||
|
Save the Bid ID returned by the application:
|
||||||
|
```
|
||||||
|
export BIDDER2_BID_ID=77dc57876a1bc5cb798863f07eaab6f041608e55c60900b2cbcf0acc7723e8ec
|
||||||
|
```
|
||||||
|
|
||||||
|
Add bidder2's bid to the auction:
|
||||||
|
```
|
||||||
|
node addBid.js org1 bidder2 auction1 $BIDDER2_BID_ID
|
||||||
|
```
|
||||||
|
|
||||||
|
### Bid as bidder3 from Org2
|
||||||
|
|
||||||
|
Bidder3 will bid for 40 tickets for 70 dollars:
|
||||||
|
```
|
||||||
|
node bid.js org2 bidder3 auction1 30 70
|
||||||
|
```
|
||||||
|
|
||||||
|
Save the Bid ID returned by the application:
|
||||||
|
```
|
||||||
|
export BIDDER3_BID_ID=7e2d0c33d0ff1030d855e5fb76f2a4eb30589549b4cb6e581da17d3705cbf77e
|
||||||
|
```
|
||||||
|
|
||||||
|
Add bidder3's bid to the auction:
|
||||||
|
```
|
||||||
|
node addBid.js org2 bidder3 auction1 $BIDDER3_BID_ID
|
||||||
|
```
|
||||||
|
|
||||||
|
Because bidder3 belongs to Org2, the last transaction will add Org2 to the list of participating organizations. You can see the Org2 MSP ID has been added to the list of `"biddingOrgs"` in the updated auction returned by the application:
|
||||||
|
```
|
||||||
|
*** Result: Auction: {
|
||||||
|
"objectType": "auction",
|
||||||
|
"ID": "tickets",
|
||||||
|
"seller": "eDUwOTo6Q049c2VsbGVyLE9VPWNsaWVudCtPVT1vcmcxK09VPWRlcGFydG1lbnQxOjpDTj1jYS5vcmcxLmV4YW1wbGUuY29tLE89b3JnMS5leGFtcGxlLmNvbSxMPUR1cmhhbSxTVD1Ob3J0aCBDYXJvbGluYSxDPVVT",
|
||||||
|
"quantity": 100,
|
||||||
|
"bidingOrgs": [
|
||||||
|
"Org1MSP",
|
||||||
|
"Org2MSP"
|
||||||
|
],
|
||||||
|
"privateBids": {
|
||||||
|
"\u0000bid\u0000auction1\u00005eb3b492a693f0063986519053cc50e1a374b94532a9fa0ef2866a1294afa2f7\u0000": {
|
||||||
|
"org": "Org2MSP",
|
||||||
|
"hash": "4446c7eb0e2d64165a916ee996348a18716f4c97e632d58d5a8c20eeec5a9238"
|
||||||
|
},
|
||||||
|
"\u0000bid\u0000auction1\u000068d3de99032700d92b2d753c79d5aff2ca79378b169efb6a70ca3e0ea43acb1b\u0000": {
|
||||||
|
"org": "Org1MSP",
|
||||||
|
"hash": "584dbad2269a44afb42bdbc7a7a4c08a7cd50deece1eb3fc38d4e49b9342f270"
|
||||||
|
},
|
||||||
|
"\u0000bid\u0000auction1\u000077dc57876a1bc5cb798863f07eaab6f041608e55c60900b2cbcf0acc7723e8ec\u0000": {
|
||||||
|
"org": "Org1MSP",
|
||||||
|
"hash": "bbcd0c7c376e6681a76d8c5482c97f8bdcda55c90c5478100c3aef17815c4fd3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"revealedBids": {},
|
||||||
|
"winners": [],
|
||||||
|
"price": 0,
|
||||||
|
"status": "open"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Now that a bid from Org2 has been added to the auction, any auction updated need to be endorsed by the Org2 peer. The applications will use `"bidingOrgs"` field to specify which organizations need to endorse a transaction that adds a bid to the organization, reveals a bid, or change the bid status before submitting the transaction.
|
||||||
|
|
||||||
|
### Bid as bidder4
|
||||||
|
|
||||||
|
Bidder4 from Org2 would like to purchase 60 tickets for 60 dollars:
|
||||||
|
```
|
||||||
|
node bid.js org2 bidder4 auction1 60 60
|
||||||
|
```
|
||||||
|
|
||||||
|
Save the Bid ID returned by the application:
|
||||||
|
```
|
||||||
|
export BIDDER4_BID_ID=083478f1af2ba391a5b8d7c590cfb790aedf11578d64ebc4e5efe793555ff212
|
||||||
|
```
|
||||||
|
|
||||||
|
Add bidder2's bid to the auction:
|
||||||
|
```
|
||||||
|
node addBid.js org2 bidder4 auction1 $BIDDER4_BID_ID
|
||||||
|
```
|
||||||
|
|
||||||
|
## Close the auction
|
||||||
|
|
||||||
|
Now that 4 bidders have joined the auction, the seller would like to close the auction and allow buyers to reveal their bids. The seller identity that created the auction needs to update the auction:
|
||||||
|
```
|
||||||
|
node closeAuction.js org1 seller auction1
|
||||||
|
```
|
||||||
|
|
||||||
|
The application will query the auction so you can verify that the auction status has changed to closed. As a test, you can try to create and add a new bid to the auction. The result will be an endorsement policy failure.
|
||||||
|
|
||||||
|
## Reveal bids
|
||||||
|
|
||||||
|
After the auction is closed, bidders can try to win the auction by revealing their bids. The transaction to reveal a bid needs to pass four checks:
|
||||||
|
1. The status of the auction is closed.
|
||||||
|
2. The transaction needs to be issued by the identity that created the bid.
|
||||||
|
3. The hash of the revealed bid matches the hash of the bid on the channel ledger. This confirms that the bid is the same as what is stored in the organizations private data collection.
|
||||||
|
4. The hash of the revealed bid matches the hash that was added to the auction. This confirms that the bid was not altered after the auction was closed.
|
||||||
|
|
||||||
|
Use the `revealBid.js` application to reveal the bid of Bidder1:
|
||||||
|
```
|
||||||
|
node revealBid.js org1 bidder1 auction1 $BIDDER1_BID_ID
|
||||||
|
```
|
||||||
|
|
||||||
|
The full bid details, including the quantity and price, have beed added to the auction:
|
||||||
|
```
|
||||||
|
*** Result: Auction: {
|
||||||
|
"objectType": "auction",
|
||||||
|
"ID": "tickets",
|
||||||
|
"seller": "eDUwOTo6Q049c2VsbGVyLE9VPWNsaWVudCtPVT1vcmcxK09VPWRlcGFydG1lbnQxOjpDTj1jYS5vcmcxLmV4YW1wbGUuY29tLE89b3JnMS5leGFtcGxlLmNvbSxMPUR1cmhhbSxTVD1Ob3J0aCBDYXJvbGluYSxDPVVT",
|
||||||
|
"quantity": 100,
|
||||||
|
"bidingOrgs": [
|
||||||
|
"Org1MSP",
|
||||||
|
"Org2MSP"
|
||||||
|
],
|
||||||
|
"privateBids": {
|
||||||
|
"\u0000bid\u0000auction1\u00002d0639c7a4ccc139c3b349b3637986748d460a4304c93a025c345ee208b0ebcb\u0000": {
|
||||||
|
"org": "Org2MSP",
|
||||||
|
"hash": "d6f661d8b664244ce55065edc2fd95a982221888bb19afdad931b185e187ed4f"
|
||||||
|
},
|
||||||
|
"\u0000bid\u0000auction1\u00004886fdaa7edacbc22285cf88c5413ab08e4d17eff4e1681ec1b90a318a8c7253\u0000": {
|
||||||
|
"org": "Org1MSP",
|
||||||
|
"hash": "bbcd0c7c376e6681a76d8c5482c97f8bdcda55c90c5478100c3aef17815c4fd3"
|
||||||
|
},
|
||||||
|
"\u0000bid\u0000auction1\u00007e2d0c33d0ff1030d855e5fb76f2a4eb30589549b4cb6e581da17d3705cbf77e\u0000": {
|
||||||
|
"org": "Org2MSP",
|
||||||
|
"hash": "4446c7eb0e2d64165a916ee996348a18716f4c97e632d58d5a8c20eeec5a9238"
|
||||||
|
},
|
||||||
|
"\u0000bid\u0000auction1\u00009b9fbd48a05e0e971efe4f57d28d9f09c976f0b8abed4482dc84d32e2d8dea55\u0000": {
|
||||||
|
"org": "Org1MSP",
|
||||||
|
"hash": "584dbad2269a44afb42bdbc7a7a4c08a7cd50deece1eb3fc38d4e49b9342f270"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"revealedBids": {
|
||||||
|
"\u0000bid\u0000auction1\u00009b9fbd48a05e0e971efe4f57d28d9f09c976f0b8abed4482dc84d32e2d8dea55\u0000": {
|
||||||
|
"objectType": "bid",
|
||||||
|
"quantity": 50,
|
||||||
|
"price": 80,
|
||||||
|
"org": "Org1MSP",
|
||||||
|
"buyer": "eDUwOTo6Q049YmlkZGVyMSxPVT1jbGllbnQrT1U9b3JnMStPVT1kZXBhcnRtZW50MTo6Q049Y2Eub3JnMS5leGFtcGxlLmNvbSxPPW9yZzEuZXhhbXBsZS5jb20sTD1EdXJoYW0sU1Q9Tm9ydGggQ2Fyb2xpbmEsQz1VUw=="
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"winners": [],
|
||||||
|
"price": 0,
|
||||||
|
"status": "closed"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
Bidder3 from Org2 will also reveal their bid:
|
||||||
|
```
|
||||||
|
node revealBid.js org2 bidder4 auction1 $BIDDER4_BID_ID
|
||||||
|
```
|
||||||
|
|
||||||
|
If the action ended now, the winners of the auction would be Bidder1 and Bidder3 Let's try to end the auction as the seller to see what happens.
|
||||||
|
|
||||||
|
|
||||||
|
```
|
||||||
|
node endAuction.js org1 seller auction1
|
||||||
|
```
|
||||||
|
|
||||||
|
Instead of ending the auction, the transaction results in an endorsement policy failure. The end of the auction needs to be endorsed by Org2. Before endorsing the transaction, the Org2 peer queries its private data collection to check if any of the bids in its collection would be a potential winner of the auction. If a winning bid has not yet been revealed by its user, the Org2 peer does not endorse the transaction. This protects the bidders from Org2, and prevents the seller from ending the auction prematurely.
|
||||||
|
|
||||||
|
In order to end the auction, we need to reveal the bid from bidder3.
|
||||||
|
```
|
||||||
|
node revealBid.js org2 bidder3 auction1 $BIDDER3_BID_ID
|
||||||
|
```
|
||||||
|
|
||||||
|
Bidder2 from Org1 would not win the auction in either case, as a sufficient number of higher bids have been already been revealed. As a result, Bidder2 decides not to reveal their bid.
|
||||||
|
|
||||||
|
## End the auction
|
||||||
|
|
||||||
|
Now that the winning bids have been revealed, we can now end the auction:
|
||||||
|
```
|
||||||
|
node endAuction org1 seller auction1
|
||||||
|
```
|
||||||
|
|
||||||
|
The transaction was successfully endorsed by both Org1 and Org2, who both calculated the same price and winners of the auction. Each winning bidder is listed next to the quantity that was allocated to them. The bid from bidder4 clears the auction, and as a result the `"price"` of the ended auction is 60. Because Bidder1 and Bidder3 bid above that price, the first 80 tickets were allocated to them. Bidder4 was allocated the remaining 20 tickets that were left.
|
||||||
|
```
|
||||||
|
*** Result: Auction: {
|
||||||
|
"objectType": "auction",
|
||||||
|
"ID": "tickets",
|
||||||
|
"seller": "eDUwOTo6Q049c2VsbGVyLE9VPWNsaWVudCtPVT1vcmcxK09VPWRlcGFydG1lbnQxOjpDTj1jYS5vcmcxLmV4YW1wbGUuY29tLE89b3JnMS5leGFtcGxlLmNvbSxMPUR1cmhhbSxTVD1Ob3J0aCBDYXJvbGluYSxDPVVT",
|
||||||
|
"quantity": 100,
|
||||||
|
"bidingOrgs": [
|
||||||
|
"Org1MSP",
|
||||||
|
"Org2MSP"
|
||||||
|
],
|
||||||
|
"privateBids": {
|
||||||
|
"\u0000bid\u0000auction1\u00002d0639c7a4ccc139c3b349b3637986748d460a4304c93a025c345ee208b0ebcb\u0000": {
|
||||||
|
"org": "Org2MSP",
|
||||||
|
"hash": "d6f661d8b664244ce55065edc2fd95a982221888bb19afdad931b185e187ed4f"
|
||||||
|
},
|
||||||
|
"\u0000bid\u0000auction1\u00004886fdaa7edacbc22285cf88c5413ab08e4d17eff4e1681ec1b90a318a8c7253\u0000": {
|
||||||
|
"org": "Org1MSP",
|
||||||
|
"hash": "bbcd0c7c376e6681a76d8c5482c97f8bdcda55c90c5478100c3aef17815c4fd3"
|
||||||
|
},
|
||||||
|
"\u0000bid\u0000auction1\u00007e2d0c33d0ff1030d855e5fb76f2a4eb30589549b4cb6e581da17d3705cbf77e\u0000": {
|
||||||
|
"org": "Org2MSP",
|
||||||
|
"hash": "4446c7eb0e2d64165a916ee996348a18716f4c97e632d58d5a8c20eeec5a9238"
|
||||||
|
},
|
||||||
|
"\u0000bid\u0000auction1\u00009b9fbd48a05e0e971efe4f57d28d9f09c976f0b8abed4482dc84d32e2d8dea55\u0000": {
|
||||||
|
"org": "Org1MSP",
|
||||||
|
"hash": "584dbad2269a44afb42bdbc7a7a4c08a7cd50deece1eb3fc38d4e49b9342f270"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"revealedBids": {
|
||||||
|
"\u0000bid\u0000auction1\u00002d0639c7a4ccc139c3b349b3637986748d460a4304c93a025c345ee208b0ebcb\u0000": {
|
||||||
|
"objectType": "bid",
|
||||||
|
"quantity": 60,
|
||||||
|
"price": 60,
|
||||||
|
"org": "Org2MSP",
|
||||||
|
"buyer": "eDUwOTo6Q049YmlkZGVyNCxPVT1jbGllbnQrT1U9b3JnMitPVT1kZXBhcnRtZW50MTo6Q049Y2Eub3JnMi5leGFtcGxlLmNvbSxPPW9yZzIuZXhhbXBsZS5jb20sTD1IdXJzbGV5LFNUPUhhbXBzaGlyZSxDPVVL"
|
||||||
|
},
|
||||||
|
"\u0000bid\u0000auction1\u00007e2d0c33d0ff1030d855e5fb76f2a4eb30589549b4cb6e581da17d3705cbf77e\u0000": {
|
||||||
|
"objectType": "bid",
|
||||||
|
"quantity": 30,
|
||||||
|
"price": 70,
|
||||||
|
"org": "Org2MSP",
|
||||||
|
"buyer": "eDUwOTo6Q049YmlkZGVyMyxPVT1jbGllbnQrT1U9b3JnMitPVT1kZXBhcnRtZW50MTo6Q049Y2Eub3JnMi5leGFtcGxlLmNvbSxPPW9yZzIuZXhhbXBsZS5jb20sTD1IdXJzbGV5LFNUPUhhbXBzaGlyZSxDPVVL"
|
||||||
|
},
|
||||||
|
"\u0000bid\u0000auction1\u00009b9fbd48a05e0e971efe4f57d28d9f09c976f0b8abed4482dc84d32e2d8dea55\u0000": {
|
||||||
|
"objectType": "bid",
|
||||||
|
"quantity": 50,
|
||||||
|
"price": 80,
|
||||||
|
"org": "Org1MSP",
|
||||||
|
"buyer": "eDUwOTo6Q049YmlkZGVyMSxPVT1jbGllbnQrT1U9b3JnMStPVT1kZXBhcnRtZW50MTo6Q049Y2Eub3JnMS5leGFtcGxlLmNvbSxPPW9yZzEuZXhhbXBsZS5jb20sTD1EdXJoYW0sU1Q9Tm9ydGggQ2Fyb2xpbmEsQz1VUw=="
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"winners": [
|
||||||
|
{
|
||||||
|
"buyer": "eDUwOTo6Q049YmlkZGVyMSxPVT1jbGllbnQrT1U9b3JnMStPVT1kZXBhcnRtZW50MTo6Q049Y2Eub3JnMS5leGFtcGxlLmNvbSxPPW9yZzEuZXhhbXBsZS5jb20sTD1EdXJoYW0sU1Q9Tm9ydGggQ2Fyb2xpbmEsQz1VUw==",
|
||||||
|
"quantity": 50
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buyer": "eDUwOTo6Q049YmlkZGVyMyxPVT1jbGllbnQrT1U9b3JnMitPVT1kZXBhcnRtZW50MTo6Q049Y2Eub3JnMi5leGFtcGxlLmNvbSxPPW9yZzIuZXhhbXBsZS5jb20sTD1IdXJzbGV5LFNUPUhhbXBzaGlyZSxDPVVL",
|
||||||
|
"quantity": 30
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"buyer": "eDUwOTo6Q049YmlkZGVyNCxPVT1jbGllbnQrT1U9b3JnMitPVT1kZXBhcnRtZW50MTo6Q049Y2Eub3JnMi5leGFtcGxlLmNvbSxPPW9yZzIuZXhhbXBsZS5jb20sTD1IdXJzbGV5LFNUPUhhbXBzaGlyZSxDPVVL",
|
||||||
|
"quantity": 20
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"price": 60,
|
||||||
|
"status": "ended"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Clean up
|
||||||
|
|
||||||
|
When your are done using the auction smart contract, you can bring down the network and clean up the environment. In the `auction/application-javascript` directory, run the following command to remove the wallets that were created to use run the applications:
|
||||||
|
```
|
||||||
|
rm -rf wallet
|
||||||
|
```
|
||||||
|
|
||||||
|
You can then navigate to the test network directory and bring down the network:
|
||||||
|
````
|
||||||
|
cd ../../test-network/
|
||||||
|
./network.sh down
|
||||||
|
````
|
||||||
108
auction/application-javascript/addBid.js
Normal file
108
auction/application-javascript/addBid.js
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* Copyright IBM Corp. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Gateway, Wallets } = require('fabric-network');
|
||||||
|
const path = require('path');
|
||||||
|
const { buildCCPOrg1, buildCCPOrg2, buildWallet } = require('../../test-application/javascript/AppUtil.js');
|
||||||
|
|
||||||
|
const myChannel = 'mychannel';
|
||||||
|
const myChaincodeName = 'auction';
|
||||||
|
|
||||||
|
|
||||||
|
function prettyJSONString(inputString) {
|
||||||
|
if (inputString) {
|
||||||
|
return JSON.stringify(JSON.parse(inputString), null, 2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return inputString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addBid(ccp,wallet,user,auctionID,bidID) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
const gateway = new Gateway();
|
||||||
|
//connect using Discovery enabled
|
||||||
|
|
||||||
|
await gateway.connect(ccp,
|
||||||
|
{ wallet: wallet, identity: user, discovery: { enabled: true, asLocalhost: true } });
|
||||||
|
|
||||||
|
const network = await gateway.getNetwork(myChannel);
|
||||||
|
const contract = network.getContract(myChaincodeName);
|
||||||
|
|
||||||
|
console.log('\n--> Evaluate Transaction: query the auction you want to join');
|
||||||
|
let auctionString = await contract.evaluateTransaction('QueryAuction',auctionID);
|
||||||
|
var auctionJSON = JSON.parse(auctionString);
|
||||||
|
|
||||||
|
let statefulTxn = contract.createTransaction('AddBid');
|
||||||
|
|
||||||
|
if (auctionJSON.bidingOrgs.length == 2) {
|
||||||
|
statefulTxn.setEndorsingOrganizations(auctionJSON.bidingOrgs[0],auctionJSON.bidingOrgs[1]);
|
||||||
|
} else {
|
||||||
|
statefulTxn.setEndorsingOrganizations(auctionJSON.bidingOrgs[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\n--> Submit Transaction: add bid to the auction');
|
||||||
|
await statefulTxn.submit(auctionID,bidID);
|
||||||
|
|
||||||
|
console.log('\n--> Evaluate Transaction: query the auction to see that our bid was added');
|
||||||
|
let result = await contract.evaluateTransaction('QueryAuction',auctionID);
|
||||||
|
console.log('*** Result: Auction: ' + prettyJSONString(result.toString()));
|
||||||
|
|
||||||
|
gateway.disconnect();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`******** FAILED to submit bid: ${error}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
if (process.argv[2] == undefined || process.argv[3] == undefined
|
||||||
|
|| process.argv[4] == undefined || process.argv[5] == undefined) {
|
||||||
|
console.log("Usage: node addBid.js org userID auctionID bidID");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const org = process.argv[2]
|
||||||
|
const user = process.argv[3];
|
||||||
|
const auctionID = process.argv[4];
|
||||||
|
const bidID = process.argv[5];
|
||||||
|
|
||||||
|
if (org == 'Org1' || org == 'org1') {
|
||||||
|
|
||||||
|
const orgMSP = 'Org1MSP';
|
||||||
|
const ccp = buildCCPOrg1();
|
||||||
|
const walletPath = path.join(__dirname, 'wallet/org1');
|
||||||
|
const wallet = await buildWallet(Wallets, walletPath);
|
||||||
|
await addBid(ccp,wallet,user,auctionID,bidID);
|
||||||
|
}
|
||||||
|
else if (org == 'Org2' || org == 'org2') {
|
||||||
|
|
||||||
|
const orgMSP = 'Org2MSP';
|
||||||
|
const ccp = buildCCPOrg2();
|
||||||
|
const walletPath = path.join(__dirname, 'wallet/org2');
|
||||||
|
const wallet = await buildWallet(Wallets, walletPath);
|
||||||
|
await addBid(ccp,wallet,user,auctionID,bidID);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log("Usage: node addBid.js org userID auctionID bidID");
|
||||||
|
console.log("Org must be Org1 or Org2");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`******** FAILED to run the application: ${error}`);
|
||||||
|
if (error.stack) {
|
||||||
|
console.error(error.stack);
|
||||||
|
}
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
main();
|
||||||
114
auction/application-javascript/bid.js
Normal file
114
auction/application-javascript/bid.js
Normal file
|
|
@ -0,0 +1,114 @@
|
||||||
|
/*
|
||||||
|
* Copyright IBM Corp. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Gateway, Wallets } = require('fabric-network');
|
||||||
|
const path = require('path');
|
||||||
|
const { buildCCPOrg1, buildCCPOrg2, buildWallet } = require('../../test-application/javascript/AppUtil.js');
|
||||||
|
|
||||||
|
const myChannel = 'mychannel';
|
||||||
|
const myChaincodeName = 'auction';
|
||||||
|
|
||||||
|
|
||||||
|
function prettyJSONString(inputString) {
|
||||||
|
if (inputString) {
|
||||||
|
return JSON.stringify(JSON.parse(inputString), null, 2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return inputString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function bid(ccp,wallet,user,orgMSP,auctionID,quantity,price) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
const gateway = new Gateway();
|
||||||
|
//connect using Discovery enabled
|
||||||
|
|
||||||
|
await gateway.connect(ccp,
|
||||||
|
{ wallet: wallet, identity: user, discovery: { enabled: true, asLocalhost: true } });
|
||||||
|
|
||||||
|
const network = await gateway.getNetwork(myChannel);
|
||||||
|
const contract = network.getContract(myChaincodeName);
|
||||||
|
|
||||||
|
console.log('\n--> Evaluate Transaction: get your client ID');
|
||||||
|
let buyer = await contract.evaluateTransaction('GetID');
|
||||||
|
console.log('*** Result: Buyer ID is ' + buyer.toString());
|
||||||
|
|
||||||
|
let bidData = { objectType: 'bid', quantity: parseInt(quantity) , price: parseInt(price), org: orgMSP, buyer: buyer.toString()};
|
||||||
|
|
||||||
|
let statefulTxn = contract.createTransaction('Bid');
|
||||||
|
statefulTxn.setEndorsingOrganizations(orgMSP);
|
||||||
|
let tmapData = Buffer.from(JSON.stringify(bidData));
|
||||||
|
statefulTxn.setTransient({
|
||||||
|
bid: tmapData
|
||||||
|
});
|
||||||
|
|
||||||
|
let bidID = statefulTxn.getTransactionId();
|
||||||
|
|
||||||
|
console.log('\n--> Submit Transaction: Create the bid that is stored in your organiztions private data collection');
|
||||||
|
await statefulTxn.submit(auctionID);
|
||||||
|
console.log('*** Result: committed');
|
||||||
|
console.log('*** Result ***SAVE THIS VALUE*** BidID: ' + bidID.toString());
|
||||||
|
|
||||||
|
console.log('\n--> Evaluate Transaction: read the bid that was just created');
|
||||||
|
let result = await contract.evaluateTransaction('QueryBid',auctionID,bidID);
|
||||||
|
console.log('*** Result: Bid: ' + prettyJSONString(result.toString()));
|
||||||
|
|
||||||
|
gateway.disconnect();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`******** FAILED to submit bid: ${error}`);
|
||||||
|
if (error.stack) {
|
||||||
|
console.error(error.stack);
|
||||||
|
}
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
if (process.argv[2] == undefined || process.argv[3] == undefined
|
||||||
|
|| process.argv[4] == undefined || process.argv[5] == undefined
|
||||||
|
|| process.argv[6] == undefined) {
|
||||||
|
console.log("Usage: node bid.js org userID auctionID quantity price");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const org = process.argv[2]
|
||||||
|
const user = process.argv[3];
|
||||||
|
const auctionID = process.argv[4];
|
||||||
|
const quantity = process.argv[5];
|
||||||
|
const price = process.argv[6];
|
||||||
|
|
||||||
|
if (org == 'Org1' || org == 'org1') {
|
||||||
|
|
||||||
|
const orgMSP = 'Org1MSP';
|
||||||
|
const ccp = buildCCPOrg1();
|
||||||
|
const walletPath = path.join(__dirname, 'wallet/org1');
|
||||||
|
const wallet = await buildWallet(Wallets, walletPath);
|
||||||
|
await bid(ccp,wallet,user,orgMSP,auctionID,quantity,price);
|
||||||
|
}
|
||||||
|
else if (org == 'Org2' || org == 'org2') {
|
||||||
|
|
||||||
|
const orgMSP = 'Org2MSP';
|
||||||
|
const ccp = buildCCPOrg2();
|
||||||
|
const walletPath = path.join(__dirname, 'wallet/org2');
|
||||||
|
const wallet = await buildWallet(Wallets, walletPath);
|
||||||
|
await bid(ccp,wallet,user,orgMSP,auctionID,quantity,price);
|
||||||
|
} else {
|
||||||
|
console.log("Usage: node bid.js org userID auctionID quantity price");
|
||||||
|
console.log("Org must be Org1 or Org2");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`******** FAILED to run the application: ${error}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
main();
|
||||||
109
auction/application-javascript/closeAuction.js
Normal file
109
auction/application-javascript/closeAuction.js
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
/*
|
||||||
|
* Copyright IBM Corp. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Gateway, Wallets } = require('fabric-network');
|
||||||
|
const path = require('path');
|
||||||
|
const { buildCCPOrg1, buildCCPOrg2, buildWallet } = require('../../test-application/javascript/AppUtil.js');
|
||||||
|
|
||||||
|
const myChannel = 'mychannel';
|
||||||
|
const myChaincodeName = 'auction';
|
||||||
|
|
||||||
|
|
||||||
|
function prettyJSONString(inputString) {
|
||||||
|
if (inputString) {
|
||||||
|
return JSON.stringify(JSON.parse(inputString), null, 2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return inputString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function closeAuction(ccp,wallet,user,auctionID) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
const gateway = new Gateway();
|
||||||
|
//connect using Discovery enabled
|
||||||
|
|
||||||
|
await gateway.connect(ccp,
|
||||||
|
{ wallet: wallet, identity: user, discovery: { enabled: true, asLocalhost: true } });
|
||||||
|
|
||||||
|
const network = await gateway.getNetwork(myChannel);
|
||||||
|
const contract = network.getContract(myChaincodeName);
|
||||||
|
|
||||||
|
// Query the auction to get the list of endorsing orgs.
|
||||||
|
//console.log('\n--> Evaluate Transaction: query the auction you want to join');
|
||||||
|
let auctionString = await contract.evaluateTransaction('QueryAuction',auctionID);
|
||||||
|
//console.log('*** Result: Bid: ' + prettyJSONString(auctionString.toString()));
|
||||||
|
var auctionJSON = JSON.parse(auctionString);
|
||||||
|
|
||||||
|
let statefulTxn = contract.createTransaction('CloseAuction');
|
||||||
|
|
||||||
|
if (auctionJSON.bidingOrgs.length == 2) {
|
||||||
|
statefulTxn.setEndorsingOrganizations(auctionJSON.bidingOrgs[0],auctionJSON.bidingOrgs[1]);
|
||||||
|
} else {
|
||||||
|
statefulTxn.setEndorsingOrganizations(auctionJSON.bidingOrgs[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\n--> Submit Transaction: close auction');
|
||||||
|
await statefulTxn.submit(auctionID);
|
||||||
|
console.log('*** Result: committed');
|
||||||
|
|
||||||
|
console.log('\n--> Evaluate Transaction: query the updated auction');
|
||||||
|
let result = await contract.evaluateTransaction('QueryAuction',auctionID);
|
||||||
|
console.log('*** Result: Auction: ' + prettyJSONString(result.toString()));
|
||||||
|
|
||||||
|
gateway.disconnect();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`******** FAILED to submit bid: ${error}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
if (process.argv[2] == undefined || process.argv[3] == undefined
|
||||||
|
|| process.argv[4] == undefined) {
|
||||||
|
console.log("Usage: node closeAuction.js org userID auctionID");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const org = process.argv[2]
|
||||||
|
const user = process.argv[3];
|
||||||
|
const auctionID = process.argv[4];
|
||||||
|
|
||||||
|
if (org == 'Org1' || org == 'org1') {
|
||||||
|
|
||||||
|
const orgMSP = 'Org1MSP';
|
||||||
|
const ccp = buildCCPOrg1();
|
||||||
|
const walletPath = path.join(__dirname, 'wallet/org1');
|
||||||
|
const wallet = await buildWallet(Wallets, walletPath);
|
||||||
|
await closeAuction(ccp,wallet,user,auctionID);
|
||||||
|
}
|
||||||
|
else if (org == 'Org2' || org == 'org2') {
|
||||||
|
|
||||||
|
const orgMSP = 'Org2MSP';
|
||||||
|
const ccp = buildCCPOrg2();
|
||||||
|
const walletPath = path.join(__dirname, 'wallet/org2');
|
||||||
|
const wallet = await buildWallet(Wallets, walletPath);
|
||||||
|
await closeAuction(ccp,wallet,user,auctionID);
|
||||||
|
} else {
|
||||||
|
console.log("Usage: node closeAuction.js org userID auctionID ");
|
||||||
|
console.log("Org must be Org1 or Org2");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`******** FAILED to run the application: ${error}`);
|
||||||
|
if (error.stack) {
|
||||||
|
console.error(error.stack);
|
||||||
|
}
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
main();
|
||||||
95
auction/application-javascript/createAuction.js
Normal file
95
auction/application-javascript/createAuction.js
Normal file
|
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
* Copyright IBM Corp. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Gateway, Wallets } = require('fabric-network');
|
||||||
|
const path = require('path');
|
||||||
|
const { buildCCPOrg1, buildCCPOrg2, buildWallet } = require('../../test-application/javascript/AppUtil.js');
|
||||||
|
|
||||||
|
const myChannel = 'mychannel';
|
||||||
|
const myChaincodeName = 'auction';
|
||||||
|
|
||||||
|
|
||||||
|
function prettyJSONString(inputString) {
|
||||||
|
if (inputString) {
|
||||||
|
return JSON.stringify(JSON.parse(inputString), null, 2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return inputString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function createAuction(ccp,wallet,user,auctionID,item,quantity) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
const gateway = new Gateway();
|
||||||
|
//connect using Discovery enabled
|
||||||
|
|
||||||
|
await gateway.connect(ccp,
|
||||||
|
{ wallet: wallet, identity: user, discovery: { enabled: true, asLocalhost: true } });
|
||||||
|
|
||||||
|
const network = await gateway.getNetwork(myChannel);
|
||||||
|
const contract = network.getContract(myChaincodeName);
|
||||||
|
|
||||||
|
let statefulTxn = contract.createTransaction('CreateAuction');
|
||||||
|
|
||||||
|
console.log('\n--> Submit Transaction: Propose a new auction');
|
||||||
|
await statefulTxn.submit(auctionID,item,parseInt(quantity));
|
||||||
|
console.log('*** Result: committed');
|
||||||
|
|
||||||
|
console.log('\n--> Evaluate Transaction: query the auction that was just created');
|
||||||
|
let result = await contract.evaluateTransaction('QueryAuction',auctionID);
|
||||||
|
console.log('*** Result: Auction: ' + prettyJSONString(result.toString()));
|
||||||
|
|
||||||
|
gateway.disconnect();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`******** FAILED to submit bid: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
if (process.argv[2] == undefined || process.argv[3] == undefined
|
||||||
|
|| process.argv[4] == undefined || process.argv[5] == undefined
|
||||||
|
|| process.argv[6] == undefined) {
|
||||||
|
console.log("Usage: node createAuction.js org userID auctionID item quantity");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const org = process.argv[2]
|
||||||
|
const user = process.argv[3];
|
||||||
|
const auctionID = process.argv[4];
|
||||||
|
const item = process.argv[5];
|
||||||
|
const quantity = process.argv[6];
|
||||||
|
|
||||||
|
if (org == 'Org1' || org == 'org1') {
|
||||||
|
|
||||||
|
const orgMSP = 'Org1MSP';
|
||||||
|
const ccp = buildCCPOrg1();
|
||||||
|
const walletPath = path.join(__dirname, 'wallet/org1');
|
||||||
|
const wallet = await buildWallet(Wallets, walletPath);
|
||||||
|
await createAuction(ccp,wallet,user,auctionID,item,quantity);
|
||||||
|
}
|
||||||
|
else if (org == 'Org2' || org == 'org2') {
|
||||||
|
|
||||||
|
const orgMSP = 'Org2MSP';
|
||||||
|
const ccp = buildCCPOrg2();
|
||||||
|
const walletPath = path.join(__dirname, 'wallet/org2');
|
||||||
|
const wallet = await buildWallet(Wallets, walletPath);
|
||||||
|
await createAuction(ccp,wallet,user,auctionID,item,quantity);
|
||||||
|
} else {
|
||||||
|
console.log("Usage: node createAuction.js org userID auctionID item quantity");
|
||||||
|
console.log("Org must be Org1 or Org2");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`******** FAILED to run the application: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
main();
|
||||||
109
auction/application-javascript/endAuction.js
Normal file
109
auction/application-javascript/endAuction.js
Normal file
|
|
@ -0,0 +1,109 @@
|
||||||
|
/*
|
||||||
|
* Copyright IBM Corp. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Gateway, Wallets } = require('fabric-network');
|
||||||
|
const path = require('path');
|
||||||
|
const { buildCCPOrg1, buildCCPOrg2, buildWallet } = require('../../test-application/javascript/AppUtil.js');
|
||||||
|
|
||||||
|
const myChannel = 'mychannel';
|
||||||
|
const myChaincodeName = 'auction';
|
||||||
|
|
||||||
|
|
||||||
|
function prettyJSONString(inputString) {
|
||||||
|
if (inputString) {
|
||||||
|
return JSON.stringify(JSON.parse(inputString), null, 2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return inputString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function endAuction(ccp,wallet,user,auctionID) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
const gateway = new Gateway();
|
||||||
|
//connect using Discovery enabled
|
||||||
|
|
||||||
|
await gateway.connect(ccp,
|
||||||
|
{ wallet: wallet, identity: user, discovery: { enabled: true, asLocalhost: true } });
|
||||||
|
|
||||||
|
const network = await gateway.getNetwork(myChannel);
|
||||||
|
const contract = network.getContract(myChaincodeName);
|
||||||
|
|
||||||
|
// Query the auction to get the list of endorsing orgs.
|
||||||
|
//console.log('\n--> Evaluate Transaction: query the auction you want to join');
|
||||||
|
let auctionString = await contract.evaluateTransaction('QueryAuction',auctionID);
|
||||||
|
//console.log('*** Result: Bid: ' + prettyJSONString(auctionString.toString()));
|
||||||
|
var auctionJSON = JSON.parse(auctionString);
|
||||||
|
|
||||||
|
let statefulTxn = contract.createTransaction('EndAuction');
|
||||||
|
|
||||||
|
if (auctionJSON.bidingOrgs.length == 2) {
|
||||||
|
statefulTxn.setEndorsingOrganizations(auctionJSON.bidingOrgs[0],auctionJSON.bidingOrgs[1]);
|
||||||
|
} else {
|
||||||
|
statefulTxn.setEndorsingOrganizations(auctionJSON.bidingOrgs[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('\n--> Submit the transaction to end the auction');
|
||||||
|
await statefulTxn.submit(auctionID);
|
||||||
|
console.log('*** Result: committed');
|
||||||
|
|
||||||
|
console.log('\n--> Evaluate Transaction: query the updated auction');
|
||||||
|
let result = await contract.evaluateTransaction('QueryAuction',auctionID);
|
||||||
|
console.log('*** Result: Auction: ' + prettyJSONString(result.toString()));
|
||||||
|
|
||||||
|
gateway.disconnect();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`******** FAILED to submit bid: ${error}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
if (process.argv[2] == undefined || process.argv[3] == undefined
|
||||||
|
|| process.argv[4] == undefined) {
|
||||||
|
console.log("Usage: node endAuction.js org userID auctionID");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const org = process.argv[2]
|
||||||
|
const user = process.argv[3];
|
||||||
|
const auctionID = process.argv[4];
|
||||||
|
|
||||||
|
if (org == 'Org1' || org == 'org1') {
|
||||||
|
|
||||||
|
const orgMSP = 'Org1MSP';
|
||||||
|
const ccp = buildCCPOrg1();
|
||||||
|
const walletPath = path.join(__dirname, 'wallet/org1');
|
||||||
|
const wallet = await buildWallet(Wallets, walletPath);
|
||||||
|
await endAuction(ccp,wallet,user,auctionID);
|
||||||
|
}
|
||||||
|
else if (org == 'Org2' || org == 'org2') {
|
||||||
|
|
||||||
|
const orgMSP = 'Org2MSP';
|
||||||
|
const ccp = buildCCPOrg2();
|
||||||
|
const walletPath = path.join(__dirname, 'wallet/org2');
|
||||||
|
const wallet = await buildWallet(Wallets, walletPath);
|
||||||
|
await endAuction(ccp,wallet,user,auctionID);
|
||||||
|
} else {
|
||||||
|
console.log("Usage: node endAuction.js org userID auctionID");
|
||||||
|
console.log("Org must be Org1 or Org2");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`******** FAILED to run the application: ${error}`);
|
||||||
|
if (error.stack) {
|
||||||
|
console.error(error.stack);
|
||||||
|
}
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
main();
|
||||||
76
auction/application-javascript/enrollAdmin.js
Normal file
76
auction/application-javascript/enrollAdmin.js
Normal file
|
|
@ -0,0 +1,76 @@
|
||||||
|
/*
|
||||||
|
* Copyright IBM Corp. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Wallets } = require('fabric-network');
|
||||||
|
const FabricCAServices = require('fabric-ca-client');
|
||||||
|
const path = require('path');
|
||||||
|
const { buildCAClient, enrollAdmin } = require('../../test-application/javascript/CAUtil.js');
|
||||||
|
const { buildCCPOrg1, buildCCPOrg2, buildWallet } = require('../../test-application/javascript/AppUtil.js');
|
||||||
|
|
||||||
|
const mspOrg1 = 'Org1MSP';
|
||||||
|
const mspOrg2 = 'Org2MSP';
|
||||||
|
|
||||||
|
function prettyJSONString(inputString) {
|
||||||
|
if (inputString) {
|
||||||
|
return JSON.stringify(JSON.parse(inputString), null, 2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return inputString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function connectToOrg1CA() {
|
||||||
|
console.log('\n--> Enrolling the Org1 CA admin');
|
||||||
|
const ccpOrg1 = buildCCPOrg1();
|
||||||
|
const caOrg1Client = buildCAClient(FabricCAServices, ccpOrg1, 'ca.org1.example.com');
|
||||||
|
|
||||||
|
const walletPathOrg1 = path.join(__dirname, 'wallet/org1');
|
||||||
|
const walletOrg1 = await buildWallet(Wallets, walletPathOrg1);
|
||||||
|
|
||||||
|
await enrollAdmin(caOrg1Client, walletOrg1, mspOrg1);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async function connectToOrg2CA() {
|
||||||
|
console.log('\n--> Enrolling the Org2 CA admin');
|
||||||
|
const ccpOrg2 = buildCCPOrg2();
|
||||||
|
const caOrg2Client = buildCAClient(FabricCAServices, ccpOrg2, 'ca.org2.example.com');
|
||||||
|
|
||||||
|
const walletPathOrg2 = path.join(__dirname, 'wallet/org2');
|
||||||
|
const walletOrg2 = await buildWallet(Wallets, walletPathOrg2);
|
||||||
|
|
||||||
|
await enrollAdmin(caOrg2Client, walletOrg2, mspOrg2);
|
||||||
|
|
||||||
|
}
|
||||||
|
async function main() {
|
||||||
|
|
||||||
|
if (process.argv[2] == undefined) {
|
||||||
|
console.log("Usage: node enrollAdmin.js Org");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const org = process.argv[2];
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
if (org == 'Org1' || org == 'org1') {
|
||||||
|
await connectToOrg1CA();
|
||||||
|
}
|
||||||
|
else if (org == 'Org2' || org == 'org2') {
|
||||||
|
await connectToOrg2CA();
|
||||||
|
} else {
|
||||||
|
console.log("Usage: node registerUser.js org userID");
|
||||||
|
console.log("Org must be Org1 or Org2");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error in enrolling admin: ${error}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
895
auction/application-javascript/package-lock.json
generated
Normal file
895
auction/application-javascript/package-lock.json
generated
Normal file
|
|
@ -0,0 +1,895 @@
|
||||||
|
{
|
||||||
|
"name": "auction",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"lockfileVersion": 1,
|
||||||
|
"requires": true,
|
||||||
|
"dependencies": {
|
||||||
|
"@grpc/grpc-js": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.0.3.tgz",
|
||||||
|
"integrity": "sha512-JKV3f5Bv2TZxK6eJSB9EarsZrnLxrvcFNwI9goq0YRXa3S6NNoCSnI3cG3lkXVIJ03Wng1WXe76kc2JQtRe7AQ==",
|
||||||
|
"requires": {
|
||||||
|
"semver": "^6.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@grpc/proto-loader": {
|
||||||
|
"version": "0.5.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.5.4.tgz",
|
||||||
|
"integrity": "sha512-HTM4QpI9B2XFkPz7pjwMyMgZchJ93TVkL3kWPW8GDMDKYxsMnmf4w2TNMJK7+KNiYHS5cJrCEAFlF+AwtXWVPA==",
|
||||||
|
"requires": {
|
||||||
|
"lodash.camelcase": "^4.3.0",
|
||||||
|
"protobufjs": "^6.8.6"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@protobufjs/aspromise": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78="
|
||||||
|
},
|
||||||
|
"@protobufjs/base64": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz",
|
||||||
|
"integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg=="
|
||||||
|
},
|
||||||
|
"@protobufjs/codegen": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg=="
|
||||||
|
},
|
||||||
|
"@protobufjs/eventemitter": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz",
|
||||||
|
"integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A="
|
||||||
|
},
|
||||||
|
"@protobufjs/fetch": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz",
|
||||||
|
"integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=",
|
||||||
|
"requires": {
|
||||||
|
"@protobufjs/aspromise": "^1.1.1",
|
||||||
|
"@protobufjs/inquire": "^1.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@protobufjs/float": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E="
|
||||||
|
},
|
||||||
|
"@protobufjs/inquire": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz",
|
||||||
|
"integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik="
|
||||||
|
},
|
||||||
|
"@protobufjs/path": {
|
||||||
|
"version": "1.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz",
|
||||||
|
"integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0="
|
||||||
|
},
|
||||||
|
"@protobufjs/pool": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz",
|
||||||
|
"integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q="
|
||||||
|
},
|
||||||
|
"@protobufjs/utf8": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz",
|
||||||
|
"integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA="
|
||||||
|
},
|
||||||
|
"@types/caseless": {
|
||||||
|
"version": "0.12.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/caseless/-/caseless-0.12.2.tgz",
|
||||||
|
"integrity": "sha512-6ckxMjBBD8URvjB6J3NcnuAn5Pkl7t3TizAg+xdlzzQGSPSmBcXf8KoIH0ua/i+tio+ZRUHEXp0HEmvaR4kt0w=="
|
||||||
|
},
|
||||||
|
"@types/long": {
|
||||||
|
"version": "4.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz",
|
||||||
|
"integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w=="
|
||||||
|
},
|
||||||
|
"@types/node": {
|
||||||
|
"version": "13.13.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.21.tgz",
|
||||||
|
"integrity": "sha512-tlFWakSzBITITJSxHV4hg4KvrhR/7h3xbJdSFbYJBVzKubrASbnnIFuSgolUh7qKGo/ZeJPKUfbZ0WS6Jp14DQ=="
|
||||||
|
},
|
||||||
|
"@types/request": {
|
||||||
|
"version": "2.48.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/request/-/request-2.48.5.tgz",
|
||||||
|
"integrity": "sha512-/LO7xRVnL3DxJ1WkPGDQrp4VTV1reX9RkC85mJ+Qzykj2Bdw+mG15aAfDahc76HtknjzE16SX/Yddn6MxVbmGQ==",
|
||||||
|
"requires": {
|
||||||
|
"@types/caseless": "*",
|
||||||
|
"@types/node": "*",
|
||||||
|
"@types/tough-cookie": "*",
|
||||||
|
"form-data": "^2.5.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"@types/tough-cookie": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/@types/tough-cookie/-/tough-cookie-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-I99sngh224D0M7XgW1s120zxCt3VYQ3IQsuw3P3jbq5GG4yc79+ZjyKznyOGIQrflfylLgcfekeZW/vk0yng6A=="
|
||||||
|
},
|
||||||
|
"ajv": {
|
||||||
|
"version": "6.12.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz",
|
||||||
|
"integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==",
|
||||||
|
"requires": {
|
||||||
|
"fast-deep-equal": "^3.1.1",
|
||||||
|
"fast-json-stable-stringify": "^2.0.0",
|
||||||
|
"json-schema-traverse": "^0.4.1",
|
||||||
|
"uri-js": "^4.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ansi-regex": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
|
||||||
|
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
|
||||||
|
},
|
||||||
|
"asn1": {
|
||||||
|
"version": "0.2.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz",
|
||||||
|
"integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==",
|
||||||
|
"requires": {
|
||||||
|
"safer-buffer": "~2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"assert-plus": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU="
|
||||||
|
},
|
||||||
|
"async": {
|
||||||
|
"version": "1.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz",
|
||||||
|
"integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo="
|
||||||
|
},
|
||||||
|
"asynckit": {
|
||||||
|
"version": "0.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
|
||||||
|
"integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k="
|
||||||
|
},
|
||||||
|
"aws-sign2": {
|
||||||
|
"version": "0.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz",
|
||||||
|
"integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg="
|
||||||
|
},
|
||||||
|
"aws4": {
|
||||||
|
"version": "1.10.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz",
|
||||||
|
"integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA=="
|
||||||
|
},
|
||||||
|
"bcrypt-pbkdf": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=",
|
||||||
|
"requires": {
|
||||||
|
"tweetnacl": "^0.14.3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bn.js": {
|
||||||
|
"version": "4.11.9",
|
||||||
|
"resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz",
|
||||||
|
"integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw=="
|
||||||
|
},
|
||||||
|
"brorand": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz",
|
||||||
|
"integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8="
|
||||||
|
},
|
||||||
|
"browser-request": {
|
||||||
|
"version": "0.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/browser-request/-/browser-request-0.3.3.tgz",
|
||||||
|
"integrity": "sha1-ns5bWsqJopkyJC4Yv5M975h2zBc="
|
||||||
|
},
|
||||||
|
"callsite": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/callsite/-/callsite-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-KAOY5dZkvXQDi28JBRU+borxvCA="
|
||||||
|
},
|
||||||
|
"camelcase": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/camelcase/-/camelcase-2.1.1.tgz",
|
||||||
|
"integrity": "sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8="
|
||||||
|
},
|
||||||
|
"caseless": {
|
||||||
|
"version": "0.12.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz",
|
||||||
|
"integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw="
|
||||||
|
},
|
||||||
|
"cliui": {
|
||||||
|
"version": "3.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz",
|
||||||
|
"integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0=",
|
||||||
|
"requires": {
|
||||||
|
"string-width": "^1.0.1",
|
||||||
|
"strip-ansi": "^3.0.1",
|
||||||
|
"wrap-ansi": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"cloudant-follow": {
|
||||||
|
"version": "0.18.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/cloudant-follow/-/cloudant-follow-0.18.2.tgz",
|
||||||
|
"integrity": "sha512-qu/AmKxDqJds+UmT77+0NbM7Yab2K3w0qSeJRzsq5dRWJTEJdWeb+XpG4OpKuTE9RKOa/Awn2gR3TTnvNr3TeA==",
|
||||||
|
"requires": {
|
||||||
|
"browser-request": "~0.3.0",
|
||||||
|
"debug": "^4.0.1",
|
||||||
|
"request": "^2.88.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"code-point-at": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
|
||||||
|
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
|
||||||
|
},
|
||||||
|
"colors": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/colors/-/colors-1.0.3.tgz",
|
||||||
|
"integrity": "sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs="
|
||||||
|
},
|
||||||
|
"combined-stream": {
|
||||||
|
"version": "1.0.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
|
||||||
|
"integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==",
|
||||||
|
"requires": {
|
||||||
|
"delayed-stream": "~1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"core-util-is": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
|
||||||
|
},
|
||||||
|
"cycle": {
|
||||||
|
"version": "1.0.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/cycle/-/cycle-1.0.3.tgz",
|
||||||
|
"integrity": "sha1-IegLK+hYD5i0aPN5QwZisEbDStI="
|
||||||
|
},
|
||||||
|
"dashdash": {
|
||||||
|
"version": "1.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz",
|
||||||
|
"integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=",
|
||||||
|
"requires": {
|
||||||
|
"assert-plus": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"debug": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-IX2ncY78vDTjZMFUdmsvIRFY2Cf4FnD0wRs+nQwJU8Lu99/tPFdb0VybiiMTPe3I6rQmwsqQqRBvxU+bZ/I8sg==",
|
||||||
|
"requires": {
|
||||||
|
"ms": "2.1.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"decamelize": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz",
|
||||||
|
"integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA="
|
||||||
|
},
|
||||||
|
"delayed-stream": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk="
|
||||||
|
},
|
||||||
|
"ecc-jsbn": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz",
|
||||||
|
"integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=",
|
||||||
|
"requires": {
|
||||||
|
"jsbn": "~0.1.0",
|
||||||
|
"safer-buffer": "^2.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"elliptic": {
|
||||||
|
"version": "6.5.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz",
|
||||||
|
"integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==",
|
||||||
|
"requires": {
|
||||||
|
"bn.js": "^4.4.0",
|
||||||
|
"brorand": "^1.0.1",
|
||||||
|
"hash.js": "^1.0.0",
|
||||||
|
"hmac-drbg": "^1.0.0",
|
||||||
|
"inherits": "^2.0.1",
|
||||||
|
"minimalistic-assert": "^1.0.0",
|
||||||
|
"minimalistic-crypto-utils": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"errs": {
|
||||||
|
"version": "0.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/errs/-/errs-0.3.2.tgz",
|
||||||
|
"integrity": "sha1-eYCZstvTfKK8dJ5TinwTB9C1BJk="
|
||||||
|
},
|
||||||
|
"extend": {
|
||||||
|
"version": "3.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
|
||||||
|
"integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g=="
|
||||||
|
},
|
||||||
|
"extsprintf": {
|
||||||
|
"version": "1.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
|
||||||
|
"integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU="
|
||||||
|
},
|
||||||
|
"eyes": {
|
||||||
|
"version": "0.1.8",
|
||||||
|
"resolved": "https://registry.npmjs.org/eyes/-/eyes-0.1.8.tgz",
|
||||||
|
"integrity": "sha1-Ys8SAjTGg3hdkCNIqADvPgzCC8A="
|
||||||
|
},
|
||||||
|
"fabric-ca-client": {
|
||||||
|
"version": "2.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fabric-ca-client/-/fabric-ca-client-2.2.2.tgz",
|
||||||
|
"integrity": "sha512-hC762b6kEjot7Z9mBfzZDkloKAQ6rNJiKPg3F9o56XfF10xeJ1wj0p6ULMKgQesSYMmre1g9TcALVdnH1NqS8w==",
|
||||||
|
"requires": {
|
||||||
|
"fabric-common": "2.2.2",
|
||||||
|
"jsrsasign": "^8.0.20",
|
||||||
|
"url": "^0.11.0",
|
||||||
|
"winston": "^2.4.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fabric-common": {
|
||||||
|
"version": "2.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fabric-common/-/fabric-common-2.2.2.tgz",
|
||||||
|
"integrity": "sha512-OhIvZTTis7tSRzztCzss3+BM6R274ZIM1yFc6VZHqubW7SSAy5qkGyzROSHQEim/6HM5meGgEKS5aHKrLzkMtw==",
|
||||||
|
"requires": {
|
||||||
|
"callsite": "^1.0.0",
|
||||||
|
"elliptic": "^6.5.2",
|
||||||
|
"fabric-protos": "2.2.2",
|
||||||
|
"js-sha3": "^0.7.0",
|
||||||
|
"jsrsasign": "^8.0.20",
|
||||||
|
"nconf": "^0.10.0",
|
||||||
|
"pkcs11js": "^1.0.6",
|
||||||
|
"promise-settle": "^0.3.0",
|
||||||
|
"sjcl": "1.0.7",
|
||||||
|
"winston": "^2.4.0",
|
||||||
|
"yn": "^3.1.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fabric-network": {
|
||||||
|
"version": "2.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fabric-network/-/fabric-network-2.2.2.tgz",
|
||||||
|
"integrity": "sha512-zPZ+yrSyHSVzDVS7f9EEMxN9nKOQ7aDg0RX76efevH7PQ7atDogijMnw2EdYnHBcw/t/ATMlMqaIxkKJNBoPjg==",
|
||||||
|
"requires": {
|
||||||
|
"fabric-common": "2.2.2",
|
||||||
|
"fabric-protos": "2.2.2",
|
||||||
|
"long": "^4.0.0",
|
||||||
|
"nano": "^8.2.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fabric-protos": {
|
||||||
|
"version": "2.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/fabric-protos/-/fabric-protos-2.2.2.tgz",
|
||||||
|
"integrity": "sha512-deXDIC4l9opKyu7zqZMXqiszxSOULKP66HmWZ435dgmQZyeo0rzFVO7dCwBZYdAVmz9pE4Ze8/qEU2GQ3suCJg==",
|
||||||
|
"requires": {
|
||||||
|
"@grpc/grpc-js": "1.0.3",
|
||||||
|
"@grpc/proto-loader": "0.5.4",
|
||||||
|
"protobufjs": "^6.9.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fast-deep-equal": {
|
||||||
|
"version": "3.1.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
|
||||||
|
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q=="
|
||||||
|
},
|
||||||
|
"fast-json-stable-stringify": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
|
||||||
|
"integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw=="
|
||||||
|
},
|
||||||
|
"forever-agent": {
|
||||||
|
"version": "0.6.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
|
||||||
|
"integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE="
|
||||||
|
},
|
||||||
|
"form-data": {
|
||||||
|
"version": "2.5.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz",
|
||||||
|
"integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==",
|
||||||
|
"requires": {
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"combined-stream": "^1.0.6",
|
||||||
|
"mime-types": "^2.1.12"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"getpass": {
|
||||||
|
"version": "0.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
|
||||||
|
"integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=",
|
||||||
|
"requires": {
|
||||||
|
"assert-plus": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"har-schema": {
|
||||||
|
"version": "2.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz",
|
||||||
|
"integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI="
|
||||||
|
},
|
||||||
|
"har-validator": {
|
||||||
|
"version": "5.1.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
|
||||||
|
"integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
|
||||||
|
"requires": {
|
||||||
|
"ajv": "^6.12.3",
|
||||||
|
"har-schema": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hash.js": {
|
||||||
|
"version": "1.1.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz",
|
||||||
|
"integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==",
|
||||||
|
"requires": {
|
||||||
|
"inherits": "^2.0.3",
|
||||||
|
"minimalistic-assert": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"hmac-drbg": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=",
|
||||||
|
"requires": {
|
||||||
|
"hash.js": "^1.0.3",
|
||||||
|
"minimalistic-assert": "^1.0.0",
|
||||||
|
"minimalistic-crypto-utils": "^1.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"http-signature": {
|
||||||
|
"version": "1.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
|
||||||
|
"integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=",
|
||||||
|
"requires": {
|
||||||
|
"assert-plus": "^1.0.0",
|
||||||
|
"jsprim": "^1.2.2",
|
||||||
|
"sshpk": "^1.7.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"inherits": {
|
||||||
|
"version": "2.0.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
|
||||||
|
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
|
||||||
|
},
|
||||||
|
"ini": {
|
||||||
|
"version": "1.3.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/ini/-/ini-1.3.5.tgz",
|
||||||
|
"integrity": "sha512-RZY5huIKCMRWDUqZlEi72f/lmXKMvuszcMBduliQ3nnWbx9X/ZBQO7DijMEYS9EhHBb2qacRUMtC7svLwe0lcw=="
|
||||||
|
},
|
||||||
|
"invert-kv": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY="
|
||||||
|
},
|
||||||
|
"is-fullwidth-code-point": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
|
||||||
|
"requires": {
|
||||||
|
"number-is-nan": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"is-typedarray": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo="
|
||||||
|
},
|
||||||
|
"isstream": {
|
||||||
|
"version": "0.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz",
|
||||||
|
"integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo="
|
||||||
|
},
|
||||||
|
"js-sha3": {
|
||||||
|
"version": "0.7.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.7.0.tgz",
|
||||||
|
"integrity": "sha512-Wpks3yBDm0UcL5qlVhwW9Jr9n9i4FfeWBFOOXP5puDS/SiudJGhw7DPyBqn3487qD4F0lsC0q3zxink37f7zeA=="
|
||||||
|
},
|
||||||
|
"jsbn": {
|
||||||
|
"version": "0.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz",
|
||||||
|
"integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM="
|
||||||
|
},
|
||||||
|
"json-schema": {
|
||||||
|
"version": "0.2.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz",
|
||||||
|
"integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM="
|
||||||
|
},
|
||||||
|
"json-schema-traverse": {
|
||||||
|
"version": "0.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
|
||||||
|
"integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg=="
|
||||||
|
},
|
||||||
|
"json-stringify-safe": {
|
||||||
|
"version": "5.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz",
|
||||||
|
"integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus="
|
||||||
|
},
|
||||||
|
"jsprim": {
|
||||||
|
"version": "1.4.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz",
|
||||||
|
"integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=",
|
||||||
|
"requires": {
|
||||||
|
"assert-plus": "1.0.0",
|
||||||
|
"extsprintf": "1.3.0",
|
||||||
|
"json-schema": "0.2.3",
|
||||||
|
"verror": "1.10.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"jsrsasign": {
|
||||||
|
"version": "8.0.24",
|
||||||
|
"resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-8.0.24.tgz",
|
||||||
|
"integrity": "sha512-u45jAyusqUpyGbFc2IbHoeE4rSkoBWQgLe/w99temHenX+GyCz4nflU5sjK7ajU1ffZTezl6le7u43Yjr/lkQg=="
|
||||||
|
},
|
||||||
|
"lcid": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU=",
|
||||||
|
"requires": {
|
||||||
|
"invert-kv": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"lodash.camelcase": {
|
||||||
|
"version": "4.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
|
||||||
|
"integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY="
|
||||||
|
},
|
||||||
|
"long": {
|
||||||
|
"version": "4.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz",
|
||||||
|
"integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA=="
|
||||||
|
},
|
||||||
|
"mime-db": {
|
||||||
|
"version": "1.44.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz",
|
||||||
|
"integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg=="
|
||||||
|
},
|
||||||
|
"mime-types": {
|
||||||
|
"version": "2.1.27",
|
||||||
|
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz",
|
||||||
|
"integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==",
|
||||||
|
"requires": {
|
||||||
|
"mime-db": "1.44.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"minimalistic-assert": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A=="
|
||||||
|
},
|
||||||
|
"minimalistic-crypto-utils": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo="
|
||||||
|
},
|
||||||
|
"ms": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
|
},
|
||||||
|
"nan": {
|
||||||
|
"version": "2.14.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz",
|
||||||
|
"integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==",
|
||||||
|
"optional": true
|
||||||
|
},
|
||||||
|
"nano": {
|
||||||
|
"version": "8.2.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/nano/-/nano-8.2.2.tgz",
|
||||||
|
"integrity": "sha512-1/rAvpd1J0Os0SazgutWQBx2buAq3KwJpmdIylPDqOwy73iQeAhTSCq3uzbGzvcNNW16Vv/BLXkk+DYcdcH+aw==",
|
||||||
|
"requires": {
|
||||||
|
"@types/request": "^2.48.4",
|
||||||
|
"cloudant-follow": "^0.18.2",
|
||||||
|
"debug": "^4.1.1",
|
||||||
|
"errs": "^0.3.2",
|
||||||
|
"request": "^2.88.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"nconf": {
|
||||||
|
"version": "0.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/nconf/-/nconf-0.10.0.tgz",
|
||||||
|
"integrity": "sha512-fKiXMQrpP7CYWJQzKkPPx9hPgmq+YLDyxcG9N8RpiE9FoCkCbzD0NyW0YhE3xn3Aupe7nnDeIx4PFzYehpHT9Q==",
|
||||||
|
"requires": {
|
||||||
|
"async": "^1.4.0",
|
||||||
|
"ini": "^1.3.0",
|
||||||
|
"secure-keys": "^1.0.0",
|
||||||
|
"yargs": "^3.19.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"number-is-nan": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
|
||||||
|
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
|
||||||
|
},
|
||||||
|
"oauth-sign": {
|
||||||
|
"version": "0.9.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz",
|
||||||
|
"integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ=="
|
||||||
|
},
|
||||||
|
"os-locale": {
|
||||||
|
"version": "1.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz",
|
||||||
|
"integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk=",
|
||||||
|
"requires": {
|
||||||
|
"lcid": "^1.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"performance-now": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz",
|
||||||
|
"integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns="
|
||||||
|
},
|
||||||
|
"pkcs11js": {
|
||||||
|
"version": "1.0.22",
|
||||||
|
"resolved": "https://registry.npmjs.org/pkcs11js/-/pkcs11js-1.0.22.tgz",
|
||||||
|
"integrity": "sha512-g0gOCCkKSa8YfoQPZFKk8VnbPvK+y3Gfx4O9NplzG6ntUX+1HSu491l8IUouxx45Jm3oEQXdDMtdTKH9t695aQ==",
|
||||||
|
"optional": true,
|
||||||
|
"requires": {
|
||||||
|
"nan": "^2.14.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"promise-settle": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/promise-settle/-/promise-settle-0.3.0.tgz",
|
||||||
|
"integrity": "sha1-tO/VcqHrdM95T4KM00naQKCOTpY="
|
||||||
|
},
|
||||||
|
"protobufjs": {
|
||||||
|
"version": "6.10.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.1.tgz",
|
||||||
|
"integrity": "sha512-pb8kTchL+1Ceg4lFd5XUpK8PdWacbvV5SK2ULH2ebrYtl4GjJmS24m6CKME67jzV53tbJxHlnNOSqQHbTsR9JQ==",
|
||||||
|
"requires": {
|
||||||
|
"@protobufjs/aspromise": "^1.1.2",
|
||||||
|
"@protobufjs/base64": "^1.1.2",
|
||||||
|
"@protobufjs/codegen": "^2.0.4",
|
||||||
|
"@protobufjs/eventemitter": "^1.1.0",
|
||||||
|
"@protobufjs/fetch": "^1.1.0",
|
||||||
|
"@protobufjs/float": "^1.0.2",
|
||||||
|
"@protobufjs/inquire": "^1.1.0",
|
||||||
|
"@protobufjs/path": "^1.1.2",
|
||||||
|
"@protobufjs/pool": "^1.1.0",
|
||||||
|
"@protobufjs/utf8": "^1.1.0",
|
||||||
|
"@types/long": "^4.0.1",
|
||||||
|
"@types/node": "^13.7.0",
|
||||||
|
"long": "^4.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"psl": {
|
||||||
|
"version": "1.8.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz",
|
||||||
|
"integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ=="
|
||||||
|
},
|
||||||
|
"punycode": {
|
||||||
|
"version": "1.3.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-1.3.2.tgz",
|
||||||
|
"integrity": "sha1-llOgNvt8HuQjQvIyXM7v6jkmxI0="
|
||||||
|
},
|
||||||
|
"qs": {
|
||||||
|
"version": "6.5.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz",
|
||||||
|
"integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA=="
|
||||||
|
},
|
||||||
|
"querystring": {
|
||||||
|
"version": "0.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/querystring/-/querystring-0.2.0.tgz",
|
||||||
|
"integrity": "sha1-sgmEkgO7Jd+CDadW50cAWHhSFiA="
|
||||||
|
},
|
||||||
|
"request": {
|
||||||
|
"version": "2.88.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz",
|
||||||
|
"integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==",
|
||||||
|
"requires": {
|
||||||
|
"aws-sign2": "~0.7.0",
|
||||||
|
"aws4": "^1.8.0",
|
||||||
|
"caseless": "~0.12.0",
|
||||||
|
"combined-stream": "~1.0.6",
|
||||||
|
"extend": "~3.0.2",
|
||||||
|
"forever-agent": "~0.6.1",
|
||||||
|
"form-data": "~2.3.2",
|
||||||
|
"har-validator": "~5.1.3",
|
||||||
|
"http-signature": "~1.2.0",
|
||||||
|
"is-typedarray": "~1.0.0",
|
||||||
|
"isstream": "~0.1.2",
|
||||||
|
"json-stringify-safe": "~5.0.1",
|
||||||
|
"mime-types": "~2.1.19",
|
||||||
|
"oauth-sign": "~0.9.0",
|
||||||
|
"performance-now": "^2.1.0",
|
||||||
|
"qs": "~6.5.2",
|
||||||
|
"safe-buffer": "^5.1.2",
|
||||||
|
"tough-cookie": "~2.5.0",
|
||||||
|
"tunnel-agent": "^0.6.0",
|
||||||
|
"uuid": "^3.3.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"form-data": {
|
||||||
|
"version": "2.3.3",
|
||||||
|
"resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
|
||||||
|
"integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
|
||||||
|
"requires": {
|
||||||
|
"asynckit": "^0.4.0",
|
||||||
|
"combined-stream": "^1.0.6",
|
||||||
|
"mime-types": "^2.1.12"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"safe-buffer": {
|
||||||
|
"version": "5.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
|
||||||
|
"integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="
|
||||||
|
},
|
||||||
|
"safer-buffer": {
|
||||||
|
"version": "2.1.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
|
||||||
|
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
|
||||||
|
},
|
||||||
|
"secure-keys": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/secure-keys/-/secure-keys-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-8MgtmKOxOah3aogIBQuCRDEIf8o="
|
||||||
|
},
|
||||||
|
"semver": {
|
||||||
|
"version": "6.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz",
|
||||||
|
"integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw=="
|
||||||
|
},
|
||||||
|
"sjcl": {
|
||||||
|
"version": "1.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/sjcl/-/sjcl-1.0.7.tgz",
|
||||||
|
"integrity": "sha1-MrNlpQ3Ju6JriLo8nfjqNCF9n0U="
|
||||||
|
},
|
||||||
|
"sshpk": {
|
||||||
|
"version": "1.16.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz",
|
||||||
|
"integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==",
|
||||||
|
"requires": {
|
||||||
|
"asn1": "~0.2.3",
|
||||||
|
"assert-plus": "^1.0.0",
|
||||||
|
"bcrypt-pbkdf": "^1.0.0",
|
||||||
|
"dashdash": "^1.12.0",
|
||||||
|
"ecc-jsbn": "~0.1.1",
|
||||||
|
"getpass": "^0.1.1",
|
||||||
|
"jsbn": "~0.1.0",
|
||||||
|
"safer-buffer": "^2.0.2",
|
||||||
|
"tweetnacl": "~0.14.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"stack-trace": {
|
||||||
|
"version": "0.0.10",
|
||||||
|
"resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz",
|
||||||
|
"integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA="
|
||||||
|
},
|
||||||
|
"string-width": {
|
||||||
|
"version": "1.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
|
||||||
|
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
|
||||||
|
"requires": {
|
||||||
|
"code-point-at": "^1.0.0",
|
||||||
|
"is-fullwidth-code-point": "^1.0.0",
|
||||||
|
"strip-ansi": "^3.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"strip-ansi": {
|
||||||
|
"version": "3.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
|
||||||
|
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
|
||||||
|
"requires": {
|
||||||
|
"ansi-regex": "^2.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tough-cookie": {
|
||||||
|
"version": "2.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
|
||||||
|
"integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==",
|
||||||
|
"requires": {
|
||||||
|
"psl": "^1.1.28",
|
||||||
|
"punycode": "^2.1.1"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"punycode": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tunnel-agent": {
|
||||||
|
"version": "0.6.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz",
|
||||||
|
"integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=",
|
||||||
|
"requires": {
|
||||||
|
"safe-buffer": "^5.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tweetnacl": {
|
||||||
|
"version": "0.14.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz",
|
||||||
|
"integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q="
|
||||||
|
},
|
||||||
|
"uri-js": {
|
||||||
|
"version": "4.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz",
|
||||||
|
"integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==",
|
||||||
|
"requires": {
|
||||||
|
"punycode": "^2.1.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"punycode": {
|
||||||
|
"version": "2.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz",
|
||||||
|
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"url": {
|
||||||
|
"version": "0.11.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/url/-/url-0.11.0.tgz",
|
||||||
|
"integrity": "sha1-ODjpfPxgUh63PFJajlW/3Z4uKPE=",
|
||||||
|
"requires": {
|
||||||
|
"punycode": "1.3.2",
|
||||||
|
"querystring": "0.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"uuid": {
|
||||||
|
"version": "3.4.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz",
|
||||||
|
"integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A=="
|
||||||
|
},
|
||||||
|
"verror": {
|
||||||
|
"version": "1.10.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz",
|
||||||
|
"integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=",
|
||||||
|
"requires": {
|
||||||
|
"assert-plus": "^1.0.0",
|
||||||
|
"core-util-is": "1.0.2",
|
||||||
|
"extsprintf": "^1.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"window-size": {
|
||||||
|
"version": "0.1.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/window-size/-/window-size-0.1.4.tgz",
|
||||||
|
"integrity": "sha1-+OGqHuWlPsW/FR/6CXQqatdpeHY="
|
||||||
|
},
|
||||||
|
"winston": {
|
||||||
|
"version": "2.4.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/winston/-/winston-2.4.5.tgz",
|
||||||
|
"integrity": "sha512-TWoamHt5yYvsMarGlGEQE59SbJHqGsZV8/lwC+iCcGeAe0vUaOh+Lv6SYM17ouzC/a/LB1/hz/7sxFBtlu1l4A==",
|
||||||
|
"requires": {
|
||||||
|
"async": "~1.0.0",
|
||||||
|
"colors": "1.0.x",
|
||||||
|
"cycle": "1.0.x",
|
||||||
|
"eyes": "0.1.x",
|
||||||
|
"isstream": "0.1.x",
|
||||||
|
"stack-trace": "0.0.x"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"async": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/async/-/async-1.0.0.tgz",
|
||||||
|
"integrity": "sha1-+PwEyjoTeErenhZBr5hXjPvWR6k="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"wrap-ansi": {
|
||||||
|
"version": "2.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz",
|
||||||
|
"integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU=",
|
||||||
|
"requires": {
|
||||||
|
"string-width": "^1.0.1",
|
||||||
|
"strip-ansi": "^3.0.1"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"y18n": {
|
||||||
|
"version": "3.2.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.1.tgz",
|
||||||
|
"integrity": "sha1-bRX7qITAhnnA136I53WegR4H+kE="
|
||||||
|
},
|
||||||
|
"yargs": {
|
||||||
|
"version": "3.32.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/yargs/-/yargs-3.32.0.tgz",
|
||||||
|
"integrity": "sha1-AwiOnr+edWtpdRYR0qXvWRSCyZU=",
|
||||||
|
"requires": {
|
||||||
|
"camelcase": "^2.0.1",
|
||||||
|
"cliui": "^3.0.3",
|
||||||
|
"decamelize": "^1.1.1",
|
||||||
|
"os-locale": "^1.4.0",
|
||||||
|
"string-width": "^1.0.1",
|
||||||
|
"window-size": "^0.1.4",
|
||||||
|
"y18n": "^3.2.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"yn": {
|
||||||
|
"version": "3.1.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz",
|
||||||
|
"integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
auction/application-javascript/package.json
Normal file
16
auction/application-javascript/package.json
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
{
|
||||||
|
"name": "auction",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "auction application implemented in JavaScript",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=12",
|
||||||
|
"npm": ">=5"
|
||||||
|
},
|
||||||
|
"engineStrict": true,
|
||||||
|
"author": "Hyperledger",
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"fabric-ca-client": "^2.2.0",
|
||||||
|
"fabric-network": "^2.2.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
86
auction/application-javascript/queryAuction.js
Normal file
86
auction/application-javascript/queryAuction.js
Normal file
|
|
@ -0,0 +1,86 @@
|
||||||
|
/*
|
||||||
|
* Copyright IBM Corp. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Gateway, Wallets } = require('fabric-network');
|
||||||
|
const path = require('path');
|
||||||
|
const { buildCCPOrg1, buildCCPOrg2, buildWallet } = require('../../test-application/javascript/AppUtil.js');
|
||||||
|
|
||||||
|
const myChannel = 'mychannel';
|
||||||
|
const myChaincodeName = 'auction';
|
||||||
|
|
||||||
|
|
||||||
|
function prettyJSONString(inputString) {
|
||||||
|
if (inputString) {
|
||||||
|
return JSON.stringify(JSON.parse(inputString), null, 2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return inputString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function queryAuction(ccp,wallet,user,auctionID) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
const gateway = new Gateway();
|
||||||
|
//connect using Discovery enabled
|
||||||
|
|
||||||
|
await gateway.connect(ccp,
|
||||||
|
{ wallet: wallet, identity: user, discovery: { enabled: true, asLocalhost: true } });
|
||||||
|
|
||||||
|
const network = await gateway.getNetwork(myChannel);
|
||||||
|
const contract = network.getContract(myChaincodeName);
|
||||||
|
|
||||||
|
console.log('\n--> Evaluate Transaction: query the auction that was just created');
|
||||||
|
let result = await contract.evaluateTransaction('QueryAuction',auctionID);
|
||||||
|
console.log('*** Result: Auction: ' + prettyJSONString(result.toString()));
|
||||||
|
|
||||||
|
gateway.disconnect();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`******** FAILED to submit bid: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
if (process.argv[2] == undefined || process.argv[3] == undefined
|
||||||
|
|| process.argv[4] == undefined) {
|
||||||
|
console.log("Usage: node queryAuction.js org userID auctionID");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const org = process.argv[2]
|
||||||
|
const user = process.argv[3];
|
||||||
|
const auctionID = process.argv[4];
|
||||||
|
|
||||||
|
if (org == 'Org1' || org == 'org1') {
|
||||||
|
|
||||||
|
const orgMSP = 'Org1MSP';
|
||||||
|
const ccp = buildCCPOrg1();
|
||||||
|
const walletPath = path.join(__dirname, 'wallet/org1');
|
||||||
|
const wallet = await buildWallet(Wallets, walletPath);
|
||||||
|
await queryAuction(ccp,wallet,user,auctionID);
|
||||||
|
}
|
||||||
|
else if (org == 'Org2' || org == 'org2') {
|
||||||
|
|
||||||
|
const orgMSP = 'Org2MSP';
|
||||||
|
const ccp = buildCCPOrg2();
|
||||||
|
const walletPath = path.join(__dirname, 'wallet/org2');
|
||||||
|
const wallet = await buildWallet(Wallets, walletPath);
|
||||||
|
await queryAuction(ccp,wallet,user,auctionID);
|
||||||
|
} else {
|
||||||
|
console.log("Usage: node queryAuction.js org userID auctionID");
|
||||||
|
console.log("Org must be Org1 or Org2");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`******** FAILED to run the application: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
main();
|
||||||
87
auction/application-javascript/queryBid.js
Normal file
87
auction/application-javascript/queryBid.js
Normal file
|
|
@ -0,0 +1,87 @@
|
||||||
|
/*
|
||||||
|
* Copyright IBM Corp. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Gateway, Wallets } = require('fabric-network');
|
||||||
|
const path = require('path');
|
||||||
|
const { buildCCPOrg1, buildCCPOrg2, buildWallet } = require('../../test-application/javascript/AppUtil.js');
|
||||||
|
|
||||||
|
const myChannel = 'mychannel';
|
||||||
|
const myChaincodeName = 'auction';
|
||||||
|
|
||||||
|
|
||||||
|
function prettyJSONString(inputString) {
|
||||||
|
if (inputString) {
|
||||||
|
return JSON.stringify(JSON.parse(inputString), null, 2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return inputString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function queryBid(ccp,wallet,user,auctionID,bidID) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
const gateway = new Gateway();
|
||||||
|
//connect using Discovery enabled
|
||||||
|
|
||||||
|
await gateway.connect(ccp,
|
||||||
|
{ wallet: wallet, identity: user, discovery: { enabled: true, asLocalhost: true } });
|
||||||
|
|
||||||
|
const network = await gateway.getNetwork(myChannel);
|
||||||
|
const contract = network.getContract(myChaincodeName);
|
||||||
|
|
||||||
|
console.log('\n--> Evaluate Transaction: read bid from private data store');
|
||||||
|
let result = await contract.evaluateTransaction('QueryBid',auctionID,bidID);
|
||||||
|
console.log('*** Result: Bid: ' + prettyJSONString(result.toString()));
|
||||||
|
|
||||||
|
gateway.disconnect();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`******** FAILED to submit bid: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
if (process.argv[2] == undefined || process.argv[3] == undefined
|
||||||
|
|| process.argv[4] == undefined || process.argv[5] == undefined) {
|
||||||
|
console.log("Usage: node bid.js org userID auctionID bidID");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const org = process.argv[2]
|
||||||
|
const user = process.argv[3];
|
||||||
|
const auctionID = process.argv[4];
|
||||||
|
const bidID = process.argv[5];
|
||||||
|
|
||||||
|
if (org == 'Org1' || org == 'org1') {
|
||||||
|
|
||||||
|
const orgMSP = 'Org1MSP';
|
||||||
|
const ccp = buildCCPOrg1();
|
||||||
|
const walletPath = path.join(__dirname, 'wallet/org1');
|
||||||
|
const wallet = await buildWallet(Wallets, walletPath);
|
||||||
|
await queryBid(ccp,wallet,user,auctionID,bidID);
|
||||||
|
}
|
||||||
|
else if (org == 'Org2' || org == 'org2') {
|
||||||
|
|
||||||
|
const orgMSP = 'Org2MSP';
|
||||||
|
const ccp = buildCCPOrg2();
|
||||||
|
const walletPath = path.join(__dirname, 'wallet/org2');
|
||||||
|
const wallet = await buildWallet(Wallets, walletPath);
|
||||||
|
await queryBid(ccp,wallet,user,auctionID,bidID);
|
||||||
|
} else {
|
||||||
|
console.log("Usage: node bid.js org userID auctionID bidID");
|
||||||
|
console.log("Org must be Org1 or Org2");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`******** FAILED to run the application: ${error}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
main();
|
||||||
77
auction/application-javascript/registerEnrollUser.js
Normal file
77
auction/application-javascript/registerEnrollUser.js
Normal file
|
|
@ -0,0 +1,77 @@
|
||||||
|
/*
|
||||||
|
* Copyright IBM Corp. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Wallets } = require('fabric-network');
|
||||||
|
const FabricCAServices = require('fabric-ca-client');
|
||||||
|
const path = require('path');
|
||||||
|
const { buildCAClient, registerAndEnrollUser } = require('../../test-application/javascript/CAUtil.js');
|
||||||
|
const { buildCCPOrg1, buildCCPOrg2, buildWallet } = require('../../test-application/javascript/AppUtil.js');
|
||||||
|
|
||||||
|
const mspOrg1 = 'Org1MSP';
|
||||||
|
const mspOrg2 = 'Org2MSP';
|
||||||
|
|
||||||
|
function prettyJSONString(inputString) {
|
||||||
|
if (inputString) {
|
||||||
|
return JSON.stringify(JSON.parse(inputString), null, 2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return inputString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function connectToOrg1CA(UserID) {
|
||||||
|
console.log('\n--> Register and enrolling new user');
|
||||||
|
const ccpOrg1 = buildCCPOrg1();
|
||||||
|
const caOrg1Client = buildCAClient(FabricCAServices, ccpOrg1, 'ca.org1.example.com');
|
||||||
|
|
||||||
|
const walletPathOrg1 = path.join(__dirname, 'wallet/org1');
|
||||||
|
const walletOrg1 = await buildWallet(Wallets, walletPathOrg1);
|
||||||
|
|
||||||
|
await registerAndEnrollUser(caOrg1Client, walletOrg1, mspOrg1, UserID, 'org1.department1');
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
async function connectToOrg2CA(UserID) {
|
||||||
|
console.log('\n--> Register and enrolling new user');
|
||||||
|
const ccpOrg2 = buildCCPOrg2();
|
||||||
|
const caOrg2Client = buildCAClient(FabricCAServices, ccpOrg2, 'ca.org2.example.com');
|
||||||
|
|
||||||
|
const walletPathOrg2 = path.join(__dirname, 'wallet/org2');
|
||||||
|
const walletOrg2 = await buildWallet(Wallets, walletPathOrg2);
|
||||||
|
|
||||||
|
await registerAndEnrollUser(caOrg2Client, walletOrg2, mspOrg2, UserID, 'org2.department1');
|
||||||
|
|
||||||
|
}
|
||||||
|
async function main() {
|
||||||
|
|
||||||
|
if (process.argv[2] == undefined && process.argv[3] == undefined) {
|
||||||
|
console.log("Usage: node registerEnrollUser.js org userID");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const org = process.argv[2];
|
||||||
|
const userId = process.argv[3];
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
if (org == 'Org1' || org == 'org1') {
|
||||||
|
await connectToOrg1CA(userId);
|
||||||
|
}
|
||||||
|
else if (org == 'Org2' || org == 'org2') {
|
||||||
|
await connectToOrg2CA(userId);
|
||||||
|
} else {
|
||||||
|
console.log("Usage: node registerEnrollUser.js org userID");
|
||||||
|
console.log("Org must be Org1 or Org2");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`Error in enrolling admin: ${error}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
main();
|
||||||
119
auction/application-javascript/revealBid.js
Normal file
119
auction/application-javascript/revealBid.js
Normal file
|
|
@ -0,0 +1,119 @@
|
||||||
|
/*
|
||||||
|
* Copyright IBM Corp. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
const { Gateway, Wallets } = require('fabric-network');
|
||||||
|
const path = require('path');
|
||||||
|
const { buildCCPOrg1, buildCCPOrg2, buildWallet } = require('../../test-application/javascript/AppUtil.js');
|
||||||
|
|
||||||
|
const myChannel = 'mychannel';
|
||||||
|
const myChaincodeName = 'auction';
|
||||||
|
|
||||||
|
|
||||||
|
function prettyJSONString(inputString) {
|
||||||
|
if (inputString) {
|
||||||
|
return JSON.stringify(JSON.parse(inputString), null, 2);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return inputString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function addBid(ccp,wallet,user,auctionID,bidID) {
|
||||||
|
try {
|
||||||
|
|
||||||
|
const gateway = new Gateway();
|
||||||
|
//connect using Discovery enabled
|
||||||
|
|
||||||
|
await gateway.connect(ccp,
|
||||||
|
{ wallet: wallet, identity: user, discovery: { enabled: true, asLocalhost: true } });
|
||||||
|
|
||||||
|
const network = await gateway.getNetwork(myChannel);
|
||||||
|
const contract = network.getContract(myChaincodeName);
|
||||||
|
|
||||||
|
console.log('\n--> Evaluate Transaction: read your bid');
|
||||||
|
let bidString = await contract.evaluateTransaction('QueryBid',auctionID,bidID);
|
||||||
|
var bidJSON = JSON.parse(bidString);
|
||||||
|
|
||||||
|
//console.log('\n--> Evaluate Transaction: query the auction you want to join');
|
||||||
|
let auctionString = await contract.evaluateTransaction('QueryAuction',auctionID);
|
||||||
|
// console.log('*** Result: Bid: ' + prettyJSONString(auctionString.toString()));
|
||||||
|
var auctionJSON = JSON.parse(auctionString);
|
||||||
|
|
||||||
|
let bidData = { objectType: 'bid', quantity: parseInt(bidJSON.quantity) , price: parseInt(bidJSON.price), org: bidJSON.org, buyer: bidJSON.buyer};
|
||||||
|
console.log('*** Result: Bid: ' + JSON.stringify(bidData,null,2));
|
||||||
|
|
||||||
|
let statefulTxn = contract.createTransaction('RevealBid');
|
||||||
|
let tmapData = Buffer.from(JSON.stringify(bidData));
|
||||||
|
statefulTxn.setTransient({
|
||||||
|
bid: tmapData
|
||||||
|
});
|
||||||
|
|
||||||
|
if (auctionJSON.bidingOrgs.length == 2) {
|
||||||
|
statefulTxn.setEndorsingOrganizations(auctionJSON.bidingOrgs[0],auctionJSON.bidingOrgs[1]);
|
||||||
|
} else {
|
||||||
|
statefulTxn.setEndorsingOrganizations(auctionJSON.bidingOrgs[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
await statefulTxn.submit(auctionID,bidID);
|
||||||
|
|
||||||
|
console.log('\n--> Evaluate Transaction: query the auction to see that our bid was added');
|
||||||
|
let result = await contract.evaluateTransaction('QueryAuction',auctionID);
|
||||||
|
console.log('*** Result: Auction: ' + prettyJSONString(result.toString()));
|
||||||
|
|
||||||
|
gateway.disconnect();
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`******** FAILED to submit bid: ${error}`);
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main() {
|
||||||
|
try {
|
||||||
|
|
||||||
|
if (process.argv[2] == undefined || process.argv[3] == undefined
|
||||||
|
|| process.argv[4] == undefined || process.argv[5] == undefined) {
|
||||||
|
console.log("Usage: node revealBid.js org userID auctionID bidID");
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
const org = process.argv[2]
|
||||||
|
const user = process.argv[3];
|
||||||
|
const auctionID = process.argv[4];
|
||||||
|
const bidID = process.argv[5];
|
||||||
|
|
||||||
|
if (org == 'Org1' || org == 'org1') {
|
||||||
|
|
||||||
|
const orgMSP = 'Org1MSP';
|
||||||
|
const ccp = buildCCPOrg1();
|
||||||
|
const walletPath = path.join(__dirname, 'wallet/org1');
|
||||||
|
const wallet = await buildWallet(Wallets, walletPath);
|
||||||
|
await addBid(ccp,wallet,user,auctionID,bidID);
|
||||||
|
}
|
||||||
|
else if (org == 'Org2' || org == 'org2') {
|
||||||
|
|
||||||
|
const orgMSP = 'Org2MSP';
|
||||||
|
const ccp = buildCCPOrg2();
|
||||||
|
const walletPath = path.join(__dirname, 'wallet/org2');
|
||||||
|
const wallet = await buildWallet(Wallets, walletPath);
|
||||||
|
await addBid(ccp,wallet,user,auctionID,bidID);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log("Usage: node revealBid.js org userID auctionID bidID");
|
||||||
|
console.log("Org must be Org1 or Org2");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error(`******** FAILED to run the application: ${error}`);
|
||||||
|
if (error.stack) {
|
||||||
|
console.error(error.stack);
|
||||||
|
}
|
||||||
|
process.exit(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
main();
|
||||||
8
auction/chaincode-go/go.mod
Normal file
8
auction/chaincode-go/go.mod
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
module github.com/hyperledger/fabric-samples/auction/chaincode-go
|
||||||
|
|
||||||
|
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
|
||||||
|
)
|
||||||
139
auction/chaincode-go/go.sum
Normal file
139
auction/chaincode-go/go.sum
Normal file
|
|
@ -0,0 +1,139 @@
|
||||||
|
cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw=
|
||||||
|
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||||
|
github.com/DATA-DOG/go-txdb v0.1.3/go.mod h1:DhAhxMXZpUJVGnT+p9IbzJoRKvlArO2pkHjnGX7o0n0=
|
||||||
|
github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI=
|
||||||
|
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
|
||||||
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M=
|
||||||
|
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
|
||||||
|
github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8=
|
||||||
|
github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw=
|
||||||
|
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=
|
||||||
|
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/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=
|
||||||
|
github.com/go-openapi/jsonpointer v0.19.3 h1:gihV7YNZK1iK6Tgwwsxo2rJbD1GTbdm72325Bq8FI3w=
|
||||||
|
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/spec v0.19.4 h1:ixzUSnHTd6hCemgtAJgluaTSGYpLNpJY4mA2DIkdOAo=
|
||||||
|
github.com/go-openapi/spec v0.19.4/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo=
|
||||||
|
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-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/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/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/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/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
|
||||||
|
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/go.mod h1:N7H3sA7Tx4k/YzFq7U0EPdqJtqvM4Kild0JoCc7C0Dc=
|
||||||
|
github.com/hyperledger/fabric-chaincode-go v0.0.0-20200728190242-9b3ae92d8664 h1:Pu/9SNpo71SJj5DGehCXOKD9QGQ3MsuWjpsLM9Mkdwg=
|
||||||
|
github.com/hyperledger/fabric-chaincode-go v0.0.0-20200728190242-9b3ae92d8664/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/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||||
|
github.com/joho/godotenv v1.3.0 h1:Zjp+RcGpHhGlrMbJzXTrZZPrWj+1vfm90La1wgB6Bhc=
|
||||||
|
github.com/joho/godotenv v1.3.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
|
||||||
|
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/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
|
||||||
|
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/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=
|
||||||
|
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/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/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=
|
||||||
|
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
|
||||||
|
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=
|
||||||
|
github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE=
|
||||||
|
github.com/spf13/cobra v0.0.5/go.mod h1:3K3wKZymM7VvHMDS9+Akkh4K60UwM26emMESw8tLCHU=
|
||||||
|
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/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s=
|
||||||
|
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/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/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
|
||||||
|
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=
|
||||||
|
github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/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=
|
||||||
|
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/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/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=
|
||||||
|
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-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=
|
||||||
|
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||||
|
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=
|
||||||
|
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=
|
||||||
|
google.golang.org/genproto v0.0.0-20180831171423-11092d34479b/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||||
|
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/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.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||||
|
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||||
|
honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||||
533
auction/chaincode-go/smart-contract/auction.go
Normal file
533
auction/chaincode-go/smart-contract/auction.go
Normal file
|
|
@ -0,0 +1,533 @@
|
||||||
|
/*
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package auction
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"crypto/sha256"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
|
||||||
|
"github.com/hyperledger/fabric-contract-api-go/contractapi"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SmartContract struct {
|
||||||
|
contractapi.Contract
|
||||||
|
}
|
||||||
|
|
||||||
|
// Auction data
|
||||||
|
type Auction struct {
|
||||||
|
Type string `json:"objectType"`
|
||||||
|
ItemSold string `json:"ID"`
|
||||||
|
Seller string `json:"seller"`
|
||||||
|
Quantity int `json:"quantity"`
|
||||||
|
BiddingOrgs []string `json:"bidingOrgs"`
|
||||||
|
PrivateBids map[string]BidHash `json:"privateBids"`
|
||||||
|
RevealedBids map[string]FullBid `json:"revealedBids"`
|
||||||
|
Winners []Winners `json:"winners"`
|
||||||
|
Price int `json:"price"`
|
||||||
|
Status string `json:"status"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// FullBid is the structure of a revealed bid
|
||||||
|
type FullBid struct {
|
||||||
|
Type string `json:"objectType"`
|
||||||
|
Quantity int `json:"quantity"`
|
||||||
|
Price int `json:"price"`
|
||||||
|
Org string `json:"org"`
|
||||||
|
Buyer string `json:"buyer"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// BidHash is the structure of a private bid
|
||||||
|
type BidHash struct {
|
||||||
|
Org string `json:"org"`
|
||||||
|
Hash string `json:"hash"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Winners stores the winners of the auction
|
||||||
|
type Winners struct {
|
||||||
|
Buyer string `json:"buyer"`
|
||||||
|
Quantity int `json:"quantity"`
|
||||||
|
}
|
||||||
|
|
||||||
|
const bidKeyType = "bid"
|
||||||
|
|
||||||
|
// CreateAuction creates on auction on the public channel. The identity that
|
||||||
|
// submits the transacion becomes the seller of the auction
|
||||||
|
func (s *SmartContract) CreateAuction(ctx contractapi.TransactionContextInterface, auctionID string, itemsold string, quantity int) error {
|
||||||
|
|
||||||
|
// get ID of submitting client
|
||||||
|
clientID, err := ctx.GetClientIdentity().GetID()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get client identity %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get org of submitting client
|
||||||
|
clientOrgID, err := ctx.GetClientIdentity().GetMSPID()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get client identity %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create auction
|
||||||
|
bidders := make(map[string]BidHash)
|
||||||
|
revealedBids := make(map[string]FullBid)
|
||||||
|
|
||||||
|
auction := Auction{
|
||||||
|
Type: "auction",
|
||||||
|
ItemSold: itemsold,
|
||||||
|
Quantity: quantity,
|
||||||
|
Price: 0,
|
||||||
|
Seller: clientID,
|
||||||
|
BiddingOrgs: []string{clientOrgID},
|
||||||
|
PrivateBids: bidders,
|
||||||
|
RevealedBids: revealedBids,
|
||||||
|
Winners: []Winners{},
|
||||||
|
Status: "open",
|
||||||
|
}
|
||||||
|
|
||||||
|
auctionBytes, err := json.Marshal(auction)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// put auction into state
|
||||||
|
err = ctx.GetStub().PutState(auctionID, auctionBytes)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to put auction in public data: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the seller of the auction as an endorser
|
||||||
|
err = setAssetStateBasedEndorsement(ctx, auctionID, clientOrgID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed setting state based endorsement for new organization: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Bid is used to add a users bid to the auction. The bid is stored in the private
|
||||||
|
// data collection on the peer of the bidders organization. A hash of the bid is stored
|
||||||
|
// in the auction private state. The function returns the transaction ID so that
|
||||||
|
// users can identify and query their bid
|
||||||
|
func (s *SmartContract) Bid(ctx contractapi.TransactionContextInterface, auctionID string) (string, error) {
|
||||||
|
|
||||||
|
// get bid from transient map
|
||||||
|
transientMap, err := ctx.GetStub().GetTransient()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("error getting transient: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
BidJSON, ok := transientMap["bid"]
|
||||||
|
if !ok {
|
||||||
|
return "", fmt.Errorf("bid key not found in the transient map")
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the implicit collection name using the bidders organization ID
|
||||||
|
collection, err := getCollectionName(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to get implicit collection name: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// the bidder has to target their peer to store the bid
|
||||||
|
err = verifyClientOrgMatchesPeerOrg(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("Cannot store bid on this peer, not a member of this org: Error %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// the transaction ID is used as a unique index for the bid
|
||||||
|
txID := ctx.GetStub().GetTxID()
|
||||||
|
|
||||||
|
// create a composite key using the transacition ID
|
||||||
|
bidKey, err := ctx.GetStub().CreateCompositeKey(bidKeyType, []string{auctionID, txID})
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to create composite key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// put the bid into the organizations implicit data collection
|
||||||
|
err = ctx.GetStub().PutPrivateData(collection, bidKey, BidJSON)
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to input price into collection: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// return the trannsaction ID so that the uset can identify their bid
|
||||||
|
return txID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// AddBid is used by the bidder to add the hash of that bid stored in private data to the
|
||||||
|
// auction. Note that this function alters the auction in private state, and needs
|
||||||
|
// to meet the auction endorsement policy. Transaction ID is used identify the bid
|
||||||
|
func (s *SmartContract) AddBid(ctx contractapi.TransactionContextInterface, auctionID string, txID string) error {
|
||||||
|
|
||||||
|
// get the MSP ID of the bidders org
|
||||||
|
clientOrgID, err := ctx.GetClientIdentity().GetMSPID()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get client MSP ID: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the auction from state
|
||||||
|
auctionBytes, err := ctx.GetStub().GetState(auctionID)
|
||||||
|
var auctionJSON Auction
|
||||||
|
|
||||||
|
if auctionBytes == nil {
|
||||||
|
return fmt.Errorf("Auction not found: %v", auctionID)
|
||||||
|
}
|
||||||
|
err = json.Unmarshal(auctionBytes, &auctionJSON)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create auction object JSON: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// the auction needs to be open for users to add their bid
|
||||||
|
Status := auctionJSON.Status
|
||||||
|
if Status != "open" {
|
||||||
|
return fmt.Errorf("cannot join closed or ended auction")
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the inplicit collection name of bidders org
|
||||||
|
collection, err := getCollectionName(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get implicit collection name: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// use the transaction ID passed as a parameter to create composite bid key
|
||||||
|
bidKey, err := ctx.GetStub().CreateCompositeKey(bidKeyType, []string{auctionID, txID})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create composite key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the hash of the bid stored in private data stored on the public ledger
|
||||||
|
bidHash, err := ctx.GetStub().GetPrivateDataHash(collection, bidKey)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read bid bash from collection: %v", err)
|
||||||
|
}
|
||||||
|
if bidHash == nil {
|
||||||
|
return fmt.Errorf("bid hash does not exist: %s", bidKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// store the hash along with the bidders organization
|
||||||
|
NewHash := BidHash{
|
||||||
|
Org: clientOrgID,
|
||||||
|
Hash: fmt.Sprintf("%x", bidHash),
|
||||||
|
}
|
||||||
|
|
||||||
|
bidders := make(map[string]BidHash)
|
||||||
|
bidders = auctionJSON.PrivateBids
|
||||||
|
bidders[bidKey] = NewHash
|
||||||
|
auctionJSON.PrivateBids = bidders
|
||||||
|
|
||||||
|
// Add the bid to the list of bidding organizations if it is not already
|
||||||
|
Orgs := auctionJSON.BiddingOrgs
|
||||||
|
if !(contains(Orgs, clientOrgID)) {
|
||||||
|
newOrgs := append(Orgs, clientOrgID)
|
||||||
|
auctionJSON.BiddingOrgs = newOrgs
|
||||||
|
|
||||||
|
err = addAssetStateBasedEndorsement(ctx, auctionID, clientOrgID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed setting state based endorsement for new organization: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
newAuctionBytes, _ := json.Marshal(auctionJSON)
|
||||||
|
|
||||||
|
err = ctx.GetStub().PutState(auctionID, newAuctionBytes)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to update auction: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// RevealBid is used by a bidder to reveal their bid after the auction is closed
|
||||||
|
func (s *SmartContract) RevealBid(ctx contractapi.TransactionContextInterface, auctionID string, txID string) error {
|
||||||
|
|
||||||
|
// get bid from transient map
|
||||||
|
transientMap, err := ctx.GetStub().GetTransient()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("error getting transient: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
transientBidJSON, ok := transientMap["bid"]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("bid key not found in the transient map")
|
||||||
|
}
|
||||||
|
|
||||||
|
// get implicit collection name of organization ID
|
||||||
|
collection, err := getCollectionName(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get implicit collection name: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// use transaction ID to create composit bid key
|
||||||
|
bidKey, err := ctx.GetStub().CreateCompositeKey(bidKeyType, []string{auctionID, txID})
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create composite key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get bid hash of bid if private bid on the public ledger
|
||||||
|
bidHash, err := ctx.GetStub().GetPrivateDataHash(collection, bidKey)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read bid bash from collection: %v", err)
|
||||||
|
}
|
||||||
|
if bidHash == nil {
|
||||||
|
return fmt.Errorf("bid hash does not exist: %s", bidKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
// get auction from public state
|
||||||
|
auctionBytes, err := ctx.GetStub().GetState(auctionID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get auction %v: %v", auctionID, err)
|
||||||
|
}
|
||||||
|
if auctionBytes == nil {
|
||||||
|
return fmt.Errorf("Auction interest object %v not found", auctionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
var auctionJSON Auction
|
||||||
|
err = json.Unmarshal(auctionBytes, &auctionJSON)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create interst object JSON: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Complete a series of three checks before we add the bid to the auction
|
||||||
|
|
||||||
|
// check 1: check that the auction is closed. We cannot reveal an
|
||||||
|
// bid to an open auction
|
||||||
|
Status := auctionJSON.Status
|
||||||
|
if Status != "closed" {
|
||||||
|
return fmt.Errorf("cannot reveal bid for open or ended auction")
|
||||||
|
}
|
||||||
|
|
||||||
|
// check 2: check that hash of revealed bid matches hash of private bid
|
||||||
|
// on the public ledger. This checks that the bidder is telling the truth
|
||||||
|
// about the value of their bid
|
||||||
|
|
||||||
|
hash := sha256.New()
|
||||||
|
hash.Write(transientBidJSON)
|
||||||
|
calculatedBidJSONHash := hash.Sum(nil)
|
||||||
|
|
||||||
|
// verify that the hash of the passed immutable properties matches the on-chain hash
|
||||||
|
if !bytes.Equal(calculatedBidJSONHash, bidHash) {
|
||||||
|
return fmt.Errorf("hash %x for bid JSON %s does not match hash in auction: %x",
|
||||||
|
calculatedBidJSONHash,
|
||||||
|
transientBidJSON,
|
||||||
|
bidHash,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// check 3; check hash of relealed bid matches hash of private bid that was
|
||||||
|
// added to the earlier bid. This ensures that the bid has not changed since it
|
||||||
|
// was added ot the auction
|
||||||
|
|
||||||
|
bidders := auctionJSON.PrivateBids
|
||||||
|
privateBidHashString := bidders[bidKey].Hash
|
||||||
|
|
||||||
|
onChainBidHashString := fmt.Sprintf("%x", bidHash)
|
||||||
|
if privateBidHashString != onChainBidHashString {
|
||||||
|
return fmt.Errorf("hash %s for bid JSON %s does not match hash in auction: %s, bidder must have changed bid",
|
||||||
|
privateBidHashString,
|
||||||
|
transientBidJSON,
|
||||||
|
onChainBidHashString,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// we can add the bid to the auction if all checks have passed
|
||||||
|
type transientBidInput struct {
|
||||||
|
Quantity int `json:"quantity"`
|
||||||
|
Price int `json:"price"`
|
||||||
|
Org string `json:"org"`
|
||||||
|
Buyer string `json:"buyer"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// unmarshal bid imput
|
||||||
|
var bidInput transientBidInput
|
||||||
|
err = json.Unmarshal(transientBidJSON, &bidInput)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to unmarshal JSON: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get ID of submitting client identity
|
||||||
|
clientID, err := ctx.GetClientIdentity().GetID()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get client identity %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// marshal transient paramters and ID and MSPID into bid object
|
||||||
|
NewBid := FullBid{
|
||||||
|
Type: bidKeyType,
|
||||||
|
Quantity: bidInput.Quantity,
|
||||||
|
Price: bidInput.Price,
|
||||||
|
Org: bidInput.Org,
|
||||||
|
Buyer: bidInput.Buyer,
|
||||||
|
}
|
||||||
|
|
||||||
|
// check 4: make sure that the bid is being revealed by the buyer
|
||||||
|
if bidInput.Buyer != clientID {
|
||||||
|
return fmt.Errorf("Permission denied, client id %v is not the owner of the bid", clientID)
|
||||||
|
}
|
||||||
|
|
||||||
|
revealedBids := make(map[string]FullBid)
|
||||||
|
revealedBids = auctionJSON.RevealedBids
|
||||||
|
revealedBids[bidKey] = NewBid
|
||||||
|
auctionJSON.RevealedBids = revealedBids
|
||||||
|
|
||||||
|
newAuctionBytes, _ := json.Marshal(auctionJSON)
|
||||||
|
|
||||||
|
// put auction with bid added back into state
|
||||||
|
err = ctx.GetStub().PutState(auctionID, newAuctionBytes)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to update auction: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// CloseAuction can be used by the seller to close the auction. This prevents
|
||||||
|
// bids from being added to the auction, and allows users to reveal their bid
|
||||||
|
func (s *SmartContract) CloseAuction(ctx contractapi.TransactionContextInterface, auctionID string) error {
|
||||||
|
|
||||||
|
auctionBytes, err := ctx.GetStub().GetState(auctionID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get auction %v: %v", auctionID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if auctionBytes == nil {
|
||||||
|
return fmt.Errorf("Auction interest object %v not found", auctionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
var auctionJSON Auction
|
||||||
|
err = json.Unmarshal(auctionBytes, &auctionJSON)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create interst object JSON: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// the auction can only be closed by the seller
|
||||||
|
|
||||||
|
// get ID of submitting client
|
||||||
|
clientID, err := ctx.GetClientIdentity().GetID()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get client identity %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Seller := auctionJSON.Seller
|
||||||
|
if Seller != clientID {
|
||||||
|
return fmt.Errorf("auction can only be closed by seller: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Status := auctionJSON.Status
|
||||||
|
if Status != "open" {
|
||||||
|
return fmt.Errorf("cannot close auction that is not open")
|
||||||
|
}
|
||||||
|
|
||||||
|
auctionJSON.Status = string("closed")
|
||||||
|
|
||||||
|
closedAuction, _ := json.Marshal(auctionJSON)
|
||||||
|
|
||||||
|
err = ctx.GetStub().PutState(auctionID, closedAuction)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to close auction: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// EndAuction both changes the auction status to closed and calculates the winners
|
||||||
|
// of the auction
|
||||||
|
func (s *SmartContract) EndAuction(ctx contractapi.TransactionContextInterface, auctionID string) error {
|
||||||
|
|
||||||
|
auctionBytes, err := ctx.GetStub().GetState(auctionID)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get auction %v: %v", auctionID, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if auctionBytes == nil {
|
||||||
|
return fmt.Errorf("Auction interest object %v not found", auctionID)
|
||||||
|
}
|
||||||
|
|
||||||
|
var auctionJSON Auction
|
||||||
|
err = json.Unmarshal(auctionBytes, &auctionJSON)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create interst object JSON: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check that the auction is being ended by the seller
|
||||||
|
|
||||||
|
// get ID of submitting client
|
||||||
|
clientID, err := ctx.GetClientIdentity().GetID()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get client identity %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Seller := auctionJSON.Seller
|
||||||
|
if Seller != clientID {
|
||||||
|
return fmt.Errorf("auction can only be ended by seller: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
Status := auctionJSON.Status
|
||||||
|
if Status != "closed" {
|
||||||
|
return fmt.Errorf("Can only end a closed auction")
|
||||||
|
}
|
||||||
|
|
||||||
|
// get the list of revealed bids
|
||||||
|
|
||||||
|
revealedBidMap := auctionJSON.RevealedBids
|
||||||
|
if len(auctionJSON.RevealedBids) == 0 {
|
||||||
|
return fmt.Errorf("No bids have been revealed, cannot end auction: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// sort the map of revealed bids to make it easier to calculate winners
|
||||||
|
var Bidders []FullBid
|
||||||
|
|
||||||
|
for _, bid := range revealedBidMap {
|
||||||
|
Bidders = append(Bidders, bid)
|
||||||
|
}
|
||||||
|
|
||||||
|
sort.Slice(Bidders, func(p, q int) bool {
|
||||||
|
return Bidders[p].Price > Bidders[q].Price
|
||||||
|
})
|
||||||
|
|
||||||
|
i := 0
|
||||||
|
remainingQuantity := auctionJSON.Quantity
|
||||||
|
|
||||||
|
// calculate the winners
|
||||||
|
for remainingQuantity > 0 {
|
||||||
|
|
||||||
|
// create the next winning bid
|
||||||
|
winner := Winners{
|
||||||
|
Buyer: Bidders[i].Buyer,
|
||||||
|
Quantity: Bidders[i].Quantity,
|
||||||
|
}
|
||||||
|
|
||||||
|
// add them to the list of winners and change the winning price
|
||||||
|
auctionJSON.Winners = append(auctionJSON.Winners, winner)
|
||||||
|
auctionJSON.Price = Bidders[i].Price
|
||||||
|
|
||||||
|
// Calculate the quantity that goes to the winner
|
||||||
|
// if there is sufficient quantity to give them the full bid
|
||||||
|
if remainingQuantity > Bidders[i].Quantity {
|
||||||
|
remainingQuantity = remainingQuantity - Bidders[i].Quantity
|
||||||
|
|
||||||
|
// if there is not, give the remainder
|
||||||
|
} else {
|
||||||
|
auctionJSON.Winners[i].Quantity = remainingQuantity
|
||||||
|
remainingQuantity = 0
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
|
||||||
|
// check if there is a winning bid that has yet to be revealed
|
||||||
|
err = queryAllBids(ctx, auctionJSON.Price, auctionJSON.RevealedBids, auctionJSON.PrivateBids)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Cannot close auction: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
auctionJSON.Status = string("ended")
|
||||||
|
|
||||||
|
closedAuction, _ := json.Marshal(auctionJSON)
|
||||||
|
|
||||||
|
err = ctx.GetStub().PutState(auctionID, closedAuction)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to close auction: %v", err)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
148
auction/chaincode-go/smart-contract/auctionQueries.go
Normal file
148
auction/chaincode-go/smart-contract/auctionQueries.go
Normal file
|
|
@ -0,0 +1,148 @@
|
||||||
|
/*
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package auction
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hyperledger/fabric-chaincode-go/shim"
|
||||||
|
"github.com/hyperledger/fabric-contract-api-go/contractapi"
|
||||||
|
)
|
||||||
|
|
||||||
|
// QueryAuction allows all members of the channel to read a public auction
|
||||||
|
func (s *SmartContract) QueryAuction(ctx contractapi.TransactionContextInterface, auctionID string) (*Auction, error) {
|
||||||
|
|
||||||
|
auctionJSON, err := ctx.GetStub().GetState(auctionID)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get auction object %v: %v", auctionID, err)
|
||||||
|
}
|
||||||
|
if auctionJSON == nil {
|
||||||
|
return nil, fmt.Errorf("auction does not exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
var auction *Auction
|
||||||
|
err = json.Unmarshal(auctionJSON, &auction)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
return auction, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// QueryBid allows the submitter of the bid to read their bid from public state
|
||||||
|
func (s *SmartContract) QueryBid(ctx contractapi.TransactionContextInterface, auctionID string, txID string) (*FullBid, error) {
|
||||||
|
|
||||||
|
err := verifyClientOrgMatchesPeerOrg(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get implicit collection name: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
clientID, err := ctx.GetClientIdentity().GetID()
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get client identity %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
collection, err := getCollectionName(ctx)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get implicit collection name: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bidKey, err := ctx.GetStub().CreateCompositeKey(bidKeyType, []string{auctionID, txID})
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to create composite key: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
bidJSON, err := ctx.GetStub().GetPrivateData(collection, bidKey)
|
||||||
|
if err != nil {
|
||||||
|
return nil, fmt.Errorf("failed to get bid %v: %v", bidKey, err)
|
||||||
|
}
|
||||||
|
if bidJSON == nil {
|
||||||
|
return nil, fmt.Errorf("bid %v does not exist", bidKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
var bid *FullBid
|
||||||
|
err = json.Unmarshal(bidJSON, &bid)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// check that the client querying the bid is the bid owner
|
||||||
|
if bid.Buyer != clientID {
|
||||||
|
return nil, fmt.Errorf("Permission denied, client id %v is not the owner of the bid", clientID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return bid, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetID is an internal helper function to allow users to get their identity
|
||||||
|
func (s *SmartContract) GetID(ctx contractapi.TransactionContextInterface) (string, error) {
|
||||||
|
|
||||||
|
// Get the MSP ID of submitting client identity
|
||||||
|
clientID, err := ctx.GetClientIdentity().GetID()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to get verified MSPID: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return clientID, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// queryAllBids is an internal function that is used to deterimine if a winning bid has yet to be revealed
|
||||||
|
func queryAllBids(ctx contractapi.TransactionContextInterface, auctionPrice int, revealedBidders map[string]FullBid, bidders map[string]BidHash) error {
|
||||||
|
|
||||||
|
// Get MSP ID of peer org
|
||||||
|
peerMSPID, err := shim.GetMSPID()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed getting the peer's MSPID: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
var error error
|
||||||
|
error = nil
|
||||||
|
|
||||||
|
for bidKey, privateBid := range bidders {
|
||||||
|
|
||||||
|
if _, bidInAuction := revealedBidders[bidKey]; bidInAuction {
|
||||||
|
|
||||||
|
//bid is already revealed, no action to take
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
collection := "_implicit_org_" + privateBid.Org
|
||||||
|
|
||||||
|
if privateBid.Org == peerMSPID {
|
||||||
|
|
||||||
|
bidJSON, err := ctx.GetStub().GetPrivateData(collection, bidKey)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to get bid %v: %v", bidKey, err)
|
||||||
|
}
|
||||||
|
if bidJSON == nil {
|
||||||
|
return fmt.Errorf("bid %v does not exist", bidKey)
|
||||||
|
}
|
||||||
|
|
||||||
|
var bid *FullBid
|
||||||
|
err = json.Unmarshal(bidJSON, &bid)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if bid.Price > auctionPrice {
|
||||||
|
error = fmt.Errorf("Cannot close auction, bidder has a higher price: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
Hash, err := ctx.GetStub().GetPrivateDataHash(collection, bidKey)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to read bid bash from collection: %v", err)
|
||||||
|
}
|
||||||
|
if Hash == nil {
|
||||||
|
return fmt.Errorf("bid hash does not exist: %s", bidKey)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return error
|
||||||
|
}
|
||||||
108
auction/chaincode-go/smart-contract/utils.go
Normal file
108
auction/chaincode-go/smart-contract/utils.go
Normal file
|
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package auction
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/hyperledger/fabric-chaincode-go/pkg/statebased"
|
||||||
|
"github.com/hyperledger/fabric-contract-api-go/contractapi"
|
||||||
|
"github.com/hyperledger/fabric-chaincode-go/shim"
|
||||||
|
)
|
||||||
|
|
||||||
|
// setAssetStateBasedEndorsement sets the endorsement policy of a new auction
|
||||||
|
func setAssetStateBasedEndorsement(ctx contractapi.TransactionContextInterface, auctionID string, orgToEndorse string) error {
|
||||||
|
|
||||||
|
endorsementPolicy, err := statebased.NewStateEP(nil)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = endorsementPolicy.AddOrgs(statebased.RoleTypePeer, orgToEndorse)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to add org to endorsement policy: %v", err)
|
||||||
|
}
|
||||||
|
policy, err := endorsementPolicy.Policy()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create endorsement policy bytes from org: %v", err)
|
||||||
|
}
|
||||||
|
err = ctx.GetStub().SetStateValidationParameter(auctionID, policy)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to set validation parameter on auction: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// addAssetStateBasedEndorsement adds a new organization as an endorser of the auction
|
||||||
|
func addAssetStateBasedEndorsement(ctx contractapi.TransactionContextInterface, auctionID string, orgToEndorse string) error {
|
||||||
|
|
||||||
|
endorsementPolicy, err := ctx.GetStub().GetStateValidationParameter(auctionID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
newEndorsementPolicy, err := statebased.NewStateEP(endorsementPolicy)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = newEndorsementPolicy.AddOrgs(statebased.RoleTypePeer, orgToEndorse)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to add org to endorsement policy: %v", err)
|
||||||
|
}
|
||||||
|
policy, err := newEndorsementPolicy.Policy()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to create endorsement policy bytes from org: %v", err)
|
||||||
|
}
|
||||||
|
err = ctx.GetStub().SetStateValidationParameter(auctionID, policy)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to set validation parameter on auction: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// getCollectionName is an internal helper function to get collection of submitting client identity.
|
||||||
|
func getCollectionName(ctx contractapi.TransactionContextInterface) (string, error) {
|
||||||
|
|
||||||
|
// Get the MSP ID of submitting client identity
|
||||||
|
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
|
||||||
|
if err != nil {
|
||||||
|
return "", fmt.Errorf("failed to get verified MSPID: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create the collection name
|
||||||
|
orgCollection := "_implicit_org_" + clientMSPID
|
||||||
|
|
||||||
|
return orgCollection, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// verifyClientOrgMatchesPeerOrg is an internal function used verify client org id and matches peer org id.
|
||||||
|
func verifyClientOrgMatchesPeerOrg(ctx contractapi.TransactionContextInterface) error {
|
||||||
|
clientMSPID, err := ctx.GetClientIdentity().GetMSPID()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed getting the client's MSPID: %v", err)
|
||||||
|
}
|
||||||
|
peerMSPID, err := shim.GetMSPID()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed getting the peer's MSPID: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if clientMSPID != peerMSPID {
|
||||||
|
return fmt.Errorf("client from org %v is not authorized to read or write private data from an org %v peer", clientMSPID, peerMSPID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func contains(sli []string, str string) bool {
|
||||||
|
for _, a := range sli {
|
||||||
|
if a == str {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
23
auction/chaincode-go/smartContract.go
Normal file
23
auction/chaincode-go/smartContract.go
Normal file
|
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
SPDX-License-Identifier: Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/hyperledger/fabric-contract-api-go/contractapi"
|
||||||
|
"github.com/hyperledger/fabric-samples/auction/chaincode-go/smart-contract"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
auctionSmartContract, err := contractapi.NewChaincode(&auction.SmartContract{})
|
||||||
|
if err != nil {
|
||||||
|
log.Panicf("Error creating auction chaincode: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := auctionSmartContract.Start(); err != nil {
|
||||||
|
log.Panicf("Error starting auction chaincode: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue