This commit is contained in:
parent
53ac61bb8b
commit
405d468e0e
|
@ -1372,6 +1372,10 @@ func (o *Community) Description() *protobuf.CommunityDescription {
|
||||||
return o.config.CommunityDescription
|
return o.config.CommunityDescription
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (o *Community) DescriptionProtocolMessage() []byte {
|
||||||
|
return o.config.CommunityDescriptionProtocolMessage
|
||||||
|
}
|
||||||
|
|
||||||
func (o *Community) marshaledDescription() ([]byte, error) {
|
func (o *Community) marshaledDescription() ([]byte, error) {
|
||||||
clone := proto.Clone(o.config.CommunityDescription).(*protobuf.CommunityDescription)
|
clone := proto.Clone(o.config.CommunityDescription).(*protobuf.CommunityDescription)
|
||||||
|
|
||||||
|
|
|
@ -1638,7 +1638,12 @@ func (m *Manager) HandleCommunityDescriptionMessage(signer *ecdsa.PublicKey, des
|
||||||
|
|
||||||
if hasTokenOwnership && verifiedOwner != nil {
|
if hasTokenOwnership && verifiedOwner != nil {
|
||||||
// Override verified owner
|
// Override verified owner
|
||||||
m.logger.Info("updating verified owner", zap.String("communityID", community.IDString()), zap.String("owner", common.PubkeyToHex(verifiedOwner)))
|
m.logger.Info("updating verified owner",
|
||||||
|
zap.String("communityID", community.IDString()),
|
||||||
|
zap.String("verifiedOwner", common.PubkeyToHex(verifiedOwner)),
|
||||||
|
zap.String("signer", common.PubkeyToHex(signer)),
|
||||||
|
zap.String("controlNode", common.PubkeyToHex(community.ControlNode())),
|
||||||
|
)
|
||||||
|
|
||||||
// If we are not the verified owner anymore, drop the private key
|
// If we are not the verified owner anymore, drop the private key
|
||||||
if !common.IsPubKeyEqual(verifiedOwner, &m.identity.PublicKey) {
|
if !common.IsPubKeyEqual(verifiedOwner, &m.identity.PublicKey) {
|
||||||
|
|
|
@ -331,7 +331,8 @@ func advertiseCommunityTo(s *suite.Suite, community *communities.Community, owne
|
||||||
messageState := user.buildMessageState()
|
messageState := user.buildMessageState()
|
||||||
messageState.CurrentMessageState = &CurrentMessageState{}
|
messageState.CurrentMessageState = &CurrentMessageState{}
|
||||||
messageState.CurrentMessageState.PublicKey = &user.identity.PublicKey
|
messageState.CurrentMessageState.PublicKey = &user.identity.PublicKey
|
||||||
err = user.handleCommunityDescription(messageState, signer, description, wrappedCommunity, nil)
|
// TODO: handle shards?
|
||||||
|
err = user.handleCommunityDescription(messageState, signer, description, wrappedCommunity, nil, nil)
|
||||||
s.Require().NoError(err)
|
s.Require().NoError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,16 +1,20 @@
|
||||||
package protocol
|
package protocol
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"github.com/golang/protobuf/proto"
|
||||||
|
|
||||||
"github.com/stretchr/testify/suite"
|
"github.com/stretchr/testify/suite"
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
gethcommon "github.com/ethereum/go-ethereum/common"
|
gethcommon "github.com/ethereum/go-ethereum/common"
|
||||||
hexutil "github.com/ethereum/go-ethereum/common/hexutil"
|
hexutil "github.com/ethereum/go-ethereum/common/hexutil"
|
||||||
|
|
||||||
|
utils "github.com/status-im/status-go/common"
|
||||||
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
|
gethbridge "github.com/status-im/status-go/eth-node/bridge/geth"
|
||||||
"github.com/status-im/status-go/eth-node/crypto"
|
"github.com/status-im/status-go/eth-node/crypto"
|
||||||
"github.com/status-im/status-go/eth-node/types"
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
|
@ -581,3 +585,159 @@ func (s *MessengerCommunitiesSignersSuite) TestNewOwnerAcceptRequestToJoin() {
|
||||||
s.joinCommunity(s.alice, community, s.bob)
|
s.joinCommunity(s.alice, community, s.bob)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (s *MessengerCommunitiesSignersSuite) testDescriptionSignature(description []byte) {
|
||||||
|
var amm protobuf.ApplicationMetadataMessage
|
||||||
|
err := proto.Unmarshal(description, &amm)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
signer, err := utils.RecoverKey(&amm)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.NotNil(signer)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MessengerCommunitiesSignersSuite) forceCommunityChange(community *communities.Community, owner *Messenger, user *Messenger) {
|
||||||
|
newDescription := community.DescriptionText() + " new"
|
||||||
|
_, err := owner.EditCommunity(&requests.EditCommunity{
|
||||||
|
CommunityID: community.ID(),
|
||||||
|
CreateCommunity: requests.CreateCommunity{
|
||||||
|
Membership: protobuf.CommunityPermissions_AUTO_ACCEPT,
|
||||||
|
Name: community.Name(),
|
||||||
|
Color: community.Color(),
|
||||||
|
Description: newDescription,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// alice receives new description
|
||||||
|
_, err = WaitOnMessengerResponse(user, func(r *MessengerResponse) bool {
|
||||||
|
return len(r.Communities()) > 0 && r.Communities()[0].DescriptionText() == newDescription
|
||||||
|
}, "new description not received")
|
||||||
|
s.Require().NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MessengerCommunitiesSignersSuite) testSyncCommunity(mintOwnerToken bool) {
|
||||||
|
|
||||||
|
community := s.createCommunity(s.john)
|
||||||
|
s.advertiseCommunityTo(s.john, community, s.alice)
|
||||||
|
s.joinCommunity(s.john, community, s.alice)
|
||||||
|
|
||||||
|
// FIXME: Remove this workaround when fixed:
|
||||||
|
// https://github.com/status-im/status-go/issues/4413
|
||||||
|
s.forceCommunityChange(community, s.john, s.alice)
|
||||||
|
|
||||||
|
aliceCommunity, err := s.alice.GetCommunityByID(community.ID())
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.testDescriptionSignature(aliceCommunity.DescriptionProtocolMessage())
|
||||||
|
|
||||||
|
if mintOwnerToken {
|
||||||
|
// john mints owner token
|
||||||
|
var chainID uint64 = 1
|
||||||
|
tokenAddress := "token-address"
|
||||||
|
tokenName := "tokenName"
|
||||||
|
tokenSymbol := "TSM"
|
||||||
|
_, err := s.john.SaveCommunityToken(&token.CommunityToken{
|
||||||
|
TokenType: protobuf.CommunityTokenType_ERC721,
|
||||||
|
CommunityID: community.IDString(),
|
||||||
|
Address: tokenAddress,
|
||||||
|
ChainID: int(chainID),
|
||||||
|
Name: tokenName,
|
||||||
|
Supply: &bigint.BigInt{},
|
||||||
|
Symbol: tokenSymbol,
|
||||||
|
PrivilegesLevel: token.OwnerLevel,
|
||||||
|
}, nil)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// john adds minted owner token to community
|
||||||
|
err = s.john.AddCommunityToken(community.IDString(), int(chainID), tokenAddress)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// update mock - the signer for the community returned by the contracts should be john
|
||||||
|
s.collectiblesServiceMock.SetSignerPubkeyForCommunity(community.ID(), common.PubkeyToHex(&s.john.identity.PublicKey))
|
||||||
|
s.collectiblesServiceMock.SetMockCollectibleContractData(chainID, tokenAddress,
|
||||||
|
&communitytokens.CollectibleContractData{TotalSupply: &bigint.BigInt{}})
|
||||||
|
|
||||||
|
// alice accepts community update
|
||||||
|
_, err = WaitOnSignaledMessengerResponse(
|
||||||
|
s.alice,
|
||||||
|
func(r *MessengerResponse) bool {
|
||||||
|
return len(r.Communities()) > 0 && len(r.Communities()[0].TokenPermissions()) == 1
|
||||||
|
},
|
||||||
|
"no communities",
|
||||||
|
)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create alice second instance
|
||||||
|
alice2, err := newMessengerWithKey(
|
||||||
|
s.shh,
|
||||||
|
s.alice.identity,
|
||||||
|
s.logger.With(zap.String("name", "alice-2")),
|
||||||
|
nil)
|
||||||
|
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
_, err = alice2.Start()
|
||||||
|
s.Require().NoError(err)
|
||||||
|
defer alice2.Shutdown() // nolint: errcheck
|
||||||
|
|
||||||
|
// Create communities backup
|
||||||
|
|
||||||
|
clock, _ := s.alice.getLastClockWithRelatedChat()
|
||||||
|
communitiesBackup, err := s.alice.backupCommunities(context.Background(), clock)
|
||||||
|
s.Require().NoError(err)
|
||||||
|
|
||||||
|
// Find wanted communities in the backup
|
||||||
|
|
||||||
|
var syncCommunityMessages []*protobuf.SyncInstallationCommunity
|
||||||
|
|
||||||
|
for _, b := range communitiesBackup {
|
||||||
|
for _, c := range b.Communities {
|
||||||
|
if bytes.Equal(c.Id, community.ID()) {
|
||||||
|
syncCommunityMessages = append(syncCommunityMessages, c)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.Require().Len(syncCommunityMessages, 1)
|
||||||
|
|
||||||
|
s.testDescriptionSignature(syncCommunityMessages[0].Description)
|
||||||
|
|
||||||
|
// Push the backup into second instance
|
||||||
|
|
||||||
|
messageState := alice2.buildMessageState()
|
||||||
|
err = alice2.HandleSyncInstallationCommunity(messageState, syncCommunityMessages[0], nil)
|
||||||
|
|
||||||
|
s.Require().NoError(err)
|
||||||
|
s.Require().Len(messageState.Response.Communities(), 1)
|
||||||
|
|
||||||
|
expectedControlNode := community.PublicKey()
|
||||||
|
if mintOwnerToken {
|
||||||
|
expectedControlNode = &s.john.identity.PublicKey
|
||||||
|
}
|
||||||
|
|
||||||
|
responseCommunity := messageState.Response.Communities()[0]
|
||||||
|
s.Require().Equal(community.IDString(), responseCommunity.IDString())
|
||||||
|
s.Require().True(common.IsPubKeyEqual(expectedControlNode, responseCommunity.ControlNode()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *MessengerCommunitiesSignersSuite) TestSyncTokenGatedCommunity() {
|
||||||
|
testCases := []struct {
|
||||||
|
name string
|
||||||
|
mintOwnerToken bool
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
name: "general community sync",
|
||||||
|
mintOwnerToken: false,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "community with token ownership",
|
||||||
|
mintOwnerToken: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tc := range testCases {
|
||||||
|
s.Run(tc.name, func() {
|
||||||
|
s.testSyncCommunity(tc.mintOwnerToken)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -20,6 +20,8 @@ import (
|
||||||
|
|
||||||
"go.uber.org/zap"
|
"go.uber.org/zap"
|
||||||
|
|
||||||
|
utils "github.com/status-im/status-go/common"
|
||||||
|
|
||||||
"github.com/status-im/status-go/account"
|
"github.com/status-im/status-go/account"
|
||||||
"github.com/status-im/status-go/eth-node/crypto"
|
"github.com/status-im/status-go/eth-node/crypto"
|
||||||
"github.com/status-im/status-go/eth-node/types"
|
"github.com/status-im/status-go/eth-node/types"
|
||||||
|
@ -2603,8 +2605,8 @@ func (m *Messenger) passStoredCommunityInfoToSignalHandler(community *communitie
|
||||||
}
|
}
|
||||||
|
|
||||||
// handleCommunityDescription handles an community description
|
// handleCommunityDescription handles an community description
|
||||||
func (m *Messenger) handleCommunityDescription(state *ReceivedMessageState, signer *ecdsa.PublicKey, description *protobuf.CommunityDescription, rawPayload []byte, shard *protobuf.Shard) error {
|
func (m *Messenger) handleCommunityDescription(state *ReceivedMessageState, signer *ecdsa.PublicKey, description *protobuf.CommunityDescription, rawPayload []byte, verifiedOwner *ecdsa.PublicKey, shard *protobuf.Shard) error {
|
||||||
communityResponse, err := m.communitiesManager.HandleCommunityDescriptionMessage(signer, description, rawPayload, nil, shard)
|
communityResponse, err := m.communitiesManager.HandleCommunityDescriptionMessage(signer, description, rawPayload, verifiedOwner, shard)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -2945,7 +2947,7 @@ func (m *Messenger) HandleSyncInstallationCommunity(messageState *ReceivedMessag
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Messenger) handleSyncInstallationCommunity(messageState *ReceivedMessageState, syncCommunity *protobuf.SyncInstallationCommunity, statusMessage *v1protocol.StatusMessage) error {
|
func (m *Messenger) handleSyncInstallationCommunity(messageState *ReceivedMessageState, syncCommunity *protobuf.SyncInstallationCommunity, statusMessage *v1protocol.StatusMessage) error {
|
||||||
logger := m.logger.Named("handleSyncCommunity")
|
logger := m.logger.Named("handleSyncInstallationCommunity")
|
||||||
|
|
||||||
// Should handle community
|
// Should handle community
|
||||||
shouldHandle, err := m.communitiesManager.ShouldHandleSyncCommunity(syncCommunity)
|
shouldHandle, err := m.communitiesManager.ShouldHandleSyncCommunity(syncCommunity)
|
||||||
|
@ -3008,8 +3010,17 @@ func (m *Messenger) handleSyncInstallationCommunity(messageState *ReceivedMessag
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is our own message, so we can trust the set community owner
|
||||||
|
// This is good to do so that we don't have to queue all the actions done after the handled community description.
|
||||||
|
// `signer` is `communityID` for a community with no owner token and `owner public key` otherwise
|
||||||
|
signer, err := utils.RecoverKey(&amm)
|
||||||
|
if err != nil {
|
||||||
|
logger.Debug("failed to recover community description signer", zap.Error(err))
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: handle shard
|
// TODO: handle shard
|
||||||
err = m.handleCommunityDescription(messageState, orgPubKey, &cd, syncCommunity.Description, nil)
|
err = m.handleCommunityDescription(messageState, signer, &cd, syncCommunity.Description, signer, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Debug("m.handleCommunityDescription error", zap.Error(err))
|
logger.Debug("m.handleCommunityDescription error", zap.Error(err))
|
||||||
return err
|
return err
|
||||||
|
@ -3031,22 +3042,6 @@ func (m *Messenger) handleSyncInstallationCommunity(messageState *ReceivedMessag
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
savedCommunity, err := m.communitiesManager.GetByID(syncCommunity.Id)
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: if the community is token gated, it will be validated asynchronously
|
|
||||||
// syncing needs to be adjusted in this case
|
|
||||||
if savedCommunity == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := m.handleCommunityTokensMetadataByPrivilegedMembers(savedCommunity); err != nil {
|
|
||||||
logger.Debug("m.handleCommunityTokensMetadataByPrivilegedMembers", zap.Error(err))
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
|
|
||||||
// if we are not waiting for approval, join or leave the community
|
// if we are not waiting for approval, join or leave the community
|
||||||
if !pending {
|
if !pending {
|
||||||
var mr *MessengerResponse
|
var mr *MessengerResponse
|
||||||
|
@ -3102,10 +3097,6 @@ func (m *Messenger) HandleSyncCommunitySettings(messageState *ReceivedMessageSta
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *Messenger) handleCommunityTokensMetadataByPrivilegedMembers(community *communities.Community) error {
|
|
||||||
return m.communitiesManager.HandleCommunityTokensMetadataByPrivilegedMembers(community)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (m *Messenger) InitHistoryArchiveTasks(communities []*communities.Community) {
|
func (m *Messenger) InitHistoryArchiveTasks(communities []*communities.Community) {
|
||||||
|
|
||||||
m.communitiesManager.LogStdout("initializing history archive tasks")
|
m.communitiesManager.LogStdout("initializing history archive tasks")
|
||||||
|
|
|
@ -2324,7 +2324,7 @@ func (m *Messenger) handleChatMessage(state *ReceivedMessageState, forceSeen boo
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = m.handleCommunityDescription(state, signer, description, receivedMessage.GetCommunity(), receivedMessage.GetShard())
|
err = m.handleCommunityDescription(state, signer, description, receivedMessage.GetCommunity(), nil, receivedMessage.GetShard())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -3667,7 +3667,7 @@ func (m *Messenger) HandlePushNotificationRequest(state *ReceivedMessageState, m
|
||||||
|
|
||||||
func (m *Messenger) HandleCommunityDescription(state *ReceivedMessageState, message *protobuf.CommunityDescription, statusMessage *v1protocol.StatusMessage) error {
|
func (m *Messenger) HandleCommunityDescription(state *ReceivedMessageState, message *protobuf.CommunityDescription, statusMessage *v1protocol.StatusMessage) error {
|
||||||
// TODO: handle shard
|
// TODO: handle shard
|
||||||
err := m.handleCommunityDescription(state, state.CurrentMessageState.PublicKey, message, statusMessage.EncryptionLayer.Payload, nil)
|
err := m.handleCommunityDescription(state, state.CurrentMessageState.PublicKey, message, statusMessage.EncryptionLayer.Payload, nil, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
m.logger.Warn("failed to handle CommunityDescription", zap.Error(err))
|
m.logger.Warn("failed to handle CommunityDescription", zap.Error(err))
|
||||||
return err
|
return err
|
||||||
|
|
Loading…
Reference in New Issue