188 lines
5.0 KiB
Go
188 lines
5.0 KiB
Go
/*
|
|
Copyright IBM Corp. All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package chaincode
|
|
|
|
import (
|
|
"sync"
|
|
|
|
"github.com/hyperledger/fabric/core/ledger"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
// HandlerRegistry maintains chaincode Handler instances.
|
|
type HandlerRegistry struct {
|
|
allowUnsolicitedRegistration bool // from cs.userRunsCC
|
|
|
|
mutex sync.Mutex // lock covering handlers and launching
|
|
handlers map[string]*Handler // chaincode cname to associated handler
|
|
launching map[string]*LaunchState // launching chaincodes to LaunchState
|
|
}
|
|
|
|
type LaunchState struct {
|
|
mutex sync.Mutex
|
|
notified bool
|
|
done chan struct{}
|
|
err error
|
|
}
|
|
|
|
func NewLaunchState() *LaunchState {
|
|
return &LaunchState{
|
|
done: make(chan struct{}),
|
|
}
|
|
}
|
|
|
|
func (l *LaunchState) Done() <-chan struct{} {
|
|
return l.done
|
|
}
|
|
|
|
func (l *LaunchState) Err() error {
|
|
l.mutex.Lock()
|
|
err := l.err
|
|
l.mutex.Unlock()
|
|
return err
|
|
}
|
|
|
|
func (l *LaunchState) Notify(err error) {
|
|
l.mutex.Lock()
|
|
if !l.notified {
|
|
l.notified = true
|
|
l.err = err
|
|
close(l.done)
|
|
}
|
|
l.mutex.Unlock()
|
|
}
|
|
|
|
// NewHandlerRegistry constructs a HandlerRegistry.
|
|
func NewHandlerRegistry(allowUnsolicitedRegistration bool) *HandlerRegistry {
|
|
return &HandlerRegistry{
|
|
handlers: map[string]*Handler{},
|
|
launching: map[string]*LaunchState{},
|
|
allowUnsolicitedRegistration: allowUnsolicitedRegistration,
|
|
}
|
|
}
|
|
|
|
// Launching indicates that chaincode is being launched. The LaunchState that
|
|
// is returned provides mechanisms to determine when the operation has
|
|
// completed and whether or not it failed. The bool indicates whether or not
|
|
// the chaincode has already been started.
|
|
func (r *HandlerRegistry) Launching(ccid string) (*LaunchState, bool) {
|
|
r.mutex.Lock()
|
|
defer r.mutex.Unlock()
|
|
|
|
// launch happened or already happening
|
|
if launchState, ok := r.launching[ccid]; ok {
|
|
return launchState, true
|
|
}
|
|
|
|
// handler registered without going through launch
|
|
if _, ok := r.handlers[ccid]; ok {
|
|
launchState := NewLaunchState()
|
|
launchState.Notify(nil)
|
|
return launchState, true
|
|
}
|
|
|
|
// first attempt to launch so the runtime needs to start
|
|
launchState := NewLaunchState()
|
|
r.launching[ccid] = launchState
|
|
return launchState, false
|
|
}
|
|
|
|
// Ready indicates that the chaincode registration has completed and the
|
|
// READY response has been sent to the chaincode.
|
|
func (r *HandlerRegistry) Ready(ccid string) {
|
|
r.mutex.Lock()
|
|
defer r.mutex.Unlock()
|
|
|
|
launchStatus := r.launching[ccid]
|
|
if launchStatus != nil {
|
|
launchStatus.Notify(nil)
|
|
}
|
|
}
|
|
|
|
// Failed indicates that registration of a launched chaincode has failed.
|
|
func (r *HandlerRegistry) Failed(ccid string, err error) {
|
|
r.mutex.Lock()
|
|
defer r.mutex.Unlock()
|
|
|
|
launchStatus := r.launching[ccid]
|
|
if launchStatus != nil {
|
|
launchStatus.Notify(err)
|
|
}
|
|
}
|
|
|
|
// Handler retrieves the handler for a chaincode instance.
|
|
func (r *HandlerRegistry) Handler(ccid string) *Handler {
|
|
r.mutex.Lock()
|
|
h := r.handlers[ccid]
|
|
r.mutex.Unlock()
|
|
return h
|
|
}
|
|
|
|
// Register adds a chaincode handler to the registry.
|
|
// An error will be returned if a handler is already registered for the
|
|
// chaincode. An error will also be returned if the chaincode has not already
|
|
// been "launched", and unsolicited registration is not allowed.
|
|
func (r *HandlerRegistry) Register(h *Handler) error {
|
|
r.mutex.Lock()
|
|
defer r.mutex.Unlock()
|
|
|
|
if r.handlers[h.chaincodeID] != nil {
|
|
chaincodeLogger.Debugf("duplicate registered handler(key:%s) return error", h.chaincodeID)
|
|
return errors.Errorf("duplicate chaincodeID: %s", h.chaincodeID)
|
|
}
|
|
|
|
// This chaincode was not launched by the peer but is attempting
|
|
// to register. Only allowed in development mode.
|
|
if r.launching[h.chaincodeID] == nil && !r.allowUnsolicitedRegistration {
|
|
return errors.Errorf("peer will not accept external chaincode connection %s (except in dev mode)", h.chaincodeID)
|
|
}
|
|
|
|
r.handlers[h.chaincodeID] = h
|
|
|
|
chaincodeLogger.Debugf("registered handler complete for chaincode %s", h.chaincodeID)
|
|
return nil
|
|
}
|
|
|
|
// Deregister clears references to state associated specified chaincode.
|
|
// As part of the cleanup, it closes the handler so it can cleanup any state.
|
|
// If the registry does not contain the provided handler, an error is returned.
|
|
func (r *HandlerRegistry) Deregister(ccid string) error {
|
|
chaincodeLogger.Debugf("deregister handler: %s", ccid)
|
|
|
|
r.mutex.Lock()
|
|
handler := r.handlers[ccid]
|
|
delete(r.handlers, ccid)
|
|
delete(r.launching, ccid)
|
|
r.mutex.Unlock()
|
|
|
|
if handler == nil {
|
|
return errors.Errorf("could not find handler: %s", ccid)
|
|
}
|
|
|
|
handler.Close()
|
|
|
|
chaincodeLogger.Debugf("deregistered handler with key: %s", ccid)
|
|
return nil
|
|
}
|
|
|
|
type TxQueryExecutorGetter struct {
|
|
HandlerRegistry *HandlerRegistry
|
|
CCID string
|
|
}
|
|
|
|
func (g *TxQueryExecutorGetter) TxQueryExecutor(chainID, txID string) ledger.SimpleQueryExecutor {
|
|
handler := g.HandlerRegistry.Handler(g.CCID)
|
|
if handler == nil {
|
|
return nil
|
|
}
|
|
txContext := handler.TXContexts.Get(chainID, txID)
|
|
if txContext == nil {
|
|
return nil
|
|
}
|
|
return txContext.TXSimulator
|
|
}
|