refactor: extract communities_messenger_token_permissions_test

`communities_messenger_test` was unnecessarily polluted with passwords
and addresses.
This commit is contained in:
Patryk Osmaczko 2023-06-27 14:20:20 +02:00 committed by osmaczko
parent 323e02eb17
commit a2a2e61163
3 changed files with 612 additions and 510 deletions

View File

@ -0,0 +1,205 @@
package protocol
import (
"context"
"crypto/ecdsa"
"encoding/json"
"errors"
"io/ioutil"
"github.com/google/uuid"
"github.com/stretchr/testify/suite"
"go.uber.org/zap"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/account/generator"
"github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/multiaccounts"
"github.com/status-im/status-go/multiaccounts/settings"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/communities"
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/requests"
"github.com/status-im/status-go/protocol/sqlite"
"github.com/status-im/status-go/protocol/tt"
)
func newCommunitiesTestMessenger(shh types.Waku, privateKey *ecdsa.PrivateKey, logger *zap.Logger, accountsManager account.Manager, tokenManager communities.TokenManager) (*Messenger, error) {
tmpfile, err := ioutil.TempFile("", "accounts-tests-")
if err != nil {
return nil, err
}
madb, err := multiaccounts.InitializeDB(tmpfile.Name())
if err != nil {
return nil, err
}
acc := generator.NewAccount(privateKey, nil)
iai := acc.ToIdentifiedAccountInfo("")
options := []Option{
WithCustomLogger(logger),
WithDatabaseConfig(":memory:", "somekey", sqlite.ReducedKDFIterationsNumber),
WithMultiAccounts(madb),
WithAccount(iai.ToMultiAccount()),
WithDatasync(),
WithTokenManager(tokenManager),
}
m, err := NewMessenger(
"Test",
privateKey,
&testNode{shh: shh},
uuid.New().String(),
nil,
accountsManager,
options...,
)
if err != nil {
return nil, err
}
err = m.Init()
if err != nil {
return nil, err
}
config := params.NodeConfig{
NetworkID: 10,
DataDir: "test",
}
networks := json.RawMessage("{}")
setting := settings.Settings{
Address: types.HexToAddress("0x1122334455667788990011223344556677889900"),
AnonMetricsShouldSend: false,
CurrentNetwork: "mainnet_rpc",
DappsAddress: types.HexToAddress("0x1122334455667788990011223344556677889900"),
InstallationID: "d3efcff6-cffa-560e-a547-21d3858cbc51",
KeyUID: "0x1122334455667788990011223344556677889900",
Name: "Test",
Networks: &networks,
LatestDerivedPath: 0,
PhotoPath: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAIAAACRXR/mAAAAjklEQVR4nOzXwQmFMBAAUZXUYh32ZB32ZB02sxYQQSZGsod55/91WFgSS0RM+SyjA56ZRZhFmEWYRRT6h+M6G16zrxv6fdJpmUWYRbxsYr13dKfanpN0WmYRZhGzXz6AWYRZRIfbaX26fT9Jk07LLMIsosPt9I/dTDotswizCG+nhFmEWYRZhFnEHQAA///z1CFkYamgfQAAAABJRU5ErkJggg==",
PreviewPrivacy: false,
PublicKey: "0x04112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900",
SigningPhrase: "yurt joey vibe",
SendPushNotifications: true,
ProfilePicturesVisibility: 1,
DefaultSyncPeriod: 777600,
UseMailservers: true,
LinkPreviewRequestEnabled: true,
SendStatusUpdates: true,
WalletRootAddress: types.HexToAddress("0x1122334455667788990011223344556677889900")}
_ = m.settings.CreateSettings(setting, config)
return m, nil
}
func createCommunity(s *suite.Suite, owner *Messenger) (*communities.Community, *Chat) {
description := &requests.CreateCommunity{
Membership: protobuf.CommunityPermissions_NO_MEMBERSHIP,
Name: "status",
Color: "#ffffff",
Description: "status community description",
}
// Create an community chat
response, err := owner.CreateCommunity(description, true)
s.Require().NoError(err)
s.Require().NotNil(response)
community := response.Communities()[0]
orgChat := &protobuf.CommunityChat{
Permissions: &protobuf.CommunityPermissions{
Access: protobuf.CommunityPermissions_NO_MEMBERSHIP,
},
Identity: &protobuf.ChatIdentity{
DisplayName: "status-core",
Emoji: "😎",
Description: "status-core community chat",
},
}
response, err = owner.CreateCommunityChat(community.ID(), orgChat)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Chats(), 1)
return community, response.Chats()[0]
}
func advertiseCommunityTo(s *suite.Suite, community *communities.Community, owner *Messenger, user *Messenger) {
chat := CreateOneToOneChat(common.PubkeyToHex(&user.identity.PublicKey), &user.identity.PublicKey, user.transport)
inputMessage := &common.Message{}
inputMessage.ChatId = chat.ID
inputMessage.Text = "some text"
inputMessage.CommunityID = community.IDString()
err := owner.SaveChat(chat)
s.Require().NoError(err)
_, err = owner.SendChatMessage(context.Background(), inputMessage)
s.Require().NoError(err)
// Ensure community is received
err = tt.RetryWithBackOff(func() error {
response, err := user.RetrieveAll()
if err != nil {
return err
}
if len(response.Communities()) == 0 {
return errors.New("community not received")
}
return nil
})
s.Require().NoError(err)
}
func joinCommunity(s *suite.Suite, community *communities.Community, owner *Messenger, user *Messenger, request *requests.RequestToJoinCommunity) {
response, err := user.RequestToJoinCommunity(request)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.RequestsToJoinCommunity, 1)
s.Require().Len(response.ActivityCenterNotifications(), 1)
notification := response.ActivityCenterNotifications()[0]
s.Require().NotNil(notification)
s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest)
s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending)
// Retrieve and accept join request
err = tt.RetryWithBackOff(func() error {
response, err := owner.RetrieveAll()
if err != nil {
return err
}
if len(response.Communities()) == 0 {
return errors.New("no communities in response (accept join request)")
}
if !response.Communities()[0].HasMember(&user.identity.PublicKey) {
return errors.New("user not accepted")
}
return nil
})
s.Require().NoError(err)
// Retrieve join request response
err = tt.RetryWithBackOff(func() error {
response, err := user.RetrieveAll()
if err != nil {
return err
}
if len(response.Communities()) == 0 {
return errors.New("no communities in response (join request response)")
}
if !response.Communities()[0].HasMember(&user.identity.PublicKey) {
return errors.New("user not a member")
}
return nil
})
s.Require().NoError(err)
}

View File

@ -14,73 +14,25 @@ import (
"time"
"github.com/golang/protobuf/proto"
"github.com/google/uuid"
"github.com/stretchr/testify/suite"
"go.uber.org/zap"
gethcommon "github.com/ethereum/go-ethereum/common"
hexutil "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/status-im/status-go/account"
"github.com/status-im/status-go/account/generator"
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/types"
"github.com/status-im/status-go/multiaccounts"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/multiaccounts/settings"
"github.com/status-im/status-go/params"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/communities"
"github.com/status-im/status-go/protocol/discord"
"github.com/status-im/status-go/protocol/encryption/multidevice"
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/requests"
"github.com/status-im/status-go/protocol/sqlite"
"github.com/status-im/status-go/protocol/transport"
"github.com/status-im/status-go/protocol/tt"
v1protocol "github.com/status-im/status-go/protocol/v1"
"github.com/status-im/status-go/waku"
)
const adminPassword = "123456"
const alicePassword = "qwerty"
const bobPassword = "bob123"
const adminAddress = "0x0100000000000000000000000000000000000000"
const aliceAddress1 = "0x0200000000000000000000000000000000000000"
const aliceAddress2 = "0x0210000000000000000000000000000000000000"
const bobAddress = "0x0300000000000000000000000000000000000000"
type AccountManagerMock struct {
AccountsMap map[string]string
}
type TestTokenManager struct {
}
func (m *TestTokenManager) GetAllChainIDs() ([]uint64, error) {
return []uint64{5}, nil
}
func (m *TestTokenManager) GetBalancesByChain(ctx context.Context, accounts, tokenAddresses []gethcommon.Address, chainIDs []uint64) (map[uint64]map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big, error) {
return nil, nil
}
func (m *AccountManagerMock) GetVerifiedWalletAccount(db *accounts.Database, address, password string) (*account.SelectedExtKey, error) {
return &account.SelectedExtKey{
Address: types.HexToAddress(address),
}, nil
}
func (m *AccountManagerMock) CanRecover(rpcParams account.RecoverParams, revealedAddress types.Address) (bool, error) {
return true, nil
}
func (m *AccountManagerMock) Sign(rpcParams account.SignParams, verifiedAccount *account.SelectedExtKey) (result types.HexBytes, err error) {
return types.HexBytes{}, nil
}
func TestMessengerCommunitiesSuite(t *testing.T) {
suite.Run(t, new(MessengerCommunitiesSuite))
}
@ -105,9 +57,9 @@ func (s *MessengerCommunitiesSuite) SetupTest() {
s.shh = gethbridge.NewGethWakuWrapper(shh)
s.Require().NoError(shh.Start())
s.admin = s.newMessengerWithWallet(adminPassword, []string{adminAddress})
s.bob = s.newMessengerWithWallet(bobPassword, []string{bobAddress})
s.alice = s.newMessengerWithWallet(alicePassword, []string{aliceAddress1, aliceAddress2})
s.admin = s.newMessenger()
s.bob = s.newMessenger()
s.alice = s.newMessenger()
_, err := s.admin.Start()
s.Require().NoError(err)
_, err = s.bob.Start()
@ -123,112 +75,18 @@ func (s *MessengerCommunitiesSuite) TearDownTest() {
_ = s.logger.Sync()
}
func (s *MessengerCommunitiesSuite) newMessengerWithOptions(shh types.Waku, privateKey *ecdsa.PrivateKey, accountsManager account.Manager, options []Option) *Messenger {
m, err := NewMessenger(
"Test",
privateKey,
&testNode{shh: shh},
uuid.New().String(),
nil,
accountsManager,
options...,
)
func (s *MessengerCommunitiesSuite) newMessengerWithKey(privateKey *ecdsa.PrivateKey) *Messenger {
messenger, err := newCommunitiesTestMessenger(s.shh, privateKey, s.logger, nil, nil)
s.Require().NoError(err)
err = m.Init()
s.Require().NoError(err)
config := params.NodeConfig{
NetworkID: 10,
DataDir: "test",
}
networks := json.RawMessage("{}")
setting := settings.Settings{
Address: types.HexToAddress("0x1122334455667788990011223344556677889900"),
AnonMetricsShouldSend: false,
CurrentNetwork: "mainnet_rpc",
DappsAddress: types.HexToAddress("0x1122334455667788990011223344556677889900"),
InstallationID: "d3efcff6-cffa-560e-a547-21d3858cbc51",
KeyUID: "0x1122334455667788990011223344556677889900",
Name: "Test",
Networks: &networks,
LatestDerivedPath: 0,
PhotoPath: "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAIAAACRXR/mAAAAjklEQVR4nOzXwQmFMBAAUZXUYh32ZB32ZB02sxYQQSZGsod55/91WFgSS0RM+SyjA56ZRZhFmEWYRRT6h+M6G16zrxv6fdJpmUWYRbxsYr13dKfanpN0WmYRZhGzXz6AWYRZRIfbaX26fT9Jk07LLMIsosPt9I/dTDotswizCG+nhFmEWYRZhFnEHQAA///z1CFkYamgfQAAAABJRU5ErkJggg==",
PreviewPrivacy: false,
PublicKey: "0x04112233445566778899001122334455667788990011223344556677889900112233445566778899001122334455667788990011223344556677889900",
SigningPhrase: "yurt joey vibe",
SendPushNotifications: true,
ProfilePicturesVisibility: 1,
DefaultSyncPeriod: 777600,
UseMailservers: true,
LinkPreviewRequestEnabled: true,
SendStatusUpdates: true,
WalletRootAddress: types.HexToAddress("0x1122334455667788990011223344556677889900")}
_ = m.settings.CreateSettings(setting, config)
return m
}
func (s *MessengerCommunitiesSuite) newMessengerWithKey(shh types.Waku, privateKey *ecdsa.PrivateKey, accountsManager account.Manager) *Messenger {
tmpfile, err := ioutil.TempFile("", "accounts-tests-")
s.Require().NoError(err)
madb, err := multiaccounts.InitializeDB(tmpfile.Name())
s.Require().NoError(err)
acc := generator.NewAccount(privateKey, nil)
iai := acc.ToIdentifiedAccountInfo("")
tm := &TestTokenManager{}
options := []Option{
WithCustomLogger(s.logger),
WithDatabaseConfig(":memory:", "somekey", sqlite.ReducedKDFIterationsNumber),
WithMultiAccounts(madb),
WithAccount(iai.ToMultiAccount()),
WithDatasync(),
WithTokenManager(tm),
}
return s.newMessengerWithOptions(shh, privateKey, accountsManager, options)
}
func (s *MessengerCommunitiesSuite) newMessenger(accountsManager account.Manager) *Messenger {
privateKey, err := crypto.GenerateKey()
s.Require().NoError(err)
return s.newMessengerWithKey(s.shh, privateKey, accountsManager)
}
func (s *MessengerCommunitiesSuite) newMessengerWithWallet(password string, walletAddresses []string) *Messenger {
accountsManager := &AccountManagerMock{}
accountsManager.AccountsMap = make(map[string]string)
for _, walletAddress := range walletAddresses {
accountsManager.AccountsMap[walletAddress] = types.EncodeHex(crypto.Keccak256([]byte(password)))
}
messenger := s.newMessenger(accountsManager)
// add wallet account with keypair
for _, walletAddress := range walletAddresses {
kp := accounts.GetProfileKeypairForTest(false, true, false)
kp.Accounts[0].Address = types.HexToAddress(walletAddress)
err := messenger.settings.SaveOrUpdateKeypair(kp)
s.Require().NoError(err)
}
walletAccounts, err := messenger.settings.GetAccounts()
s.Require().NoError(err)
s.Require().Len(walletAccounts, len(walletAddresses))
for i := range walletAddresses {
s.Require().Equal(walletAccounts[i].Type, accounts.AccountTypeGenerated)
}
return messenger
}
func (s *MessengerCommunitiesSuite) requestToJoinCommunity(user *Messenger, communityID types.HexBytes, password string, addresses []string) (*MessengerResponse, error) {
passwdHash := types.EncodeHex(crypto.Keccak256([]byte(password)))
request := &requests.RequestToJoinCommunity{CommunityID: communityID, Password: passwdHash, AddressesToReveal: addresses}
return user.RequestToJoinCommunity(request)
func (s *MessengerCommunitiesSuite) newMessenger() *Messenger {
privateKey, err := crypto.GenerateKey()
s.Require().NoError(err)
return s.newMessengerWithKey(privateKey)
}
func (s *MessengerCommunitiesSuite) TestCreateCommunity() {
@ -263,7 +121,7 @@ func (s *MessengerCommunitiesSuite) TestCreateCommunity_WithoutDefaultChannel()
}
func (s *MessengerCommunitiesSuite) TestRetrieveCommunity() {
alice := s.newMessengerWithWallet(alicePassword, []string{aliceAddress1})
alice := s.newMessenger()
description := &requests.CreateCommunity{
Membership: protobuf.CommunityPermissions_NO_MEMBERSHIP,
@ -520,114 +378,17 @@ func (s *MessengerCommunitiesSuite) TestJoinCommunity() {
}
func (s *MessengerCommunitiesSuite) createCommunity() *communities.Community {
description := &requests.CreateCommunity{
Membership: protobuf.CommunityPermissions_NO_MEMBERSHIP,
Name: "status",
Color: "#ffffff",
Description: "status community description",
}
// Create an community chat
response, err := s.admin.CreateCommunity(description, true)
s.Require().NoError(err)
s.Require().NotNil(response)
community := response.Communities()[0]
orgChat := &protobuf.CommunityChat{
Permissions: &protobuf.CommunityPermissions{
Access: protobuf.CommunityPermissions_NO_MEMBERSHIP,
},
Identity: &protobuf.ChatIdentity{
DisplayName: "status-core",
Emoji: "😎",
Description: "status-core community chat",
},
}
response, err = s.admin.CreateCommunityChat(community.ID(), orgChat)
s.Require().NoError(err)
s.Require().NotNil(response)
community, _ := createCommunity(&s.Suite, s.admin)
return community
}
func (s *MessengerCommunitiesSuite) advertiseCommunityTo(community *communities.Community, user *Messenger) {
chat := CreateOneToOneChat(common.PubkeyToHex(&user.identity.PublicKey), &user.identity.PublicKey, user.transport)
inputMessage := &common.Message{}
inputMessage.ChatId = chat.ID
inputMessage.Text = "some text"
inputMessage.CommunityID = community.IDString()
err := s.admin.SaveChat(chat)
s.Require().NoError(err)
_, err = s.admin.SendChatMessage(context.Background(), inputMessage)
s.Require().NoError(err)
// Ensure community is received
err = tt.RetryWithBackOff(func() error {
response, err := user.RetrieveAll()
if err != nil {
return err
}
if len(response.Communities()) == 0 {
return errors.New("community not received")
}
return nil
})
s.Require().NoError(err)
advertiseCommunityTo(&s.Suite, community, s.admin, user)
}
func (s *MessengerCommunitiesSuite) joinCommunityWithAddresses(community *communities.Community, user *Messenger, password string, addresses []string) {
// Request to join the community
response, err := s.requestToJoinCommunity(user, community.ID(), password, addresses)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.RequestsToJoinCommunity, 1)
s.Require().Len(response.ActivityCenterNotifications(), 1)
notification := response.ActivityCenterNotifications()[0]
s.Require().NotNil(notification)
s.Require().Equal(notification.Type, ActivityCenterNotificationTypeCommunityRequest)
s.Require().Equal(notification.MembershipStatus, ActivityCenterMembershipStatusPending)
// Retrieve and accept join request
err = tt.RetryWithBackOff(func() error {
response, err := s.admin.RetrieveAll()
if err != nil {
return err
}
if len(response.Communities()) == 0 {
return errors.New("no communities in response (accept join request)")
}
if !response.Communities()[0].HasMember(&user.identity.PublicKey) {
return errors.New("user not accepted")
}
return nil
})
s.Require().NoError(err)
// Retrieve join request response
err = tt.RetryWithBackOff(func() error {
response, err := user.RetrieveAll()
if err != nil {
return err
}
if len(response.Communities()) == 0 {
return errors.New("no communities in response (join request response)")
}
if !response.Communities()[0].HasMember(&user.identity.PublicKey) {
return errors.New("user not a member")
}
return nil
})
s.Require().NoError(err)
}
func (s *MessengerCommunitiesSuite) joinCommunity(community *communities.Community, user *Messenger, password string) {
s.joinCommunityWithAddresses(community, user, password, []string{})
func (s *MessengerCommunitiesSuite) joinCommunity(community *communities.Community, user *Messenger) {
request := &requests.RequestToJoinCommunity{CommunityID: community.ID()}
joinCommunity(&s.Suite, community, s.admin, user, request)
}
func (s *MessengerCommunitiesSuite) TestCommunityContactCodeAdvertisement() {
@ -644,8 +405,8 @@ func (s *MessengerCommunitiesSuite) TestCommunityContactCodeAdvertisement() {
s.advertiseCommunityTo(community, s.bob)
s.advertiseCommunityTo(community, s.alice)
s.joinCommunity(community, s.bob, bobPassword)
s.joinCommunity(community, s.alice, alicePassword)
s.joinCommunity(community, s.bob)
s.joinCommunity(community, s.alice)
// Trigger ContactCodeAdvertisement
err = s.bob.SetDisplayName("bobby")
@ -2291,151 +2052,13 @@ func (s *MessengerCommunitiesSuite) TestDeclineAccess() {
s.Require().Len(requestsToJoin, 0)
}
func (s *MessengerCommunitiesSuite) TestCreateTokenPermission() {
community := s.createCommunity()
createTokenPermission := &requests.CreateCommunityTokenPermission{
CommunityID: community.ID(),
Type: protobuf.CommunityTokenPermission_BECOME_MEMBER,
TokenCriteria: []*protobuf.TokenCriteria{
&protobuf.TokenCriteria{
Type: protobuf.CommunityTokenType_ERC20,
ContractAddresses: map[uint64]string{uint64(1): "0x123"},
Symbol: "TEST",
Amount: "100",
Decimals: uint64(18),
},
},
}
response, err := s.admin.CreateCommunityTokenPermission(createTokenPermission)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Communities(), 1)
tokenPermissions := response.Communities()[0].TokenPermissions()
for _, tokenPermission := range tokenPermissions {
for _, tc := range tokenPermission.TokenCriteria {
s.Require().Equal(tc.Type, protobuf.CommunityTokenType_ERC20)
s.Require().Equal(tc.Symbol, "TEST")
s.Require().Equal(tc.Amount, "100")
s.Require().Equal(tc.Decimals, uint64(18))
}
}
}
func (s *MessengerCommunitiesSuite) TestEditTokenPermission() {
community := s.createCommunity()
tokenPermission := &requests.CreateCommunityTokenPermission{
CommunityID: community.ID(),
Type: protobuf.CommunityTokenPermission_BECOME_MEMBER,
TokenCriteria: []*protobuf.TokenCriteria{
&protobuf.TokenCriteria{
Type: protobuf.CommunityTokenType_ERC20,
ContractAddresses: map[uint64]string{uint64(1): "0x123"},
Symbol: "TEST",
Amount: "100",
Decimals: uint64(18),
},
},
}
response, err := s.admin.CreateCommunityTokenPermission(tokenPermission)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Communities(), 1)
tokenPermissions := response.Communities()[0].TokenPermissions()
var tokenPermissionID string
for id := range tokenPermissions {
tokenPermissionID = id
}
tokenPermission.TokenCriteria[0].Symbol = "TESTUpdated"
tokenPermission.TokenCriteria[0].Amount = "200"
tokenPermission.TokenCriteria[0].Decimals = uint64(20)
editTokenPermission := &requests.EditCommunityTokenPermission{
PermissionID: tokenPermissionID,
CreateCommunityTokenPermission: *tokenPermission,
}
response2, err := s.admin.EditCommunityTokenPermission(editTokenPermission)
s.Require().NoError(err)
// wait for `checkMemberPermissions` to finish
time.Sleep(1 * time.Second)
s.Require().NotNil(response2)
s.Require().Len(response2.Communities(), 1)
tokenPermissions = response2.Communities()[0].TokenPermissions()
for _, tokenPermission := range tokenPermissions {
for _, tc := range tokenPermission.TokenCriteria {
s.Require().Equal(tc.Type, protobuf.CommunityTokenType_ERC20)
s.Require().Equal(tc.Symbol, "TESTUpdated")
s.Require().Equal(tc.Amount, "200")
s.Require().Equal(tc.Decimals, uint64(20))
}
}
}
func (s *MessengerCommunitiesSuite) TestRequestAccessWithENSTokenPermission() {
community := s.createCommunity()
createTokenPermission := &requests.CreateCommunityTokenPermission{
CommunityID: community.ID(),
Type: protobuf.CommunityTokenPermission_BECOME_MEMBER,
TokenCriteria: []*protobuf.TokenCriteria{
&protobuf.TokenCriteria{
Type: protobuf.CommunityTokenType_ENS,
EnsPattern: "test.stateofus.eth",
},
},
}
response, err := s.admin.CreateCommunityTokenPermission(createTokenPermission)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Communities(), 1)
s.advertiseCommunityTo(community, s.alice)
requestToJoin := &requests.RequestToJoinCommunity{CommunityID: community.ID()}
// We try to join the org
response, err = s.alice.RequestToJoinCommunity(requestToJoin)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.RequestsToJoinCommunity, 1)
requestToJoin1 := response.RequestsToJoinCommunity[0]
s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin1.State)
// Retrieve request to join
err = tt.RetryWithBackOff(func() error {
response, err = s.admin.RetrieveAll()
return err
})
s.Require().NoError(err)
// We don't expect a requestToJoin in the response because due
// to missing revealed wallet addresses, the request should've
// been declined right away
s.Require().Len(response.RequestsToJoinCommunity, 0)
// Ensure alice is not a member of the community
allCommunities, err := s.admin.Communities()
if bytes.Equal(allCommunities[0].ID(), community.ID()) {
s.Require().False(allCommunities[0].HasMember(&s.alice.identity.PublicKey))
}
}
func (s *MessengerCommunitiesSuite) TestLeaveAndRejoinCommunity() {
community := s.createCommunity()
s.advertiseCommunityTo(community, s.alice)
s.advertiseCommunityTo(community, s.bob)
s.joinCommunity(community, s.alice, alicePassword)
s.joinCommunity(community, s.bob, bobPassword)
s.joinCommunity(community, s.alice)
s.joinCommunity(community, s.bob)
joinedCommunities, err := s.admin.communitiesManager.Joined()
s.Require().NoError(err)
@ -2498,7 +2121,7 @@ func (s *MessengerCommunitiesSuite) TestLeaveAndRejoinCommunity() {
s.Require().Equal(3, numberInactiveChats)
// alice can rejoin
s.joinCommunity(community, s.alice, alicePassword)
s.joinCommunity(community, s.alice)
joinedCommunities, err = s.admin.communitiesManager.Joined()
s.Require().NoError(err)
@ -2629,11 +2252,10 @@ func (s *MessengerCommunitiesSuite) TestBanUser() {
func (s *MessengerCommunitiesSuite) TestSyncCommunitySettings() {
// Create new device
alicesOtherDevice, err := newMessengerWithKey(s.shh, s.alice.identity, s.logger, nil)
s.Require().NoError(err)
alicesOtherDevice := s.newMessengerWithKey(s.alice.identity)
// Pair devices
err = alicesOtherDevice.SetInstallationMetadata(alicesOtherDevice.installationID, &multidevice.InstallationMetadata{
err := alicesOtherDevice.SetInstallationMetadata(alicesOtherDevice.installationID, &multidevice.InstallationMetadata{
Name: "their-name",
DeviceType: "their-device-type",
})
@ -2690,11 +2312,10 @@ func (s *MessengerCommunitiesSuite) TestSyncCommunitySettings() {
func (s *MessengerCommunitiesSuite) TestSyncCommunitySettings_EditCommunity() {
// Create new device
alicesOtherDevice, err := newMessengerWithKey(s.shh, s.alice.identity, s.logger, nil)
s.Require().NoError(err)
alicesOtherDevice := s.newMessengerWithKey(s.alice.identity)
// Pair devices
err = alicesOtherDevice.SetInstallationMetadata(alicesOtherDevice.installationID, &multidevice.InstallationMetadata{
err := alicesOtherDevice.SetInstallationMetadata(alicesOtherDevice.installationID, &multidevice.InstallationMetadata{
Name: "their-name",
DeviceType: "their-device-type",
})
@ -2787,8 +2408,7 @@ func (s *MessengerCommunitiesSuite) TestSyncCommunitySettings_EditCommunity() {
// TestSyncCommunity tests basic sync functionality between 2 Messengers
func (s *MessengerCommunitiesSuite) TestSyncCommunity() {
// Create new device
alicesOtherDevice, err := newMessengerWithKey(s.shh, s.alice.identity, s.logger, nil)
s.Require().NoError(err)
alicesOtherDevice := s.newMessengerWithKey(s.alice.identity)
tcs, err := alicesOtherDevice.communitiesManager.All()
s.Require().NoError(err, "alicesOtherDevice.communitiesManager.All")
@ -2881,8 +2501,7 @@ func (s *MessengerCommunitiesSuite) TestSyncCommunity_RequestToJoin() {
s.Require().NoError(err)
// Create Alice's other device
alicesOtherDevice, err := newMessengerWithKey(s.shh, s.alice.identity, s.logger, nil)
s.Require().NoError(err)
alicesOtherDevice := s.newMessengerWithKey(s.alice.identity)
im1 := &multidevice.InstallationMetadata{
Name: "alice's-other-device",
@ -3124,8 +2743,7 @@ func (s *MessengerCommunitiesSuite) TestSyncCommunity_Leave() {
s.Require().NoError(err)
// Create Alice's other device
alicesOtherDevice, err := newMessengerWithKey(s.shh, s.alice.identity, s.logger, nil)
s.Require().NoError(err)
alicesOtherDevice := s.newMessengerWithKey(s.alice.identity)
im1 := &multidevice.InstallationMetadata{
Name: "alice's-other-device",
@ -3451,48 +3069,6 @@ func (s *MessengerCommunitiesSuite) TestExtractDiscordChannelsAndCategories_With
s.Require().Len(errs, 1)
}
func (s *MessengerCommunitiesSuite) TestCommunityTokensMetadata() {
description := &requests.CreateCommunity{
Membership: protobuf.CommunityPermissions_NO_MEMBERSHIP,
Name: "status",
Color: "#ffffff",
Description: "status community description",
}
// Create an community chat
response, err := s.bob.CreateCommunity(description, true)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Communities(), 1)
s.Require().Len(response.CommunitiesSettings(), 1)
community := response.Communities()[0]
tokensMetadata := community.CommunityTokensMetadata()
s.Require().Len(tokensMetadata, 0)
newToken := &protobuf.CommunityTokenMetadata{
ContractAddresses: map[uint64]string{3: "0xasd"},
Description: "desc1",
Image: "IMG1",
TokenType: protobuf.CommunityTokenType_ERC721,
Symbol: "SMB",
}
_, err = community.AddCommunityTokensMetadata(newToken)
s.Require().NoError(err)
tokensMetadata = community.CommunityTokensMetadata()
s.Require().Len(tokensMetadata, 1)
s.Require().Equal(tokensMetadata[0].ContractAddresses, newToken.ContractAddresses)
s.Require().Equal(tokensMetadata[0].Description, newToken.Description)
s.Require().Equal(tokensMetadata[0].Image, newToken.Image)
s.Require().Equal(tokensMetadata[0].TokenType, newToken.TokenType)
s.Require().Equal(tokensMetadata[0].Symbol, newToken.Symbol)
s.Require().Equal(tokensMetadata[0].Name, newToken.Name)
}
func (s *MessengerCommunitiesSuite) TestCommunityBanUserRequesToJoin() {
description := &requests.CreateCommunity{
Membership: protobuf.CommunityPermissions_NO_MEMBERSHIP,
@ -3545,8 +3121,7 @@ func (s *MessengerCommunitiesSuite) TestCommunityBanUserRequesToJoin() {
s.Require().NoError(err)
s.Require().Len(response.Communities(), 1)
passwdHash := types.EncodeHex(crypto.Keccak256([]byte(alicePassword)))
request := &requests.RequestToJoinCommunity{CommunityID: community.ID(), Password: passwdHash}
request := &requests.RequestToJoinCommunity{CommunityID: community.ID()}
// We try to join the org
_, rtj, err := s.alice.communitiesManager.RequestToJoin(&s.alice.identity.PublicKey, request)
@ -3698,59 +3273,3 @@ func (s *MessengerCommunitiesSuite) TestHandleImport() {
s.Require().NotNil(chat)
s.Require().Equal(0, int(chat.UnviewedMessagesCount))
}
func (s *MessengerCommunitiesSuite) TestJoinedCommunityMembersSharedAddress() {
community := s.createCommunity()
s.advertiseCommunityTo(community, s.alice)
s.advertiseCommunityTo(community, s.bob)
s.joinCommunity(community, s.alice, alicePassword)
s.joinCommunity(community, s.bob, bobPassword)
community, err := s.admin.GetCommunityByID(community.ID())
s.Require().NoError(err)
s.Require().Equal(3, community.MembersCount())
for pubKey, member := range community.Members() {
if pubKey != common.PubkeyToHex(&s.admin.identity.PublicKey) {
switch pubKey {
case common.PubkeyToHex(&s.alice.identity.PublicKey):
s.Require().Len(member.RevealedAccounts, 2)
s.Require().Equal(member.RevealedAccounts[0].Address, aliceAddress1)
s.Require().Equal(member.RevealedAccounts[1].Address, aliceAddress2)
case common.PubkeyToHex(&s.bob.identity.PublicKey):
s.Require().Len(member.RevealedAccounts, 1)
s.Require().Equal(member.RevealedAccounts[0].Address, bobAddress)
default:
s.Require().Fail("pubKey does not match expected keys")
}
}
}
}
func (s *MessengerCommunitiesSuite) TestJoinedCommunityMembersSelectedSharedAddress() {
community := s.createCommunity()
s.advertiseCommunityTo(community, s.alice)
s.joinCommunityWithAddresses(community, s.alice, alicePassword, []string{aliceAddress2})
community, err := s.admin.GetCommunityByID(community.ID())
s.Require().NoError(err)
s.Require().Equal(2, community.MembersCount())
for pubKey, member := range community.Members() {
if pubKey != common.PubkeyToHex(&s.admin.identity.PublicKey) {
s.Require().Len(member.RevealedAccounts, 1)
switch pubKey {
case common.PubkeyToHex(&s.alice.identity.PublicKey):
s.Require().Equal(member.RevealedAccounts[0].Address, aliceAddress2)
default:
s.Require().Fail("pubKey does not match expected keys")
}
}
}
}

View File

@ -0,0 +1,378 @@
package protocol
import (
"bytes"
"context"
"testing"
"time"
"github.com/stretchr/testify/suite"
"go.uber.org/zap"
gethcommon "github.com/ethereum/go-ethereum/common"
hexutil "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/status-im/status-go/account"
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/types"
"github.com/status-im/status-go/multiaccounts/accounts"
"github.com/status-im/status-go/protocol/common"
"github.com/status-im/status-go/protocol/communities"
"github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/requests"
"github.com/status-im/status-go/protocol/tt"
"github.com/status-im/status-go/waku"
)
const ownerPassword = "123456"
const alicePassword = "qwerty"
const bobPassword = "bob123"
const ownerAddress = "0x0100000000000000000000000000000000000000"
const aliceAddress1 = "0x0200000000000000000000000000000000000000"
const aliceAddress2 = "0x0210000000000000000000000000000000000000"
const bobAddress = "0x0300000000000000000000000000000000000000"
type AccountManagerMock struct {
AccountsMap map[string]string
}
func (m *AccountManagerMock) GetVerifiedWalletAccount(db *accounts.Database, address, password string) (*account.SelectedExtKey, error) {
return &account.SelectedExtKey{
Address: types.HexToAddress(address),
}, nil
}
func (m *AccountManagerMock) CanRecover(rpcParams account.RecoverParams, revealedAddress types.Address) (bool, error) {
return true, nil
}
func (m *AccountManagerMock) Sign(rpcParams account.SignParams, verifiedAccount *account.SelectedExtKey) (result types.HexBytes, err error) {
return types.HexBytes{}, nil
}
type TokenManagerMock struct {
}
func (m *TokenManagerMock) GetAllChainIDs() ([]uint64, error) {
return []uint64{5}, nil
}
func (m *TokenManagerMock) GetBalancesByChain(ctx context.Context, accounts, tokenAddresses []gethcommon.Address, chainIDs []uint64) (map[uint64]map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big, error) {
return nil, nil
}
func TestMessengerCommunitiesTokenPermissionsSuite(t *testing.T) {
suite.Run(t, new(MessengerCommunitiesTokenPermissionsSuite))
}
type MessengerCommunitiesTokenPermissionsSuite struct {
suite.Suite
owner *Messenger
bob *Messenger
alice *Messenger
// If one wants to send messages between different instances of Messenger,
// a single Waku service should be shared.
shh types.Waku
logger *zap.Logger
}
func (s *MessengerCommunitiesTokenPermissionsSuite) SetupTest() {
s.logger = tt.MustCreateTestLogger()
config := waku.DefaultConfig
config.MinimumAcceptedPoW = 0
shh := waku.New(&config, s.logger)
s.shh = gethbridge.NewGethWakuWrapper(shh)
s.Require().NoError(shh.Start())
s.owner = s.newMessenger(ownerPassword, []string{ownerAddress})
s.bob = s.newMessenger(bobPassword, []string{bobAddress})
s.alice = s.newMessenger(alicePassword, []string{aliceAddress1, aliceAddress2})
_, err := s.owner.Start()
s.Require().NoError(err)
_, err = s.bob.Start()
s.Require().NoError(err)
_, err = s.alice.Start()
s.Require().NoError(err)
}
func (s *MessengerCommunitiesTokenPermissionsSuite) TearDownTest() {
s.Require().NoError(s.owner.Shutdown())
s.Require().NoError(s.bob.Shutdown())
s.Require().NoError(s.alice.Shutdown())
_ = s.logger.Sync()
}
func (s *MessengerCommunitiesTokenPermissionsSuite) newMessenger(password string, walletAddresses []string) *Messenger {
accountsManagerMock := &AccountManagerMock{}
accountsManagerMock.AccountsMap = make(map[string]string)
for _, walletAddress := range walletAddresses {
accountsManagerMock.AccountsMap[walletAddress] = types.EncodeHex(crypto.Keccak256([]byte(password)))
}
tokenManagerMock := &TokenManagerMock{}
privateKey, err := crypto.GenerateKey()
s.Require().NoError(err)
messenger, err := newCommunitiesTestMessenger(s.shh, privateKey, s.logger, accountsManagerMock, tokenManagerMock)
s.Require().NoError(err)
// add wallet account with keypair
for _, walletAddress := range walletAddresses {
kp := accounts.GetProfileKeypairForTest(false, true, false)
kp.Accounts[0].Address = types.HexToAddress(walletAddress)
err := messenger.settings.SaveOrUpdateKeypair(kp)
s.Require().NoError(err)
}
walletAccounts, err := messenger.settings.GetAccounts()
s.Require().NoError(err)
s.Require().Len(walletAccounts, len(walletAddresses))
for i := range walletAddresses {
s.Require().Equal(walletAccounts[i].Type, accounts.AccountTypeGenerated)
}
return messenger
}
func (s *MessengerCommunitiesTokenPermissionsSuite) joinCommunityWithAddresses(community *communities.Community, user *Messenger, password string, addresses []string) {
passwdHash := types.EncodeHex(crypto.Keccak256([]byte(password)))
request := &requests.RequestToJoinCommunity{CommunityID: community.ID(), Password: passwdHash, AddressesToReveal: addresses}
joinCommunity(&s.Suite, community, s.owner, user, request)
}
func (s *MessengerCommunitiesTokenPermissionsSuite) TestCreateTokenPermission() {
community, _ := createCommunity(&s.Suite, s.owner)
createTokenPermission := &requests.CreateCommunityTokenPermission{
CommunityID: community.ID(),
Type: protobuf.CommunityTokenPermission_BECOME_MEMBER,
TokenCriteria: []*protobuf.TokenCriteria{
&protobuf.TokenCriteria{
Type: protobuf.CommunityTokenType_ERC20,
ContractAddresses: map[uint64]string{uint64(1): "0x123"},
Symbol: "TEST",
Amount: "100",
Decimals: uint64(18),
},
},
}
response, err := s.owner.CreateCommunityTokenPermission(createTokenPermission)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Communities(), 1)
tokenPermissions := response.Communities()[0].TokenPermissions()
for _, tokenPermission := range tokenPermissions {
for _, tc := range tokenPermission.TokenCriteria {
s.Require().Equal(tc.Type, protobuf.CommunityTokenType_ERC20)
s.Require().Equal(tc.Symbol, "TEST")
s.Require().Equal(tc.Amount, "100")
s.Require().Equal(tc.Decimals, uint64(18))
}
}
}
func (s *MessengerCommunitiesTokenPermissionsSuite) TestEditTokenPermission() {
community, _ := createCommunity(&s.Suite, s.owner)
tokenPermission := &requests.CreateCommunityTokenPermission{
CommunityID: community.ID(),
Type: protobuf.CommunityTokenPermission_BECOME_MEMBER,
TokenCriteria: []*protobuf.TokenCriteria{
&protobuf.TokenCriteria{
Type: protobuf.CommunityTokenType_ERC20,
ContractAddresses: map[uint64]string{uint64(1): "0x123"},
Symbol: "TEST",
Amount: "100",
Decimals: uint64(18),
},
},
}
response, err := s.owner.CreateCommunityTokenPermission(tokenPermission)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Communities(), 1)
tokenPermissions := response.Communities()[0].TokenPermissions()
var tokenPermissionID string
for id := range tokenPermissions {
tokenPermissionID = id
}
tokenPermission.TokenCriteria[0].Symbol = "TESTUpdated"
tokenPermission.TokenCriteria[0].Amount = "200"
tokenPermission.TokenCriteria[0].Decimals = uint64(20)
editTokenPermission := &requests.EditCommunityTokenPermission{
PermissionID: tokenPermissionID,
CreateCommunityTokenPermission: *tokenPermission,
}
response2, err := s.owner.EditCommunityTokenPermission(editTokenPermission)
s.Require().NoError(err)
// wait for `checkMemberPermissions` to finish
time.Sleep(1 * time.Second)
s.Require().NotNil(response2)
s.Require().Len(response2.Communities(), 1)
tokenPermissions = response2.Communities()[0].TokenPermissions()
for _, tokenPermission := range tokenPermissions {
for _, tc := range tokenPermission.TokenCriteria {
s.Require().Equal(tc.Type, protobuf.CommunityTokenType_ERC20)
s.Require().Equal(tc.Symbol, "TESTUpdated")
s.Require().Equal(tc.Amount, "200")
s.Require().Equal(tc.Decimals, uint64(20))
}
}
}
func (s *MessengerCommunitiesTokenPermissionsSuite) TestCommunityTokensMetadata() {
description := &requests.CreateCommunity{
Membership: protobuf.CommunityPermissions_NO_MEMBERSHIP,
Name: "status",
Color: "#ffffff",
Description: "status community description",
}
// Create an community chat
response, err := s.bob.CreateCommunity(description, true)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Communities(), 1)
s.Require().Len(response.CommunitiesSettings(), 1)
community := response.Communities()[0]
tokensMetadata := community.CommunityTokensMetadata()
s.Require().Len(tokensMetadata, 0)
newToken := &protobuf.CommunityTokenMetadata{
ContractAddresses: map[uint64]string{3: "0xasd"},
Description: "desc1",
Image: "IMG1",
TokenType: protobuf.CommunityTokenType_ERC721,
Symbol: "SMB",
}
_, err = community.AddCommunityTokensMetadata(newToken)
s.Require().NoError(err)
tokensMetadata = community.CommunityTokensMetadata()
s.Require().Len(tokensMetadata, 1)
s.Require().Equal(tokensMetadata[0].ContractAddresses, newToken.ContractAddresses)
s.Require().Equal(tokensMetadata[0].Description, newToken.Description)
s.Require().Equal(tokensMetadata[0].Image, newToken.Image)
s.Require().Equal(tokensMetadata[0].TokenType, newToken.TokenType)
s.Require().Equal(tokensMetadata[0].Symbol, newToken.Symbol)
s.Require().Equal(tokensMetadata[0].Name, newToken.Name)
}
func (s *MessengerCommunitiesTokenPermissionsSuite) TestRequestAccessWithENSTokenPermission() {
community, _ := createCommunity(&s.Suite, s.owner)
createTokenPermission := &requests.CreateCommunityTokenPermission{
CommunityID: community.ID(),
Type: protobuf.CommunityTokenPermission_BECOME_MEMBER,
TokenCriteria: []*protobuf.TokenCriteria{
&protobuf.TokenCriteria{
Type: protobuf.CommunityTokenType_ENS,
EnsPattern: "test.stateofus.eth",
},
},
}
response, err := s.owner.CreateCommunityTokenPermission(createTokenPermission)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Communities(), 1)
advertiseCommunityTo(&s.Suite, community, s.owner, s.alice)
requestToJoin := &requests.RequestToJoinCommunity{CommunityID: community.ID()}
// We try to join the org
response, err = s.alice.RequestToJoinCommunity(requestToJoin)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.RequestsToJoinCommunity, 1)
requestToJoin1 := response.RequestsToJoinCommunity[0]
s.Require().Equal(communities.RequestToJoinStatePending, requestToJoin1.State)
// Retrieve request to join
err = tt.RetryWithBackOff(func() error {
response, err = s.owner.RetrieveAll()
return err
})
s.Require().NoError(err)
// We don't expect a requestToJoin in the response because due
// to missing revealed wallet addresses, the request should've
// been declined right away
s.Require().Len(response.RequestsToJoinCommunity, 0)
// Ensure alice is not a member of the community
allCommunities, err := s.owner.Communities()
if bytes.Equal(allCommunities[0].ID(), community.ID()) {
s.Require().False(allCommunities[0].HasMember(&s.alice.identity.PublicKey))
}
}
func (s *MessengerCommunitiesTokenPermissionsSuite) TestJoinedCommunityMembersSharedAddress() {
community, _ := createCommunity(&s.Suite, s.owner)
advertiseCommunityTo(&s.Suite, community, s.owner, s.alice)
advertiseCommunityTo(&s.Suite, community, s.owner, s.bob)
s.joinCommunityWithAddresses(community, s.alice, alicePassword, []string{})
s.joinCommunityWithAddresses(community, s.bob, bobPassword, []string{})
community, err := s.owner.GetCommunityByID(community.ID())
s.Require().NoError(err)
s.Require().Equal(3, community.MembersCount())
for pubKey, member := range community.Members() {
if pubKey != common.PubkeyToHex(&s.owner.identity.PublicKey) {
switch pubKey {
case common.PubkeyToHex(&s.alice.identity.PublicKey):
s.Require().Len(member.RevealedAccounts, 2)
s.Require().Equal(member.RevealedAccounts[0].Address, aliceAddress1)
s.Require().Equal(member.RevealedAccounts[1].Address, aliceAddress2)
case common.PubkeyToHex(&s.bob.identity.PublicKey):
s.Require().Len(member.RevealedAccounts, 1)
s.Require().Equal(member.RevealedAccounts[0].Address, bobAddress)
default:
s.Require().Fail("pubKey does not match expected keys")
}
}
}
}
func (s *MessengerCommunitiesTokenPermissionsSuite) TestJoinedCommunityMembersSelectedSharedAddress() {
community, _ := createCommunity(&s.Suite, s.owner)
advertiseCommunityTo(&s.Suite, community, s.owner, s.alice)
s.joinCommunityWithAddresses(community, s.alice, alicePassword, []string{aliceAddress2})
community, err := s.owner.GetCommunityByID(community.ID())
s.Require().NoError(err)
s.Require().Equal(2, community.MembersCount())
for pubKey, member := range community.Members() {
if pubKey != common.PubkeyToHex(&s.owner.identity.PublicKey) {
s.Require().Len(member.RevealedAccounts, 1)
switch pubKey {
case common.PubkeyToHex(&s.alice.identity.PublicKey):
s.Require().Equal(member.RevealedAccounts[0].Address, aliceAddress2)
default:
s.Require().Fail("pubKey does not match expected keys")
}
}
}
}