fabric-samples/auction/dutch-auction
nikhil550 bf1d9fc667
Create auction sample (#355)
Signed-off-by: NIKHIL E GUPTA <negupta@us.ibm.com>

Co-authored-by: NIKHIL E GUPTA <negupta@us.ibm.com>
2020-11-06 13:09:35 +01:00
..
README.md Create auction sample (#355) 2020-11-06 13:09:35 +01:00

Running a Dutch auction

The auction sample smart contract can be used to implement a Dutch auction to sell multiple items of the same good. All items are sold at the price that clears the auction. This tutorial creates an auction to sell 100 tickets to multiple bidders.

Deploy the chaincode

Change into the test network directory.

cd fabric-samples/test-network

If the test network is already running, run the following command to bring the network down and start from a clean initial state.

./network.sh down

You can then run the following command to deploy a new network.

./network.sh up createChannel -ca

Run the following command to deploy the auction smart contract.

./network.sh deployCC -ccn auction -ccp ../auction/chaincode-go/ -ccep "OR('Org1MSP.peer','Org2MSP.peer')"

Install the application dependencies

Change into the application-javascript directory:

cd fabric-samples/auction/application-javascript

From this directory, run the following command to download the application dependencies if you have not done so already:

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 of 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 and enroll 2 bidders from Org1 and another 3 bidders from Org2:

node registerEnrollUser.js org1 bidder1
node registerEnrollUser.js org1 bidder2
node registerEnrollUser.js org2 bidder3
node registerEnrollUser.js org2 bidder4
node registerEnrollUser.js org2 bidder5

Create the auction

The seller from Org1 would like to create an auction to sell 100 tickets. Run the following command to use the seller wallet to run the createAuction.js application. The seller needs to provide an ID for the auction, the item to be sold, and the quantity to be sold to create the auction:

node createAuction.js org1 seller auction1 tickets 100

You will see the application query the auction after it is created.

Bid on the auction

We can now use the bidder wallets to submit bids to the auction:

Bid as bidder1

Bidder1 will create a bid to purchase 50 tickets for 80 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=="
}

The bid.js application also prints the bidID:

*** Result ***SAVE THIS VALUE*** BidID: 61e9b0fc1913f10872625bea4a6555522c70070416209848cc1d8fb6101133ad

The BidID acts as the unique identifier for the bid. This ID allows you to query the bid using the queryBid.js program and add the bid to the auction. Save the bidID returned by the application as an environment variable in your terminal:

export BIDDER1_BID_ID=61e9b0fc1913f10872625bea4a6555522c70070416209848cc1d8fb6101133ad

This value will be different for each transaction, so you will need to use the value returned in your terminal.

Now that the bid has been created, you can submit the bid to the auction. Run the following command to submit the bid that was just created:

node submitBid.js org1 bidder1 auction1 $BIDDER1_BID_ID

The hash of bid is added to the list of private bids in that have been submitted to auction1. Storing the hash on the public auction ledger allows users to prove the accuracy of the bids they reveal once bidding is closed. The application queries the auction to verify that the bid was added:

*** Result: Auction: {
  "objectType": "auction",
  "item": "tickets",
  "seller": "eDUwOTo6Q049c2VsbGVyLE9VPWNsaWVudCtPVT1vcmcxK09VPWRlcGFydG1lbnQxOjpDTj1jYS5vcmcxLmV4YW1wbGUuY29tLE89b3JnMS5leGFtcGxlLmNvbSxMPUR1cmhhbSxTVD1Ob3J0aCBDYXJvbGluYSxDPVVT",
  "quantity": 100,
  "organizations": [
    "Org1MSP"
  ],
  "privateBids": {
    "\u0000bid\u0000auction1\u000061e9b0fc1913f10872625bea4a6555522c70070416209848cc1d8fb6101133ad\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=911c7920a7ba4643a531cb2d5d274d303fdb2e6800f50aeb6b725af0b7162ea2

Submit bidder2's bid to the auction:

node submitBid.js org1 bidder2 auction1 $BIDDER2_BID_ID

Bid as bidder3 from Org2

Bidder3 will bid for 30 tickets at 70 dollars:

node bid.js org2 bidder3 auction1 30 70

Save the Bid ID returned by the application:

export BIDDER3_BID_ID=93a8164628fa28290554b5dc6f505cbb8c7498d8f7c60f7df33d4a1cffb8fa47

Add bidder3's bid to the auction:

node submitBid.js org2 bidder3 auction1 $BIDDER3_BID_ID

Because bidder3 belongs to Org2, submitting the bid will add Org2 to the list of participating organizations. You can see the Org2 MSP ID has been added to the list of "organizations" in the updated auction returned by the application:

*** Result: Auction: {
  "objectType": "auction",
  "item": "tickets",
  "seller": "eDUwOTo6Q049c2VsbGVyLE9VPWNsaWVudCtPVT1vcmcxK09VPWRlcGFydG1lbnQxOjpDTj1jYS5vcmcxLmV4YW1wbGUuY29tLE89b3JnMS5leGFtcGxlLmNvbSxMPUR1cmhhbSxTVD1Ob3J0aCBDYXJvbGluYSxDPVVT",
  "quantity": 100,
  "organizations": [
    "Org1MSP",
    "Org2MSP"
  ],
  "privateBids": {
    "\u0000bid\u0000auction1\u000061e9b0fc1913f10872625bea4a6555522c70070416209848cc1d8fb6101133ad\u0000": {
      "org": "Org1MSP",
      "hash": "584dbad2269a44afb42bdbc7a7a4c08a7cd50deece1eb3fc38d4e49b9342f270"
    },
    "\u0000bid\u0000auction1\u0000911c7920a7ba4643a531cb2d5d274d303fdb2e6800f50aeb6b725af0b7162ea2\u0000": {
      "org": "Org1MSP",
      "hash": "bbcd0c7c376e6681a76d8c5482c97f8bdcda55c90c5478100c3aef17815c4fd3"
    },
    "\u0000bid\u0000auction1\u000093a8164628fa28290554b5dc6f505cbb8c7498d8f7c60f7df33d4a1cffb8fa47\u0000": {
      "org": "Org2MSP",
      "hash": "4446c7eb0e2d64165a916ee996348a18716f4c97e632d58d5a8c20eeec5a9238"
    }
  },
  "revealedBids": {},
  "winners": [],
  "price": 0,
  "status": "open"
}

Now that a bid from Org2 has been added to the auction, any updates to the auction need to be endorsed by the Org2 peer. The applications will use the "organizations" field to specify which organizations need to endorse submitting a new bid, revealing a bid, or updating the auction status.

Bid as bidder4

Bidder4 from Org2 would like to purchase 15 tickets for 60 dollars:

node bid.js org2 bidder4 auction1 15 60

Save the Bid ID returned by the application:

export BIDDER4_BID_ID=324de04c459c5a38f103e9096dea06e19faca88f84dd5175ba0e6fd6a9d7d140

Add bidder4's bid to the auction:

node submitBid.js org2 bidder4 auction1 $BIDDER4_BID_ID

Bid as bidder5

Bidder5 from Org2 will bid for 20 tickets at 60 dollars:

node bid.js org2 bidder4 auction1 20 60

Save the Bid ID returned by the application:

export BIDDER5_BID_ID=3245049bd81a8cecfbc006f29eed1df0570e385e63daedd19c06b2c92d2067ae

Add bidder4's bid to the auction:

node submitBid.js org2 bidder5 auction1 $BIDDER5_BID_ID

Close the auction

Now that all five 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 submit the transaction:

node closeAuction.js org1 seller auction1

The application will query the auction to allow you to verify that the auction status has changed to closed.

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 auction is closed.
  2. The transaction was submitted 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 the bid that is stored in the private data collection.
  4. The hash of the revealed bid matches the hash that was submitted 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, are now visible:

*** Result: Auction: {
  "objectType": "auction",
  "item": "tickets",
  "seller": "eDUwOTo6Q049c2VsbGVyLE9VPWNsaWVudCtPVT1vcmcxK09VPWRlcGFydG1lbnQxOjpDTj1jYS5vcmcxLmV4YW1wbGUuY29tLE89b3JnMS5leGFtcGxlLmNvbSxMPUR1cmhhbSxTVD1Ob3J0aCBDYXJvbGluYSxDPVVT",
  "quantity": 100,
  "organizations": [
    "Org1MSP",
    "Org2MSP"
  ],
  "privateBids": {
    "\u0000bid\u0000auction1\u00003245049bd81a8cecfbc006f29eed1df0570e385e63daedd19c06b2c92d2067ae\u0000": {
      "org": "Org2MSP",
      "hash": "5a51070f188e6480fe606acdc5ad1a1c36330adc3547eb021aaf87aa00ec79f7"
    },
    "\u0000bid\u0000auction1\u0000324de04c459c5a38f103e9096dea06e19faca88f84dd5175ba0e6fd6a9d7d140\u0000": {
      "org": "Org2MSP",
      "hash": "516c775f7da2fd653dab71d20d60a6e8bea8ff856803af4b59f943ca2ba40699"
    },
    "\u0000bid\u0000auction1\u000061e9b0fc1913f10872625bea4a6555522c70070416209848cc1d8fb6101133ad\u0000": {
      "org": "Org1MSP",
      "hash": "584dbad2269a44afb42bdbc7a7a4c08a7cd50deece1eb3fc38d4e49b9342f270"
    },
    "\u0000bid\u0000auction1\u0000911c7920a7ba4643a531cb2d5d274d303fdb2e6800f50aeb6b725af0b7162ea2\u0000": {
      "org": "Org1MSP",
      "hash": "bbcd0c7c376e6681a76d8c5482c97f8bdcda55c90c5478100c3aef17815c4fd3"
    },
    "\u0000bid\u0000auction1\u000093a8164628fa28290554b5dc6f505cbb8c7498d8f7c60f7df33d4a1cffb8fa47\u0000": {
      "org": "Org2MSP",
      "hash": "4446c7eb0e2d64165a916ee996348a18716f4c97e632d58d5a8c20eeec5a9238"
    }
  },
  "revealedBids": {
    "\u0000bid\u0000auction1\u000061e9b0fc1913f10872625bea4a6555522c70070416209848cc1d8fb6101133ad\u0000": {
      "objectType": "bid",
      "quantity": 50,
      "price": 80,
      "org": "Org1MSP",
      "buyer": "eDUwOTo6Q049YmlkZGVyMSxPVT1jbGllbnQrT1U9b3JnMStPVT1kZXBhcnRtZW50MTo6Q049Y2Eub3JnMS5leGFtcGxlLmNvbSxPPW9yZzEuZXhhbXBsZS5jb20sTD1EdXJoYW0sU1Q9Tm9ydGggQ2Fyb2xpbmEsQz1VUw=="
    }
  },
  "winners": [],
  "price": 0,
  "status": "closed"
}

All bidders will reveal their bid to participate in the auction. Run the following commands to reveal the bids of the remaining four bidders:

node revealBid.js org1 bidder2 auction1 $BIDDER2_BID_ID
node revealBid.js org2 bidder3 auction1 $BIDDER3_BID_ID
node revealBid.js org2 bidder4 auction1 $BIDDER4_BID_ID
node revealBid.js org2 bidder5 auction1 $BIDDER5_BID_ID

End the auction

Now that the winning bids have been revealed, we can 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.

*** Result: Auction: {
  "objectType": "auction",
  "item": "tickets",
  "seller": "eDUwOTo6Q049c2VsbGVyLE9VPWNsaWVudCtPVT1vcmcxK09VPWRlcGFydG1lbnQxOjpDTj1jYS5vcmcxLmV4YW1wbGUuY29tLE89b3JnMS5leGFtcGxlLmNvbSxMPUR1cmhhbSxTVD1Ob3J0aCBDYXJvbGluYSxDPVVT",
  "quantity": 100,
  "organizations": [
    "Org1MSP",
    "Org2MSP"
  ],
  "privateBids": {
    "\u0000bid\u0000auction1\u00003245049bd81a8cecfbc006f29eed1df0570e385e63daedd19c06b2c92d2067ae\u0000": {
      "org": "Org2MSP",
      "hash": "5a51070f188e6480fe606acdc5ad1a1c36330adc3547eb021aaf87aa00ec79f7"
    },
    "\u0000bid\u0000auction1\u0000324de04c459c5a38f103e9096dea06e19faca88f84dd5175ba0e6fd6a9d7d140\u0000": {
      "org": "Org2MSP",
      "hash": "516c775f7da2fd653dab71d20d60a6e8bea8ff856803af4b59f943ca2ba40699"
    },
    "\u0000bid\u0000auction1\u000061e9b0fc1913f10872625bea4a6555522c70070416209848cc1d8fb6101133ad\u0000": {
      "org": "Org1MSP",
      "hash": "584dbad2269a44afb42bdbc7a7a4c08a7cd50deece1eb3fc38d4e49b9342f270"
    },
    "\u0000bid\u0000auction1\u0000911c7920a7ba4643a531cb2d5d274d303fdb2e6800f50aeb6b725af0b7162ea2\u0000": {
      "org": "Org1MSP",
      "hash": "bbcd0c7c376e6681a76d8c5482c97f8bdcda55c90c5478100c3aef17815c4fd3"
    },
    "\u0000bid\u0000auction1\u000093a8164628fa28290554b5dc6f505cbb8c7498d8f7c60f7df33d4a1cffb8fa47\u0000": {
      "org": "Org2MSP",
      "hash": "4446c7eb0e2d64165a916ee996348a18716f4c97e632d58d5a8c20eeec5a9238"
    }
  },
  "revealedBids": {
    "\u0000bid\u0000auction1\u00003245049bd81a8cecfbc006f29eed1df0570e385e63daedd19c06b2c92d2067ae\u0000": {
      "objectType": "bid",
      "quantity": 20,
      "price": 60,
      "org": "Org2MSP",
      "buyer": "eDUwOTo6Q049YmlkZGVyNCxPVT1jbGllbnQrT1U9b3JnMitPVT1kZXBhcnRtZW50MTo6Q049Y2Eub3JnMi5leGFtcGxlLmNvbSxPPW9yZzIuZXhhbXBsZS5jb20sTD1IdXJzbGV5LFNUPUhhbXBzaGlyZSxDPVVL"
    },
    "\u0000bid\u0000auction1\u0000324de04c459c5a38f103e9096dea06e19faca88f84dd5175ba0e6fd6a9d7d140\u0000": {
      "objectType": "bid",
      "quantity": 15,
      "price": 60,
      "org": "Org2MSP",
      "buyer": "eDUwOTo6Q049YmlkZGVyNCxPVT1jbGllbnQrT1U9b3JnMitPVT1kZXBhcnRtZW50MTo6Q049Y2Eub3JnMi5leGFtcGxlLmNvbSxPPW9yZzIuZXhhbXBsZS5jb20sTD1IdXJzbGV5LFNUPUhhbXBzaGlyZSxDPVVL"
    },
    "\u0000bid\u0000auction1\u000061e9b0fc1913f10872625bea4a6555522c70070416209848cc1d8fb6101133ad\u0000": {
      "objectType": "bid",
      "quantity": 50,
      "price": 80,
      "org": "Org1MSP",
      "buyer": "eDUwOTo6Q049YmlkZGVyMSxPVT1jbGllbnQrT1U9b3JnMStPVT1kZXBhcnRtZW50MTo6Q049Y2Eub3JnMS5leGFtcGxlLmNvbSxPPW9yZzEuZXhhbXBsZS5jb20sTD1EdXJoYW0sU1Q9Tm9ydGggQ2Fyb2xpbmEsQz1VUw=="
    },
    "\u0000bid\u0000auction1\u0000911c7920a7ba4643a531cb2d5d274d303fdb2e6800f50aeb6b725af0b7162ea2\u0000": {
      "objectType": "bid",
      "quantity": 40,
      "price": 50,
      "org": "Org1MSP",
      "buyer": "eDUwOTo6Q049YmlkZGVyMixPVT1jbGllbnQrT1U9b3JnMStPVT1kZXBhcnRtZW50MTo6Q049Y2Eub3JnMS5leGFtcGxlLmNvbSxPPW9yZzEuZXhhbXBsZS5jb20sTD1EdXJoYW0sU1Q9Tm9ydGggQ2Fyb2xpbmEsQz1VUw=="
    },
    "\u0000bid\u0000auction1\u000093a8164628fa28290554b5dc6f505cbb8c7498d8f7c60f7df33d4a1cffb8fa47\u0000": {
      "objectType": "bid",
      "quantity": 30,
      "price": 70,
      "org": "Org2MSP",
      "buyer": "eDUwOTo6Q049YmlkZGVyMyxPVT1jbGllbnQrT1U9b3JnMitPVT1kZXBhcnRtZW50MTo6Q049Y2Eub3JnMi5leGFtcGxlLmNvbSxPPW9yZzIuZXhhbXBsZS5jb20sTD1IdXJzbGV5LFNUPUhhbXBzaGlyZSxDPVVL"
    }
  },
  "winners": [
    {
      "buyer": "eDUwOTo6Q049YmlkZGVyMSxPVT1jbGllbnQrT1U9b3JnMStPVT1kZXBhcnRtZW50MTo6Q049Y2Eub3JnMS5leGFtcGxlLmNvbSxPPW9yZzEuZXhhbXBsZS5jb20sTD1EdXJoYW0sU1Q9Tm9ydGggQ2Fyb2xpbmEsQz1VUw==",
      "quantity": 50
    },
    {
      "buyer": "eDUwOTo6Q049YmlkZGVyMyxPVT1jbGllbnQrT1U9b3JnMitPVT1kZXBhcnRtZW50MTo6Q049Y2Eub3JnMi5leGFtcGxlLmNvbSxPPW9yZzIuZXhhbXBsZS5jb20sTD1IdXJzbGV5LFNUPUhhbXBzaGlyZSxDPVVL",
      "quantity": 30
    },
    {
      "buyer": "eDUwOTo6Q049YmlkZGVyNCxPVT1jbGllbnQrT1U9b3JnMitPVT1kZXBhcnRtZW50MTo6Q049Y2Eub3JnMi5leGFtcGxlLmNvbSxPPW9yZzIuZXhhbXBsZS5jb20sTD1IdXJzbGV5LFNUPUhhbXBzaGlyZSxDPVVL",
      "quantity": 15
    },
    {
      "buyer": "eDUwOTo6Q049YmlkZGVyNCxPVT1jbGllbnQrT1U9b3JnMitPVT1kZXBhcnRtZW50MTo6Q049Y2Eub3JnMi5leGFtcGxlLmNvbSxPPW9yZzIuZXhhbXBsZS5jb20sTD1IdXJzbGV5LFNUPUhhbXBzaGlyZSxDPVVL",
      "quantity": 5
    }
  ],
  "price": 60,
  "status": "ended"
}

The auction allocates tickets to the highest bids first. Because all 100 tickets are sold after allocating tickets to the bids that were submitted at 60, 60 is the "price" that clears the auction. The first 80 tickets are allocated to Bidder1 and Bidder3. The remaining 20 tickers are allocated to Bidder4 and Bidder5. When bids are tied, the auction smart contract fills the smaller bids first. As a result, Bidder4 is awarded their full bid of 15 tickets, while Bidder5 is allocated the remaining 5 tickets.

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 used to 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