lccc.go 12.4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
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.
*/

package lccc

import (
	"fmt"

	"github.com/golang/protobuf/proto"
	"github.com/hyperledger/fabric/core/chaincode"
	"github.com/hyperledger/fabric/core/chaincode/shim"
25
26
	ledger "github.com/hyperledger/fabric/core/ledgernext"
	"github.com/hyperledger/fabric/core/ledgernext/kvledger"
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
	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"
54
55
56

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

//---------- 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))
}

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

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

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

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

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
//-------------- 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
func (lccc *LifeCycleSysCC) acl(stub shim.ChaincodeStubInterface, chainname chaincode.ChainName, cds *pb.ChaincodeDeploymentSpec) error {
	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
}

238
239
240
241
242
243
244
245
246
//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
}

247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
//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()

264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
	//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)
	}

	ctxt = context.WithValue(ctxt, chaincode.TXSimulatorKey, dummytxsim)

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

284
285
286
287
288
289
290
291
292
293
294
295
296
	_, 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)
297

298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
	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
	}

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

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

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

331
332
333
334
335
336
337
338
	/**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
		 *}
		 **/

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

	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
389
	case GETCCINFO, GETDEPSPEC:
390
391
392
393
394
395
396
397
398
399
400
401
402
403
		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)
		}

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

	return nil, InvalidFunctionErr(function)
}

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