github.com/osdi23p228/fabric@v0.0.0-20221218062954-77808885f5db/docs/source/chaincode4ade.rst (about) 1 Writing Your First Chaincode 2 ============================ 3 4 What is Chaincode? 5 ------------------ 6 7 Chaincode is a program, written in `Go <https://golang.org>`_, `Node.js <https://nodejs.org>`_, 8 or `Java <https://java.com/en/>`_ that implements a prescribed interface. 9 Chaincode runs in a separate process from the peer and initializes and manages 10 the ledger state through transactions submitted by applications. 11 12 A chaincode typically handles business logic agreed to by members of the 13 network, so it similar to a "smart contract". A chaincode can be invoked to update or query 14 the ledger in a proposal transaction. Given the appropriate permission, a chaincode 15 may invoke another chaincode, either in the same channel or in different channels, to access its state. 16 Note that, if the called chaincode is on a different channel from the calling chaincode, 17 only read query is allowed. That is, the called chaincode on a different channel is only a ``Query``, 18 which does not participate in state validation checks in subsequent commit phase. 19 20 In the following sections, we will explore chaincode through the eyes of an 21 application developer. We'll present a asset-transfer chaincode sample walkthrough, 22 and the purpose of each method in the Fabric Contract API. If you 23 are a network operator who is deploying a chaincode to running network, 24 visit the :doc:`deploy_chaincode` tutorial and the :doc:`chaincode_lifecycle` 25 concept topic. 26 27 This tutorial provides an overview of the high level APIs provided by the Fabric Contract API. 28 To learn more about developing smart contracts using the Fabric contract API, visit the :doc:`developapps/smartcontract` topic. 29 30 Fabric Contract API 31 ------------------- 32 33 The ``fabric-contract-api`` provides the contract interface, a high level API for application developers to implement Smart Contracts. 34 Within Hyperledger Fabric, Smart Contracts are also known as Chaincode. Working with this API provides a high level entry point to writing business logic. 35 Documentation of the Fabric Contract API for different languages can be found at the links below: 36 37 - `Go <https://godoc.org/github.com/hyperledger/fabric-contract-api-go/contractapi>`__ 38 - `Node.js <https://hyperledger.github.io/fabric-chaincode-node/{BRANCH}/api/>`__ 39 - `Java <https://hyperledger.github.io/fabric-chaincode-java/{BRANCH}/api/org/hyperledger/fabric/contract/package-summary.html>`__ 40 41 42 Note that when using the contract api, each chaincode function that is called is passed a transaction context "ctx", from which 43 you can get the chaincode stub (GetStub() ), which has functions to access the ledger (e.g. GetState() ) and make requests 44 to update the ledger (e.g. PutState() ). You can learn more at the language-specific links below. 45 46 - `Go <https://godoc.org/github.com/hyperledger/fabric-chaincode-go/shim#Chaincode>`__ 47 - `Node.js <https://hyperledger.github.io/fabric-chaincode-node/{BRANCH}/api/fabric-shim.ChaincodeInterface.html>`__ 48 - `Java <https://hyperledger.github.io/fabric-chaincode-java/{BRANCH}/api/org/hyperledger/fabric/shim/Chaincode.html>`__ 49 50 51 In this tutorial using Go chaincode, we will demonstrate the use of these APIs 52 by implementing a asset-transfer chaincode application that manages simple "assets". 53 54 .. _Asset Transfer Chaincode: 55 56 Asset Transfer Chaincode 57 ------------------------ 58 Our application is a basic sample chaincode to initialize a ledger with assets, create, read, update, and delete assets, check to see 59 if an asset exists, and transfer assets from one owner to another. 60 61 Choosing a Location for the Code 62 ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ 63 64 If you haven't been doing programming in Go, you may want to make sure that 65 you have `Go <https://golang.org>`_ installed and your system properly configured. We assume 66 you are using a version that supports modules. 67 68 Now, you will want to create a directory for your chaincode application. 69 70 To keep things simple, let's use the following command: 71 72 .. code:: bash 73 74 // atcc is shorthand for asset transfer chaincode 75 mkdir atcc && cd atcc 76 77 Now, let's create the module and the source file that we'll fill in with code: 78 79 .. code:: bash 80 81 go mod init atcc 82 touch atcc.go 83 84 Housekeeping 85 ^^^^^^^^^^^^ 86 87 First, let's start with some housekeeping. As with every chaincode, it implements the 88 `fabric-contract-api interface <https://godoc.org/github.com/hyperledger/fabric-contract-api-go/contractapi>`_, 89 so let's add the Go import statements for the necessary dependencies for our chaincode. We'll import the 90 fabric contract api package and define our SmartContract. 91 92 .. code:: go 93 94 package main 95 96 import ( 97 "fmt" 98 "log" 99 "github.com/hyperledger/fabric-contract-api-go/contractapi" 100 ) 101 102 // SmartContract provides functions for managing an Asset 103 type SmartContract struct { 104 contractapi.Contract 105 } 106 107 Next, let's add a struct ``Asset`` to represent simple assets on the ledger. 108 Note the JSON annotations, which will be used to marshal the asset to JSON which is stored on the ledger. 109 110 .. code:: go 111 112 // Asset describes basic details of what makes up a simple asset 113 type Asset struct { 114 ID string `json:"ID"` 115 Color string `json:"color"` 116 Size int `json:"size"` 117 Owner string `json:"owner"` 118 AppraisedValue int `json:"appraisedValue"` 119 } 120 121 Initializing the Chaincode 122 ^^^^^^^^^^^^^^^^^^^^^^^^^^ 123 124 Next, we'll implement the ``InitLedger`` function to populate the ledger with some initial data. 125 126 .. code:: go 127 128 // InitLedger adds a base set of assets to the ledger 129 func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error { 130 assets := []Asset{ 131 {ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300}, 132 {ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400}, 133 {ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500}, 134 {ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600}, 135 {ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700}, 136 {ID: "asset6", Color: "white", Size: 15, Owner: "Michel", AppraisedValue: 800}, 137 } 138 139 for _, asset := range assets { 140 assetJSON, err := json.Marshal(asset) 141 if err != nil { 142 return err 143 } 144 145 err = ctx.GetStub().PutState(asset.ID, assetJSON) 146 if err != nil { 147 return fmt.Errorf("failed to put to world state. %v", err) 148 } 149 } 150 151 return nil 152 } 153 154 Next, we write a function to create an asset on the ledger that does not yet exist. When writing chaincode, it 155 is a good idea to check for the existence of something on the ledger prior to taking an action on it, as is demonstrated 156 in the ``CreateAsset`` function below. 157 158 159 .. code:: go 160 161 // CreateAsset issues a new asset to the world state with given details. 162 func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error { 163 exists, err := s.AssetExists(ctx, id) 164 if err != nil { 165 return err 166 } 167 if exists { 168 return fmt.Errorf("the asset %s already exists", id) 169 } 170 171 asset := Asset{ 172 ID: id, 173 Color: color, 174 Size: size, 175 Owner: owner, 176 AppraisedValue: appraisedValue, 177 } 178 assetJSON, err := json.Marshal(asset) 179 if err != nil { 180 return err 181 } 182 183 return ctx.GetStub().PutState(id, assetJSON) 184 } 185 186 Now that we have populated the ledger with some initial assets and created an asset, 187 let's write a function ``ReadAsset`` that allows us to read an asset from the ledger. 188 189 .. code:: go 190 191 // ReadAsset returns the asset stored in the world state with given id. 192 func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) { 193 assetJSON, err := ctx.GetStub().GetState(id) 194 if err != nil { 195 return nil, fmt.Errorf("failed to read from world state: %v", err) 196 } 197 if assetJSON == nil { 198 return nil, fmt.Errorf("the asset %s does not exist", id) 199 } 200 201 var asset Asset 202 err = json.Unmarshal(assetJSON, &asset) 203 if err != nil { 204 return nil, err 205 } 206 207 return &asset, nil 208 } 209 210 Now that we have assets on our ledger we can interact with, let's write a chaincode function 211 ``UpdateAsset`` that allows us to update attributes of the asset that we are allowed to change. 212 213 .. code:: go 214 215 // UpdateAsset updates an existing asset in the world state with provided parameters. 216 func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error { 217 exists, err := s.AssetExists(ctx, id) 218 if err != nil { 219 return err 220 } 221 if !exists { 222 return fmt.Errorf("the asset %s does not exist", id) 223 } 224 225 // overwriting original asset with new asset 226 asset := Asset{ 227 ID: id, 228 Color: color, 229 Size: size, 230 Owner: owner, 231 AppraisedValue: appraisedValue, 232 } 233 assetJSON, err := json.Marshal(asset) 234 if err != nil { 235 return err 236 } 237 238 return ctx.GetStub().PutState(id, assetJSON) 239 } 240 241 There may be cases where we need the ability to delete an asset from the ledger, 242 so let's write a ``DeleteAsset`` function to handle that requirement. 243 244 .. code:: go 245 246 // DeleteAsset deletes an given asset from the world state. 247 func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, id string) error { 248 exists, err := s.AssetExists(ctx, id) 249 if err != nil { 250 return err 251 } 252 if !exists { 253 return fmt.Errorf("the asset %s does not exist", id) 254 } 255 256 return ctx.GetStub().DelState(id) 257 } 258 259 260 We said earlier that is was a good idea to check to see if an asset exists before 261 taking an action on it, so let's write a function called ``AssetExists`` to implement that requirement. 262 263 .. code:: go 264 265 // AssetExists returns true when asset with given ID exists in world state 266 func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) { 267 assetJSON, err := ctx.GetStub().GetState(id) 268 if err != nil { 269 return false, fmt.Errorf("failed to read from world state: %v", err) 270 } 271 272 return assetJSON != nil, nil 273 } 274 275 Next, we'll write a function we'll call ``TransferAsset`` that enables the transfer of an asset from one owner to another. 276 277 .. code:: go 278 279 // TransferAsset updates the owner field of asset with given id in world state. 280 func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) error { 281 asset, err := s.ReadAsset(ctx, id) 282 if err != nil { 283 return err 284 } 285 286 asset.Owner = newOwner 287 assetJSON, err := json.Marshal(asset) 288 if err != nil { 289 return err 290 } 291 292 return ctx.GetStub().PutState(id, assetJSON) 293 } 294 295 Let's write a function we'll call ``GetAllAssets`` that enables the querying of the ledger to 296 return all of the assets on the ledger. 297 298 .. code:: go 299 300 // GetAllAssets returns all assets found in world state 301 func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]*Asset, error) { 302 // range query with empty string for startKey and endKey does an 303 // open-ended query of all assets in the chaincode namespace. 304 resultsIterator, err := ctx.GetStub().GetStateByRange("", "") 305 if err != nil { 306 return nil, err 307 } 308 defer resultsIterator.Close() 309 310 var assets []*Asset 311 for resultsIterator.HasNext() { 312 queryResponse, err := resultsIterator.Next() 313 if err != nil { 314 return nil, err 315 } 316 317 var asset Asset 318 err = json.Unmarshal(queryResponse.Value, &asset) 319 if err != nil { 320 return nil, err 321 } 322 assets = append(assets, &asset) 323 } 324 325 return assets, nil 326 } 327 328 .. _Chaincode Sample: 329 330 331 .. Note:: The full chaincode sample below is presented as a way to 332 to keep this tutorial as clear and straightforward as possible. In a 333 real-world implementation, it is likely that packages will be segmented 334 where a ``main`` package imports the chaincode package to allow for easy unit testing. 335 To see what this looks like, see the asset-transfer `Go chaincode <https://github.com/hyperledger/fabric-samples/tree/master/asset-transfer-basic/chaincode-go>`__ 336 in fabric-samples. If you look at ``assetTransfer.go``, you will see that 337 it contains ``package main`` and imports ``package chaincode`` defined in ``smartcontract.go`` and 338 located at ``fabric-samples/asset-transfer-basic/chaincode-go/chaincode/``. 339 340 341 342 Pulling it All Together 343 ^^^^^^^^^^^^^^^^^^^^^^^ 344 345 Finally, we need to add the ``main`` function, which will call the 346 `ContractChaincode.Start <https://godoc.org/github.com/hyperledger/fabric-contract-api-go/contractapi#ContractChaincode.Start>`_ 347 function. Here's the whole chaincode program source. 348 349 .. code:: go 350 351 package main 352 353 import ( 354 "encoding/json" 355 "fmt" 356 "log" 357 358 "github.com/hyperledger/fabric-contract-api-go/contractapi" 359 ) 360 361 // SmartContract provides functions for managing an Asset 362 type SmartContract struct { 363 contractapi.Contract 364 } 365 366 // Asset describes basic details of what makes up a simple asset 367 type Asset struct { 368 ID string `json:"ID"` 369 Color string `json:"color"` 370 Size int `json:"size"` 371 Owner string `json:"owner"` 372 AppraisedValue int `json:"appraisedValue"` 373 } 374 375 // InitLedger adds a base set of assets to the ledger 376 func (s *SmartContract) InitLedger(ctx contractapi.TransactionContextInterface) error { 377 assets := []Asset{ 378 {ID: "asset1", Color: "blue", Size: 5, Owner: "Tomoko", AppraisedValue: 300}, 379 {ID: "asset2", Color: "red", Size: 5, Owner: "Brad", AppraisedValue: 400}, 380 {ID: "asset3", Color: "green", Size: 10, Owner: "Jin Soo", AppraisedValue: 500}, 381 {ID: "asset4", Color: "yellow", Size: 10, Owner: "Max", AppraisedValue: 600}, 382 {ID: "asset5", Color: "black", Size: 15, Owner: "Adriana", AppraisedValue: 700}, 383 {ID: "asset6", Color: "white", Size: 15, Owner: "Michel", AppraisedValue: 800}, 384 } 385 386 for _, asset := range assets { 387 assetJSON, err := json.Marshal(asset) 388 if err != nil { 389 return err 390 } 391 392 err = ctx.GetStub().PutState(asset.ID, assetJSON) 393 if err != nil { 394 return fmt.Errorf("failed to put to world state. %v", err) 395 } 396 } 397 398 return nil 399 } 400 401 // CreateAsset issues a new asset to the world state with given details. 402 func (s *SmartContract) CreateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error { 403 exists, err := s.AssetExists(ctx, id) 404 if err != nil { 405 return err 406 } 407 if exists { 408 return fmt.Errorf("the asset %s already exists", id) 409 } 410 411 asset := Asset{ 412 ID: id, 413 Color: color, 414 Size: size, 415 Owner: owner, 416 AppraisedValue: appraisedValue, 417 } 418 assetJSON, err := json.Marshal(asset) 419 if err != nil { 420 return err 421 } 422 423 return ctx.GetStub().PutState(id, assetJSON) 424 } 425 426 // ReadAsset returns the asset stored in the world state with given id. 427 func (s *SmartContract) ReadAsset(ctx contractapi.TransactionContextInterface, id string) (*Asset, error) { 428 assetJSON, err := ctx.GetStub().GetState(id) 429 if err != nil { 430 return nil, fmt.Errorf("failed to read from world state: %v", err) 431 } 432 if assetJSON == nil { 433 return nil, fmt.Errorf("the asset %s does not exist", id) 434 } 435 436 var asset Asset 437 err = json.Unmarshal(assetJSON, &asset) 438 if err != nil { 439 return nil, err 440 } 441 442 return &asset, nil 443 } 444 445 // UpdateAsset updates an existing asset in the world state with provided parameters. 446 func (s *SmartContract) UpdateAsset(ctx contractapi.TransactionContextInterface, id string, color string, size int, owner string, appraisedValue int) error { 447 exists, err := s.AssetExists(ctx, id) 448 if err != nil { 449 return err 450 } 451 if !exists { 452 return fmt.Errorf("the asset %s does not exist", id) 453 } 454 455 // overwriting original asset with new asset 456 asset := Asset{ 457 ID: id, 458 Color: color, 459 Size: size, 460 Owner: owner, 461 AppraisedValue: appraisedValue, 462 } 463 assetJSON, err := json.Marshal(asset) 464 if err != nil { 465 return err 466 } 467 468 return ctx.GetStub().PutState(id, assetJSON) 469 } 470 471 // DeleteAsset deletes an given asset from the world state. 472 func (s *SmartContract) DeleteAsset(ctx contractapi.TransactionContextInterface, id string) error { 473 exists, err := s.AssetExists(ctx, id) 474 if err != nil { 475 return err 476 } 477 if !exists { 478 return fmt.Errorf("the asset %s does not exist", id) 479 } 480 481 return ctx.GetStub().DelState(id) 482 } 483 484 // AssetExists returns true when asset with given ID exists in world state 485 func (s *SmartContract) AssetExists(ctx contractapi.TransactionContextInterface, id string) (bool, error) { 486 assetJSON, err := ctx.GetStub().GetState(id) 487 if err != nil { 488 return false, fmt.Errorf("failed to read from world state: %v", err) 489 } 490 491 return assetJSON != nil, nil 492 } 493 494 // TransferAsset updates the owner field of asset with given id in world state. 495 func (s *SmartContract) TransferAsset(ctx contractapi.TransactionContextInterface, id string, newOwner string) error { 496 asset, err := s.ReadAsset(ctx, id) 497 if err != nil { 498 return err 499 } 500 501 asset.Owner = newOwner 502 assetJSON, err := json.Marshal(asset) 503 if err != nil { 504 return err 505 } 506 507 return ctx.GetStub().PutState(id, assetJSON) 508 } 509 510 // GetAllAssets returns all assets found in world state 511 func (s *SmartContract) GetAllAssets(ctx contractapi.TransactionContextInterface) ([]*Asset, error) { 512 // range query with empty string for startKey and endKey does an 513 // open-ended query of all assets in the chaincode namespace. 514 resultsIterator, err := ctx.GetStub().GetStateByRange("", "") 515 if err != nil { 516 return nil, err 517 } 518 defer resultsIterator.Close() 519 520 var assets []*Asset 521 for resultsIterator.HasNext() { 522 queryResponse, err := resultsIterator.Next() 523 if err != nil { 524 return nil, err 525 } 526 527 var asset Asset 528 err = json.Unmarshal(queryResponse.Value, &asset) 529 if err != nil { 530 return nil, err 531 } 532 assets = append(assets, &asset) 533 } 534 535 return assets, nil 536 } 537 538 func main() { 539 assetChaincode, err := contractapi.NewChaincode(&SmartContract{}) 540 if err != nil { 541 log.Panicf("Error creating asset-transfer-basic chaincode: %v", err) 542 } 543 544 if err := assetChaincode.Start(); err != nil { 545 log.Panicf("Error starting asset-transfer-basic chaincode: %v", err) 546 } 547 } 548 549 Chaincode access control 550 ------------------------ 551 552 Chaincode can utilize the client (submitter) certificate for access 553 control decisions with ``ctx.GetStub().GetCreator()``. Additionally 554 the Fabric Contract API provides extension APIs that extract client identity 555 from the submitter's certificate that can be used for access control decisions, 556 whether that is based on client identity itself, or the org identity, 557 or on a client identity attribute. 558 559 For example an asset that is represented as a key/value may include the 560 client's identity as part of the value (for example as a JSON attribute 561 indicating that asset owner), and only this client may be authorized 562 to make updates to the key/value in the future. The client identity 563 library extension APIs can be used within chaincode to retrieve this 564 submitter information to make such access control decisions. 565 566 567 .. _vendoring: 568 569 Managing external dependencies for chaincode written in Go 570 ---------------------------------------------------------- 571 Your Go chaincode depends on Go packages (like the chaincode shim) that are not 572 part of the standard library. The source to these packages must be included in 573 your chaincode package when it is installed to a peer. If you have structured 574 your chaincode as a module, the easiest way to do this is to "vendor" the 575 dependencies with ``go mod vendor`` before packaging your chaincode. 576 577 .. code:: bash 578 579 go mod tidy 580 go mod vendor 581 582 This places the external dependencies for your chaincode into a local ``vendor`` 583 directory. 584 585 Once dependencies are vendored in your chaincode directory, ``peer chaincode package`` 586 and ``peer chaincode install`` operations will then include code associated with the 587 dependencies into the chaincode package. 588 589 .. Licensed under Creative Commons Attribution 4.0 International License 590 https://creativecommons.org/licenses/by/4.0/