chaincode_support.go 11.5 KB
Newer Older
1
/*
2
Copyright IBM Corp. All Rights Reserved.
3

4
SPDX-License-Identifier: Apache-2.0
5
6
7
8
9
10
11
12
13
*/

package chaincode

import (
	"fmt"
	"time"

	"github.com/golang/protobuf/proto"
14
	"github.com/hyperledger/fabric/common/metrics"
15
	"github.com/hyperledger/fabric/common/util"
16
	"github.com/hyperledger/fabric/core/chaincode/platforms"
17
	"github.com/hyperledger/fabric/core/common/ccprovider"
18
	"github.com/hyperledger/fabric/core/common/sysccprovider"
19
	"github.com/hyperledger/fabric/core/container/ccintf"
20
	"github.com/hyperledger/fabric/core/ledger"
21
	"github.com/hyperledger/fabric/core/peer"
22
	pb "github.com/hyperledger/fabric/protos/peer"
23
	"github.com/pkg/errors"
24
25
)

26
27
// Runtime is used to manage chaincode runtime instances.
type Runtime interface {
28
29
	Start(ccci *ccprovider.ChaincodeContainerInfo, codePackage []byte) error
	Stop(ccci *ccprovider.ChaincodeContainerInfo) error
30
31
}

32
33
// Launcher is used to launch chaincode runtimes.
type Launcher interface {
34
	Launch(ccci *ccprovider.ChaincodeContainerInfo) error
35
36
}

37
38
// Lifecycle provides a way to retrieve chaincode definitions and the packages necessary to run them
type Lifecycle interface {
39
40
	// ChaincodeDefinition returns the details for a chaincode by name
	ChaincodeDefinition(chaincodeName string, txSim ledger.QueryExecutor) (ccprovider.ChaincodeDefinition, error)
41

42
	// ChaincodeContainerInfo returns the package necessary to launch a chaincode
43
	ChaincodeContainerInfo(chainID string, chaincodeID string) (*ccprovider.ChaincodeContainerInfo, error)
44
45
}

46
47
// ChaincodeSupport responsible for providing interfacing with chaincodes from the Peer.
type ChaincodeSupport struct {
48
49
50
51
52
53
54
55
56
	Keepalive        time.Duration
	ExecuteTimeout   time.Duration
	UserRunsCC       bool
	Runtime          Runtime
	ACLProvider      ACLProvider
	HandlerRegistry  *HandlerRegistry
	Launcher         Launcher
	SystemCCProvider sysccprovider.SystemChaincodeProvider
	Lifecycle        Lifecycle
57
	appConfig        ApplicationConfigRetriever
58
	Metrics          *Metrics
59
60
}

61
// NewChaincodeSupport creates a new ChaincodeSupport instance.
62
func NewChaincodeSupport(
63
	config *Config,
64
	peerAddress string,
65
	userRunsCC bool,
66
67
	caCert []byte,
	certGenerator CertGenerator,
68
	packageProvider PackageProvider,
69
	lifecycle Lifecycle,
70
	aclProvider ACLProvider,
71
	processor Processor,
72
	SystemCCProvider sysccprovider.SystemChaincodeProvider,
73
	platformRegistry *platforms.Registry,
74
	appConfig ApplicationConfigRetriever,
75
	metricsProvider metrics.Provider,
76
) *ChaincodeSupport {
77
	cs := &ChaincodeSupport{
78
79
80
81
82
83
84
		UserRunsCC:       userRunsCC,
		Keepalive:        config.Keepalive,
		ExecuteTimeout:   config.ExecuteTimeout,
		HandlerRegistry:  NewHandlerRegistry(userRunsCC),
		ACLProvider:      aclProvider,
		SystemCCProvider: SystemCCProvider,
		Lifecycle:        lifecycle,
85
		appConfig:        appConfig,
86
		Metrics:          NewMetrics(metricsProvider),
87
	}
88

89
	// Keep TestQueries working
90
	if !config.TLSEnabled {
91
92
93
		certGenerator = nil
	}

94
	cs.Runtime = &ContainerRuntime{
95
96
97
98
99
		CertGenerator:    certGenerator,
		Processor:        processor,
		CACert:           caCert,
		PeerAddress:      peerAddress,
		PlatformRegistry: platformRegistry,
100
		CommonEnv: []string{
101
102
103
			"CORE_CHAINCODE_LOGGING_LEVEL=" + config.LogLevel,
			"CORE_CHAINCODE_LOGGING_SHIM=" + config.ShimLogLevel,
			"CORE_CHAINCODE_LOGGING_FORMAT=" + config.LogFormat,
104
105
106
		},
	}

107
	cs.Launcher = &RuntimeLauncher{
108
		Runtime:         cs.Runtime,
109
		Registry:        cs.HandlerRegistry,
110
		PackageProvider: packageProvider,
111
		StartupTimeout:  config.StartupTimeout,
112
113
	}

114
	return cs
115
116
}

117
118
119
// LaunchForInit bypasses getting the chaincode spec from the LSCC table
// as in the case of v1.0-v1.2 lifecycle, the chaincode will not yet be
// defined in the LSCC table
120
121
func (cs *ChaincodeSupport) LaunchInit(ccci *ccprovider.ChaincodeContainerInfo) error {
	cname := ccci.Name + ":" + ccci.Version
122
123
124
125
	if cs.HandlerRegistry.Handler(cname) != nil {
		return nil
	}

126
	return cs.Launcher.Launch(ccci)
127
128
}

129
130
131
// Launch starts executing chaincode if it is not already running. This method
// blocks until the peer side handler gets into ready state or encounters a fatal
// error. If the chaincode is already running, it simply returns.
132
func (cs *ChaincodeSupport) Launch(chainID, chaincodeName, chaincodeVersion string) (*Handler, error) {
133
	cname := chaincodeName + ":" + chaincodeVersion
134
135
	if h := cs.HandlerRegistry.Handler(cname); h != nil {
		return h, nil
136
137
	}

138
	ccci, err := cs.Lifecycle.ChaincodeContainerInfo(chainID, chaincodeName)
139
	if err != nil {
140
141
142
143
144
145
146
		// TODO: There has to be a better way to do this...
		if cs.UserRunsCC {
			chaincodeLogger.Error(
				"You are attempting to perform an action other than Deploy on Chaincode that is not ready and you are in developer mode. Did you forget to Deploy your chaincode?",
			)
		}

147
		return nil, errors.Wrapf(err, "[channel %s] failed to get chaincode container info for %s", chainID, cname)
148
	}
149

150
151
152
153
154
155
156
157
158
159
	if err := cs.Launcher.Launch(ccci); err != nil {
		return nil, errors.Wrapf(err, "[channel %s] could not launch chaincode %s", chainID, cname)
	}

	h := cs.HandlerRegistry.Handler(cname)
	if h == nil {
		return nil, errors.Wrapf(err, "[channel %s] claimed to start chaincode container for %s but could not find handler", chainID, cname)
	}

	return h, nil
160
161
}

162
// Stop stops a chaincode if running.
163
func (cs *ChaincodeSupport) Stop(ccci *ccprovider.ChaincodeContainerInfo) error {
164
	return cs.Runtime.Stop(ccci)
165
166
167
}

// HandleChaincodeStream implements ccintf.HandleChaincodeStream for all vms to call with appropriate stream
168
func (cs *ChaincodeSupport) HandleChaincodeStream(stream ccintf.ChaincodeStream) error {
169
	handler := &Handler{
170
		Invoker:                    cs,
171
		DefinitionGetter:           cs.Lifecycle,
172
173
174
175
176
		Keepalive:                  cs.Keepalive,
		Registry:                   cs.HandlerRegistry,
		ACLProvider:                cs.ACLProvider,
		TXContexts:                 NewTransactionContexts(),
		ActiveTransactions:         NewActiveTransactions(),
177
		SystemCCProvider:           cs.SystemCCProvider,
178
179
		SystemCCVersion:            util.GetSysCCVersion(),
		InstantiationPolicyChecker: CheckInstantiationPolicyFunc(ccprovider.CheckInstantiationPolicy),
180
		QueryResponseBuilder:       &QueryResponseGenerator{MaxResultLimit: 100},
181
182
		UUIDGenerator:              UUIDGeneratorFunc(util.GenerateUUID),
		LedgerGetter:               peer.Default,
183
		AppConfig:                  cs.appConfig,
184
		Metrics:                    cs.Metrics,
185
186
187
	}

	return handler.ProcessStream(stream)
188
189
190
}

// Register the bidi stream entry point called by chaincode to register with the Peer.
191
func (cs *ChaincodeSupport) Register(stream pb.ChaincodeSupport_RegisterServer) error {
192
	return cs.HandleChaincodeStream(stream)
193
194
}

195
// createCCMessage creates a transaction message.
196
func createCCMessage(messageType pb.ChaincodeMessage_Type, cid string, txid string, cMsg *pb.ChaincodeInput) (*pb.ChaincodeMessage, error) {
197
198
199
200
	payload, err := proto.Marshal(cMsg)
	if err != nil {
		return nil, err
	}
201
202
203
204
205
206
207
	ccmsg := &pb.ChaincodeMessage{
		Type:      messageType,
		Payload:   payload,
		Txid:      txid,
		ChannelId: cid,
	}
	return ccmsg, nil
208
209
}

210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
// ExecuteLegacyInit is a temporary method which should be removed once the old style lifecycle
// is entirely deprecated.  Ideally one release after the introduction of the new lifecycle.
// It does not attempt to start the chaincode based on the information from lifecycle, but instead
// accepts the container information directly in the form of a ChaincodeDeploymentSpec.
func (cs *ChaincodeSupport) ExecuteLegacyInit(txParams *ccprovider.TransactionParams, cccid *ccprovider.CCContext, spec *pb.ChaincodeDeploymentSpec) (*pb.Response, *pb.ChaincodeEvent, error) {
	ccci := ccprovider.DeploymentSpecToChaincodeContainerInfo(spec)
	ccci.Version = cccid.Version

	err := cs.LaunchInit(ccci)
	if err != nil {
		return nil, nil, err
	}

	cname := ccci.Name + ":" + ccci.Version
	h := cs.HandlerRegistry.Handler(cname)
	if h == nil {
		return nil, nil, errors.Wrapf(err, "[channel %s] claimed to start chaincode container for %s but could not find handler", txParams.ChannelID, cname)
	}

	resp, err := cs.execute(pb.ChaincodeMessage_INIT, txParams, cccid, spec.GetChaincodeSpec().Input, h)
230
	return processChaincodeExecutionResult(txParams.TxID, cccid.Name, resp, err)
231
232
}

233
// Execute invokes chaincode and returns the original response.
234
235
func (cs *ChaincodeSupport) Execute(txParams *ccprovider.TransactionParams, cccid *ccprovider.CCContext, input *pb.ChaincodeInput) (*pb.Response, *pb.ChaincodeEvent, error) {
	resp, err := cs.Invoke(txParams, cccid, input)
236
	return processChaincodeExecutionResult(txParams.TxID, cccid.Name, resp, err)
237
238
}

239
func processChaincodeExecutionResult(txid, ccName string, resp *pb.ChaincodeMessage, err error) (*pb.Response, *pb.ChaincodeEvent, error) {
240
	if err != nil {
241
		return nil, nil, errors.Wrapf(err, "failed to execute transaction %s", txid)
242
243
	}
	if resp == nil {
244
		return nil, nil, errors.Errorf("nil response from transaction %s", txid)
245
246
247
	}

	if resp.ChaincodeEvent != nil {
248
249
		resp.ChaincodeEvent.ChaincodeId = ccName
		resp.ChaincodeEvent.TxId = txid
250
251
	}

252
253
	switch resp.Type {
	case pb.ChaincodeMessage_COMPLETED:
254
		res := &pb.Response{}
255
256
		err := proto.Unmarshal(resp.Payload, res)
		if err != nil {
257
			return nil, nil, errors.Wrapf(err, "failed to unmarshal response for transaction %s", txid)
258
259
260
		}
		return res, resp.ChaincodeEvent, nil

261
262
	case pb.ChaincodeMessage_ERROR:
		return nil, resp.ChaincodeEvent, errors.Errorf("transaction returned with failure: %s", resp.Payload)
263

264
	default:
265
		return nil, nil, errors.Errorf("unexpected response type %d for transaction %s", resp.Type, txid)
266
267
268
	}
}

269
270
func (cs *ChaincodeSupport) InvokeInit(txParams *ccprovider.TransactionParams, cccid *ccprovider.CCContext, input *pb.ChaincodeInput) (*pb.ChaincodeMessage, error) {
	h, err := cs.Launch(txParams.ChannelID, cccid.Name, cccid.Version)
271
272
273
274
	if err != nil {
		return nil, err
	}

275
	return cs.execute(pb.ChaincodeMessage_INIT, txParams, cccid, input, h)
276
277
}

278
279
// Invoke will invoke chaincode and return the message containing the response.
// The chaincode will be launched if it is not already running.
280
281
func (cs *ChaincodeSupport) Invoke(txParams *ccprovider.TransactionParams, cccid *ccprovider.CCContext, input *pb.ChaincodeInput) (*pb.ChaincodeMessage, error) {
	h, err := cs.Launch(txParams.ChannelID, cccid.Name, cccid.Version)
282
283
284
285
	if err != nil {
		return nil, err
	}

286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
	// TODO add Init exactly once semantics here once new lifecycle
	// is available.  Enforced if the target channel is using the new lifecycle
	//
	// First, the function name of the chaincode to invoke should be checked.  If it is
	// "init", then consider this invocation to be of type pb.ChaincodeMessage_INIT,
	// otherwise consider it to be of type pb.ChaincodeMessage_TRANSACTION,
	//
	// Secondly, A check should be made whether the chaincode has been
	// inited, then, if true, only allow cctyp pb.ChaincodeMessage_TRANSACTION,
	// otherwise, only allow cctype pb.ChaincodeMessage_INIT,
	cctype := pb.ChaincodeMessage_TRANSACTION

	return cs.execute(cctype, txParams, cccid, input, h)
}

// execute executes a transaction and waits for it to complete until a timeout value.
func (cs *ChaincodeSupport) execute(cctyp pb.ChaincodeMessage_Type, txParams *ccprovider.TransactionParams, cccid *ccprovider.CCContext, input *pb.ChaincodeInput, h *Handler) (*pb.ChaincodeMessage, error) {
303
304
	input.Decorations = txParams.ProposalDecorations
	ccMsg, err := createCCMessage(cctyp, txParams.ChannelID, txParams.TxID, input)
305
306
307
308
	if err != nil {
		return nil, errors.WithMessage(err, "failed to create chaincode message")
	}

309
	ccresp, err := h.Execute(txParams, cccid, ccMsg, cs.ExecuteTimeout)
310
	if err != nil {
311
		return nil, errors.WithMessage(err, fmt.Sprintf("error sending"))
312
313
	}

314
	return ccresp, nil
315
}