diff --git a/README.MD b/README.MD index ca09401..11d3b5c 100644 --- a/README.MD +++ b/README.MD @@ -5,8 +5,8 @@ This project contains sample blockchain smart contracts and generic user interfa These contracts are intended for exploration of the IBM Blockchain Bluemix environment and the Hyperledger fabric. They can be used to seed smart contract development with advanced features. > **Note:** The release strategies for Hyperledger and for this project are evolving, so contract development is currently separated into folders that remain compatible with specific generations of the Hyperledger project on Bluemix. - --- + ## Hyperledger Supported Components The Hyperledger Bluemix environment will evolve with the intent that releases will be tagged in the Hyperledger [fabric project](https://github.com/hyperledger/fabric) to indicate Bluemix compatibility levels. At the time of writing -- 16 June 2016 -- the Hyperledger fabric on Bluemix is compatible with the following folders in this project: diff --git a/cashMachine/cashMachine/README.MD b/cashMachine/cashMachine/README.MD index db911d8..2606024 100644 --- a/cashMachine/cashMachine/README.MD +++ b/cashMachine/cashMachine/README.MD @@ -1,24 +1,34 @@ -#Quick set-up +# Quick set-up -##Use case: -Track cash machine data - asset id, ActionType (Initial Balance, Deposit or Withdraw), Amount (amount of a particular transaction), Balance (total balance of the Cash Machine) and Timestamp. On create, an initial balance is set for the machine. On update, Deposit or Withdraw action take place and the balance is updated. The current state of the machine, retrieved on read, will have the asset id, the last Action type, the amount for the action type, the current balance and the last action's timestamp. The contract will track history and will return history in descending order. The functions are createCashMachine, updateCashMachine, readCashMachine, readCashMachineHistory and deleteCashMachine.Get monitoring ui to work with the same. +## Use Case + +Track cash machine data - asset id, ActionType (Initial Balance, Deposit or Withdraw), Amount (amount of a particular transaction), Balance (total balance of the Cash Machine) and Timestamp. On create, an initial balance is set for the machine. On update, Deposit or Withdraw action take place and the balance is updated. The current state of the machine, retrieved on read, will have the asset id, the last Action type, the amount for the action type, the current balance and the last action's timestamp. The contract will track history and will return history in descending order. The functions are createCashMachine, updateCashMachine, readCashMachine, readCashMachineHistory and deleteCashMachine. Get monitoring ui to work with the same. The ask was to be able to run the code on a Sandbox environment, so the instructions below are around how to achieve it. This code is compatible with a newer version of Hyperledger than the one in BlueMix today. Once BlueMix is updated, it can be run on BlueMix. -Assumption : Hyperledger set up and built. +> Assumption : Hyperledger set up and built. + Download the cashMachine folder and save it in the go code path . For example: /Users/spnair/go/src/github.com/hyperledger/fabric/examples/chaincode/go -##Local Machine +## Local Machine + LOCALDEVDIR will be something similar to /Users/spnair/go/src/github.com/hyperledger/fabric/devenv Its not mandatory that this be set. -###1. Navigate to the folder with the Vagrant file (LOCALDEVDIR). Bring up vagrant -``` +### 1. Navigate to the folder with the Vagrant file (LOCALDEVDIR). Bring up vagrant + +``` bash + FORWARD_DOCKER_PORTS='true' vagrant up + ``` -##Inside Vagrant Ubuntu + +## Inside Vagrant Ubuntu + There are two key folders you will need to use: the folder where the peer executable is, and the chain code folder. For convenience, you could add these as environment variables in bashrc -``` + +``` bash + vi ~/.bashrc Towards the end of the file, add the following: #path to chaincode folder - could vary based on the path where the code is saved @@ -27,56 +37,93 @@ export CCPATH #path to peer PRPATH="$GOPATH/src/github.com/hyperledger/fabric/peer" export PRPATH + ``` + The above is not mandatory. The following steps _do not assume_ that this has been done, instead, it shows the full path of the concerned folders. -###You will need **3 terminal windows** +### You will need **3 terminal windows** + Vagrant needs to be running on all windows. (don't forget to vagrant ssh) -###2. Navigate to the ‘peer’ folder -``` +### 2. Navigate to the ‘peer’ folder + +``` bash + cd $GOPATH/src/github.com/hyperledger/fabric/peer OR cd $PRPATH (If you set the bashprofile) -``` -###3. Start the peer in dev mode ``` + +### 3. Start the peer in dev mode + +``` bash + ./peer node start --peer-chaincodedev + ``` -###4. In the second tab, navigate to the folder where you have the chaincode. for example, -``` + +### 4. In the second tab, navigate to the folder where you have the chaincode. for example, + +``` bash + cd /opt/gopath/src/github.com/hyperledger/fabric/examples/chaincode/go/cashMachine OR cd $CCPATH/cashMachine (If you set the bashprofile) + ``` -###5. build the chaincode -``` + +### 5. build the chaincode + +``` bash + go build + ``` + If there are build errors, they will be crearly reported and listed with line number, so it should be easy to identify and fix them. -###6. register +### 6. register + In Sandbox environment, we can register the chain code with a name we specify. This step applies __only to sandbox__. In DevNet, the name would be assigned (long 128 b alphanumeric string) when you deploy the chain code -``` + +``` bash + CORE_CHAINCODE_ID_NAME=cash CORE_PEER_ADDRESS=0.0.0.0:30303 ./cashMachine + ``` -###7. On the third terminal window, run the chaincode commands from the peer folder -``` + +### 7. On the third terminal window, run the chaincode commands from the peer folder + +``` bash + cd $GOPATH/src/github.com/hyperledger/fabric/peer OR cd $PRPATH (If you set the bashprofile) -``` -####a. deploy ``` - ./peer chaincode deploy -n cash -c '{"Function":"Init", "Args": ["{\"version\":\"1.0\"}"]}' + +#### a. deploy + +``` bash + +./peer chaincode deploy -n cash -c '{"Function":"Init", "Args": ["{\"version\":\"1.0\"}"]}' + ``` + This returns the name of the chaincode - in this case, 'cash' In a DevNet environment, the new chaincode id would be created and returned. -####b. Read a sample to see what data should be sent in -``` + +#### b. Read a sample to see what data should be sent in + +``` bash + ./peer chaincode query -n cash -c '{"Function":"readCashMachineSamples", "Args":[]}' + ``` + This returns: -``` + +``` json + { "event": { "assetID": "The ID of a managed asset. In this case, the cash machine's unique id wrt monetary transactions.For query operations, only assetID needs to be sent in.", @@ -95,46 +142,72 @@ This returns: "Timestamp": "A string with timestamp. If not sent in, it is set to the transaction time in the fabric" } } + ``` + The 'event' section indicates what needs to be sent in for an invoke or query call. The 'initEvent' section tells you what to send in while initializing (deploying) the chaincode. The 'state' section tells you what to expect as a response to query calls (the 'read' calls - except for readCashMachineSamples which is explained here) -####c. invoke : createCashMachine - create an id for the cashmachine with the initial balance -``` - ./peer chaincode invoke -n cash -c '{"Function":"createCashMachine", "Args":["{\"assetID\":\"CM01\",\"actiontype\":\"InitialBalance\", \"amount\":1000, \"timestamp\":\"2016-06-07 10:01:01 UTC\"}"]}' - ``` +#### c. invoke : createCashMachine - create an id for the cashmachine with the initial balance + +``` bash + +./peer chaincode invoke -n cash -c '{"Function":"createCashMachine", "Args":["{\"assetID\":\"CM01\",\"actiontype\":\"InitialBalance\", \"amount\":1000, \"timestamp\":\"2016-06-07 10:01:01 UTC\"}"]}' -####d. query: readCashMachine - check the most recent status of the machine ``` + +#### d. query: readCashMachine - check the most recent status of the machine + +``` bash + ./peer chaincode query -n cash -c '{"Function":"readCashMachine", "Args":["{\"assetID\":\"CM01\"}"]}' + ``` -####e. invoke : updateCashMachine - Update the cash machine balance - operations could be "Deposit" or "Withdraw". Right now it assumes that if the ActionType is not a 'Deposit", it mush be a withdrawal. -``` - ./peer chaincode invoke -n cash -c '{"Function":"updateCashMachine", "Args":["{\"assetID\":\"CM01\",\"actiontype\":\"Deposit\", \"amount\":10}"]}' -OR - ./peer chaincode invoke -n cash -c '{"Function":"updateCashMachine", "Args":["{\"assetID\":\"CM01\",\"actiontype\":\"Withdraw\", \"amount\":20}"]}' - ``` -####f. query : readCashMachineHistory - see all transactions so far by Asset ID. + +#### e. invoke : updateCashMachine - Update the cash machine balance - operations could be "Deposit" or "Withdraw". Right now it assumes that if the ActionType is not a 'Deposit", it mush be a withdrawal. + +``` bash + +./peer chaincode invoke -n cash -c '{"Function":"updateCashMachine", "Args":["{\"assetID\":\"CM01\",\"actiontype\":\"Deposit\", \"amount\":10}"]}' +OR +./peer chaincode invoke -n cash -c '{"Function":"updateCashMachine", "Args":["{\"assetID\":\"CM01\",\"actiontype\":\"Withdraw\", \"amount\":20}"]}' + ``` + +#### f. query : readCashMachineHistory - see all transactions so far by Asset ID. + +``` bash + ./peer chaincode query -n cash -c '{"Function":"readCashMachineHistory", "Args":["{\"assetID\":\"CM01\"}"]}' + ``` + This returns all the transactions in descending order -``` + +``` json + {"cashhistory":["{\"assetid\":\"CM01\",\"actiontype\":\"Withdraw\",\"amount\":20,\"balance\":990,\"timestamp\":\"2016-06-07 13:24:31.752701888 +0000 UTC\"}","{\"assetid\":\"CM01\",\"actiontype\":\"Deposit\",\"amount\":10,\"balance\":1010,\"timestamp\":\"2016-06-07 13:23:58.336522709 +0000 UTC\"}","{\"assetid\":\"CM01\",\"actiontype\":\"InitialBalance\",\"amount\":1000,\"balance\":1000,\"timestamp\":\"2016-06-07 13:22:52.732470093 +0000 UTC\"}"]} + ``` -####g.invoke: deleteCashMachine - delete the asset record -``` + +#### g.invoke: deleteCashMachine - delete the asset record + +``` bash + ./peer chaincode invoke -n cash -c '{"Function":"deleteCashMachine", "Args":["{\"assetID\":\"CM01\"}"]}' + ``` + If you run the readCashMachine command for the same asset now, it will return an error _Error: Error querying chaincode: rpc error: code = 2 desc = "Error:Transaction or query returned with failure: Unable to get asset state from ledger"_ -##Testing on Postman +## Testing on Postman + Test scripts are in this folder as 'CashMachine.postman_collection' -##Testing with the monitoring UI +## Testing with the monitoring UI + https://www.youtube.com/watch?v=p8SSAnuMZec https://www.youtube.com/watch?v=cPuIgOygxzo https://www.youtube.com/watch?v=30jE2xLlq_k -The changes mentioned in the video are implemented in the version of monitoring_UI in this branch - +The changes mentioned in the videos are implemented in the `cashmachine_UI` as derived from the `monitoring_UI` in this folder. \ No newline at end of file diff --git a/cashMachine/cashMachine_ui/README.md b/cashMachine/cashMachine_ui/README.md index e49ba9e..6596ed6 100755 --- a/cashMachine/cashMachine_ui/README.md +++ b/cashMachine/cashMachine_ui/README.md @@ -1,4 +1,4 @@ -Blockchain Monitoring UI +# Blockchain Monitoring UI ========================= A dynamically generated UI for IBM IoT Blockchain. diff --git a/sdk/docs/assetsAndEvents.md b/sdk/docs/assetsAndEvents.md index 5c1db3b..8cf9a2e 100644 --- a/sdk/docs/assetsAndEvents.md +++ b/sdk/docs/assetsAndEvents.md @@ -4,10 +4,134 @@ ### **Asset** -The focus of an IoT smart contract is often referred to as an asset. Some tamgible thing that For example, in a contract designed to track warranty status on cars, it could be the warranty itself, or the car. +The primary focus of an IoT smart contract is often referred to as an asset. Some tangible thing that tracks the state of something important. An example might be the warranty status on a car. The primary asset could be the warranty itself or the car. When tracking of only one asset is desired, as in a contract derived from the [`iot_sample_contract`](../iot_sample_contract), the choice must be made as to which asset is the one most likely to be referenced. -For simple contracts, the primary asset is selected and the API is designed to manipulate that asset class. As discussed in the [Introduction to Hyperledger Smart Contracts for IoT, Best Practices and Patterns](HyperledgerContractsIntroBestPracticesPatterns.html) document, these samples use a pattern based on a simplified CRUD (create, read update, delete) API for manipulating assets. The IoT sample, for example, had functions called `createAsset`, `readAsset`, `updateAsset`, and `deleteAsset` among others. +With the example of a warranty contract, it is easy to imagine that the car's VIN will always be readily accessible when the owner or a concerned 3rd party such as an authorized repair center would like to check on the warranty status. It is obvious from a rudimentary discussion of the primary scenarios that the VIN is the right choice for the lookup key (which we call the `assetID`) and thus the car itself is the primary contract asset. -Generic names reduce the need for rework when the sample is extended or adapted into other domains. However, the generic names do not reinforce the asset class in use, and so the advanced multi-asset aviation contract sample adopts more explicit and accurate naming conventions. For example, there are three asset classes in the +Once the primary asset is selected, the API is designed to manipulate that asset class. As discussed in the [Introduction to Hyperledger Smart Contracts for IoT, Best Practices and Patterns](HyperledgerContractsIntroBestPracticesPatterns.html) document, these samples use a pattern based on a simplified CRUD (create, read update, delete) API for manipulating assets. The IoT sample, for example, has this delegation for its asset CRUD functions: + +``` go + +func (t *SimpleChaincode) Invoke(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) { + if function == "createAsset" { + return t.createAsset(stub, args) + } else if function == "updateAsset" { + return t.updateAsset(stub, args) + } else if function == "deleteAsset" { + return t.deleteAsset(stub, args) + } else if function == "deleteAllAssets" { + return t.deleteAllAssets(stub, args) + } else if function == "deletePropertiesFromAsset" { + return t.deletePropertiesFromAsset(stub, args) + } else if function == "setLoggingLevel" { + return nil, t.setLoggingLevel(stub, args) + } else if function == "setCreateOnUpdate" { + return nil, t.setCreateOnUpdate(stub, args) + } + err := fmt.Errorf("Invoke received unknown invocation: %s", function) + log.Warning(err) + return nil, err +} + +``` + +There is little reason to change the crud pattern described above to more specific asset class names (e.g. warranty, automobile) in a single asset contract, which we often call these recorder contracts as they are mainly concerned with recording asset status. The default names (i.e. boiler plate) in the IoT sample contract implementation work perfectly fine for a single asset class, so calling it "asset" throughout the contract causes no confusion. + +So as a general rule for single-asset-class contracts, generic names work and have the property of reducing the need for rework when a sample is extended or adapted into other domains. However, generic names do not differentiate asset classes in an *advanced multi-asset* contract such as the aviation sample contract, so these contracts would adopt the more explicit naming convention for assets. Like this: + +``` go + +func (t *SimpleChaincode) Invoke(stub *shim.ChaincodeStub, function string, args []string) ([]byte, error) { + // asset CRUD API + if function == "createAssetAirline" { + return t.createAssetAirline(stub, args) + } else if function == "createAssetAircraft" { + return t.createAssetAircraft(stub, args) + } else if function == "createAssetAssembly" { + return t.createAssetAssembly(stub, args) + } else if function == "updateAssetAirline" { + return t.updateAssetAirline(stub, args) + } else if function == "updateAssetAircraft" { + return t.updateAssetAircraft(stub, args) + } else if function == "updateAssetAssembly" { + return t.updateAssetAssembly(stub, args) + } else if function == "deleteAssetAirline" { + return t.deleteAssetAirline(stub, args) + } else if function == "deleteAssetAircraft" { + return t.deleteAssetAircraft(stub, args) + } else if function == "deleteAssetAssembly" { + return t.deleteAssetAssembly(stub, args) + } else if function == "deleteAllAssetsAirline" { + return t.deleteAllAssetsAirline(stub, args) + } else if function == "deleteAllAssetsAircraft" { + return t.deleteAllAssetsAircraft(stub, args) + } else if function == "deleteAllAssetsAssembly" { + return t.deleteAllAssetsAssembly(stub, args) + } else if function == "deletePropertiesFromAssetAirline" { + return t.deletePropertiesFromAssetAirline(stub, args) + } else if function == "deletePropertiesFromAssetAircraft" { + return t.deletePropertiesFromAssetAircraft(stub, args) + } else if function == "deletePropertiesFromAssetAssembly" { + return t.deletePropertiesFromAssetAssembly(stub, args) + + // event API + } else if function == "eventFlight" { + return eventFlight(stub, args) + } else if function == "eventInspection" { + return eventInspection(stub, args) + } else if function == "eventAnalyticAdjustment" { + return eventAnalyticAdjustment(stub, args) + } else if function == "eventMaintenance" { + return eventMaintenance(stub, args) + + // contract dynamic config API + } else if function == "updateContractConfig" { + return nil, updateContractConfig(stub, args) + + // contract state / behavior API + } else if function == "setLoggingLevel" { + return nil, t.setLoggingLevel(stub, args) + } else if function == "setCreateOnUpdate" { + return nil, t.setCreateOnUpdate(stub, args) + + // debugging API + } else if function == "deleteWorldState" { + return nil, t.deleteWorldState(stub) + } + err := fmt.Errorf("Invoke received unknown invocation: %s", function) + log.Warning(err) + return nil, err +} + +``` + +As you can see, API proliferation is unaviodable in such a substantial smart contract, so it is necessary to follow clear naming conventions (patterns) and to not deviate at any point. The CRUD pattern continues to serve us well for assets, and so the adaptation is to simply add the asset's name to the standard name like this: + +|Single Asset|Multi-Asset| +|:------------:|:-----------:| +|createAsset|createAssetAircraft| +|readAsset|readAssetAircraft| +|updateAsset|updateAssetAircraft| +|deleteAsset|deleteAssetAircraft| + +> **Caution**: *Do not get carried away with name adaptation for asset classes. Only the asset CRUD functions should be renamed. It is **not** appropriate to rename the service function `readAssetSchemas`, which is used for integration into applications such as the [Watson IoT Platform](http://www.ibm.com/internet-of-things/iot-solutions/watson-iot-platform/). This function is a convention that cannot change in order to preserve smooth interoperation with the platform and with tools such as the generic [monitoring_UI](../../monitoring_UI).* + +### Event + +The word event is defined as: + +> **event** +noun +a thing that happens, especially one of importance. +"one of the main political events of the late 20th century" + +And for a smart contract, events are important indeed. Events drive the contract state to change, as they do in any computer program, especially those that handle events from physical hardware like mice and keyboards. Each click, movement, keypress and so on is an event as far as the program is concerned. + +It is much the same with smart contracts. But since event is one of the most overloaded words in the history of programming, we will draw some distinctions between *kinds* of events. + +#### Device Events + +The Internet of Things is about, well, *things*. In our contracts, we call the big things assets. They move, or encapsulate some important functions, or cost a lot of money and must be protected or whatever. + +There are many smaller things that can tell us what is happening to the bigger things (assets), and we call the smaller things *devices*. -BLAH BLAH XX \ No newline at end of file