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 }