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

     1  package protocol
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  
     7  	"github.com/ethereum/go-ethereum/common/hexutil"
     8  
     9  	"github.com/golang/protobuf/proto"
    10  	"go.uber.org/zap"
    11  
    12  	"github.com/status-im/status-go/eth-node/crypto"
    13  	"github.com/status-im/status-go/eth-node/types"
    14  	"github.com/status-im/status-go/protocol/common"
    15  	"github.com/status-im/status-go/protocol/common/shard"
    16  	"github.com/status-im/status-go/protocol/communities"
    17  	"github.com/status-im/status-go/protocol/protobuf"
    18  	"github.com/status-im/status-go/protocol/transport"
    19  	v1protocol "github.com/status-im/status-go/protocol/v1"
    20  )
    21  
    22  func (m *Messenger) sendPublicCommunityShardInfo(community *communities.Community) error {
    23  	if !community.IsControlNode() {
    24  		return communities.ErrNotControlNode
    25  	}
    26  
    27  	publicShardInfo := &protobuf.PublicShardInfo{
    28  		Clock:       community.Clock(),
    29  		CommunityId: community.ID(),
    30  		Shard:       community.Shard().Protobuffer(),
    31  		ChainId:     communities.CommunityDescriptionTokenOwnerChainID(community.Description()),
    32  	}
    33  
    34  	payload, err := proto.Marshal(publicShardInfo)
    35  	if err != nil {
    36  		return err
    37  	}
    38  
    39  	signature, err := crypto.Sign(crypto.Keccak256(payload), community.PrivateKey())
    40  	if err != nil {
    41  		return err
    42  	}
    43  
    44  	signedShardInfo := &protobuf.CommunityPublicShardInfo{
    45  		Signature: signature,
    46  		Payload:   payload,
    47  	}
    48  
    49  	payload, err = proto.Marshal(signedShardInfo)
    50  	if err != nil {
    51  		return err
    52  	}
    53  
    54  	rawMessage := common.RawMessage{
    55  		Payload: payload,
    56  		Sender:  community.PrivateKey(),
    57  		// we don't want to wrap in an encryption layer message
    58  		SkipEncryptionLayer: true,
    59  		MessageType:         protobuf.ApplicationMetadataMessage_COMMUNITY_PUBLIC_SHARD_INFO,
    60  		PubsubTopic:         shard.DefaultNonProtectedPubsubTopic(), // it must be sent always to default shard pubsub topic
    61  		Priority:            &common.HighPriority,
    62  	}
    63  
    64  	chatName := transport.CommunityShardInfoTopic(community.IDString())
    65  	messageID, err := m.sender.SendPublic(context.Background(), chatName, rawMessage)
    66  	if err == nil {
    67  		m.logger.Debug("published public community shard info",
    68  			zap.String("communityID", community.IDString()),
    69  			zap.String("messageID", hexutil.Encode(messageID)),
    70  		)
    71  	}
    72  	return err
    73  }
    74  
    75  func (m *Messenger) HandleCommunityPublicShardInfo(state *ReceivedMessageState, a *protobuf.CommunityPublicShardInfo, statusMessage *v1protocol.StatusMessage) error {
    76  	publicShardInfo := &protobuf.PublicShardInfo{}
    77  	err := proto.Unmarshal(a.Payload, publicShardInfo)
    78  	if err != nil {
    79  		return err
    80  	}
    81  
    82  	logError := func(err error) {
    83  		m.logger.Error("HandleCommunityPublicShardInfo failed: ", zap.Error(err), zap.String("communityID", types.EncodeHex(publicShardInfo.CommunityId)))
    84  	}
    85  
    86  	err = m.verifyCommunitySignature(a.Payload, a.Signature, publicShardInfo.CommunityId, publicShardInfo.ChainId)
    87  	if err != nil {
    88  		logError(err)
    89  		return err
    90  	}
    91  
    92  	err = m.communitiesManager.SaveCommunityShard(publicShardInfo.CommunityId, shard.FromProtobuff(publicShardInfo.Shard), publicShardInfo.Clock)
    93  	if err != nil && err != communities.ErrOldShardInfo {
    94  		logError(err)
    95  		return err
    96  	}
    97  	return nil
    98  }
    99  
   100  func (m *Messenger) verifyCommunitySignature(payload, signature, communityID []byte, chainID uint64) error {
   101  	if len(signature) == 0 {
   102  		return errors.New("missing signature")
   103  	}
   104  	pubKey, err := crypto.SigToPub(crypto.Keccak256(payload), signature)
   105  	if err != nil {
   106  		return err
   107  	}
   108  	pubKeyStr := common.PubkeyToHex(pubKey)
   109  
   110  	var ownerPublicKey string
   111  	if chainID > 0 {
   112  		owner, err := m.communitiesManager.SafeGetSignerPubKey(chainID, types.EncodeHex(communityID))
   113  		if err != nil {
   114  			return err
   115  		}
   116  		ownerPublicKey = owner
   117  	} else {
   118  		communityPubkey, err := crypto.DecompressPubkey(communityID)
   119  		if err != nil {
   120  			return err
   121  		}
   122  		ownerPublicKey = common.PubkeyToHex(communityPubkey)
   123  	}
   124  
   125  	if pubKeyStr != ownerPublicKey {
   126  		return errors.New("signed not by a community owner")
   127  	}
   128  	return nil
   129  }