-
Notifications
You must be signed in to change notification settings - Fork 143
/
tutorial-using-contractinterface.html
452 lines (368 loc) · 20.1 KB
/
tutorial-using-contractinterface.html
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>Hyperledger Fabric Contract API Tutorial: Using the Contract Interface</title>
<!--[if lt IE 9]>
<script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<link type="text/css" rel="stylesheet" href="styles/sunlight.default.css">
<link type="text/css" rel="stylesheet" href="styles/site.cosmo.css">
</head>
<body>
<div class="navbar navbar-default navbar-fixed-top ">
<div class="container">
<div class="navbar-header">
<a class="navbar-brand" href="index.html">Hyperledger Fabric Contract API</a>
<button class="navbar-toggle" type="button" data-toggle="collapse" data-target="#topNavigation">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
</div>
<div class="navbar-collapse collapse" id="topNavigation">
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="classes.list.html" class="dropdown-toggle" data-toggle="dropdown">Classes<b class="caret"></b></a>
<ul class="dropdown-menu ">
<li><a href="fabric-contract-api.Context.html">fabric-contract-api.Context</a></li><li><a href="fabric-contract-api.Contract.html">fabric-contract-api.Contract</a></li><li><a href="fabric-shim.ChaincodeInterface.html">fabric-shim.ChaincodeInterface</a></li><li><a href="fabric-shim.ChaincodeProposalPayload.html">fabric-shim.ChaincodeProposalPayload</a></li><li><a href="fabric-shim.ChaincodeStub.html">fabric-shim.ChaincodeStub</a></li><li><a href="fabric-shim.ChannelHeader.html">fabric-shim.ChannelHeader</a></li><li><a href="fabric-shim.ClientIdentity.html">fabric-shim.ClientIdentity</a></li><li><a href="fabric-shim.CommonIterator.html">fabric-shim.CommonIterator</a></li><li><a href="fabric-shim.ErrorResponse.html">fabric-shim.ErrorResponse</a></li><li><a href="fabric-shim.FunctionAndParameters.html">fabric-shim.FunctionAndParameters</a></li><li><a href="fabric-shim.Header.html">fabric-shim.Header</a></li><li><a href="fabric-shim.HistoryQueryIterator.html">fabric-shim.HistoryQueryIterator</a></li><li><a href="fabric-shim.PaginationQueryResponse.html">fabric-shim.PaginationQueryResponse</a></li><li><a href="fabric-shim.PrivateQueryResponse.html">fabric-shim.PrivateQueryResponse</a></li><li><a href="fabric-shim.Proposal.html">fabric-shim.Proposal</a></li><li><a href="fabric-shim.ProposalCreator.html">fabric-shim.ProposalCreator</a></li><li><a href="fabric-shim.QueryResponseMetadata.html">fabric-shim.QueryResponseMetadata</a></li><li><a href="fabric-shim.Response.html">fabric-shim.Response</a></li><li><a href="fabric-shim.Shim.html">fabric-shim.Shim</a></li><li><a href="fabric-shim.SignatureHeader.html">fabric-shim.SignatureHeader</a></li><li><a href="fabric-shim.SignedProposal.html">fabric-shim.SignedProposal</a></li><li><a href="fabric-shim.StateQueryIterator.html">fabric-shim.StateQueryIterator</a></li><li><a href="fabric-shim.SuccessResponse.html">fabric-shim.SuccessResponse</a></li><li><a href="module.exports.html">module.exports</a></li><li><a href="module-fabric-ledger.Ledger.html">fabric-ledger.Ledger</a></li>
</ul>
</li>
<li class="dropdown">
<a href="interfaces.list.html" class="dropdown-toggle" data-toggle="dropdown">Interfaces<b class="caret"></b></a>
<ul class="dropdown-menu ">
<li><a href="ChaincodeServerOpts.html">ChaincodeServerOpts</a></li><li><a href="ChaincodeServerTLSProperties.html">ChaincodeServerTLSProperties</a></li>
</ul>
</li>
<li class="dropdown">
<a href="tutorials.list.html" class="dropdown-toggle" data-toggle="dropdown">Tutorials<b class="caret"></b></a>
<ul class="dropdown-menu ">
<li><a href="tutorial-annotated-contract-metadata.html">Walkthrough of annotated metadata.json</a></li><li><a href="tutorial-data-types-and-contracts.html">Details of type handling</a></li><li><a href="tutorial-deep-dive-contract-interface.html">Deep dive on Contract Interface</a></li><li><a href="tutorial-using-chaincodeinterface.html">Using the Chaincode Interface</a></li><li><a href="tutorial-using-contractinterface.html">Using the Contract Interface</a></li><li><a href="tutorial-using-iterators.html">Working with apis that return iterators</a></li><li><a href="tutorial-using-typescript-decorators.html">Using TypeScript Decorators</a></li>
</ul>
</li>
</ul>
<div class="col-sm-3 col-md-3">
<form class="navbar-form" role="search">
<div class="input-group">
<input type="text" class="form-control" placeholder="Search" name="q" id="search-input">
<div class="input-group-btn">
<button class="btn btn-default" id="search-submit"><i class="glyphicon glyphicon-search"></i></button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
<div class="container" id="toc-content">
<div class="row">
<div class="col-md-12">
<div id="main">
<section class="tutorial-section">
<header>
<h2>Using the Contract Interface</h2>
</header>
<article>
<p>This outlines the theory of the how the new node module works; with the fabric samples project you will find scenario-based approaches.</p>
<h2>Writing the chaincode</h2>
<h3>1: Chaincode is created as an npm module.</h3>
<p>An initial <code>package.json</code> is as follows;</p>
<p>The dependencies of <code>fabric-contract-api</code> and <code>fabric-shim</code> will be required.</p>
<pre class="prettyprint source"><code>{
"name": "chaincode",
"description": "My first exciting chaincode implemented in node.js",
"engines": {
"node": "^12.16.1",
"npm": "^6.4.1"
},
"scripts": {
"test":"mocha.....
},
"engine-strict": true,
"engineStrict": true,
"version": "1.0.0",
"main": "index.js",
"author": "",
"license": "Apache-2.0",
"dependencies": {
"fabric-contract-api": "^2.4.1",
"fabric-shim": "^2.4.1"
}
}
</code></pre>
<p>Remember to add in any additional business logic, and testing libraries needed</p>
<p>Adding <code>fabric-shim</code> as a dependency, gives a command <code>fabric-chaincode-node</code> that is the script to run for <code>npm start</code>.</p>
<pre class="prettyprint source"><code> "scripts": {
"start": "fabric-chaincode-node start",
"test": "nyc mocha test",
....
},
</code></pre>
<h3>2: How is chaincode deployed?</h3>
<p>Chaincode is deployed by the peer in response to issuing a number of (usually CLI) commands. For node.js chaincode the location of the chaincode npm project is required (the directory that the package.json is in). This does not need to be an installed project, but has to have all the code, and the package.json.</p>
<p>A docker image is built for this chaincode, the package.json and code copied in. and <code>npm install</code> run.</p>
<p>After the install there is a 'bootstrap' process that starts the chaincode up (more details later). The constructors of the exported Contracts will be run at this point; these constructors are for setting the name and optional setup of the 'error/monitoring functions', (again more later). This instance of the contract will existing whilst this chaincode docker image is up.</p>
<p>When chaincode is instantiated or updated, the <code>init()</code> function is the chaincode is called. As with the <code>invoke()</code> call from the client, a fn name and parameters can be passed. Remember therefore to have specific functions to call on <code>init()</code> and <code>update()</code> in order to do any data initialisation or migration that might be needed. These two functions have been abstracted away to focus on specific function implementations.</p>
<p>It is strongly recommended to use the npm shrinkwrap mechanism so the versions of the modules that are used are fixed.</p>
<p>Within the class you can defined as many or functions as you wish. These transaction functions will form the basis of the business logic you contract needs to execute. These are <code>async</code> functions, and can take parameters and return values. There is a single mandatory parameter of the 'transaction context'; this represents the currently executing transaction and is the way functions can access the world state, and other APIs.</p>
<h3>3: What needs to be exported?</h3>
<p>Node states that module exports are defined in <code>index.js</code></p>
<p>In this example we have a single value that can be queried and updated. This has been split into to parts for demonstration purposes.</p>
<pre class="prettyprint source"><code>// index.js
'use strict';
const UpdateValues = require('./updatevalues')
const RemoveValues = require('./removevalues')
module.exports.contracts = [UpdateValues,RemoveValues];
</code></pre>
<p>This exports two classes that together form the Contract. There can be other code that within the model that is used in a support role.
<em>Note that the 'contracts' word is mandatory.</em></p>
<h3>4: What do these classes need to contain?</h3>
<p>As an example the <code>updatevalues</code> will look like this (with the function bodies remove for clarity)</p>
<pre class="prettyprint source"><code>// updatevalues.js
'use strict';
// SDK Library to asset with writing the logic
const { Contract } = require('fabric-contract-api');
// Business logic (well just util but still it's general purpose logic)
const util = require('util');
/**
* Support the Updating of values within the SmartContract
*/
class UpdateValues extends Contract
constructor(){
super('UpdateValuesContract');
}
async instantiate(ctx){
// .....
}
async setNewAssetValue(ctx, newValue) {
// .....
}
async doubleAssetValue(ctx) {
// .....
}
};
module.exports = UpdateValues;
</code></pre>
<p>Note that ALL the functions defined in these modules will be called by the client SDK.</p>
<ul>
<li>There are 3 functions <code>setup</code> <code>setNewAssetValue</code> and <code>doubleAssetValue</code> that can be called by issuing the appropriate invoke client side</li>
<li>The <code>ctx</code> in the function is a transaction context; each time a invoke is called this will be a new instance that can be used by the function implementation to access apis such as the world state of information on invoking identity.</li>
<li>The arguments are split out from the array passed on the invoke.</li>
<li>The constructor contains a 'name' to help identify the sets of functions</li>
</ul>
<h2>Running chaincode in development mode</h2>
<p>This is quite easy - as you need to run the startChaincode command.</p>
<pre class="prettyprint source"><code>$ $(npm bin)/fabric-chaincode-node start --peer.address localhost:7052 --chaincode-id-name "mycontract:v0"
</code></pre>
<p>(this is actually what the peer does; this does mean that any chaincode that is written using the existing chaincode interface will continue to work as is.)</p>
<h2>Using this chaincode</h2>
<p>Each of the functions can be invoked with arbitrary arguments. The name of the function is of the format</p>
<pre class="prettyprint source"><code>[name:]functionname
</code></pre>
<p>If a name is given in the constructor then it will be prefixed separated by a : (colon)</p>
<blockquote>
<p><em>assuming that you have a fabric up and running with the appropriate environment variables set</em></p>
</blockquote>
<pre class="prettyprint source"><code>$ peer chaincode install --lang node --name mycontract --version v0 --path ~/chaincode-examples
$ peer chaincode instantiate --orderer localhost:7050 --channelID mychannel --lang node --name mycontract --version v0 -c '{"Args":["UpdateValuesContract:setup"]}'
</code></pre>
<p>Will get things working...
Then you can invoke the chaincode via this command.</p>
<pre class="prettyprint source"><code>$ peer chaincode invoke --orderer localhost:7050 --channelID mychannel -c '{"Args":["UpdateValuesContract:getAssetValue"]}' -n mycontract4
</code></pre>
<h2>Additional support provided by the SmartContract class</h2>
<p>In the case where you ask for a function to be executed, it could be the case that this doesn't exist.
You can provide you own function to be executed in this case, the default is to throw and error but you're able to customise this if you wish.</p>
<p>For example</p>
<pre class="prettyprint source"><code> /**
* Sets a name so that the functions in this particular class can
* be separated from others.
*/
constructor() {
super('UpdateValuesContract');
}
/** The function to invoke if something unkown comes in.
*
*/
async unknownTransaction(ctx){
throw new Error('a custom error message')
}
async beforeTransaction(ctx){
console.info(`Transaction ID: ${ctx.stub.getTxID()}`);
}
async afterTransaction(ctx,result){
// log result to preferred log implementation
// emit events etc...
}
async aroundTransaction(ctx, fn, parameters) {
try {
// don't forget to call super, or your transaction function won't run!
super.aroundTransaction(ctx, fn, parameters)
} catch (error) {
// do something with the error, then rethrow
throw error
}
}
</code></pre>
<h3>Structure of the Transaction Context</h3>
<p>In Fabric, there is a <em>stub</em> api that provides chaincode with functionality.
No functionality has been removed, but a new approach to providing abstractions on this to facilitate programming.</p>
<p><em>user additions</em>: additional properties can be added to the object to support for example common handling of the data serialization.</p>
<p>The context object contains</p>
<ul>
<li><code>ctx.stub</code> the same stub instance as in earlier versions for compatibility</li>
<li><code>ctx.identity</code> and instance of the Client Identity object</li>
</ul>
<p>You are at liberty to create a subclass of the Context to provide additional functions, or per-transaction context storage. For example</p>
<pre class="prettyprint source"><code> /**
* Custom context for use within this contract
*/
createContext(){
return new ScenarioContext();
}
</code></pre>
<p>and the Context class itself is</p>
<pre class="prettyprint source"><code>const { Context } = require('fabric-contract-api');
class ScenarioContext extends Context{
constructor(){
super();
}
generateKey(){
return this.stub.createCompositeKey('type',['keyvalue']);
}
}
</code></pre>
<h2>Node.js Chaincode API rules</h2>
<p>Definitions as per https://www.ietf.org/rfc/rfc2119.txt</p>
<ul>
<li>All the functions that are present in the prototype of a class that extends <em>Contract</em> will be invokable</li>
<li>The exports from the node module <em>MUST</em> include <em>contracts</em> that is an array of constructors (1 or more)</li>
<li>Each class <em>MAY</em> call in it's constructor pass a name. This is prefixed to each of the function names by an _ (underscore)</li>
<li>Each class <em>MAY</em> define functions that are executed before and functions that are executed after the invoked function.
<ul>
<li>These are part of the same fabric transaction</li>
<li>They are scoped per name</li>
</ul>
</li>
<li>Each class <em>MAY</em> define a function that would be executed if a matching function name does not exist; otherwise a 'no function exists' error will be thrown</li>
<li>If too many parameters are passed, they will be discarded</li>
<li>If too few parameters are passed, then the remainder will be set to undefined
<ul>
<li>as per node.js language standard</li>
</ul>
</li>
<li>Duplicate function names in a single class is an error</li>
<li>Any function that is dynamically added will not be registered as an invokable function</li>
<li>There are no specific function that is invoked per Fabric's <em>init</em> chaincode spi. The instantiate flow can pass function name and parameters; therefore consider
a dedicated function that will be called for new chaincode deployments, and for upgrade deployments.</li>
</ul>
<h2>Restrictions on programming in side a Contract function</h2>
<p>Hyperledger Fabric's consensus algorithm permits the ability to use general purpose languages; rather than a more restrictive language. But the following restrictions apply</p>
<ul>
<li>Functions should not create random variables, or use any function whose return values are functions of the current time or location of execution
<ul>
<li>i.e. the function will be executed in another context (i.e. peer process). This could potentially be in a different time zone in a different locale.</li>
</ul>
</li>
<li>Functions should be away that they may read state, and write state. But they are producing a set of changes that will be applied to the state. The implication is that updates to the state
may not be read back.</li>
</ul>
<pre class="prettyprint source"><code>let v1 = getState("key")
v1=="hello" // is true
putState("key","world")
let v2 = getState("key")
v2=="world" // is false, v2 is "hello"
</code></pre>
<p>In any subsequent invocation, the value would be seen to be updated.</p>
<p>Note that if you have use any Flux architecture implications such as Redux, the above restrictions will be familiar.</p>
</article>
</section>
</div>
</div>
<div class="clearfix"></div>
</div>
</div>
<div class="modal fade" id="searchResults">
<div class="modal-dialog">
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
<h4 class="modal-title">Search results</h4>
</div>
<div class="modal-body"></div>
<div class="modal-footer">
<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>
</div>
</div><!-- /.modal-content -->
</div><!-- /.modal-dialog -->
</div>
<footer>
<span class="jsdoc-message">
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.6.4</a>
on 2021-11-29T13:37:02+00:00
using the <a href="https://github.com/docstrap/docstrap">DocStrap template</a>.
</span>
</footer>
<script src="scripts/docstrap.lib.js"></script>
<script src="scripts/toc.js"></script>
<script type="text/javascript" src="scripts/fulltext-search-ui.js"></script>
<script>
$( function () {
$( "[id*='$']" ).each( function () {
var $this = $( this );
$this.attr( "id", $this.attr( "id" ).replace( "$", "__" ) );
} );
$( ".tutorial-section pre, .readme-section pre, pre.prettyprint.source" ).each( function () {
var $this = $( this );
var example = $this.find( "code" );
exampleText = example.html();
var lang = /{@lang (.*?)}/.exec( exampleText );
if ( lang && lang[1] ) {
exampleText = exampleText.replace( lang[0], "" );
example.html( exampleText );
lang = lang[1];
} else {
var langClassMatch = example.parent()[0].className.match(/lang\-(\S+)/);
lang = langClassMatch ? langClassMatch[1] : "javascript";
}
if ( lang ) {
$this
.addClass( "sunlight-highlight-" + lang )
.addClass( "linenums" )
.html( example.html() );
}
} );
Sunlight.highlightAll( {
lineNumbers : false,
showMenu : true,
enableDoclinks : true
} );
$.catchAnchorLinks( {
navbarOffset: 10
} );
$( "#toc" ).toc( {
anchorName : function ( i, heading, prefix ) {
return $( heading ).attr( "id" ) || ( prefix + i );
},
selectors : "#toc-content h1,#toc-content h2,#toc-content h3,#toc-content h4",
showAndHide : false,
smoothScrolling: true
} );
$( "#main span[id^='toc']" ).addClass( "toc-shim" );
$( '.dropdown-toggle' ).dropdown();
$( "table" ).each( function () {
var $this = $( this );
$this.addClass('table');
} );
} );
</script>
<!--Navigation and Symbol Display-->
<!--Google Analytics-->
<script type="text/javascript">
$(document).ready(function() {
SearcherDisplay.init();
});
</script>
</body>
</html>