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:
Tiffany Harris 2020-05-14 16:03:40 -04:00 committed by denyeart
parent 0c61f84af7
commit df292e0e8d
3 changed files with 173 additions and 63 deletions

View file

@ -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"]}'
```
![Org1 and Org2 agree on transfer](images/transfer_marbles_2.png)
@ -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

View file

@ -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))

View file

@ -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
}