github.com/status-im/status-go@v1.1.0/protocol/communities/communnity_privileged_member_sync_msg.go (about)

     1  package communities
     2  
     3  import (
     4  	"crypto/ecdsa"
     5  	"database/sql"
     6  	"errors"
     7  
     8  	"go.uber.org/zap"
     9  
    10  	multiaccountscommon "github.com/status-im/status-go/multiaccounts/common"
    11  
    12  	"github.com/status-im/status-go/eth-node/types"
    13  	"github.com/status-im/status-go/protocol/common"
    14  	"github.com/status-im/status-go/protocol/protobuf"
    15  )
    16  
    17  var ErrOutdatedSharedRequestToJoinClock = errors.New("outdated clock in shared request to join")
    18  var ErrOutdatedSharedRequestToJoinState = errors.New("outdated state in shared request to join")
    19  
    20  type CommunityPrivilegedMemberSyncMessage struct {
    21  	Receivers                          []*ecdsa.PublicKey
    22  	CommunityPrivilegedUserSyncMessage *protobuf.CommunityPrivilegedUserSyncMessage
    23  }
    24  
    25  func (m *Manager) HandleRequestToJoinPrivilegedUserSyncMessage(message *protobuf.CommunityPrivilegedUserSyncMessage, community *Community) ([]*RequestToJoin, error) {
    26  	var state RequestToJoinState
    27  	if message.Type == protobuf.CommunityPrivilegedUserSyncMessage_CONTROL_NODE_ACCEPT_REQUEST_TO_JOIN {
    28  		state = RequestToJoinStateAccepted
    29  	} else {
    30  		state = RequestToJoinStateDeclined
    31  	}
    32  
    33  	myPk := common.PubkeyToHex(&m.identity.PublicKey)
    34  
    35  	requestsToJoin := make([]*RequestToJoin, 0)
    36  	for signer, requestToJoinProto := range message.RequestToJoin {
    37  		if signer == myPk {
    38  			continue
    39  		}
    40  
    41  		requestToJoin := &RequestToJoin{
    42  			PublicKey:          signer,
    43  			Clock:              requestToJoinProto.Clock,
    44  			ENSName:            requestToJoinProto.EnsName,
    45  			CustomizationColor: multiaccountscommon.IDToColorFallbackToBlue(requestToJoinProto.CustomizationColor),
    46  			CommunityID:        requestToJoinProto.CommunityId,
    47  			State:              state,
    48  			RevealedAccounts:   requestToJoinProto.RevealedAccounts,
    49  		}
    50  		requestToJoin.CalculateID()
    51  
    52  		err := m.processPrivilegedUserSharedRequestToJoin(community, requestToJoin)
    53  		if err != nil {
    54  			m.logger.Warn("error to handle shared request to join",
    55  				zap.String("communityID", community.IDString()),
    56  				zap.String("requestToJoinID", types.Bytes2Hex(requestToJoin.ID)),
    57  				zap.String("publicKey", requestToJoin.PublicKey),
    58  				zap.String("error", err.Error()))
    59  			continue
    60  		}
    61  
    62  		requestsToJoin = append(requestsToJoin, requestToJoin)
    63  	}
    64  
    65  	return requestsToJoin, nil
    66  }
    67  
    68  func (m *Manager) HandleSyncAllRequestToJoinForNewPrivilegedMember(message *protobuf.CommunityPrivilegedUserSyncMessage, community *Community) ([]*RequestToJoin, error) {
    69  	nonAcceptedRequestsToJoin := []*RequestToJoin{}
    70  	myPk := common.PubkeyToHex(&m.identity.PublicKey)
    71  
    72  	for _, syncRequestToJoin := range message.SyncRequestsToJoin {
    73  		if syncRequestToJoin.PublicKey == myPk {
    74  			continue
    75  		}
    76  
    77  		requestToJoin := new(RequestToJoin)
    78  		requestToJoin.InitFromSyncProtobuf(syncRequestToJoin)
    79  
    80  		err := m.processPrivilegedUserSharedRequestToJoin(community, requestToJoin)
    81  		if err != nil {
    82  			m.logger.Warn("error to handle shared request to join from sync all requests to join msg",
    83  				zap.String("communityID", community.IDString()),
    84  				zap.String("requestToJoinID", types.Bytes2Hex(requestToJoin.ID)),
    85  				zap.String("publicKey", requestToJoin.PublicKey),
    86  				zap.String("error", err.Error()))
    87  			continue
    88  		}
    89  
    90  		if requestToJoin.State != RequestToJoinStateAccepted {
    91  			nonAcceptedRequestsToJoin = append(nonAcceptedRequestsToJoin, requestToJoin)
    92  		}
    93  	}
    94  	return nonAcceptedRequestsToJoin, nil
    95  }
    96  
    97  func (m *Manager) HandleEditSharedAddressesPrivilegedUserSyncMessage(message *protobuf.CommunityPrivilegedUserSyncMessage, community *Community) error {
    98  	if !(community.IsTokenMaster() || community.IsOwner()) {
    99  		return ErrNotEnoughPermissions
   100  	}
   101  
   102  	publicKey := message.SyncEditSharedAddresses.PublicKey
   103  	editSharedAddress := message.SyncEditSharedAddresses.EditSharedAddress
   104  	if err := community.ValidateEditSharedAddresses(publicKey, editSharedAddress); err != nil {
   105  		return err
   106  	}
   107  
   108  	return m.handleCommunityEditSharedAddresses(publicKey, community.ID(), editSharedAddress.RevealedAccounts, message.Clock)
   109  }
   110  
   111  func (m *Manager) processPrivilegedUserSharedRequestToJoin(community *Community, requestToJoin *RequestToJoin) error {
   112  	existingRequestToJoin, err := m.persistence.GetCommunityRequestToJoinWithRevealedAddresses(requestToJoin.PublicKey, community.ID())
   113  	if err != nil && err != sql.ErrNoRows {
   114  		return err
   115  	}
   116  
   117  	statusUpdate := existingRequestToJoin != nil && existingRequestToJoin.Clock == requestToJoin.Clock
   118  
   119  	if existingRequestToJoin != nil && existingRequestToJoin.Clock > requestToJoin.Clock {
   120  		return ErrOutdatedSharedRequestToJoinClock
   121  	}
   122  
   123  	revealedAccountsExists := requestToJoin.RevealedAccounts != nil && len(requestToJoin.RevealedAccounts) > 0
   124  
   125  	if member, memberExists := community.Members()[requestToJoin.PublicKey]; memberExists && member.LastUpdateClock > requestToJoin.Clock {
   126  		return ErrOutdatedSharedRequestToJoinClock
   127  	}
   128  
   129  	if statusUpdate {
   130  		isCurrentStateAccepted := existingRequestToJoin.State == RequestToJoinStateAccepted
   131  		isNewRequestAccepted := requestToJoin.State == RequestToJoinStateAccepted
   132  		isNewAcceptedRequestWithoutAccounts := isNewRequestAccepted && !revealedAccountsExists
   133  		isCurrentStateDeclined := existingRequestToJoin.State == RequestToJoinStateDeclined
   134  
   135  		if (isCurrentStateAccepted && (!isNewRequestAccepted || isNewAcceptedRequestWithoutAccounts)) ||
   136  			(isCurrentStateDeclined && !isNewRequestAccepted) {
   137  			return ErrOutdatedSharedRequestToJoinState
   138  		}
   139  
   140  		err = m.persistence.SetRequestToJoinState(requestToJoin.PublicKey, community.ID(), requestToJoin.State)
   141  		if err != nil {
   142  			return err
   143  		}
   144  	} else {
   145  		err = m.persistence.SaveRequestToJoin(requestToJoin)
   146  		if err != nil {
   147  			return err
   148  		}
   149  	}
   150  
   151  	// If we are a token master or owner without private key and we received request to join without
   152  	// revealed accounts - there is a chance, that we lost our role and did't get
   153  	// CommunityDescription update. But it also can indicate, that we received an outdated
   154  	// request to join, when we were admins
   155  	// Decision - is not to delete existing revealed accounts
   156  	if (community.IsTokenMaster() || community.IsOwner()) && !revealedAccountsExists {
   157  		return nil
   158  	}
   159  
   160  	err = m.persistence.RemoveRequestToJoinRevealedAddresses(requestToJoin.ID)
   161  	if err != nil {
   162  		return err
   163  	}
   164  
   165  	if revealedAccountsExists {
   166  		return m.persistence.SaveRequestToJoinRevealedAddresses(requestToJoin.ID, requestToJoin.RevealedAccounts)
   167  	}
   168  
   169  	return nil
   170  }