chore: check and manualy verify community if during the fetchCommunity, community was added to the verification loop (#4533)

This commit is contained in:
Mykhailo Prakhov 2024-01-05 18:09:38 +01:00 committed by GitHub
parent 7814f39cd7
commit 5c704b2ec2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 183 additions and 57 deletions

View File

@ -435,65 +435,82 @@ func (m *Manager) runOwnerVerificationLoop() {
for id, communities := range communitiesToValidate {
m.logger.Info("validating communities", zap.String("id", id), zap.Int("count", len(communities)))
for _, communityToValidate := range communities {
signer, description, err := UnwrapCommunityDescriptionMessage(communityToValidate.payload)
if err != nil {
m.logger.Error("failed to unwrap community", zap.Error(err))
continue
}
chainID := CommunityDescriptionTokenOwnerChainID(description)
if chainID == 0 {
// This should not happen
m.logger.Error("chain id is 0, ignoring")
continue
}
m.logger.Info("validating community", zap.String("id", types.EncodeHex(communityToValidate.id)), zap.String("signer", common.PubkeyToHex(signer)))
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()
owner, err := m.ownerVerifier.SafeGetSignerPubKey(ctx, chainID, id)
if err != nil {
m.logger.Error("failed to get owner", zap.Error(err))
continue
}
ownerPK, err := common.HexToPubkey(owner)
if err != nil {
m.logger.Error("failed to convert pk string to ecdsa", zap.Error(err))
continue
}
// TODO: handle shards
response, err := m.HandleCommunityDescriptionMessage(signer, description, communityToValidate.payload, ownerPK, nil)
if err != nil {
m.logger.Error("failed to handle community", zap.Error(err))
err = m.persistence.DeleteCommunityToValidate(communityToValidate.id, communityToValidate.clock)
if err != nil {
m.logger.Error("failed to delete community to validate", zap.Error(err))
}
continue
}
if response != nil {
m.logger.Info("community validated", zap.String("id", types.EncodeHex(communityToValidate.id)), zap.String("signer", common.PubkeyToHex(signer)))
m.publish(&Subscription{TokenCommunityValidated: response})
err := m.persistence.DeleteCommunitiesToValidateByCommunityID(communityToValidate.id)
if err != nil {
m.logger.Error("failed to delete communities to validate", zap.Error(err))
}
break
}
}
_, _ = m.validateCommunity(communities)
}
}
}
}()
}
func (m *Manager) ValidateCommunityByID(communityID types.HexBytes) (*CommunityResponse, error) {
communityToValidate, err := m.persistence.getCommunityToValidateByID(communityID)
if err != nil {
m.logger.Error("failed to validate community by ID", zap.String("id", communityID.String()), zap.Error(err))
return nil, err
}
return m.validateCommunity(communityToValidate)
}
func (m *Manager) validateCommunity(communityToValidateData []communityToValidate) (*CommunityResponse, error) {
for _, communityToValidate := range communityToValidateData {
signer, description, err := UnwrapCommunityDescriptionMessage(communityToValidate.payload)
if err != nil {
m.logger.Error("failed to unwrap community", zap.Error(err))
continue
}
chainID := CommunityDescriptionTokenOwnerChainID(description)
if chainID == 0 {
// This should not happen
m.logger.Error("chain id is 0, ignoring")
continue
}
m.logger.Info("validating community", zap.String("id", types.EncodeHex(communityToValidate.id)), zap.String("signer", common.PubkeyToHex(signer)))
ctx, cancel := context.WithTimeout(context.Background(), time.Second*3)
defer cancel()
owner, err := m.ownerVerifier.SafeGetSignerPubKey(ctx, chainID, types.EncodeHex(communityToValidate.id))
if err != nil {
m.logger.Error("failed to get owner", zap.Error(err))
continue
}
ownerPK, err := common.HexToPubkey(owner)
if err != nil {
m.logger.Error("failed to convert pk string to ecdsa", zap.Error(err))
continue
}
// TODO: handle shards
response, err := m.HandleCommunityDescriptionMessage(signer, description, communityToValidate.payload, ownerPK, nil)
if err != nil {
m.logger.Error("failed to handle community", zap.Error(err))
err = m.persistence.DeleteCommunityToValidate(communityToValidate.id, communityToValidate.clock)
if err != nil {
m.logger.Error("failed to delete community to validate", zap.Error(err))
}
continue
}
if response != nil {
m.logger.Info("community validated", zap.String("id", types.EncodeHex(communityToValidate.id)), zap.String("signer", common.PubkeyToHex(signer)))
m.publish(&Subscription{TokenCommunityValidated: response})
err := m.persistence.DeleteCommunitiesToValidateByCommunityID(communityToValidate.id)
if err != nil {
m.logger.Error("failed to delete communities to validate", zap.Error(err))
}
return response, nil
}
}
return nil, nil
}
func (m *Manager) Stop() error {
m.stopped = true
close(m.quit)

View File

@ -1489,6 +1489,30 @@ func (p *Persistence) getCommunitiesToValidate() (map[string][]communityToValida
}
func (p *Persistence) getCommunityToValidateByID(communityID types.HexBytes) ([]communityToValidate, error) {
communityToValidateArray := []communityToValidate{}
rows, err := p.db.Query(`SELECT id, clock, payload, signer FROM communities_validate_signer WHERE id = ? AND validate_at <= ? ORDER BY clock DESC`, communityID, time.Now().UnixNano())
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
communityToValidate := communityToValidate{}
err := rows.Scan(&communityToValidate.id, &communityToValidate.clock, &communityToValidate.payload, &communityToValidate.signer)
if err != nil {
return nil, err
}
communityToValidateArray = append(communityToValidateArray, communityToValidate)
}
return communityToValidateArray, nil
}
func (p *Persistence) DeleteCommunitiesToValidateByCommunityID(communityID []byte) error {
_, err := p.db.Exec(`DELETE FROM communities_validate_signer WHERE id = ?`, communityID)
return err

View File

@ -913,3 +913,11 @@ func (s *PersistenceSuite) TestSaveShardInfo() {
s.Require().Error(err, sql.ErrNoRows)
s.Require().Nil(resultShard)
}
func (s *PersistenceSuite) TestGetCommunityToValidateByID() {
communityID := types.HexBytes{1, 2, 3, 4, 5, 6, 7, 8}
result, err := s.db.getCommunityToValidateByID(communityID)
s.Require().NoError(err)
s.Require().Len(result, 0)
}

View File

@ -373,15 +373,40 @@ func (r *storeNodeRequest) shouldFetchNextPage(envelopesCount int) (bool, uint32
// Try to get community from database
switch r.requestID.RequestType {
case storeNodeCommunityRequest:
community, err := r.manager.messenger.communitiesManager.GetByIDString(r.requestID.DataID)
communityID, err := types.DecodeHex(r.requestID.DataID)
if err != nil {
logger.Error("failed to read from database",
logger.Error("failed to decode community ID",
zap.String("communityID", r.requestID.DataID),
zap.Error(err))
r.result = storeNodeRequestResult{
community: nil,
err: fmt.Errorf("failed to read from database: %w", err),
err: fmt.Errorf("failed to decode community ID: %w", err),
}
return false, 0 // failed to decode community ID, no sense to continue the procedure
}
// check if community is waiting for a verification and do a verification manually
_, err = r.manager.messenger.communitiesManager.ValidateCommunityByID(communityID)
if err != nil {
logger.Error("failed to validate community by ID",
zap.String("communityID", r.requestID.DataID),
zap.Error(err))
r.result = storeNodeRequestResult{
community: nil,
err: fmt.Errorf("failed to validate community by ID: %w", err),
}
return false, 0 // failed to validate community, no sense to continue the procedure
}
community, err := r.manager.messenger.communitiesManager.GetByID(communityID)
if err != nil {
logger.Error("failed to read community from database",
zap.String("communityID", r.requestID.DataID),
zap.Error(err))
r.result = storeNodeRequestResult{
community: nil,
err: fmt.Errorf("failed to read community from database: %w", err),
}
return false, 0 // failed to read from database, no sense to continue the procedure
}

View File

@ -8,6 +8,7 @@ import (
"time"
"github.com/ethereum/go-ethereum/common/hexutil"
"github.com/status-im/status-go/protocol/communities/token"
"github.com/status-im/status-go/protocol/transport"
"github.com/status-im/status-go/multiaccounts/accounts"
@ -29,7 +30,9 @@ import (
"github.com/status-im/status-go/protocol/requests"
"github.com/status-im/status-go/t/helpers"
"github.com/status-im/status-go/services/communitytokens"
mailserversDB "github.com/status-im/status-go/services/mailservers"
"github.com/status-im/status-go/services/wallet/bigint"
waku2 "github.com/status-im/status-go/wakuv2"
wakuV2common "github.com/status-im/status-go/wakuv2/common"
)
@ -59,6 +62,8 @@ type MessengerStoreNodeRequestSuite struct {
ownerWaku types.Waku
bobWaku types.Waku
collectiblesServiceMock *CollectiblesServiceMock
logger *zap.Logger
}
@ -121,6 +126,8 @@ func (s *MessengerStoreNodeRequestSuite) SetupTest() {
s.storeNodeAddress = storeNodeListenAddresses[0]
s.logger.Info("store node ready", zap.String("address", s.storeNodeAddress))
s.collectiblesServiceMock = &CollectiblesServiceMock{}
}
func (s *MessengerStoreNodeRequestSuite) TearDown() {
@ -173,6 +180,7 @@ func (s *MessengerStoreNodeRequestSuite) newMessenger(shh types.Waku, logger *za
WithClusterConfig(params.ClusterConfig{
Fleet: localFleet,
}),
WithCommunityTokensService(s.collectiblesServiceMock),
}
messenger, err := newMessengerWithKey(shh, privateKey, logger, options)
@ -217,6 +225,8 @@ func (s *MessengerStoreNodeRequestSuite) requireCommunitiesEqual(c *communities.
s.Require().Equal(expected.Color(), c.Color())
s.Require().Equal(expected.Tags(), c.Tags())
s.Require().Equal(expected.Shard(), c.Shard())
s.Require().Equal(expected.TokenPermissions(), c.TokenPermissions())
s.Require().Equal(expected.CommunityTokensMetadata(), c.CommunityTokensMetadata())
}
func (s *MessengerStoreNodeRequestSuite) requireContactsEqual(c *Contact, expected *Contact) {
@ -818,3 +828,45 @@ func (s *MessengerStoreNodeRequestSuite) TestFetchRealCommunity() {
fmt.Printf("%s --- %s\n", storeNodeName, result.toString())
}
}
func (s *MessengerStoreNodeRequestSuite) TestFetchingCommunityWithOwnerToken() {
s.createOwner()
s.createBob()
s.waitForAvailableStoreNode(s.owner)
community := s.createCommunity(s.owner)
// owner mints owner token
var chainID uint64 = 1
tokenAddress := "token-address"
tokenName := "tokenName"
tokenSymbol := "TSM"
_, err := s.owner.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)
// owner adds minted owner token to community
err = s.owner.AddCommunityToken(community.IDString(), int(chainID), tokenAddress)
s.Require().NoError(err)
// update mock - the signer for the community returned by the contracts should be owner
s.collectiblesServiceMock.SetSignerPubkeyForCommunity(community.ID(), common.PubkeyToHex(&s.owner.identity.PublicKey))
s.collectiblesServiceMock.SetMockCollectibleContractData(chainID, tokenAddress,
&communitytokens.CollectibleContractData{TotalSupply: &bigint.BigInt{}})
community, err = s.owner.communitiesManager.GetByID(community.ID())
s.Require().NoError(err)
s.Require().Len(community.TokenPermissions(), 1)
s.waitForAvailableStoreNode(s.bob)
s.fetchCommunity(s.bob, community.CommunityShard(), community)
}