mirror of
https://github.com/hyperledger/fabric-samples.git
synced 2026-06-17 15:35:09 +00:00
Implement sale, bid, and history queries
- Add marbles proposed sale and bid prices queries - Add marbles history query - Add verification of ownership when selling marble Signed-off-by: Tiffany Harris <tiffany.harris@ibm.com> Signed-off-by: Dereck Luo <dereckluo@gmail.com>
This commit is contained in:
parent
0c61f84af7
commit
df292e0e8d
3 changed files with 173 additions and 63 deletions
|
|
@ -180,12 +180,12 @@ export MARBLE_PROPERTIES=$(echo -n "{\"object_type\":\"marble_properties\",\"mar
|
|||
```
|
||||
We can now use the following command to create a marble that belongs to Org1:
|
||||
```
|
||||
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles_transfer -c '{"function":"CreateMarble","Args":["marble1"]}' --transient "{\"marble_properties\":\"$MARBLE_PROPERTIES\"}"
|
||||
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles_transfer -c '{"function":"IssueAsset","Args":["marble1"]}' --transient "{\"marble_properties\":\"$MARBLE_PROPERTIES\"}"
|
||||
```
|
||||
|
||||
We can can query the Org1 implicit data collection to see the marble that was created:
|
||||
```
|
||||
peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles_transfer -c '{"function":"QueryMarblePrivateImmutableProperties","Args":["marble1"]}'
|
||||
peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles_transfer -c '{"function":"GetAssetPrivateProperties","Args":["marble1"]}'
|
||||
```
|
||||
|
||||
When successful, the command will return the following result:
|
||||
|
|
@ -195,7 +195,7 @@ When successful, the command will return the following result:
|
|||
|
||||
We can also query the ledger to see the public ownership record:
|
||||
```
|
||||
peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles_transfer -c '{"function":"QueryMarble","Args":["marble1"]}'
|
||||
peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles_transfer -c '{"function":"GetAsset","Args":["marble1"]}'
|
||||
```
|
||||
The command will return the record that the marble1 is owned by Org1:
|
||||
```
|
||||
|
|
@ -207,7 +207,7 @@ peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.exa
|
|||
```
|
||||
Query the ledger again to see the updated description:
|
||||
```
|
||||
peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles_transfer -c '{"function":"QueryMarble","Args":["marble1"]}'
|
||||
peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles_transfer -c '{"function":"GetAsset","Args":["marble1"]}'
|
||||
```
|
||||
We can now see that the marble is for sale:
|
||||
```
|
||||
|
|
@ -221,7 +221,7 @@ We can now see that the marble is for sale:
|
|||
|
||||
If we operate from the Org2 terminal, we can use the smart contract query the public marble data:
|
||||
```
|
||||
peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles_transfer -c '{"function":"QueryMarble","Args":["marble1"]}'
|
||||
peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles_transfer -c '{"function":"GetAsset","Args":["marble1"]}'
|
||||
```
|
||||
From this query, Org2 learns that marble1 is for sale:
|
||||
```
|
||||
|
|
@ -251,7 +251,7 @@ peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.exa
|
|||
|
||||
We can query the Org1 private data collection to read the agreed to selling price:
|
||||
```
|
||||
peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles_transfer -c '{"function":"QueryMarbleSalesPrice","Args":["marble1"]}'
|
||||
peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles_transfer -c '{"function":"GetAssetSalesPrice","Args":["marble1"]}'
|
||||
```
|
||||
|
||||
## Agree to buy as Org2
|
||||
|
|
@ -263,7 +263,7 @@ peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.exa
|
|||
```
|
||||
You can read the agreed purchase price from the Org2 implicit data collection:
|
||||
```
|
||||
peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles_transfer -c '{"function":"QueryMarbleBidPrice","Args":["marble1"]}'
|
||||
peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles_transfer -c '{"function":"GetAssetBidPrice","Args":["marble1"]}'
|
||||
```
|
||||
|
||||

|
||||
|
|
@ -278,7 +278,7 @@ After both organizations have agreed to their price, Org1 can attempt to transfe
|
|||
Operate from the Org1 terminal. The owner of the marble needs to initiate the transfer. Note that the command below uses the `--peerAddresses` flag to target the peers of both Org1 and Org2. Both organizations need to endorse the transfer.
|
||||
|
||||
```
|
||||
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles_transfer -c '{"function":"TransferMarble","Args":["marble1","Org2MSP"]}' --transient "{\"marble_properties\":\"$MARBLE_PROPERTIES\",\"marble_price\":\"$MARBLE_PRICE\"}" --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
|
||||
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles_transfer -c '{"function":"TransferAsset","Args":["marble1","Org2MSP"]}' --transient "{\"marble_properties\":\"$MARBLE_PROPERTIES\",\"marble_price\":\"$MARBLE_PRICE\"}" --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
|
||||
```
|
||||
Because the two organizations have not agreed to the same price, the transfer cannot be completed:
|
||||
```
|
||||
|
|
@ -293,13 +293,13 @@ peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.exa
|
|||
|
||||
Now that the buyer and seller have agreed to the same price, Org1 can transfer the marble to Org2.
|
||||
```
|
||||
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles_transfer -c '{"function":"TransferMarble","Args":["marble1","Org2MSP"]}' --transient "{\"marble_properties\":\"$MARBLE_PROPERTIES\",\"marble_price\":\"$MARBLE_PRICE\"}" --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
|
||||
peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles_transfer -c '{"function":"TransferAsset","Args":["marble1","Org2MSP"]}' --transient "{\"marble_properties\":\"$MARBLE_PROPERTIES\",\"marble_price\":\"$MARBLE_PRICE\"}" --peerAddresses localhost:7051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org1.example.com/peers/peer0.org1.example.com/tls/ca.crt --peerAddresses localhost:9051 --tlsRootCertFiles ${PWD}/organizations/peerOrganizations/org2.example.com/peers/peer0.org2.example.com/tls/ca.crt
|
||||
```
|
||||
|
||||
You can query the marble ownership record to verify that the transfer was successful.
|
||||
|
||||
```
|
||||
peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles_transfer -c '{"function":"QueryMarble","Args":["marble1"]}'
|
||||
peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles_transfer -c '{"function":"GetAsset","Args":["marble1"]}'
|
||||
```
|
||||
|
||||
The record now lists Org2 as the Marble owner:
|
||||
|
|
@ -314,7 +314,7 @@ The record now lists Org2 as the Marble owner:
|
|||
|
||||
Operate from the Org2 terminal. Now that Org2 owns the marble, we can read the marble details from the Org2 implicit data collection:
|
||||
```
|
||||
peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles_transfer -c '{"function":"QueryMarblePrivateImmutableProperties","Args":["marble1"]}'
|
||||
peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles_transfer -c '{"function":"GetAssetPrivateProperties","Args":["marble1"]}'
|
||||
```
|
||||
|
||||
Org2 can now update the marble public description:
|
||||
|
|
@ -324,7 +324,7 @@ peer chaincode invoke -o localhost:7050 --ordererTLSHostnameOverride orderer.exa
|
|||
|
||||
Query the ledger to verify that the marble is no longer for sale:
|
||||
```
|
||||
peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles_transfer -c '{"function":"QueryMarble","Args":["marble1"]}'
|
||||
peer chaincode query -o localhost:7050 --ordererTLSHostnameOverride orderer.example.com --tls --cafile ${PWD}/organizations/ordererOrganizations/example.com/orderers/orderer.example.com/msp/tlscacerts/tlsca.example.com-cert.pem -C mychannel -n marbles_transfer -c '{"function":"GetAsset","Args":["marble1"]}'
|
||||
```
|
||||
|
||||
## Clean up
|
||||
|
|
|
|||
|
|
@ -47,8 +47,8 @@ type Marble struct {
|
|||
PublicDescription string `json:"public_description"`
|
||||
}
|
||||
|
||||
// CreateMarble creates a marble and sets it as owned by the client's org
|
||||
func (s *SmartContract) CreateMarble(ctx contractapi.TransactionContextInterface, marbleID string) error {
|
||||
// IssueAsset creates a marble and sets it as owned by the client's org
|
||||
func (s *SmartContract) IssueAsset(ctx contractapi.TransactionContextInterface, marbleID string) error {
|
||||
|
||||
transMap, err := ctx.GetStub().GetTransient()
|
||||
if err != nil {
|
||||
|
|
@ -113,7 +113,7 @@ func (s *SmartContract) ChangePublicDescription(ctx contractapi.TransactionConte
|
|||
return fmt.Errorf("failed to get verified OrgID: %s", err.Error())
|
||||
}
|
||||
|
||||
marble, err := s.QueryMarble(ctx, marbleID)
|
||||
marble, err := s.GetAsset(ctx, marbleID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get marble: %s", err.Error())
|
||||
}
|
||||
|
|
@ -135,17 +135,31 @@ func (s *SmartContract) ChangePublicDescription(ctx contractapi.TransactionConte
|
|||
|
||||
// AgreeToSell adds seller's asking price to seller's implicit private data collection
|
||||
func (s *SmartContract) AgreeToSell(ctx contractapi.TransactionContextInterface, marbleID string) error {
|
||||
// Query marble and verify that this clientOrgId actually owns the marble.
|
||||
marble, err := s.GetAsset(ctx, marbleID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return agreeToMarblePrice(ctx, marbleID, typeMarbleForSale)
|
||||
clientOrgID, err := getClientOrgID(ctx, true)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get verified OrgID: %s", err.Error())
|
||||
}
|
||||
|
||||
if clientOrgID != marble.OwnerOrg {
|
||||
return fmt.Errorf("a client from %s cannot sell a marble owned by %s", clientOrgID, marble.OwnerOrg)
|
||||
}
|
||||
|
||||
return agreeToPrice(ctx, marbleID, typeMarbleForSale)
|
||||
}
|
||||
|
||||
// AgreeToBuy adds buyer's bid price to buyer's implicit private data collection
|
||||
func (s *SmartContract) AgreeToBuy(ctx contractapi.TransactionContextInterface, marbleID string) error {
|
||||
return agreeToMarblePrice(ctx, marbleID, typeMarbleBid)
|
||||
return agreeToPrice(ctx, marbleID, typeMarbleBid)
|
||||
}
|
||||
|
||||
// agreeToMarblePrice adds a bid or ask price to caller's implicit private data collection
|
||||
func agreeToMarblePrice(ctx contractapi.TransactionContextInterface, marbleID string, priceType string) error {
|
||||
// agreeToPrice adds a bid or ask price to caller's implicit private data collection
|
||||
func agreeToPrice(ctx contractapi.TransactionContextInterface, marbleID string, priceType string) error {
|
||||
|
||||
// Get client org id and verify it matches peer org id.
|
||||
// In this scenario, client is only authorized to read/write private data from its own peer.
|
||||
|
|
@ -154,9 +168,6 @@ func agreeToMarblePrice(ctx contractapi.TransactionContextInterface, marbleID st
|
|||
return fmt.Errorf("failed to get verified OrgID: %s", err.Error())
|
||||
}
|
||||
|
||||
// TODO query marble and verify that this clientOrgId actually owns the marble.
|
||||
// That is, You can only put the marble for sale if you own it.
|
||||
|
||||
// price is private, therefore it gets passed in transient field
|
||||
transMap, err := ctx.GetStub().GetTransient()
|
||||
if err != nil {
|
||||
|
|
@ -193,9 +204,9 @@ func agreeToMarblePrice(ctx contractapi.TransactionContextInterface, marbleID st
|
|||
// Org2 would call a verify function on his peer.
|
||||
// The properties and salt would passed in, get hashed in the chaincode, and compared with the on-chain hash of the marble properties (queried via GetPrivateDataHash).
|
||||
|
||||
// TransferMarble checks transfer conditions and then transfers marble state to buyer.
|
||||
// TransferMarble can only be called by current owner
|
||||
func (s *SmartContract) TransferMarble(ctx contractapi.TransactionContextInterface, marbleID string, buyerOrgID string) error {
|
||||
// TransferAsset checks transfer conditions and then transfers marble state to buyer.
|
||||
// TransferAsset can only be called by current owner
|
||||
func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, marbleID string, buyerOrgID string) error {
|
||||
|
||||
// Get client org id and verify it matches peer org id.
|
||||
// For a transfer, selling client must get endorsement from their own peer and from buyer peer, therefore don't verify client org id matches peer org id
|
||||
|
|
@ -219,7 +230,7 @@ func (s *SmartContract) TransferMarble(ctx contractapi.TransactionContextInterfa
|
|||
return fmt.Errorf("marble_price key not found in the transient map")
|
||||
}
|
||||
|
||||
marble, err := s.QueryMarble(ctx, marbleID)
|
||||
marble, err := s.GetAsset(ctx, marbleID)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get marble: %s", err.Error())
|
||||
}
|
||||
|
|
@ -360,27 +371,37 @@ func transferMarbleState(ctx contractapi.TransactionContextInterface, marble *Ma
|
|||
// getClientOrgID gets the client org ID.
|
||||
// The client org ID can optionally be verified against the peer org ID, to ensure that a client from another org doesn't attempt to read or write private data from this peer.
|
||||
// The only exception in this scenario is for TransferMarble, since the current owner needs to get an endorsement from the buyer's peer.
|
||||
func getClientOrgID(ctx contractapi.TransactionContextInterface, verifyClientOrgMatchesPeerOrg bool) (string, error) {
|
||||
func getClientOrgID(ctx contractapi.TransactionContextInterface, verifyOrg bool) (string, error) {
|
||||
|
||||
clientOrgID, err := ctx.GetClientIdentity().GetMSPID()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed getting client's orgID: %s", err.Error())
|
||||
}
|
||||
|
||||
if verifyClientOrgMatchesPeerOrg {
|
||||
peerOrgID, err := shim.GetMSPID()
|
||||
if verifyOrg {
|
||||
err = verifyClientOrgMatchesPeerOrg(clientOrgID)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed getting peer's orgID: %s", err.Error())
|
||||
}
|
||||
|
||||
if clientOrgID != peerOrgID {
|
||||
return "", fmt.Errorf("client from org %s is not authorized to read or write private data from an org %s peer", clientOrgID, peerOrgID)
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
|
||||
return clientOrgID, nil
|
||||
}
|
||||
|
||||
// verify client org id and matches peer org id.
|
||||
func verifyClientOrgMatchesPeerOrg(clientOrgID string) error {
|
||||
peerOrgID, err := shim.GetMSPID()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed getting peer's orgID: %s", err.Error())
|
||||
}
|
||||
|
||||
if clientOrgID != peerOrgID {
|
||||
return fmt.Errorf("client from org %s is not authorized to read or write private data from an org %s peer", clientOrgID, peerOrgID)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setMarbleStateBasedEndorsement adds an endorsement policy to a marble so that only a peer from an owning org can update or transfer the marble.
|
||||
func setMarbleStateBasedEndorsement(ctx contractapi.TransactionContextInterface, marbleID string, orgToEndorse string) error {
|
||||
|
||||
|
|
@ -402,6 +423,21 @@ func setMarbleStateBasedEndorsement(ctx contractapi.TransactionContextInterface,
|
|||
return nil
|
||||
}
|
||||
|
||||
func getClientImplicitCollectionName(ctx contractapi.TransactionContextInterface) (string, error) {
|
||||
clientOrgID, err := getClientOrgID(ctx, true)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get verified OrgID: %s", err.Error())
|
||||
}
|
||||
|
||||
err = verifyClientOrgMatchesPeerOrg(clientOrgID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
collection := "_implicit_org_" + clientOrgID
|
||||
return collection, nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
chaincode, err := contractapi.NewChaincode(new(SmartContract))
|
||||
|
|
|
|||
|
|
@ -22,12 +22,26 @@ package main
|
|||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/hyperledger/fabric-contract-api-go/contractapi"
|
||||
)
|
||||
|
||||
// QueryMarble returns the public marble data
|
||||
func (s *SmartContract) QueryMarble(ctx contractapi.TransactionContextInterface, marbleID string) (*Marble, error) {
|
||||
// QueryResult structure used for handling result of query
|
||||
type QueryResult struct {
|
||||
Record *Marble
|
||||
TxId string `json:"txId"`
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
}
|
||||
|
||||
type Agreement struct {
|
||||
ID string `json:"marble_id"`
|
||||
Price int `json:"price"`
|
||||
TradeID string `json:"trade_id"`
|
||||
}
|
||||
|
||||
// GetAsset returns the public marble data
|
||||
func (s *SmartContract) GetAsset(ctx contractapi.TransactionContextInterface, marbleID string) (*Marble, error) {
|
||||
|
||||
// since only public data is accessed in this function, no access control is required
|
||||
|
||||
|
|
@ -45,18 +59,16 @@ func (s *SmartContract) QueryMarble(ctx contractapi.TransactionContextInterface,
|
|||
return marble, nil
|
||||
}
|
||||
|
||||
// QueryMarblePrivateImmutableProperties returns the immutable marble properties from owner's private data collection
|
||||
func (s *SmartContract) QueryMarblePrivateImmutableProperties(ctx contractapi.TransactionContextInterface, marbleID string) (string, error) {
|
||||
// GetAssetPrivateProperties returns the immutable marble properties from owner's private data collection
|
||||
func (s *SmartContract) GetAssetPrivateProperties(ctx contractapi.TransactionContextInterface, marbleID string) (string, error) {
|
||||
|
||||
// Get client org id and verify it matches peer org id.
|
||||
// In this scenario, client is only authorized to read/write private data from its own peer.
|
||||
clientOrgID, err := getClientOrgID(ctx, true)
|
||||
collection, err := getClientImplicitCollectionName(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get verified OrgID: %s", err.Error())
|
||||
return "", err
|
||||
}
|
||||
|
||||
collection := "_implicit_org_" + clientOrgID
|
||||
|
||||
immutableProperties, err := ctx.GetStub().GetPrivateData(collection, marbleID)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to read marble private properties from client org's collection: %s", err.Error())
|
||||
|
|
@ -68,28 +80,24 @@ func (s *SmartContract) QueryMarblePrivateImmutableProperties(ctx contractapi.Tr
|
|||
return string(immutableProperties), nil
|
||||
}
|
||||
|
||||
// QueryMarbleSalesPrice returns the sales price as an integer
|
||||
func (s *SmartContract) QueryMarbleSalesPrice(ctx contractapi.TransactionContextInterface, marbleID string) (string, error) {
|
||||
return queryMarblePrice(ctx, marbleID, typeMarbleForSale)
|
||||
// GetAssetSalesPrice returns the sales price as an integer
|
||||
func (s *SmartContract) GetAssetSalesPrice(ctx contractapi.TransactionContextInterface, marbleID string) (string, error) {
|
||||
return getAssetPrice(ctx, marbleID, typeMarbleForSale)
|
||||
}
|
||||
|
||||
// QueryMarbleBidPrice returns the bid price as an integer
|
||||
func (s *SmartContract) QueryMarbleBidPrice(ctx contractapi.TransactionContextInterface, marbleID string) (string, error) {
|
||||
return queryMarblePrice(ctx, marbleID, typeMarbleBid)
|
||||
// GetAssetBidPrice returns the bid price as an integer
|
||||
func (s *SmartContract) GetAssetBidPrice(ctx contractapi.TransactionContextInterface, marbleID string) (string, error) {
|
||||
return getAssetPrice(ctx, marbleID, typeMarbleBid)
|
||||
}
|
||||
|
||||
// queryMarblePrice gets the bid or ask price from caller's implicit private data collection
|
||||
func queryMarblePrice(ctx contractapi.TransactionContextInterface, marbleID string, priceType string) (string, error) {
|
||||
// getAssetPrice gets the bid or ask price from caller's implicit private data collection
|
||||
func getAssetPrice(ctx contractapi.TransactionContextInterface, marbleID string, priceType string) (string, error) {
|
||||
|
||||
// Get client org id and verify it matches peer org id.
|
||||
// In this scenario, client is only authorized to read/write private data from its own peer.
|
||||
clientOrgID, err := getClientOrgID(ctx, true)
|
||||
collection, err := getClientImplicitCollectionName(ctx)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get verified OrgID: %s", err.Error())
|
||||
return "", err
|
||||
}
|
||||
|
||||
collection := "_implicit_org_" + clientOrgID
|
||||
|
||||
marblePriceKey, err := ctx.GetStub().CreateCompositeKey(priceType, []string{marbleID})
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to create composite key: %s", err.Error())
|
||||
|
|
@ -106,16 +114,82 @@ func queryMarblePrice(ctx contractapi.TransactionContextInterface, marbleID stri
|
|||
return string(marblePriceJSON), nil
|
||||
}
|
||||
|
||||
// TODO add a query to get all of an organization's proposed sales
|
||||
// Use GetPrivateDataByPartialCompositeKey to find all keys starting with typeMarbleForSale
|
||||
// hint: see sample at https://github.com/hyperledger/fabric-samples/blob/master/chaincode/marbles02/go/marbles_chaincode.go#L458
|
||||
// QueryAssetSaleAgreements returns all of an organization's proposed sales
|
||||
func (s *SmartContract) QueryAssetSaleAgreements(ctx contractapi.TransactionContextInterface) ([]Agreement, error) {
|
||||
return queryAgreementsByType(ctx, typeMarbleForSale)
|
||||
}
|
||||
|
||||
// TODO add a query to get all of an organization's proposed buys
|
||||
// Use GetPrivateDataByPartialCompositeKey to find all keys starting with typeMarbleBid
|
||||
// QueryAssetBuyAgreements returns all of an organization's proposed buys
|
||||
func (s *SmartContract) QueryAssetBuyAgreements(ctx contractapi.TransactionContextInterface) ([]Agreement, error) {
|
||||
return queryAgreementsByType(ctx, typeMarbleBid)
|
||||
}
|
||||
|
||||
func queryAgreementsByType(ctx contractapi.TransactionContextInterface, agreeType string) ([]Agreement, error) {
|
||||
collection, err := getClientImplicitCollectionName(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Query for any object type starting with `agreeType`
|
||||
agreementsIterator, err := ctx.GetStub().GetPrivateDataByPartialCompositeKey(collection, agreeType, []string{})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("failed to read from private data collection: %s", err.Error())
|
||||
}
|
||||
defer agreementsIterator.Close()
|
||||
|
||||
agreements := []Agreement{}
|
||||
|
||||
for agreementsIterator.HasNext() {
|
||||
resp, err := agreementsIterator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
newAgree := new(Agreement)
|
||||
err = json.Unmarshal(resp.Value, newAgree)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
agreements = append(agreements, *newAgree)
|
||||
}
|
||||
|
||||
return agreements, nil
|
||||
}
|
||||
|
||||
// TODO add a JSON index and query to return all of an organization's marbles larger than a certain size (only works when using CouchDB state database)
|
||||
// hint: see sample index at https://github.com/hyperledger/fabric-samples/blob/master/chaincode/marbles02/go/META-INF/statedb/couchdb/indexes/indexOwner.json
|
||||
// hint: see sample query at https://github.com/hyperledger/fabric-samples/blob/master/chaincode/marbles02/go/marbles_chaincode.go#L515
|
||||
|
||||
// TODO add a history query so that users can see the chain of custody for a marble since issuance
|
||||
// hint: see sample at https://github.com/hyperledger/fabric-samples/blob/master/chaincode/marbles02/go/marbles_chaincode.go#L692
|
||||
// QueryAssetHistory returns the chain of custody for a marble since issuance
|
||||
func (s *SmartContract) QueryAssetHistory(ctx contractapi.TransactionContextInterface, marbleID string) ([]QueryResult, error) {
|
||||
resultsIterator, err := ctx.GetStub().GetHistoryForKey(marbleID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resultsIterator.Close()
|
||||
|
||||
records := []QueryResult{}
|
||||
|
||||
for resultsIterator.HasNext() {
|
||||
response, err := resultsIterator.Next()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
marble := new(Marble)
|
||||
err = json.Unmarshal(response.Value, marble)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
record := QueryResult{
|
||||
TxId: response.TxId,
|
||||
Timestamp: time.Unix(response.Timestamp.Seconds, int64(response.Timestamp.Nanos)),
|
||||
Record: marble,
|
||||
}
|
||||
records = append(records, record)
|
||||
}
|
||||
|
||||
return records, nil
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue