github.com/myafeier/fabric@v1.0.1-0.20170722181825-3a4b1f2bce86/docs/source/chaincode4ade.rst (about) 1 Chaincode for Developers 2 ======================== 3 4 What is Chaincode? 5 ------------------ 6 7 Chaincode is a program, written in `Go <https://golang.org>`_ that implements a 8 prescribed interface. Eventually, other programming languages such as Java, 9 will be supported. Chaincode runs in a secured Docker container isolated from 10 the endorsing peer process. Chaincode initializes and manages the ledger state 11 through transactions submitted by applications. 12 13 A chaincode typically handles business logic agreed to by members of the 14 network, so it similar to a "smart contract". Ledger state created by 15 a chaincode is scoped exclusively to that chaincode and can't be accessed 16 directly by another chaincode. Given the appropriate permission, a chaincode may 17 invoke another chaincode to access its state within the same network. 18 19 In the following sections, we will explore chaincode through the eyes of an 20 application developer. We'll present a simple chaincode sample application 21 and walk through the purpose of each method in the Chaincode Shim API. 22 23 Chaincode API 24 ------------- 25 26 Every chaincode program must implement the 27 `Chaincode interface <https://github.com/hyperledger/fabric/blob/master/core/chaincode/shim/interfaces.go#L28>`_ 28 whose methods are called in response to received transactions. 29 In particular the ``Init`` method is called when a 30 chaincode receives an ``instantiate`` or ``upgrade`` transaction so that the 31 chaincode may perform any necessary initialization, including initialization of 32 application state. The ``Invoke`` method is called in response to receiving an 33 ``invoke`` transaction to process transaction proposals. 34 35 The other interface in the chaincode "shim" APIs is the 36 `ChaincodeStubInterface <https://github.com/hyperledger/fabric/blob/master/core/chaincode/shim/interfaces.go#L42>`_ 37 which is used to access and modify the ledger, and to make invocations between 38 chaincodes. 39 40 In this tutorial, we will demonstrate the use of these APIs by implementing a 41 simple chaincode application that manages simple "assets". 42 43 .. _Simple Asset Chaincode: 44 45 Simple Asset Chaincode 46 ---------------------- 47 Our application is a basic sample chaincode to create assets 48 (key-value pairs) on the ledger. 49 50 Choosing a Location for the Code 51 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 52 53 If you haven't been doing programming in Go, you may want to make sure that 54 you have :ref:`Golang` installed and your system properly configured. 55 56 Now, you will want to create a directory for your chaincode application as a 57 child directory of ``$GOPATH/src/``. 58 59 To keep things simple, let's use the following command: 60 61 .. code:: bash 62 63 mkdir -p $GOPATH/src/sacc && cd $GOPATH/src/sacc 64 65 Now, let's create the source file that we'll fill in with code: 66 67 .. code:: bash 68 69 touch sacc.go 70 71 Housekeeping 72 ^^^^^^^^^^^^ 73 74 First, let's start with some housekeeping. As with every chaincode, it implements the 75 `Chaincode interface` <https://github.com/hyperledger/fabric/blob/master/core/chaincode/shim/interfaces.go#L28>_, 76 in particular, ``Init`` and ``Invoke`` functions. So, let's add the go import 77 statements for the necessary dependencies for our chaincode. We'll import the 78 chaincode shim package and the peer protobuf package. 79 80 .. code:: go 81 82 package main 83 84 import ( 85 "fmt" 86 87 "github.com/hyperledger/fabric/core/chaincode/shim" 88 "github.com/hyperledger/fabric/protos/peer" 89 ) 90 91 Initializing the Chaincode 92 ^^^^^^^^^^^^^^^^^^^^^^^^^^ 93 94 Next, we'll implement the ``Init`` function. 95 96 .. code:: go 97 98 // Init is called during chaincode instantiation to initialize any data. 99 func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response { 100 101 } 102 103 .. note:: Note that chaincode upgrade also calls this function. When writing a 104 chaincode that will upgrade an existing one, make sure to modify the ``Init`` 105 function appropriately. In particular, provide an empty "Init" method if there's 106 no "migration" or nothing to be initialized as part of the upgrade. 107 108 Next, we'll retrieve the arguments to the ``Init`` call using the 109 ``ChaincodeStubInterface.GetStringArgs`` function and check for validity. 110 In our case, we are expecting a key-value pair. 111 112 .. code:: go 113 114 // Init is called during chaincode instantiation to initialize any 115 // data. Note that chaincode upgrade also calls this function to reset 116 // or to migrate data, so be careful to avoid a scenario where you 117 // inadvertently clobber your ledger's data! 118 func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response { 119 // Get the args from the transaction proposal 120 args := stub.GetStringArgs() 121 if len(args) != 2 { 122 return shim.Error("Incorrect arguments. Expecting a key and a value") 123 } 124 } 125 126 Next, now that we have established that the call is valid, we'll store the 127 initial state in the ledger. To do this, we will call 128 ``ChaincodeStubInterface.PutState`` with the key and value passed in as 129 the arguments. Assuming all went well, return a peer.Response object that 130 indicates the initialization was a success. 131 132 .. code:: go 133 134 // Init is called during chaincode instantiation to initialize any 135 // data. Note that chaincode upgrade also calls this function to reset 136 // or to migrate data, so be careful to avoid a scenario where you 137 // inadvertently clobber your ledger's data! 138 func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response { 139 // Get the args from the transaction proposal 140 args := stub.GetStringArgs() 141 if len(args) != 2 { 142 return shim.Error("Incorrect arguments. Expecting a key and a value") 143 } 144 145 // Set up any variables or assets here by calling stub.PutState() 146 147 // We store the key and the value on the ledger 148 err := stub.PutState(args[0], []byte(args[1])) 149 if err != nil { 150 return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[0])) 151 } 152 return shim.Success(nil) 153 } 154 155 Invoking the Chaincode 156 ^^^^^^^^^^^^^^^^^^^^^^ 157 158 First, let's add the ``Invoke`` function's signature. 159 160 .. code:: go 161 162 // Invoke is called per transaction on the chaincode. Each transaction is 163 // either a 'get' or a 'set' on the asset created by Init function. The 'set' 164 // method may create a new asset by specifying a new key-value pair. 165 func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response { 166 167 } 168 169 As with the ``Init`` function above, we need to extract the arguments from the 170 ``ChaincodeStubInterface``. The ``Invoke`` function's arguments will be the 171 name of the chaincode application function to invoke. In our case, our application 172 will simply have two functions: ``set`` and ``get``, that allow the value of an 173 asset to be set or its current state to be retrieved. We first call 174 ``ChaincodeStubInterface.GetFunctionAndParameters`` to extract the function 175 name and the parameters to that chaincode application function. 176 177 .. code:: go 178 179 // Invoke is called per transaction on the chaincode. Each transaction is 180 // either a 'get' or a 'set' on the asset created by Init function. The Set 181 // method may create a new asset by specifying a new key-value pair. 182 func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response { 183 // Extract the function and args from the transaction proposal 184 fn, args := stub.GetFunctionAndParameters() 185 186 } 187 188 Next, we'll validate the function name as being either ``set`` or ``get``, and 189 invoke those chaincode application functions, returning an appropriate 190 response via the ``shim.Success`` or ``shim.Error`` functions that will 191 serialize the response into a gRPC protobuf message. 192 193 .. code:: go 194 195 // Invoke is called per transaction on the chaincode. Each transaction is 196 // either a 'get' or a 'set' on the asset created by Init function. The Set 197 // method may create a new asset by specifying a new key-value pair. 198 func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response { 199 // Extract the function and args from the transaction proposal 200 fn, args := stub.GetFunctionAndParameters() 201 202 var result string 203 var err error 204 if fn == "set" { 205 result, err = set(stub, args) 206 } else { 207 result, err = get(stub, args) 208 } 209 if err != nil { 210 return shim.Error(err.Error()) 211 } 212 213 // Return the result as success payload 214 return shim.Success([]byte(result)) 215 } 216 217 Implementing the Chaincode Application 218 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 219 220 As noted, our chaincode application implements two functions that can be 221 invoked via the ``Invoke`` function. Let's implement those functions now. 222 Note that as we mentioned above, to access the ledger's state, we will leverage 223 the ``ChaincodeStubInterface.PutState`` and ``ChaincodeStubInterface.GetState`` 224 functions of the chaincode shim API. 225 226 .. code:: go 227 228 // Set stores the asset (both key and value) on the ledger. If the key exists, 229 // it will override the value with the new one 230 func set(stub shim.ChaincodeStubInterface, args []string) (string, error) { 231 if len(args) != 2 { 232 return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value") 233 } 234 235 err := stub.PutState(args[0], []byte(args[1])) 236 if err != nil { 237 return "", fmt.Errorf("Failed to set asset: %s", args[0]) 238 } 239 return args[1], nil 240 } 241 242 // Get returns the value of the specified asset key 243 func get(stub shim.ChaincodeStubInterface, args []string) (string, error) { 244 if len(args) != 1 { 245 return "", fmt.Errorf("Incorrect arguments. Expecting a key") 246 } 247 248 value, err := stub.GetState(args[0]) 249 if err != nil { 250 return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[0], err) 251 } 252 if value == nil { 253 return "", fmt.Errorf("Asset not found: %s", args[0]) 254 } 255 return string(value), nil 256 } 257 258 .. _Chaincode Sample: 259 260 Pulling it All Together 261 ^^^^^^^^^^^^^^^^^^^^^^^ 262 263 Finally, we need to add the ``main`` function, which will call the 264 ``shim.Start`` function. Here's the whole chaincode program source. 265 266 .. code:: go 267 268 package main 269 270 import ( 271 "fmt" 272 273 "github.com/hyperledger/fabric/core/chaincode/shim" 274 "github.com/hyperledger/fabric/protos/peer" 275 ) 276 277 // SimpleAsset implements a simple chaincode to manage an asset 278 type SimpleAsset struct { 279 } 280 281 // Init is called during chaincode instantiation to initialize any 282 // data. Note that chaincode upgrade also calls this function to reset 283 // or to migrate data. 284 func (t *SimpleAsset) Init(stub shim.ChaincodeStubInterface) peer.Response { 285 // Get the args from the transaction proposal 286 args := stub.GetStringArgs() 287 if len(args) != 2 { 288 return shim.Error("Incorrect arguments. Expecting a key and a value") 289 } 290 291 // Set up any variables or assets here by calling stub.PutState() 292 293 // We store the key and the value on the ledger 294 err := stub.PutState(args[0], []byte(args[1])) 295 if err != nil { 296 return shim.Error(fmt.Sprintf("Failed to create asset: %s", args[0])) 297 } 298 return shim.Success(nil) 299 } 300 301 // Invoke is called per transaction on the chaincode. Each transaction is 302 // either a 'get' or a 'set' on the asset created by Init function. The Set 303 // method may create a new asset by specifying a new key-value pair. 304 func (t *SimpleAsset) Invoke(stub shim.ChaincodeStubInterface) peer.Response { 305 // Extract the function and args from the transaction proposal 306 fn, args := stub.GetFunctionAndParameters() 307 308 var result string 309 var err error 310 if fn == "set" { 311 result, err = set(stub, args) 312 } else { // assume 'get' even if fn is nil 313 result, err = get(stub, args) 314 } 315 if err != nil { 316 return shim.Error(err.Error()) 317 } 318 319 // Return the result as success payload 320 return shim.Success([]byte(result)) 321 } 322 323 // Set stores the asset (both key and value) on the ledger. If the key exists, 324 // it will override the value with the new one 325 func set(stub shim.ChaincodeStubInterface, args []string) (string, error) { 326 if len(args) != 2 { 327 return "", fmt.Errorf("Incorrect arguments. Expecting a key and a value") 328 } 329 330 err := stub.PutState(args[0], []byte(args[1])) 331 if err != nil { 332 return "", fmt.Errorf("Failed to set asset: %s", args[0]) 333 } 334 return args[1], nil 335 } 336 337 // Get returns the value of the specified asset key 338 func get(stub shim.ChaincodeStubInterface, args []string) (string, error) { 339 if len(args) != 1 { 340 return "", fmt.Errorf("Incorrect arguments. Expecting a key") 341 } 342 343 value, err := stub.GetState(args[0]) 344 if err != nil { 345 return "", fmt.Errorf("Failed to get asset: %s with error: %s", args[0], err) 346 } 347 if value == nil { 348 return "", fmt.Errorf("Asset not found: %s", args[0]) 349 } 350 return string(value), nil 351 } 352 353 // main function starts up the chaincode in the container during instantiate 354 func main() { 355 if err := shim.Start(new(SimpleAsset)); err != nil { 356 fmt.Printf("Error starting SimpleAsset chaincode: %s", err) 357 } 358 } 359 360 Building Chaincode 361 ^^^^^^^^^^^^^^^^^^ 362 363 Now let's compile your chaincode. 364 365 .. code:: bash 366 367 go get -u --tags nopkcs11 github.com/hyperledger/fabric/core/chaincode/shim 368 go build --tags nopkcs11 369 370 Assuming there are no errors, now we can proceed to the next step, testing 371 your chaincode. 372 373 Testing Using dev mode 374 ^^^^^^^^^^^^^^^^^^^^^^ 375 376 Normally chaincodes are started and maintained by peer. However in “dev 377 mode", chaincode is built and started by the user. This mode is useful 378 during chaincode development phase for rapid code/build/run/debug cycle 379 turnaround. 380 381 We start "dev mode" by leveraging pre-generated orderer and channel artifacts for 382 a sample dev network. As such, the user can immediately jump into the process 383 of compiling chaincode and driving calls. 384 385 Install Hyperledger Fabric Samples 386 ---------------------- 387 388 If you haven't already done so, please install the :doc:`samples`. 389 390 Navigate to the ``chaincode-docker-devmode`` directory of the ``fabric-samples`` 391 clone: 392 393 .. code:: bash 394 395 cd chaincode-docker-devmode 396 397 Download Docker images 398 ---------------------- 399 400 We need four Docker images in order for "dev mode" to run against the supplied 401 docker compose script. If you installed the ``fabric-samples`` repo clone and 402 followed the instructions to :ref:`download-platform-specific-binaries`, then 403 you should have the necessary Docker images installed locally. 404 405 .. note:: If you choose to manually pull the images then you must retag them as 406 ``latest``. 407 408 Issue a ``docker images`` command to reveal your local Docker Registry. You 409 should see something similar to following: 410 411 .. code:: bash 412 413 docker images 414 REPOSITORY TAG IMAGE ID CREATED SIZE 415 hyperledger/fabric-tools latest e09f38f8928d 4 hours ago 1.32 GB 416 hyperledger/fabric-tools x86_64-1.0.0 e09f38f8928d 4 hours ago 1.32 GB 417 hyperledger/fabric-orderer latest 0df93ba35a25 4 hours ago 179 MB 418 hyperledger/fabric-orderer x86_64-1.0.0 0df93ba35a25 4 hours ago 179 MB 419 hyperledger/fabric-peer latest 533aec3f5a01 4 hours ago 182 MB 420 hyperledger/fabric-peer x86_64-1.0.0 533aec3f5a01 4 hours ago 182 MB 421 hyperledger/fabric-ccenv latest 4b70698a71d3 4 hours ago 1.29 GB 422 hyperledger/fabric-ccenv x86_64-1.0.0 4b70698a71d3 4 hours ago 1.29 GB 423 424 .. note:: If you retrieved the images through the :ref:`download-platform-specific-binaries`, 425 then you will see additional images listed. However, we are only concerned with 426 these four. 427 428 Now open three terminals and navigate to your ``chaincode-docker-devmode`` 429 directory in each. 430 431 Terminal 1 - Start the network 432 ------------------------------ 433 434 .. code:: bash 435 436 docker-compose -f docker-compose-simple.yaml up 437 438 The above starts the network with the ``SingleSampleMSPSolo`` orderer profile and 439 launches the peer in "dev mode". It also launches two additional containers - 440 one for the chaincode environment and a CLI to interact with the chaincode. The 441 commands for create and join channel are embedded in the CLI container, so we 442 can jump immediately to the chaincode calls. 443 444 Terminal 2 - Build & start the chaincode 445 ---------------------------------------- 446 447 .. code:: bash 448 449 docker exec -it chaincode bash 450 451 You should see the following: 452 453 .. code:: bash 454 455 root@d2629980e76b:/opt/gopath/src/chaincode# 456 457 Now, compile your chaincode: 458 459 .. code:: bash 460 461 cd sacc 462 go build 463 464 Now run the chaincode: 465 466 .. code:: bash 467 468 CORE_PEER_ADDRESS=peer:7051 CORE_CHAINCODE_ID_NAME=mycc:0 ./sacc 469 470 The chaincode is started with peer and chaincode logs indicating successful registration with the peer. 471 Note that at this stage the chaincode is not associated with any channel. This is done in subsequent steps 472 using the ``instantiate`` command. 473 474 Terminal 3 - Use the chaincode 475 ------------------------------ 476 477 Even though you are in ``--peer-chaincodedev`` mode, you still have to install the 478 chaincode so the life-cycle system chaincode can go through its checks normally. 479 This requirement may be removed in future when in ``--peer-chaincodedev`` mode. 480 481 We'll leverage the CLI container to drive these calls. 482 483 .. code:: bash 484 485 docker exec -it cli bash 486 487 .. code:: bash 488 489 peer chaincode install -p chaincodedev/chaincode/sacc -n mycc -v 0 490 peer chaincode instantiate -n mycc -v 0 -c '{"Args":["a","10"]}' -C myc 491 492 Now issue an invoke to change the value of "a" to "20". 493 494 .. code:: bash 495 496 peer chaincode invoke -n mycc -c '{"Args":["set", "a", "20"]}' -C myc 497 498 Finally, query ``a``. We should see a value of ``20``. 499 500 .. code:: bash 501 502 peer chaincode query -n mycc -c '{"Args":["query","a"]}' -C myc 503 504 Testing new chaincode 505 --------------------- 506 507 By default, we mount only ``sacc``. However, you can easily test different 508 chaincodes by adding them to the ``chaincode`` subdirectory and relaunching 509 your network. At this point they will be accessible in your ``chaincode`` container. 510 511 .. Licensed under Creative Commons Attribution 4.0 International License 512 https://creativecommons.org/licenses/by/4.0/