github.com/hyperledger/aries-framework-go@v0.3.2/pkg/didcomm/protocol/introduce/service.go (about)

     1  /*
     2  Copyright SecureKey Technologies Inc. All Rights Reserved.
     3  
     4  SPDX-License-Identifier: Apache-2.0
     5  */
     6  
     7  package introduce
     8  
     9  import (
    10  	"encoding/json"
    11  	"errors"
    12  	"fmt"
    13  	"sort"
    14  	"time"
    15  
    16  	"github.com/google/uuid"
    17  
    18  	"github.com/hyperledger/aries-framework-go/pkg/common/log"
    19  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/model"
    20  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/common/service"
    21  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/decorator"
    22  	"github.com/hyperledger/aries-framework-go/pkg/didcomm/protocol/outofband"
    23  	"github.com/hyperledger/aries-framework-go/spi/storage"
    24  )
    25  
    26  const (
    27  	// Introduce protocol name.
    28  	Introduce = "introduce"
    29  	// IntroduceSpec defines the introduce spec.
    30  	IntroduceSpec = "https://didcomm.org/introduce/1.0/"
    31  	// ProposalMsgType defines the introduce proposal message type.
    32  	ProposalMsgType = IntroduceSpec + "proposal"
    33  	// RequestMsgType defines the introduce request message type.
    34  	RequestMsgType = IntroduceSpec + "request"
    35  	// ResponseMsgType defines the introduce response message type.
    36  	ResponseMsgType = IntroduceSpec + "response"
    37  	// AckMsgType defines the introduce ack message type.
    38  	AckMsgType = IntroduceSpec + "ack"
    39  	// ProblemReportMsgType defines the introduce problem-report message type.
    40  	ProblemReportMsgType = IntroduceSpec + "problem-report"
    41  	stateOOBInitial      = "initial"
    42  )
    43  
    44  const (
    45  	maxIntroducees         = 2
    46  	participantsKey        = "participants_%s_%s"
    47  	stateNameKey           = "state_name_"
    48  	transitionalPayloadKey = "transitionalPayload_%s"
    49  	metadataKey            = "metadata_%s"
    50  	jsonMetadata           = "_internal_metadata"
    51  )
    52  
    53  var (
    54  	logger = log.New("aries-framework/introduce/service")
    55  
    56  	errProtocolStopped = errors.New("protocol was stopped")
    57  )
    58  
    59  // customError is a wrapper to determine custom error against internal error.
    60  type customError struct{ error }
    61  
    62  // Recipient keeps information needed for the service
    63  // 'To' field is needed for the proposal message
    64  // 'MyDID' and 'TheirDID' fields are needed for sending messages e.g report-problem, proposal, ack etc.
    65  type Recipient struct {
    66  	To       *To    `json:"to"`
    67  	Goal     string `json:"goal"`
    68  	GoalCode string `json:"goal_code"`
    69  	MyDID    string `json:"my_did,omitempty"`
    70  	TheirDID string `json:"their_did,omitempty"`
    71  }
    72  
    73  // Action contains helpful information about action.
    74  type Action struct {
    75  	// Protocol instance ID
    76  	PIID     string
    77  	Msg      service.DIDCommMsgMap
    78  	MyDID    string
    79  	TheirDID string
    80  }
    81  
    82  // transitionalPayload keeps payload needed for Continue function to proceed with the action.
    83  type transitionalPayload struct {
    84  	Action
    85  	StateName string
    86  }
    87  
    88  // metaData type to store data for internal usage.
    89  type metaData struct {
    90  	transitionalPayload
    91  	state        state
    92  	msgClone     service.DIDCommMsg
    93  	participants []*participant
    94  	rejected     bool
    95  	inbound      bool
    96  	saveMetadata func(msg service.DIDCommMsgMap, thID string) error
    97  	// err is used to determine whether callback was stopped
    98  	// e.g the user received an action event and executes Stop(err) function
    99  	// in that case `err` is equal to `err` which was passing to Stop function
   100  	err error
   101  }
   102  
   103  // Service for introduce protocol.
   104  type Service struct {
   105  	service.Action
   106  	service.Message
   107  	store       storage.Store
   108  	callbacks   chan *metaData
   109  	oobEvent    chan service.StateMsg
   110  	messenger   service.Messenger
   111  	initialized bool
   112  }
   113  
   114  // Provider contains dependencies for the DID exchange protocol and is typically created by using aries.Context().
   115  type Provider interface {
   116  	Messenger() service.Messenger
   117  	StorageProvider() storage.Provider
   118  	Service(id string) (interface{}, error)
   119  }
   120  
   121  // New returns introduce service.
   122  func New(p Provider) (*Service, error) {
   123  	svc := Service{}
   124  
   125  	err := svc.Initialize(p)
   126  	if err != nil {
   127  		return nil, err
   128  	}
   129  
   130  	return &svc, nil
   131  }
   132  
   133  // Initialize initializes the Service. If Initialize succeeds, any further call is a no-op.
   134  func (s *Service) Initialize(prov interface{}) error {
   135  	if s.initialized {
   136  		return nil
   137  	}
   138  
   139  	p, ok := prov.(Provider)
   140  	if !ok {
   141  		return fmt.Errorf("expected provider of type `%T`, got type `%T`", Provider(nil), prov)
   142  	}
   143  
   144  	store, err := p.StorageProvider().OpenStore(Introduce)
   145  	if err != nil {
   146  		return err
   147  	}
   148  
   149  	err = p.StorageProvider().SetStoreConfig(Introduce,
   150  		storage.StoreConfiguration{TagNames: []string{transitionalPayloadKey, participantsKey}})
   151  	if err != nil {
   152  		return fmt.Errorf("failed to set store configuration: %w", err)
   153  	}
   154  
   155  	oobSvc, err := p.Service(outofband.Name)
   156  	if err != nil {
   157  		return fmt.Errorf("load the %s service: %w", outofband.Name, err)
   158  	}
   159  
   160  	oobService, ok := oobSvc.(service.Event)
   161  	if !ok {
   162  		return fmt.Errorf("cast service to service.Event")
   163  	}
   164  
   165  	s.messenger = p.Messenger()
   166  	s.store = store
   167  	s.callbacks = make(chan *metaData)
   168  	s.oobEvent = make(chan service.StateMsg)
   169  
   170  	if err = oobService.RegisterMsgEvent(s.oobEvent); err != nil {
   171  		return fmt.Errorf("oob register msg event: %w", err)
   172  	}
   173  
   174  	// start the listener
   175  	go s.startInternalListener()
   176  
   177  	s.initialized = true
   178  
   179  	return nil
   180  }
   181  
   182  // startInternalListener listens to messages in gochannel for callback messages from clients.
   183  func (s *Service) startInternalListener() {
   184  	for {
   185  		select {
   186  		case msg := <-s.callbacks:
   187  			// if no error or it was rejected do handle
   188  			if msg.err == nil || msg.rejected {
   189  				msg.err = s.handle(msg)
   190  			}
   191  
   192  			// no error - continue
   193  			if msg.err == nil {
   194  				continue
   195  			}
   196  
   197  			msg.state = &abandoning{Code: codeInternalError}
   198  
   199  			logInternalError(msg.err)
   200  
   201  			if err := s.handle(msg); err != nil {
   202  				logger.Errorf("listener handle: %s", err)
   203  			}
   204  		case event := <-s.oobEvent:
   205  			if err := s.OOBMessageReceived(event); err != nil {
   206  				logger.Errorf("listener oob message received: %s", err)
   207  			}
   208  		}
   209  	}
   210  }
   211  
   212  func logInternalError(err error) {
   213  	if !errors.As(err, &customError{}) {
   214  		logger.Errorf("go to abandoning: %v", err)
   215  	}
   216  }
   217  
   218  func getPIID(msg service.DIDCommMsgMap) (string, error) {
   219  	piID := msg.Metadata()[metaPIID]
   220  	if piID, ok := piID.(string); ok && piID != "" {
   221  		return piID, nil
   222  	}
   223  
   224  	return threadID(msg)
   225  }
   226  
   227  func threadID(msg service.DIDCommMsgMap) (string, error) {
   228  	if pthID := msg.ParentThreadID(); pthID != "" {
   229  		return pthID, nil
   230  	}
   231  
   232  	thID, err := msg.ThreadID()
   233  	if errors.Is(err, service.ErrThreadIDNotFound) {
   234  		msg["@id"] = uuid.New().String()
   235  		return msg["@id"].(string), nil
   236  	}
   237  
   238  	return thID, err
   239  }
   240  
   241  func (s *Service) doHandle(msg service.DIDCommMsg, outbound bool) (*metaData, error) {
   242  	msgMap := msg.Clone()
   243  
   244  	piID, err := getPIID(msgMap)
   245  	if err != nil {
   246  		return nil, fmt.Errorf("piID: %w", err)
   247  	}
   248  
   249  	stateName, err := s.currentStateName(piID)
   250  	if err != nil {
   251  		return nil, fmt.Errorf("currentStateName: %w", err)
   252  	}
   253  
   254  	current := stateFromName(stateName)
   255  
   256  	next, err := nextState(msgMap, outbound)
   257  	if err != nil {
   258  		return nil, fmt.Errorf("nextState: %w", err)
   259  	}
   260  
   261  	if !current.CanTransitionTo(next) {
   262  		return nil, fmt.Errorf("invalid state transition: %s -> %s", current.Name(), next.Name())
   263  	}
   264  
   265  	return &metaData{
   266  		transitionalPayload: transitionalPayload{
   267  			StateName: next.Name(),
   268  			Action: Action{
   269  				Msg:  msg.Clone(),
   270  				PIID: piID,
   271  			},
   272  		},
   273  		saveMetadata: s.saveMetadata,
   274  		state:        next,
   275  		msgClone:     msgMap.Clone(),
   276  	}, nil
   277  }
   278  
   279  // OOBMessageReceived is used to finish the state machine
   280  // the function should be called by the out-of-band service after receiving an oob message.
   281  func (s *Service) OOBMessageReceived(msg service.StateMsg) error {
   282  	// TODO we should verify if this parent thread ID is an instance of the introduce protocol
   283  	if msg.StateID != stateOOBInitial || msg.Type != service.PreState || msg.Msg.ParentThreadID() == "" {
   284  		return nil
   285  	}
   286  
   287  	// NOTE: the message is being used internally.
   288  	// Do not modify the payload such as ID and Thread.
   289  	_, err := s.HandleInbound(service.NewDIDCommMsgMap(&model.Ack{
   290  		Type:   AckMsgType,
   291  		ID:     uuid.New().String(),
   292  		Thread: &decorator.Thread{ID: msg.Msg.ParentThreadID()},
   293  	}), service.EmptyDIDCommContext())
   294  
   295  	return err
   296  }
   297  
   298  // HandleInbound handles inbound message (introduce protocol).
   299  func (s *Service) HandleInbound(msg service.DIDCommMsg, ctx service.DIDCommContext) (string, error) {
   300  	aEvent := s.ActionEvent()
   301  
   302  	// throw error if there is no action event registered for inbound messages
   303  	if aEvent == nil {
   304  		return "", errors.New("no clients are registered to handle the message")
   305  	}
   306  
   307  	if err := s.populateMetadata(msg.(service.DIDCommMsgMap)); err != nil {
   308  		return "", fmt.Errorf("populate metadata: %w", err)
   309  	}
   310  
   311  	md, err := s.doHandle(msg, false)
   312  	if err != nil {
   313  		return "", fmt.Errorf("doHandle: %w", err)
   314  	}
   315  
   316  	// sets inbound payload
   317  	md.inbound = true
   318  	md.MyDID = ctx.MyDID()
   319  	md.TheirDID = ctx.TheirDID()
   320  
   321  	// trigger action event based on message type for inbound messages
   322  	if canTriggerActionEvents(msg) {
   323  		err = s.saveTransitionalPayload(md.PIID, md.transitionalPayload)
   324  		if err != nil {
   325  			return "", fmt.Errorf("save transitional payload: %w", err)
   326  		}
   327  
   328  		aEvent <- s.newDIDCommActionMsg(md)
   329  
   330  		return md.PIID, nil
   331  	}
   332  
   333  	// if no action event is triggered, continue the execution
   334  	return md.PIID, s.handle(md)
   335  }
   336  
   337  // HandleOutbound handles outbound message (introduce protocol).
   338  func (s *Service) HandleOutbound(msg service.DIDCommMsg, myDID, theirDID string) (string, error) {
   339  	md, err := s.doHandle(msg, true)
   340  	if err != nil {
   341  		return "", fmt.Errorf("doHandle: %w", err)
   342  	}
   343  
   344  	// sets outbound payload
   345  	md.MyDID = myDID
   346  	md.TheirDID = theirDID
   347  
   348  	return md.PIID, s.handle(md)
   349  }
   350  
   351  // sendMsgEvents triggers the message events.
   352  func (s *Service) sendMsgEvents(md *metaData, stateID string, stateType service.StateMsgType) {
   353  	// trigger the message events
   354  	for _, handler := range s.MsgEvents() {
   355  		handler <- service.StateMsg{
   356  			ProtocolName: Introduce,
   357  			Type:         stateType,
   358  			Msg:          md.msgClone,
   359  			StateID:      stateID,
   360  			Properties:   newEventProps(md),
   361  		}
   362  	}
   363  }
   364  
   365  // newDIDCommActionMsg creates new DIDCommAction message.
   366  func (s *Service) newDIDCommActionMsg(md *metaData) service.DIDCommAction {
   367  	// create the message for the channel
   368  	// trigger the registered action event
   369  	actionStop := func(err error) {
   370  		// if introducee received Proposal rejected must be true
   371  		if md.Msg.Type() == ProposalMsgType {
   372  			md.rejected = true
   373  		}
   374  
   375  		md.err = err
   376  		s.processCallback(md)
   377  	}
   378  
   379  	return service.DIDCommAction{
   380  		ProtocolName: Introduce,
   381  		Message:      md.msgClone,
   382  		Continue: func(opt interface{}) {
   383  			if fn, ok := opt.(Opt); ok {
   384  				fn(md.Msg.Metadata())
   385  			}
   386  
   387  			if md.Msg.Type() == RequestMsgType {
   388  				if md.Msg.Metadata()[metaRecipients] == nil {
   389  					md.err = errors.New("no recipients")
   390  				}
   391  			}
   392  
   393  			if err := s.deleteTransitionalPayload(md.PIID); err != nil {
   394  				logger.Errorf("delete transitional payload", err)
   395  			}
   396  
   397  			s.processCallback(md)
   398  		},
   399  		Stop: func(err error) {
   400  			if err == nil {
   401  				err = errProtocolStopped
   402  			}
   403  
   404  			actionStop(customError{error: err})
   405  		},
   406  		Properties: newEventProps(md),
   407  	}
   408  }
   409  
   410  // ActionContinue allows proceeding with the action by the piID.
   411  func (s *Service) ActionContinue(piID string, opt Opt) error {
   412  	tPayload, err := s.getTransitionalPayload(piID)
   413  	if err != nil {
   414  		return fmt.Errorf("get transitional payload: %w", err)
   415  	}
   416  
   417  	md := &metaData{
   418  		transitionalPayload: *tPayload,
   419  		state:               stateFromName(tPayload.StateName),
   420  		msgClone:            tPayload.Msg.Clone(),
   421  		inbound:             true,
   422  		saveMetadata:        s.saveMetadata,
   423  	}
   424  
   425  	if opt != nil {
   426  		opt(md.Msg.Metadata())
   427  	}
   428  
   429  	if md.Msg.Type() == RequestMsgType {
   430  		if md.Msg.Metadata()[metaRecipients] == nil {
   431  			return errors.New("no recipients")
   432  		}
   433  	}
   434  
   435  	if err := s.deleteTransitionalPayload(md.PIID); err != nil {
   436  		logger.Errorf("delete transitional payload", err)
   437  	}
   438  
   439  	s.processCallback(md)
   440  
   441  	return nil
   442  }
   443  
   444  // ActionStop allows stopping the action by the piID.
   445  func (s *Service) ActionStop(piID string, cErr error) error {
   446  	tPayload, err := s.getTransitionalPayload(piID)
   447  	if err != nil {
   448  		return fmt.Errorf("get transitional payload: %w", err)
   449  	}
   450  
   451  	md := &metaData{
   452  		transitionalPayload: *tPayload,
   453  		state:               stateFromName(tPayload.StateName),
   454  		msgClone:            tPayload.Msg.Clone(),
   455  		inbound:             true,
   456  		saveMetadata:        s.saveMetadata,
   457  	}
   458  
   459  	if err := s.deleteTransitionalPayload(md.PIID); err != nil {
   460  		return fmt.Errorf("delete transitional payload: %w", err)
   461  	}
   462  
   463  	// if introducee received Proposal rejected must be true
   464  	if md.Msg.Type() == ProposalMsgType {
   465  		md.rejected = true
   466  	}
   467  
   468  	if cErr == nil {
   469  		cErr = errProtocolStopped
   470  	}
   471  
   472  	md.err = customError{error: cErr}
   473  	s.processCallback(md)
   474  
   475  	return nil
   476  }
   477  
   478  func (s *Service) processCallback(msg *metaData) {
   479  	// pass the callback data to internal channel. This is created to unblock consumer go routine and wrap the callback
   480  	// channel internally.
   481  	s.callbacks <- msg
   482  }
   483  
   484  func nextState(msg service.DIDCommMsg, outbound bool) (state, error) {
   485  	switch msg.Type() {
   486  	case RequestMsgType:
   487  		if outbound {
   488  			return &requesting{}, nil
   489  		}
   490  
   491  		return &arranging{}, nil
   492  	case ProposalMsgType:
   493  		if outbound {
   494  			return &arranging{}, nil
   495  		}
   496  
   497  		return &deciding{}, nil
   498  	case ResponseMsgType:
   499  		return &arranging{}, nil
   500  	case ProblemReportMsgType:
   501  		return &abandoning{}, nil
   502  	case AckMsgType:
   503  		return &done{}, nil
   504  	default:
   505  		return nil, fmt.Errorf("unrecognized msgType: %s", msg.Type())
   506  	}
   507  }
   508  
   509  func (s *Service) currentStateName(piID string) (string, error) {
   510  	src, err := s.store.Get(stateNameKey + piID)
   511  	if errors.Is(err, storage.ErrDataNotFound) {
   512  		return stateNameStart, nil
   513  	}
   514  
   515  	return string(src), err
   516  }
   517  
   518  // Actions returns actions for the async usage.
   519  func (s *Service) Actions() ([]Action, error) {
   520  	records, err := s.store.Query(transitionalPayloadKey)
   521  	if err != nil {
   522  		return nil, fmt.Errorf("failed to query store: %w", err)
   523  	}
   524  
   525  	defer storage.Close(records, logger)
   526  
   527  	var actions []Action
   528  
   529  	more, err := records.Next()
   530  	if err != nil {
   531  		return nil, fmt.Errorf("failed to get next record: %w", err)
   532  	}
   533  
   534  	for more {
   535  		var action Action
   536  
   537  		value, err := records.Value()
   538  		if err != nil {
   539  			return nil, fmt.Errorf("failed to get value from records: %w", err)
   540  		}
   541  
   542  		if errUnmarshal := json.Unmarshal(value, &action); errUnmarshal != nil {
   543  			return nil, fmt.Errorf("unmarshal: %w", errUnmarshal)
   544  		}
   545  
   546  		actions = append(actions, action)
   547  
   548  		more, err = records.Next()
   549  		if err != nil {
   550  			return nil, fmt.Errorf("failed to get next record: %w", err)
   551  		}
   552  	}
   553  
   554  	return actions, nil
   555  }
   556  
   557  func (s *Service) deleteTransitionalPayload(id string) error {
   558  	return s.store.Delete(fmt.Sprintf(transitionalPayloadKey, id))
   559  }
   560  
   561  func (s *Service) saveTransitionalPayload(id string, data transitionalPayload) error {
   562  	src, err := json.Marshal(data)
   563  	if err != nil {
   564  		return fmt.Errorf("marshal transitional payload: %w", err)
   565  	}
   566  
   567  	return s.store.Put(fmt.Sprintf(transitionalPayloadKey, id), src, storage.Tag{Name: transitionalPayloadKey})
   568  }
   569  
   570  func (s *Service) getTransitionalPayload(id string) (*transitionalPayload, error) {
   571  	src, err := s.store.Get(fmt.Sprintf(transitionalPayloadKey, id))
   572  	if err != nil {
   573  		return nil, fmt.Errorf("store get: %w", err)
   574  	}
   575  
   576  	t := &transitionalPayload{}
   577  
   578  	err = json.Unmarshal(src, t)
   579  	if err != nil {
   580  		return nil, fmt.Errorf("unmarshal transitional payload: %w", err)
   581  	}
   582  
   583  	return t, err
   584  }
   585  
   586  func (s *Service) saveStateName(piID, stateName string) error {
   587  	return s.store.Put(stateNameKey+piID, []byte(stateName))
   588  }
   589  
   590  // nolint: gocyclo
   591  // stateFromName returns the state by given name.
   592  func stateFromName(name string) state {
   593  	switch name {
   594  	case stateNameNoop:
   595  		return &noOp{}
   596  	case stateNameStart:
   597  		return &start{}
   598  	case stateNameDone:
   599  		return &done{}
   600  	case stateNameArranging:
   601  		return &arranging{}
   602  	case stateNameDelivering:
   603  		return &delivering{}
   604  	case stateNameConfirming:
   605  		return &confirming{}
   606  	case stateNameAbandoning:
   607  		return &abandoning{}
   608  	case stateNameRequesting:
   609  		return &requesting{}
   610  	case stateNameDeciding:
   611  		return &deciding{}
   612  	case stateNameWaiting:
   613  		return &waiting{}
   614  	default:
   615  		return &noOp{}
   616  	}
   617  }
   618  
   619  // canTriggerActionEvents checks if the incoming message can trigger an action event.
   620  func canTriggerActionEvents(msg service.DIDCommMsg) bool {
   621  	switch msg.Type() {
   622  	case ProposalMsgType, RequestMsgType, ProblemReportMsgType:
   623  		return true
   624  	}
   625  
   626  	return false
   627  }
   628  
   629  func isNoOp(s state) bool {
   630  	_, ok := s.(*noOp)
   631  	return ok
   632  }
   633  
   634  // isSkipProposal is a helper function to determine whether this is skip proposal or not.
   635  func isSkipProposal(md *metaData) bool {
   636  	if md.Msg.Metadata()[metaSkipProposal] == nil {
   637  		return false
   638  	}
   639  
   640  	return md.Msg.Metadata()[metaSkipProposal].(bool)
   641  }
   642  
   643  func (s *Service) handle(md *metaData) error {
   644  	if err := s.saveResponse(md); err != nil {
   645  		return err
   646  	}
   647  
   648  	var (
   649  		current   = md.state
   650  		actions   []stateAction
   651  		stateName string
   652  	)
   653  
   654  	for !isNoOp(current) {
   655  		stateName = current.Name()
   656  
   657  		next, action, err := s.execute(current, md)
   658  		if err != nil {
   659  			return fmt.Errorf("execute: %w", err)
   660  		}
   661  
   662  		actions = append(actions, action)
   663  
   664  		if !isNoOp(next) && !current.CanTransitionTo(next) {
   665  			return fmt.Errorf("invalid state transition: %s --> %s", current.Name(), next.Name())
   666  		}
   667  
   668  		current = next
   669  	}
   670  
   671  	if err := s.saveStateName(md.PIID, stateName); err != nil {
   672  		return fmt.Errorf("failed to persist state %s: %w", stateName, err)
   673  	}
   674  
   675  	for _, action := range actions {
   676  		if err := action(); err != nil {
   677  			return err
   678  		}
   679  	}
   680  
   681  	return nil
   682  }
   683  
   684  func contextOOBMessage(msg service.DIDCommMsg) map[string]interface{} {
   685  	var oobMsg map[string]interface{}
   686  
   687  	switch v := msg.Metadata()[metaOOBMessage].(type) {
   688  	case service.DIDCommMsgMap:
   689  		oobMsg = v.Clone()
   690  
   691  		for k := range v.Metadata() {
   692  			delete(oobMsg, k)
   693  		}
   694  	case map[string]interface{}:
   695  		oobMsg = v
   696  	}
   697  
   698  	return oobMsg
   699  }
   700  
   701  type participant struct {
   702  	OOBMessage map[string]interface{}
   703  	Approve    bool
   704  	Message    service.DIDCommMsgMap
   705  	MyDID      string
   706  	TheirDID   string
   707  	ThreadID   string
   708  	CreatedAt  time.Time
   709  }
   710  
   711  func (s *Service) saveResponse(md *metaData) error {
   712  	// ignore if message is not response
   713  	if md.Msg.Type() != ResponseMsgType {
   714  		return nil
   715  	}
   716  
   717  	// checks whether response was already handled
   718  	for _, p := range md.participants {
   719  		if p.Message.ID() == md.Msg.ID() {
   720  			return nil
   721  		}
   722  	}
   723  
   724  	r := Response{}
   725  	if err := md.Msg.Decode(&r); err != nil {
   726  		return err
   727  	}
   728  
   729  	thID, err := md.Msg.ThreadID()
   730  	if err != nil {
   731  		return fmt.Errorf("threadID: %w", err)
   732  	}
   733  
   734  	err = s.saveParticipant(md.PIID, &participant{
   735  		OOBMessage: r.OOBMessage,
   736  		Approve:    r.Approve,
   737  		Message:    md.Msg,
   738  		MyDID:      md.MyDID,
   739  		TheirDID:   md.TheirDID,
   740  		ThreadID:   thID,
   741  		CreatedAt:  time.Now(),
   742  	})
   743  	if err != nil {
   744  		return fmt.Errorf("save participant: %w", err)
   745  	}
   746  
   747  	md.participants, err = s.getParticipants(md.PIID)
   748  
   749  	return err
   750  }
   751  
   752  func (s *Service) saveParticipant(piID string, p *participant) error {
   753  	src, err := json.Marshal(p)
   754  	if err != nil {
   755  		return fmt.Errorf("marshal: %w", err)
   756  	}
   757  
   758  	return s.store.Put(fmt.Sprintf(participantsKey, piID, uuid.New().String()), src, storage.Tag{
   759  		Name:  participantsKey,
   760  		Value: piID,
   761  	})
   762  }
   763  
   764  func (s *Service) getParticipants(piID string) ([]*participant, error) {
   765  	records, err := s.store.Query(fmt.Sprintf("%s:%s", participantsKey, piID))
   766  	if err != nil {
   767  		return nil, fmt.Errorf("failed to query store: %w", err)
   768  	}
   769  
   770  	defer storage.Close(records, logger)
   771  
   772  	var participants []*participant
   773  
   774  	more, err := records.Next()
   775  	if err != nil {
   776  		return nil, fmt.Errorf("failed to get next record: %w", err)
   777  	}
   778  
   779  	for more {
   780  		value, err := records.Value()
   781  		if err != nil {
   782  			return nil, fmt.Errorf("failed to get value from records: %w", err)
   783  		}
   784  
   785  		var participant *participant
   786  		if errUnmarshal := json.Unmarshal(value, &participant); errUnmarshal != nil {
   787  			return nil, fmt.Errorf("unmarshal: %w", errUnmarshal)
   788  		}
   789  
   790  		participants = append(participants, participant)
   791  
   792  		more, err = records.Next()
   793  		if err != nil {
   794  			return nil, fmt.Errorf("failed to get next record: %w", err)
   795  		}
   796  	}
   797  
   798  	sort.Slice(participants, func(i, j int) bool {
   799  		return participants[i].CreatedAt.UnixNano() < participants[j].CreatedAt.UnixNano()
   800  	})
   801  
   802  	return participants, nil
   803  }
   804  
   805  func (s *Service) execute(next state, md *metaData) (state, stateAction, error) {
   806  	md.state = next
   807  	s.sendMsgEvents(md, next.Name(), service.PreState)
   808  
   809  	defer s.sendMsgEvents(md, next.Name(), service.PostState)
   810  
   811  	var (
   812  		followup state
   813  		err      error
   814  		action   func() error
   815  	)
   816  
   817  	if md.inbound {
   818  		followup, action, err = next.ExecuteInbound(s.messenger, md)
   819  	} else {
   820  		followup, action, err = next.ExecuteOutbound(s.messenger, md)
   821  	}
   822  
   823  	if err != nil {
   824  		return nil, nil, fmt.Errorf("execute state %s %w", next.Name(), err)
   825  	}
   826  
   827  	return followup, action, nil
   828  }
   829  
   830  func (s *Service) populateMetadata(msg service.DIDCommMsgMap) error {
   831  	thID, err := msg.ThreadID()
   832  	if err != nil {
   833  		return fmt.Errorf("threadID: %w", err)
   834  	}
   835  
   836  	rec, err := s.store.Get(fmt.Sprintf(metadataKey, thID))
   837  	if errors.Is(err, storage.ErrDataNotFound) {
   838  		return nil
   839  	}
   840  
   841  	if err != nil {
   842  		return fmt.Errorf("get record: %w", err)
   843  	}
   844  
   845  	res := map[string]interface{}{}
   846  
   847  	err = json.Unmarshal(rec, &res)
   848  	if err != nil {
   849  		return fmt.Errorf("get record: %w", err)
   850  	}
   851  
   852  	msg[jsonMetadata] = res
   853  
   854  	return nil
   855  }
   856  
   857  func (s *Service) saveMetadata(msg service.DIDCommMsgMap, thID string) error {
   858  	metadata := msg.Metadata()
   859  	if len(metadata) == 0 {
   860  		return nil
   861  	}
   862  
   863  	src, err := json.Marshal(metadata)
   864  	if err != nil {
   865  		return fmt.Errorf("marshal: %w", err)
   866  	}
   867  
   868  	return s.store.Put(fmt.Sprintf(metadataKey, thID), src)
   869  }
   870  
   871  // Name returns service name.
   872  func (s *Service) Name() string {
   873  	return Introduce
   874  }
   875  
   876  // Accept msg checks the msg type.
   877  func (s *Service) Accept(msgType string) bool {
   878  	switch msgType {
   879  	case ProposalMsgType, RequestMsgType, ResponseMsgType, AckMsgType, ProblemReportMsgType:
   880  		return true
   881  	}
   882  
   883  	return false
   884  }