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