lccc.go 12.3 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/*
Copyright IBM Corp. 2016 All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

		 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

17
package chaincode
18
19
20
21
22
23

import (
	"fmt"

	"github.com/golang/protobuf/proto"
	"github.com/hyperledger/fabric/core/chaincode/shim"
24
25
	"github.com/hyperledger/fabric/core/ledger"
	"github.com/hyperledger/fabric/core/ledger/kvledger"
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
	pb "github.com/hyperledger/fabric/protos"
	"github.com/op/go-logging"
	"golang.org/x/net/context"
)

//The life cycle system chaincode manages chaincodes deployed
//on this peer. It manages chaincodes via Invoke proposals.
//     "Args":["deploy",<ChaincodeDeploymentSpec>]
//     "Args":["upgrade",<ChaincodeDeploymentSpec>]
//     "Args":["stop",<ChaincodeInvocationSpec>]
//     "Args":["start",<ChaincodeInvocationSpec>]

var logger = logging.MustGetLogger("lccc")

const (
	//CHAINCODETABLE prefix for chaincode tables
	CHAINCODETABLE = "chaincodes"

	//chaincode lifecyle commands

	//DEPLOY deploy command
	DEPLOY = "deploy"

	//chaincode query commands

	//GETCCINFO get chaincode
	GETCCINFO = "getid"
53
54
55

	//GETDEPSPEC get ChaincodeDeploymentSpec
	GETDEPSPEC = "getdepspec"
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
)

//---------- the LCCC -----------------

// LifeCycleSysCC implements chaincode lifecycle and policies aroud it
type LifeCycleSysCC struct {
}

//----------------errors---------------

//AlreadyRegisteredErr Already registered error
type AlreadyRegisteredErr string

func (f AlreadyRegisteredErr) Error() string {
	return fmt.Sprintf("%s already registered", string(f))
}

//InvalidFunctionErr invalid function error
type InvalidFunctionErr string

func (f InvalidFunctionErr) Error() string {
	return fmt.Sprintf("invalid function to lccc %s", string(f))
}

//InvalidArgsLenErr invalid arguments length error
type InvalidArgsLenErr int

func (i InvalidArgsLenErr) Error() string {
	return fmt.Sprintf("invalid number of argument to lccc %d", int(i))
}

//InvalidArgsErr invalid arguments error
type InvalidArgsErr int

func (i InvalidArgsErr) Error() string {
	return fmt.Sprintf("invalid argument (%d) to lccc", int(i))
}

//TXExistsErr transaction exists error
type TXExistsErr string

func (t TXExistsErr) Error() string {
	return fmt.Sprintf("transaction exists %s", string(t))
}

//TXNotFoundErr transaction not found error
type TXNotFoundErr string

func (t TXNotFoundErr) Error() string {
	return fmt.Sprintf("transaction not found %s", string(t))
}

//InvalidDeploymentSpecErr invalide chaincode deployment spec error
type InvalidDeploymentSpecErr string

func (f InvalidDeploymentSpecErr) Error() string {
	return fmt.Sprintf("Invalid deployment spec : %s", string(f))
}

//ChaincodeExistsErr chaincode exists error
type ChaincodeExistsErr string

func (t ChaincodeExistsErr) Error() string {
	return fmt.Sprintf("Chaincode exists %s", string(t))
}

122
//InvalidChainNameErr invalid chain name error
123
124
125
126
127
128
type InvalidChainNameErr string

func (f InvalidChainNameErr) Error() string {
	return fmt.Sprintf("invalid chain name %s", string(f))
}

129
130
131
132
133
134
135
//InvalidChaincodeNameErr invalid chaincode name error
type InvalidChaincodeNameErr string

func (f InvalidChaincodeNameErr) Error() string {
	return fmt.Sprintf("invalid chain code name %s", string(f))
}

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
//-------------- helper functions ------------------
//create the table to maintain list of chaincodes maintained in this
//blockchain.
func (lccc *LifeCycleSysCC) createChaincodeTable(stub shim.ChaincodeStubInterface, cctable string) error {
	// Create table one
	var colDefs []*shim.ColumnDefinition
	nameColDef := shim.ColumnDefinition{Name: "name",
		Type: shim.ColumnDefinition_STRING, Key: true}
	versColDef := shim.ColumnDefinition{Name: "version",
		Type: shim.ColumnDefinition_INT32, Key: false}

	//QUESTION - Should code be separately maintained ?
	codeDef := shim.ColumnDefinition{Name: "code",
		Type: shim.ColumnDefinition_BYTES, Key: false}
	colDefs = append(colDefs, &nameColDef)
	colDefs = append(colDefs, &versColDef)
	colDefs = append(colDefs, &codeDef)
	return stub.CreateTable(cctable, colDefs)
}

//register create the chaincode table. name can be used to different
//tables of chaincodes. This would provide the way to associate chaincodes
//with chains(and ledgers)
func (lccc *LifeCycleSysCC) register(stub shim.ChaincodeStubInterface, name string) error {
	ccname := CHAINCODETABLE + "-" + name

	row, err := stub.GetTable(ccname)
	if err == nil && row != nil { //table exists, do nothing
		return AlreadyRegisteredErr(name)
	}

	//there may be other err's but assume "not exists". Anything
	//more serious than that bound to show up
	err = lccc.createChaincodeTable(stub, ccname)

	return err
}

//create the chaincode on the given chain
func (lccc *LifeCycleSysCC) createChaincode(stub shim.ChaincodeStubInterface, chainname string, ccname string, cccode []byte) (*shim.Row, error) {
	var columns []*shim.Column

	nameCol := shim.Column{Value: &shim.Column_String_{String_: ccname}}
	versCol := shim.Column{Value: &shim.Column_Int32{Int32: 0}}
	codeCol := shim.Column{Value: &shim.Column_Bytes{Bytes: cccode}}

	columns = append(columns, &nameCol)
	columns = append(columns, &versCol)
	columns = append(columns, &codeCol)

	row := &shim.Row{Columns: columns}
	_, err := stub.InsertRow(CHAINCODETABLE+"-"+chainname, *row)
	if err != nil {
		return nil, fmt.Errorf("insertion of chaincode failed. %s", err)
	}
	return row, nil
}

//checks for existence of chaincode on the given chain
func (lccc *LifeCycleSysCC) getChaincode(stub shim.ChaincodeStubInterface, chainname string, ccname string) (shim.Row, bool, error) {
	var columns []shim.Column
	nameCol := shim.Column{Value: &shim.Column_String_{String_: ccname}}
	columns = append(columns, nameCol)

	row, err := stub.GetRow(CHAINCODETABLE+"-"+chainname, columns)
	if err != nil {
		return shim.Row{}, false, err
	}

	if len(row.Columns) > 0 {
		return row, true, nil
	}
	return row, false, nil
}

//getChaincodeDeploymentSpec returns a ChaincodeDeploymentSpec given args
func (lccc *LifeCycleSysCC) getChaincodeDeploymentSpec(code []byte) (*pb.ChaincodeDeploymentSpec, error) {
	cds := &pb.ChaincodeDeploymentSpec{}

	err := proto.Unmarshal(code, cds)
	if err != nil {
		return nil, InvalidDeploymentSpecErr(err.Error())
	}

	return cds, nil
}

//do access control
224
func (lccc *LifeCycleSysCC) acl(stub shim.ChaincodeStubInterface, chainname ChainName, cds *pb.ChaincodeDeploymentSpec) error {
225
226
227
228
229
230
231
232
233
234
235
236
	return nil
}

//check validity of chain name
func (lccc *LifeCycleSysCC) isValidChainName(chainname string) bool {
	//TODO we probably need more checks and have
	if chainname == "" {
		return false
	}
	return true
}

237
238
239
240
241
242
243
244
245
//check validity of chaincode name
func (lccc *LifeCycleSysCC) isValidChaincodeName(chaincodename string) bool {
	//TODO we probably need more checks and have
	if chaincodename == "" {
		return false
	}
	return true
}

246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
//deploy the chaincode on to the chain
func (lccc *LifeCycleSysCC) deploy(stub shim.ChaincodeStubInterface, chainname string, cds *pb.ChaincodeDeploymentSpec) error {
	//TODO : this needs to be converted to another data structure to be handled
	//       by the chaincode framework (which currently handles "Transaction")
	t, err := lccc.toTransaction(cds)
	if err != nil {
		return fmt.Errorf("could not convert proposal to transaction %s", err)
	}

	//if unit testing, just return..we cannot do the actual deploy
	if _, ismock := stub.(*shim.MockStub); ismock {
		//we got this far just stop short of actual deploy for test purposes
		return nil
	}

	ctxt := context.Background()

263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
	//TODO - we are in the LCCC chaincode simulating an "invoke" to deploy another
	//chaincode. Deploying the chaincode and calling its "init" involves ledger access
	//for the called chaincode - ie, it needs to undergo state simulatio as well.
	//How do we handle the simulation for the called called chaincode ?
	//    1) don't allow state initialization on deploy
	//    2) combine both LCCC and the called chaincodes into one RW set
	//    3) just drop the second
	lgr := kvledger.GetLedger(chainname)

	var dummytxsim ledger.TxSimulator

	if dummytxsim, err = lgr.NewTxSimulator(); err != nil {
		return fmt.Errorf("Could not get simulator for %s", chainname)
	}

278
	ctxt = context.WithValue(ctxt, TXSimulatorKey, dummytxsim)
279

280
	//TODO - create chaincode support for chainname, for now use DefaultChain
281
	chaincodeSupport := GetChain(ChainName(chainname))
282

283
284
285
286
287
288
289
290
291
292
293
294
295
	_, err = chaincodeSupport.Deploy(ctxt, t)
	if err != nil {
		return fmt.Errorf("Failed to deploy chaincode spec(%s)", err)
	}

	//launch and wait for ready
	_, _, err = chaincodeSupport.Launch(ctxt, t)
	if err != nil {
		return fmt.Errorf("%s", err)
	}

	//stop now that we are done
	chaincodeSupport.Stop(ctxt, cds)
296

297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
	return nil
}

//this implements "deploy" Invoke transaction
func (lccc *LifeCycleSysCC) executeDeploy(stub shim.ChaincodeStubInterface, chainname string, code []byte) error {
	//lazy creation of chaincode table for chainname...its possible
	//there are chains without chaincodes
	if err := lccc.register(stub, chainname); err != nil {
		//if its already registered, ok... proceed
		if _, ok := err.(AlreadyRegisteredErr); !ok {
			return err
		}
	}

	cds, err := lccc.getChaincodeDeploymentSpec(code)

	if err != nil {
		return err
	}

317
318
319
320
	if !lccc.isValidChaincodeName(cds.ChaincodeSpec.ChaincodeID.Name) {
		return InvalidChaincodeNameErr(cds.ChaincodeSpec.ChaincodeID.Name)
	}

321
	if err = lccc.acl(stub, DefaultChain, cds); err != nil {
322
323
324
		return err
	}

325
326
327
	_, exists, err := lccc.getChaincode(stub, chainname, cds.ChaincodeSpec.ChaincodeID.Name)
	if exists {
		return ChaincodeExistsErr(cds.ChaincodeSpec.ChaincodeID.Name)
328
329
	}

330
331
332
333
334
335
336
337
	/**TODO - this is done in the endorser service for now so we can
		 * collect all state changes under one TXSim. Revisit this ...
	         * maybe this *is* the right solution
		 *if err = lccc.deploy(stub, chainname, cds); err != nil {
		 *	return err
		 *}
		 **/

338
	_, err = lccc.createChaincode(stub, chainname, cds.ChaincodeSpec.ChaincodeID.Name, code)
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

	return err
}

//TODO - this is temporary till we use Transaction in chaincode code
func (lccc *LifeCycleSysCC) toTransaction(cds *pb.ChaincodeDeploymentSpec) (*pb.Transaction, error) {
	return pb.NewChaincodeDeployTransaction(cds, cds.ChaincodeSpec.ChaincodeID.Name)
}

//-------------- the chaincode stub interface implementation ----------

//Init does nothing
func (lccc *LifeCycleSysCC) Init(stub shim.ChaincodeStubInterface) ([]byte, error) {
	return nil, nil
}

// Invoke implements lifecycle functions "deploy", "start", "stop", "upgrade".
// Deploy's arguments -  {[]byte("deploy"), []byte(<chainname>), <unmarshalled pb.ChaincodeDeploymentSpec>}
//
// Invoke also implements some query-like functions
// Get chaincode arguments -  {[]byte("getid"), []byte(<chainname>), []byte(<chaincodename>)}
func (lccc *LifeCycleSysCC) Invoke(stub shim.ChaincodeStubInterface) ([]byte, error) {
	args := stub.GetArgs()
	if len(args) < 1 {
		return nil, InvalidArgsLenErr(len(args))
	}

	function := string(args[0])

	switch function {
	case DEPLOY:
		if len(args) != 3 {
			return nil, InvalidArgsLenErr(len(args))
		}

		//chain the chaincode shoud be associated with. It
		//should be created with a register call
		chainname := string(args[1])

		if !lccc.isValidChainName(chainname) {
			return nil, InvalidChainNameErr(chainname)
		}

		//bytes corresponding to deployment spec
		code := args[2]

		err := lccc.executeDeploy(stub, chainname, code)

		return nil, err
388
	case GETCCINFO, GETDEPSPEC:
389
390
391
392
393
394
395
396
397
398
399
400
401
402
		if len(args) != 3 {
			return nil, InvalidArgsLenErr(len(args))
		}

		chain := string(args[1])
		ccname := string(args[2])
		//get chaincode given <chain, name>

		ccrow, exists, _ := lccc.getChaincode(stub, chain, ccname)
		if !exists {
			logger.Debug("ChaincodeID [%s/%s] does not exist", chain, ccname)
			return nil, TXNotFoundErr(chain + "/" + ccname)
		}

403
404
405
406
		if function == GETCCINFO {
			return []byte(ccrow.Columns[1].GetString_()), nil
		}
		return ccrow.Columns[2].GetBytes(), nil
407
408
409
410
411
412
413
414
415
	}

	return nil, InvalidFunctionErr(function)
}

// Query is no longer implemented. Will be removed
func (lccc *LifeCycleSysCC) Query(stub shim.ChaincodeStubInterface) ([]byte, error) {
	return nil, nil
}