1897 lines
67 KiB
Go
1897 lines
67 KiB
Go
package communities
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"image"
|
|
"image/png"
|
|
"math"
|
|
"math/big"
|
|
"os"
|
|
"testing"
|
|
"time"
|
|
|
|
gethcommon "github.com/ethereum/go-ethereum/common"
|
|
"github.com/ethereum/go-ethereum/common/hexutil"
|
|
"github.com/status-im/status-go/appdatabase"
|
|
"github.com/status-im/status-go/eth-node/types"
|
|
userimages "github.com/status-im/status-go/images"
|
|
"github.com/status-im/status-go/params"
|
|
"github.com/status-im/status-go/protocol/common"
|
|
"github.com/status-im/status-go/protocol/requests"
|
|
"github.com/status-im/status-go/protocol/transport"
|
|
v1 "github.com/status-im/status-go/protocol/v1"
|
|
"github.com/status-im/status-go/services/wallet/bigint"
|
|
walletCommon "github.com/status-im/status-go/services/wallet/common"
|
|
"github.com/status-im/status-go/services/wallet/thirdparty"
|
|
"github.com/status-im/status-go/services/wallet/token"
|
|
"github.com/status-im/status-go/t/helpers"
|
|
|
|
"github.com/golang/protobuf/proto"
|
|
_ "github.com/mutecomm/go-sqlcipher/v4" // require go-sqlcipher that overrides default implementation
|
|
|
|
"github.com/stretchr/testify/suite"
|
|
|
|
"github.com/status-im/status-go/eth-node/crypto"
|
|
"github.com/status-im/status-go/protocol/protobuf"
|
|
"github.com/status-im/status-go/protocol/sqlite"
|
|
)
|
|
|
|
func TestManagerSuite(t *testing.T) {
|
|
suite.Run(t, new(ManagerSuite))
|
|
}
|
|
|
|
type ManagerSuite struct {
|
|
suite.Suite
|
|
manager *Manager
|
|
}
|
|
|
|
func (s *ManagerSuite) buildManager(ownerVerifier OwnerVerifier) *Manager {
|
|
db, err := helpers.SetupTestMemorySQLDB(appdatabase.DbInitializer{})
|
|
s.Require().NoError(err, "creating sqlite db instance")
|
|
err = sqlite.Migrate(db)
|
|
s.Require().NoError(err, "protocol migrate")
|
|
|
|
key, err := crypto.GenerateKey()
|
|
s.Require().NoError(err)
|
|
s.Require().NoError(err)
|
|
m, err := NewManager(key, "", db, nil, nil, nil, ownerVerifier, nil, &TimeSourceStub{}, nil)
|
|
s.Require().NoError(err)
|
|
s.Require().NoError(m.Start())
|
|
return m
|
|
}
|
|
|
|
func (s *ManagerSuite) SetupTest() {
|
|
m := s.buildManager(nil)
|
|
SetValidateInterval(30 * time.Millisecond)
|
|
s.manager = m
|
|
}
|
|
|
|
func intToBig(n int64) *hexutil.Big {
|
|
return (*hexutil.Big)(big.NewInt(n))
|
|
}
|
|
|
|
func uintToDecBig(n uint64) *bigint.BigInt {
|
|
return &bigint.BigInt{Int: big.NewInt(int64(n))}
|
|
}
|
|
|
|
func tokenBalance(tokenID uint64, balance uint64) thirdparty.TokenBalance {
|
|
return thirdparty.TokenBalance{
|
|
TokenID: uintToDecBig(tokenID),
|
|
Balance: uintToDecBig(balance),
|
|
}
|
|
}
|
|
|
|
func (s *ManagerSuite) getHistoryTasksCount() int {
|
|
// sync.Map doesn't have a Len function, so we need to count manually
|
|
count := 0
|
|
s.manager.historyArchiveTasks.Range(func(_, _ interface{}) bool {
|
|
count++
|
|
return true
|
|
})
|
|
return count
|
|
}
|
|
|
|
type testCollectiblesManager struct {
|
|
response map[uint64]map[gethcommon.Address]thirdparty.TokenBalancesPerContractAddress
|
|
}
|
|
|
|
func (m *testCollectiblesManager) setResponse(chainID uint64, walletAddress gethcommon.Address, contractAddress gethcommon.Address, balances []thirdparty.TokenBalance) {
|
|
if m.response == nil {
|
|
m.response = make(map[uint64]map[gethcommon.Address]thirdparty.TokenBalancesPerContractAddress)
|
|
}
|
|
if m.response[chainID] == nil {
|
|
m.response[chainID] = make(map[gethcommon.Address]thirdparty.TokenBalancesPerContractAddress)
|
|
}
|
|
if m.response[chainID][walletAddress] == nil {
|
|
m.response[chainID][walletAddress] = make(thirdparty.TokenBalancesPerContractAddress)
|
|
}
|
|
|
|
m.response[chainID][walletAddress][contractAddress] = balances
|
|
}
|
|
|
|
func (m *testCollectiblesManager) FetchBalancesByOwnerAndContractAddress(ctx context.Context, chainID walletCommon.ChainID, ownerAddress gethcommon.Address, contractAddresses []gethcommon.Address) (thirdparty.TokenBalancesPerContractAddress, error) {
|
|
return m.response[uint64(chainID)][ownerAddress], nil
|
|
}
|
|
|
|
type testTokenManager struct {
|
|
response map[uint64]map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big
|
|
}
|
|
|
|
func (m *testTokenManager) setResponse(chainID uint64, walletAddress, tokenAddress gethcommon.Address, balance int64) {
|
|
|
|
if m.response == nil {
|
|
m.response = make(map[uint64]map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big)
|
|
}
|
|
|
|
if m.response[chainID] == nil {
|
|
m.response[chainID] = make(map[gethcommon.Address]map[gethcommon.Address]*hexutil.Big)
|
|
}
|
|
|
|
if m.response[chainID][walletAddress] == nil {
|
|
m.response[chainID][walletAddress] = make(map[gethcommon.Address]*hexutil.Big)
|
|
}
|
|
|
|
m.response[chainID][walletAddress][tokenAddress] = intToBig(balance)
|
|
|
|
}
|
|
|
|
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 m.response, nil
|
|
}
|
|
|
|
func (m *testTokenManager) FindOrCreateTokenByAddress(ctx context.Context, chainID uint64, address gethcommon.Address) *token.Token {
|
|
return nil
|
|
}
|
|
|
|
func (s *ManagerSuite) setupManagerForTokenPermissions() (*Manager, *testCollectiblesManager, *testTokenManager) {
|
|
db, err := helpers.SetupTestMemorySQLDB(appdatabase.DbInitializer{})
|
|
s.NoError(err, "creating sqlite db instance")
|
|
err = sqlite.Migrate(db)
|
|
s.NoError(err, "protocol migrate")
|
|
|
|
key, err := crypto.GenerateKey()
|
|
s.Require().NoError(err)
|
|
s.Require().NoError(err)
|
|
|
|
cm := &testCollectiblesManager{}
|
|
tm := &testTokenManager{}
|
|
|
|
options := []ManagerOption{
|
|
WithWalletConfig(¶ms.WalletConfig{
|
|
OpenseaAPIKey: "some-key",
|
|
}),
|
|
WithCollectiblesManager(cm),
|
|
WithTokenManager(tm),
|
|
}
|
|
|
|
m, err := NewManager(key, "", db, nil, nil, nil, nil, nil, &TimeSourceStub{}, nil, options...)
|
|
s.Require().NoError(err)
|
|
s.Require().NoError(m.Start())
|
|
|
|
return m, cm, tm
|
|
}
|
|
|
|
func (s *ManagerSuite) TestRetrieveTokens() {
|
|
m, _, tm := s.setupManagerForTokenPermissions()
|
|
|
|
var chainID uint64 = 5
|
|
contractAddresses := make(map[uint64]string)
|
|
contractAddresses[chainID] = "0x3d6afaa395c31fcd391fe3d562e75fe9e8ec7e6a"
|
|
var decimals uint64 = 18
|
|
|
|
var tokenCriteria = []*protobuf.TokenCriteria{
|
|
&protobuf.TokenCriteria{
|
|
ContractAddresses: contractAddresses,
|
|
Symbol: "STT",
|
|
Type: protobuf.CommunityTokenType_ERC20,
|
|
Name: "Status Test Token",
|
|
Amount: "1.000000000000000000",
|
|
Decimals: decimals,
|
|
},
|
|
}
|
|
|
|
var permissions = []*CommunityTokenPermission{
|
|
&CommunityTokenPermission{
|
|
CommunityTokenPermission: &protobuf.CommunityTokenPermission{
|
|
Id: "some-id",
|
|
Type: protobuf.CommunityTokenPermission_BECOME_MEMBER,
|
|
TokenCriteria: tokenCriteria,
|
|
},
|
|
},
|
|
}
|
|
|
|
accountChainIDsCombination := []*AccountChainIDsCombination{
|
|
&AccountChainIDsCombination{
|
|
Address: gethcommon.HexToAddress("0xD6b912e09E797D291E8D0eA3D3D17F8000e01c32"),
|
|
ChainIDs: []uint64{chainID},
|
|
},
|
|
}
|
|
// Set response to exactly the right one
|
|
tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), int64(1*math.Pow(10, float64(decimals))))
|
|
resp, err := m.PermissionChecker.CheckPermissions(permissions, accountChainIDsCombination, false)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(resp)
|
|
s.Require().True(resp.Satisfied)
|
|
|
|
// Set response to 0
|
|
tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), 0)
|
|
resp, err = m.PermissionChecker.CheckPermissions(permissions, accountChainIDsCombination, false)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(resp)
|
|
s.Require().False(resp.Satisfied)
|
|
}
|
|
|
|
func (s *ManagerSuite) TestRetrieveCollectibles() {
|
|
m, cm, _ := s.setupManagerForTokenPermissions()
|
|
|
|
var chainID uint64 = 5
|
|
contractAddresses := make(map[uint64]string)
|
|
contractAddresses[chainID] = "0x3d6afaa395c31fcd391fe3d562e75fe9e8ec7e6a"
|
|
|
|
tokenID := uint64(10)
|
|
var tokenBalances []thirdparty.TokenBalance
|
|
|
|
var tokenCriteria = []*protobuf.TokenCriteria{
|
|
&protobuf.TokenCriteria{
|
|
ContractAddresses: contractAddresses,
|
|
TokenIds: []uint64{tokenID},
|
|
Type: protobuf.CommunityTokenType_ERC721,
|
|
},
|
|
}
|
|
|
|
var permissions = []*CommunityTokenPermission{
|
|
&CommunityTokenPermission{
|
|
CommunityTokenPermission: &protobuf.CommunityTokenPermission{
|
|
Id: "some-id",
|
|
Type: protobuf.CommunityTokenPermission_BECOME_MEMBER,
|
|
TokenCriteria: tokenCriteria,
|
|
},
|
|
},
|
|
}
|
|
|
|
accountChainIDsCombination := []*AccountChainIDsCombination{
|
|
&AccountChainIDsCombination{
|
|
Address: gethcommon.HexToAddress("0xD6b912e09E797D291E8D0eA3D3D17F8000e01c32"),
|
|
ChainIDs: []uint64{chainID},
|
|
},
|
|
}
|
|
|
|
// Set response to exactly the right one
|
|
tokenBalances = []thirdparty.TokenBalance{tokenBalance(tokenID, 1)}
|
|
cm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), tokenBalances)
|
|
resp, err := m.PermissionChecker.CheckPermissions(permissions, accountChainIDsCombination, false)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(resp)
|
|
s.Require().True(resp.Satisfied)
|
|
|
|
// Set balances to 0
|
|
tokenBalances = []thirdparty.TokenBalance{}
|
|
cm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), tokenBalances)
|
|
resp, err = m.PermissionChecker.CheckPermissions(permissions, accountChainIDsCombination, false)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(resp)
|
|
s.Require().False(resp.Satisfied)
|
|
}
|
|
|
|
func (s *ManagerSuite) TestCreateCommunity() {
|
|
request := &requests.CreateCommunity{
|
|
Name: "status",
|
|
Description: "token membership description",
|
|
Membership: protobuf.CommunityPermissions_AUTO_ACCEPT,
|
|
}
|
|
|
|
community, err := s.manager.CreateCommunity(request, true)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(community)
|
|
|
|
communities, err := s.manager.All()
|
|
s.Require().NoError(err)
|
|
// Consider status default community
|
|
s.Require().Len(communities, 2)
|
|
|
|
actualCommunity := communities[0]
|
|
if bytes.Equal(community.ID(), communities[1].ID()) {
|
|
actualCommunity = communities[1]
|
|
}
|
|
|
|
s.Require().Equal(community.ID(), actualCommunity.ID())
|
|
s.Require().Equal(community.PrivateKey(), actualCommunity.PrivateKey())
|
|
s.Require().True(community.IsControlNode())
|
|
s.Require().True(proto.Equal(community.config.CommunityDescription, actualCommunity.config.CommunityDescription))
|
|
}
|
|
|
|
func (s *ManagerSuite) TestCreateCommunity_WithBanner() {
|
|
// Generate test image bigger than BannerDim
|
|
testImage := image.NewRGBA(image.Rect(0, 0, 20, 10))
|
|
|
|
tmpTestFilePath := s.T().TempDir() + "/test.png"
|
|
file, err := os.Create(tmpTestFilePath)
|
|
s.NoError(err)
|
|
defer file.Close()
|
|
|
|
err = png.Encode(file, testImage)
|
|
s.Require().NoError(err)
|
|
|
|
request := &requests.CreateCommunity{
|
|
Name: "with_banner",
|
|
Description: "community with banner ",
|
|
Membership: protobuf.CommunityPermissions_AUTO_ACCEPT,
|
|
Banner: userimages.CroppedImage{
|
|
ImagePath: tmpTestFilePath,
|
|
X: 1,
|
|
Y: 1,
|
|
Width: 10,
|
|
Height: 5,
|
|
},
|
|
}
|
|
|
|
community, err := s.manager.CreateCommunity(request, true)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(community)
|
|
|
|
communities, err := s.manager.All()
|
|
s.Require().NoError(err)
|
|
// Consider status default community
|
|
s.Require().Len(communities, 2)
|
|
s.Require().Equal(len(community.config.CommunityDescription.Identity.Images), 1)
|
|
testIdentityImage, isMapContainsKey := community.config.CommunityDescription.Identity.Images[userimages.BannerIdentityName]
|
|
s.Require().True(isMapContainsKey)
|
|
s.Require().Positive(len(testIdentityImage.Payload))
|
|
}
|
|
|
|
func (s *ManagerSuite) TestEditCommunity() {
|
|
//create community
|
|
createRequest := &requests.CreateCommunity{
|
|
Name: "status",
|
|
Description: "status community description",
|
|
Membership: protobuf.CommunityPermissions_AUTO_ACCEPT,
|
|
}
|
|
|
|
community, err := s.manager.CreateCommunity(createRequest, true)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(community)
|
|
|
|
update := &requests.EditCommunity{
|
|
CommunityID: community.ID(),
|
|
CreateCommunity: requests.CreateCommunity{
|
|
Name: "statusEdited",
|
|
Description: "status community description edited",
|
|
},
|
|
}
|
|
|
|
updatedCommunity, err := s.manager.EditCommunity(update)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(updatedCommunity)
|
|
|
|
//ensure updated community successfully stored
|
|
communities, err := s.manager.All()
|
|
s.Require().NoError(err)
|
|
// Consider status default community
|
|
s.Require().Len(communities, 2)
|
|
|
|
storedCommunity := communities[0]
|
|
if bytes.Equal(community.ID(), communities[1].ID()) {
|
|
storedCommunity = communities[1]
|
|
}
|
|
|
|
s.Require().Equal(storedCommunity.ID(), updatedCommunity.ID())
|
|
s.Require().Equal(storedCommunity.PrivateKey(), updatedCommunity.PrivateKey())
|
|
s.Require().Equal(storedCommunity.config.CommunityDescription.Identity.DisplayName, update.CreateCommunity.Name)
|
|
s.Require().Equal(storedCommunity.config.CommunityDescription.Identity.Description, update.CreateCommunity.Description)
|
|
}
|
|
|
|
func (s *ManagerSuite) TestGetControlledCommunitiesChatIDs() {
|
|
community, _, err := s.buildCommunityWithChat()
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(community)
|
|
|
|
controlledChatIDs, err := s.manager.GetOwnedCommunitiesChatIDs()
|
|
|
|
s.Require().NoError(err)
|
|
s.Require().Len(controlledChatIDs, 1)
|
|
}
|
|
|
|
func (s *ManagerSuite) TestStartAndStopTorrentClient() {
|
|
torrentConfig := buildTorrentConfig()
|
|
s.manager.SetTorrentConfig(&torrentConfig)
|
|
|
|
err := s.manager.StartTorrentClient()
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(s.manager.torrentClient)
|
|
defer s.manager.StopTorrentClient()
|
|
|
|
_, err = os.Stat(torrentConfig.DataDir)
|
|
s.Require().NoError(err)
|
|
s.Require().Equal(s.manager.TorrentClientStarted(), true)
|
|
}
|
|
|
|
func (s *ManagerSuite) TestStartHistoryArchiveTasksInterval() {
|
|
|
|
torrentConfig := buildTorrentConfig()
|
|
s.manager.SetTorrentConfig(&torrentConfig)
|
|
|
|
err := s.manager.StartTorrentClient()
|
|
s.Require().NoError(err)
|
|
defer s.manager.StopTorrentClient()
|
|
|
|
community, _, err := s.buildCommunityWithChat()
|
|
s.Require().NoError(err)
|
|
|
|
interval := 10 * time.Second
|
|
go s.manager.StartHistoryArchiveTasksInterval(community, interval)
|
|
// Due to async exec we need to wait a bit until we check
|
|
// the task count.
|
|
time.Sleep(5 * time.Second)
|
|
|
|
count := s.getHistoryTasksCount()
|
|
s.Require().Equal(count, 1)
|
|
|
|
// We wait another 5 seconds to ensure the first tick has kicked in
|
|
time.Sleep(5 * time.Second)
|
|
|
|
_, err = os.Stat(s.manager.torrentFile(community.IDString()))
|
|
s.Require().Error(err)
|
|
|
|
s.manager.StopHistoryArchiveTasksInterval(community.ID())
|
|
s.manager.historyArchiveTasksWaitGroup.Wait()
|
|
count = s.getHistoryTasksCount()
|
|
s.Require().Equal(count, 0)
|
|
}
|
|
|
|
func (s *ManagerSuite) TestStopHistoryArchiveTasksIntervals() {
|
|
|
|
torrentConfig := buildTorrentConfig()
|
|
s.manager.SetTorrentConfig(&torrentConfig)
|
|
|
|
err := s.manager.StartTorrentClient()
|
|
s.Require().NoError(err)
|
|
defer s.manager.StopTorrentClient()
|
|
|
|
community, _, err := s.buildCommunityWithChat()
|
|
s.Require().NoError(err)
|
|
|
|
interval := 10 * time.Second
|
|
go s.manager.StartHistoryArchiveTasksInterval(community, interval)
|
|
|
|
time.Sleep(2 * time.Second)
|
|
|
|
count := s.getHistoryTasksCount()
|
|
s.Require().Equal(count, 1)
|
|
|
|
s.manager.StopHistoryArchiveTasksIntervals()
|
|
|
|
count = s.getHistoryTasksCount()
|
|
s.Require().Equal(count, 0)
|
|
}
|
|
|
|
func (s *ManagerSuite) TestStopTorrentClient_ShouldStopHistoryArchiveTasks() {
|
|
torrentConfig := buildTorrentConfig()
|
|
s.manager.SetTorrentConfig(&torrentConfig)
|
|
|
|
err := s.manager.StartTorrentClient()
|
|
s.Require().NoError(err)
|
|
defer s.manager.StopTorrentClient()
|
|
|
|
community, _, err := s.buildCommunityWithChat()
|
|
s.Require().NoError(err)
|
|
|
|
interval := 10 * time.Second
|
|
go s.manager.StartHistoryArchiveTasksInterval(community, interval)
|
|
// Due to async exec we need to wait a bit until we check
|
|
// the task count.
|
|
time.Sleep(2 * time.Second)
|
|
|
|
count := s.getHistoryTasksCount()
|
|
s.Require().Equal(count, 1)
|
|
|
|
errs := s.manager.StopTorrentClient()
|
|
s.Require().Len(errs, 0)
|
|
|
|
count = s.getHistoryTasksCount()
|
|
s.Require().Equal(count, 0)
|
|
}
|
|
|
|
func (s *ManagerSuite) TestCreateHistoryArchiveTorrent_WithoutMessages() {
|
|
|
|
torrentConfig := buildTorrentConfig()
|
|
s.manager.SetTorrentConfig(&torrentConfig)
|
|
|
|
community, chatID, err := s.buildCommunityWithChat()
|
|
s.Require().NoError(err)
|
|
|
|
topic := types.BytesToTopic(transport.ToTopic(chatID))
|
|
topics := []types.TopicType{topic}
|
|
|
|
// Time range of 7 days
|
|
startDate := time.Date(2020, 1, 1, 00, 00, 00, 0, time.UTC)
|
|
endDate := time.Date(2020, 1, 7, 00, 00, 00, 0, time.UTC)
|
|
// Partition of 7 days
|
|
partition := 7 * 24 * time.Hour
|
|
|
|
_, err = s.manager.CreateHistoryArchiveTorrentFromDB(community.ID(), topics, startDate, endDate, partition, false)
|
|
s.Require().NoError(err)
|
|
|
|
// There are no waku messages in the database so we don't expect
|
|
// any archives to be created
|
|
_, err = os.Stat(s.manager.archiveDataFile(community.IDString()))
|
|
s.Require().Error(err)
|
|
_, err = os.Stat(s.manager.archiveIndexFile(community.IDString()))
|
|
s.Require().Error(err)
|
|
_, err = os.Stat(s.manager.torrentFile(community.IDString()))
|
|
s.Require().Error(err)
|
|
}
|
|
|
|
func (s *ManagerSuite) TestCreateHistoryArchiveTorrent_ShouldCreateArchive() {
|
|
torrentConfig := buildTorrentConfig()
|
|
s.manager.SetTorrentConfig(&torrentConfig)
|
|
|
|
community, chatID, err := s.buildCommunityWithChat()
|
|
s.Require().NoError(err)
|
|
|
|
topic := types.BytesToTopic(transport.ToTopic(chatID))
|
|
topics := []types.TopicType{topic}
|
|
|
|
// Time range of 7 days
|
|
startDate := time.Date(2020, 1, 1, 00, 00, 00, 0, time.UTC)
|
|
endDate := time.Date(2020, 1, 7, 00, 00, 00, 0, time.UTC)
|
|
// Partition of 7 days, this should create a single archive
|
|
partition := 7 * 24 * time.Hour
|
|
|
|
message1 := buildMessage(startDate.Add(1*time.Hour), topic, []byte{1})
|
|
message2 := buildMessage(startDate.Add(2*time.Hour), topic, []byte{2})
|
|
// This message is outside of the startDate-endDate range and should not
|
|
// be part of the archive
|
|
message3 := buildMessage(endDate.Add(2*time.Hour), topic, []byte{3})
|
|
|
|
err = s.manager.StoreWakuMessage(&message1)
|
|
s.Require().NoError(err)
|
|
err = s.manager.StoreWakuMessage(&message2)
|
|
s.Require().NoError(err)
|
|
err = s.manager.StoreWakuMessage(&message3)
|
|
s.Require().NoError(err)
|
|
|
|
_, err = s.manager.CreateHistoryArchiveTorrentFromDB(community.ID(), topics, startDate, endDate, partition, false)
|
|
s.Require().NoError(err)
|
|
|
|
_, err = os.Stat(s.manager.archiveDataFile(community.IDString()))
|
|
s.Require().NoError(err)
|
|
_, err = os.Stat(s.manager.archiveIndexFile(community.IDString()))
|
|
s.Require().NoError(err)
|
|
_, err = os.Stat(s.manager.torrentFile(community.IDString()))
|
|
s.Require().NoError(err)
|
|
|
|
index, err := s.manager.LoadHistoryArchiveIndexFromFile(s.manager.identity, community.ID())
|
|
s.Require().NoError(err)
|
|
s.Require().Len(index.Archives, 1)
|
|
|
|
totalData, err := os.ReadFile(s.manager.archiveDataFile(community.IDString()))
|
|
s.Require().NoError(err)
|
|
|
|
for _, metadata := range index.Archives {
|
|
archive := &protobuf.WakuMessageArchive{}
|
|
data := totalData[metadata.Offset : metadata.Offset+metadata.Size-metadata.Padding]
|
|
|
|
err = proto.Unmarshal(data, archive)
|
|
s.Require().NoError(err)
|
|
|
|
s.Require().Len(archive.Messages, 2)
|
|
}
|
|
}
|
|
|
|
func (s *ManagerSuite) TestCreateHistoryArchiveTorrent_ShouldCreateMultipleArchives() {
|
|
torrentConfig := buildTorrentConfig()
|
|
s.manager.SetTorrentConfig(&torrentConfig)
|
|
|
|
community, chatID, err := s.buildCommunityWithChat()
|
|
s.Require().NoError(err)
|
|
|
|
topic := types.BytesToTopic(transport.ToTopic(chatID))
|
|
topics := []types.TopicType{topic}
|
|
|
|
// Time range of 3 weeks
|
|
startDate := time.Date(2020, 1, 1, 00, 00, 00, 0, time.UTC)
|
|
endDate := time.Date(2020, 1, 21, 00, 00, 00, 0, time.UTC)
|
|
// 7 days partition, this should create three archives
|
|
partition := 7 * 24 * time.Hour
|
|
|
|
message1 := buildMessage(startDate.Add(1*time.Hour), topic, []byte{1})
|
|
message2 := buildMessage(startDate.Add(2*time.Hour), topic, []byte{2})
|
|
// We expect 2 archives to be created for startDate - endDate of each
|
|
// 7 days of data. This message should end up in the second archive
|
|
message3 := buildMessage(startDate.Add(8*24*time.Hour), topic, []byte{3})
|
|
// This one should end up in the third archive
|
|
message4 := buildMessage(startDate.Add(14*24*time.Hour), topic, []byte{4})
|
|
|
|
err = s.manager.StoreWakuMessage(&message1)
|
|
s.Require().NoError(err)
|
|
err = s.manager.StoreWakuMessage(&message2)
|
|
s.Require().NoError(err)
|
|
err = s.manager.StoreWakuMessage(&message3)
|
|
s.Require().NoError(err)
|
|
err = s.manager.StoreWakuMessage(&message4)
|
|
s.Require().NoError(err)
|
|
|
|
_, err = s.manager.CreateHistoryArchiveTorrentFromDB(community.ID(), topics, startDate, endDate, partition, false)
|
|
s.Require().NoError(err)
|
|
|
|
index, err := s.manager.LoadHistoryArchiveIndexFromFile(s.manager.identity, community.ID())
|
|
s.Require().NoError(err)
|
|
s.Require().Len(index.Archives, 3)
|
|
|
|
totalData, err := os.ReadFile(s.manager.archiveDataFile(community.IDString()))
|
|
s.Require().NoError(err)
|
|
|
|
// First archive has 2 messages
|
|
// Second archive has 1 message
|
|
// Third archive has 1 message
|
|
fromMap := map[uint64]int{
|
|
uint64(startDate.Unix()): 2,
|
|
uint64(startDate.Add(partition).Unix()): 1,
|
|
uint64(startDate.Add(partition * 2).Unix()): 1,
|
|
}
|
|
|
|
for _, metadata := range index.Archives {
|
|
archive := &protobuf.WakuMessageArchive{}
|
|
data := totalData[metadata.Offset : metadata.Offset+metadata.Size-metadata.Padding]
|
|
|
|
err = proto.Unmarshal(data, archive)
|
|
s.Require().NoError(err)
|
|
s.Require().Len(archive.Messages, fromMap[metadata.Metadata.From])
|
|
}
|
|
}
|
|
|
|
func (s *ManagerSuite) TestCreateHistoryArchiveTorrent_ShouldAppendArchives() {
|
|
torrentConfig := buildTorrentConfig()
|
|
s.manager.SetTorrentConfig(&torrentConfig)
|
|
|
|
community, chatID, err := s.buildCommunityWithChat()
|
|
s.Require().NoError(err)
|
|
|
|
topic := types.BytesToTopic(transport.ToTopic(chatID))
|
|
topics := []types.TopicType{topic}
|
|
|
|
// Time range of 1 week
|
|
startDate := time.Date(2020, 1, 1, 00, 00, 00, 0, time.UTC)
|
|
endDate := time.Date(2020, 1, 7, 00, 00, 00, 0, time.UTC)
|
|
// 7 days partition, this should create one archive
|
|
partition := 7 * 24 * time.Hour
|
|
|
|
message1 := buildMessage(startDate.Add(1*time.Hour), topic, []byte{1})
|
|
err = s.manager.StoreWakuMessage(&message1)
|
|
s.Require().NoError(err)
|
|
|
|
_, err = s.manager.CreateHistoryArchiveTorrentFromDB(community.ID(), topics, startDate, endDate, partition, false)
|
|
s.Require().NoError(err)
|
|
|
|
index, err := s.manager.LoadHistoryArchiveIndexFromFile(s.manager.identity, community.ID())
|
|
s.Require().NoError(err)
|
|
s.Require().Len(index.Archives, 1)
|
|
|
|
// Time range of next week
|
|
startDate = time.Date(2020, 1, 7, 00, 00, 00, 0, time.UTC)
|
|
endDate = time.Date(2020, 1, 14, 00, 00, 00, 0, time.UTC)
|
|
|
|
message2 := buildMessage(startDate.Add(2*time.Hour), topic, []byte{2})
|
|
err = s.manager.StoreWakuMessage(&message2)
|
|
s.Require().NoError(err)
|
|
|
|
_, err = s.manager.CreateHistoryArchiveTorrentFromDB(community.ID(), topics, startDate, endDate, partition, false)
|
|
s.Require().NoError(err)
|
|
|
|
index, err = s.manager.LoadHistoryArchiveIndexFromFile(s.manager.identity, community.ID())
|
|
s.Require().NoError(err)
|
|
s.Require().Len(index.Archives, 2)
|
|
}
|
|
|
|
func (s *ManagerSuite) TestCreateHistoryArchiveTorrentFromMessages() {
|
|
torrentConfig := buildTorrentConfig()
|
|
s.manager.SetTorrentConfig(&torrentConfig)
|
|
|
|
community, chatID, err := s.buildCommunityWithChat()
|
|
s.Require().NoError(err)
|
|
|
|
topic := types.BytesToTopic(transport.ToTopic(chatID))
|
|
topics := []types.TopicType{topic}
|
|
|
|
// Time range of 7 days
|
|
startDate := time.Date(2020, 1, 1, 00, 00, 00, 0, time.UTC)
|
|
endDate := time.Date(2020, 1, 7, 00, 00, 00, 0, time.UTC)
|
|
// Partition of 7 days, this should create a single archive
|
|
partition := 7 * 24 * time.Hour
|
|
|
|
message1 := buildMessage(startDate.Add(1*time.Hour), topic, []byte{1})
|
|
message2 := buildMessage(startDate.Add(2*time.Hour), topic, []byte{2})
|
|
// This message is outside of the startDate-endDate range and should not
|
|
// be part of the archive
|
|
message3 := buildMessage(endDate.Add(2*time.Hour), topic, []byte{3})
|
|
|
|
_, err = s.manager.CreateHistoryArchiveTorrentFromMessages(community.ID(), []*types.Message{&message1, &message2, &message3}, topics, startDate, endDate, partition, false)
|
|
s.Require().NoError(err)
|
|
|
|
_, err = os.Stat(s.manager.archiveDataFile(community.IDString()))
|
|
s.Require().NoError(err)
|
|
_, err = os.Stat(s.manager.archiveIndexFile(community.IDString()))
|
|
s.Require().NoError(err)
|
|
_, err = os.Stat(s.manager.torrentFile(community.IDString()))
|
|
s.Require().NoError(err)
|
|
|
|
index, err := s.manager.LoadHistoryArchiveIndexFromFile(s.manager.identity, community.ID())
|
|
s.Require().NoError(err)
|
|
s.Require().Len(index.Archives, 1)
|
|
|
|
totalData, err := os.ReadFile(s.manager.archiveDataFile(community.IDString()))
|
|
s.Require().NoError(err)
|
|
|
|
for _, metadata := range index.Archives {
|
|
archive := &protobuf.WakuMessageArchive{}
|
|
data := totalData[metadata.Offset : metadata.Offset+metadata.Size-metadata.Padding]
|
|
|
|
err = proto.Unmarshal(data, archive)
|
|
s.Require().NoError(err)
|
|
|
|
s.Require().Len(archive.Messages, 2)
|
|
}
|
|
}
|
|
|
|
func (s *ManagerSuite) TestCreateHistoryArchiveTorrentFromMessages_ShouldCreateMultipleArchives() {
|
|
torrentConfig := buildTorrentConfig()
|
|
s.manager.SetTorrentConfig(&torrentConfig)
|
|
|
|
community, chatID, err := s.buildCommunityWithChat()
|
|
s.Require().NoError(err)
|
|
|
|
topic := types.BytesToTopic(transport.ToTopic(chatID))
|
|
topics := []types.TopicType{topic}
|
|
|
|
// Time range of 3 weeks
|
|
startDate := time.Date(2020, 1, 1, 00, 00, 00, 0, time.UTC)
|
|
endDate := time.Date(2020, 1, 21, 00, 00, 00, 0, time.UTC)
|
|
// 7 days partition, this should create three archives
|
|
partition := 7 * 24 * time.Hour
|
|
|
|
message1 := buildMessage(startDate.Add(1*time.Hour), topic, []byte{1})
|
|
message2 := buildMessage(startDate.Add(2*time.Hour), topic, []byte{2})
|
|
// We expect 2 archives to be created for startDate - endDate of each
|
|
// 7 days of data. This message should end up in the second archive
|
|
message3 := buildMessage(startDate.Add(8*24*time.Hour), topic, []byte{3})
|
|
// This one should end up in the third archive
|
|
message4 := buildMessage(startDate.Add(14*24*time.Hour), topic, []byte{4})
|
|
|
|
_, err = s.manager.CreateHistoryArchiveTorrentFromMessages(community.ID(), []*types.Message{&message1, &message2, &message3, &message4}, topics, startDate, endDate, partition, false)
|
|
s.Require().NoError(err)
|
|
|
|
index, err := s.manager.LoadHistoryArchiveIndexFromFile(s.manager.identity, community.ID())
|
|
s.Require().NoError(err)
|
|
s.Require().Len(index.Archives, 3)
|
|
|
|
totalData, err := os.ReadFile(s.manager.archiveDataFile(community.IDString()))
|
|
s.Require().NoError(err)
|
|
|
|
// First archive has 2 messages
|
|
// Second archive has 1 message
|
|
// Third archive has 1 message
|
|
fromMap := map[uint64]int{
|
|
uint64(startDate.Unix()): 2,
|
|
uint64(startDate.Add(partition).Unix()): 1,
|
|
uint64(startDate.Add(partition * 2).Unix()): 1,
|
|
}
|
|
|
|
for _, metadata := range index.Archives {
|
|
archive := &protobuf.WakuMessageArchive{}
|
|
data := totalData[metadata.Offset : metadata.Offset+metadata.Size-metadata.Padding]
|
|
|
|
err = proto.Unmarshal(data, archive)
|
|
s.Require().NoError(err)
|
|
s.Require().Len(archive.Messages, fromMap[metadata.Metadata.From])
|
|
}
|
|
}
|
|
|
|
func (s *ManagerSuite) TestCreateHistoryArchiveTorrentFromMessages_ShouldAppendArchives() {
|
|
torrentConfig := buildTorrentConfig()
|
|
s.manager.SetTorrentConfig(&torrentConfig)
|
|
|
|
community, chatID, err := s.buildCommunityWithChat()
|
|
s.Require().NoError(err)
|
|
|
|
topic := types.BytesToTopic(transport.ToTopic(chatID))
|
|
topics := []types.TopicType{topic}
|
|
|
|
// Time range of 1 week
|
|
startDate := time.Date(2020, 1, 1, 00, 00, 00, 0, time.UTC)
|
|
endDate := time.Date(2020, 1, 7, 00, 00, 00, 0, time.UTC)
|
|
// 7 days partition, this should create one archive
|
|
partition := 7 * 24 * time.Hour
|
|
|
|
message1 := buildMessage(startDate.Add(1*time.Hour), topic, []byte{1})
|
|
|
|
_, err = s.manager.CreateHistoryArchiveTorrentFromMessages(community.ID(), []*types.Message{&message1}, topics, startDate, endDate, partition, false)
|
|
s.Require().NoError(err)
|
|
|
|
index, err := s.manager.LoadHistoryArchiveIndexFromFile(s.manager.identity, community.ID())
|
|
s.Require().NoError(err)
|
|
s.Require().Len(index.Archives, 1)
|
|
|
|
// Time range of next week
|
|
startDate = time.Date(2020, 1, 7, 00, 00, 00, 0, time.UTC)
|
|
endDate = time.Date(2020, 1, 14, 00, 00, 00, 0, time.UTC)
|
|
|
|
message2 := buildMessage(startDate.Add(2*time.Hour), topic, []byte{2})
|
|
|
|
_, err = s.manager.CreateHistoryArchiveTorrentFromMessages(community.ID(), []*types.Message{&message2}, topics, startDate, endDate, partition, false)
|
|
s.Require().NoError(err)
|
|
|
|
index, err = s.manager.LoadHistoryArchiveIndexFromFile(s.manager.identity, community.ID())
|
|
s.Require().NoError(err)
|
|
s.Require().Len(index.Archives, 2)
|
|
}
|
|
|
|
func (s *ManagerSuite) TestSeedHistoryArchiveTorrent() {
|
|
torrentConfig := buildTorrentConfig()
|
|
s.manager.SetTorrentConfig(&torrentConfig)
|
|
|
|
err := s.manager.StartTorrentClient()
|
|
s.Require().NoError(err)
|
|
defer s.manager.StopTorrentClient()
|
|
|
|
community, chatID, err := s.buildCommunityWithChat()
|
|
s.Require().NoError(err)
|
|
|
|
topic := types.BytesToTopic(transport.ToTopic(chatID))
|
|
topics := []types.TopicType{topic}
|
|
|
|
startDate := time.Date(2020, 1, 1, 00, 00, 00, 0, time.UTC)
|
|
endDate := time.Date(2020, 1, 7, 00, 00, 00, 0, time.UTC)
|
|
partition := 7 * 24 * time.Hour
|
|
|
|
message1 := buildMessage(startDate.Add(1*time.Hour), topic, []byte{1})
|
|
err = s.manager.StoreWakuMessage(&message1)
|
|
s.Require().NoError(err)
|
|
|
|
_, err = s.manager.CreateHistoryArchiveTorrentFromDB(community.ID(), topics, startDate, endDate, partition, false)
|
|
s.Require().NoError(err)
|
|
|
|
err = s.manager.SeedHistoryArchiveTorrent(community.ID())
|
|
s.Require().NoError(err)
|
|
s.Require().Len(s.manager.torrentTasks, 1)
|
|
|
|
metaInfoHash := s.manager.torrentTasks[community.IDString()]
|
|
torrent, ok := s.manager.torrentClient.Torrent(metaInfoHash)
|
|
defer torrent.Drop()
|
|
|
|
s.Require().Equal(ok, true)
|
|
s.Require().Equal(torrent.Seeding(), true)
|
|
}
|
|
|
|
func (s *ManagerSuite) TestUnseedHistoryArchiveTorrent() {
|
|
torrentConfig := buildTorrentConfig()
|
|
s.manager.SetTorrentConfig(&torrentConfig)
|
|
|
|
err := s.manager.StartTorrentClient()
|
|
s.Require().NoError(err)
|
|
defer s.manager.StopTorrentClient()
|
|
|
|
community, chatID, err := s.buildCommunityWithChat()
|
|
s.Require().NoError(err)
|
|
|
|
topic := types.BytesToTopic(transport.ToTopic(chatID))
|
|
topics := []types.TopicType{topic}
|
|
|
|
startDate := time.Date(2020, 1, 1, 00, 00, 00, 0, time.UTC)
|
|
endDate := time.Date(2020, 1, 7, 00, 00, 00, 0, time.UTC)
|
|
partition := 7 * 24 * time.Hour
|
|
|
|
message1 := buildMessage(startDate.Add(1*time.Hour), topic, []byte{1})
|
|
err = s.manager.StoreWakuMessage(&message1)
|
|
s.Require().NoError(err)
|
|
|
|
_, err = s.manager.CreateHistoryArchiveTorrentFromDB(community.ID(), topics, startDate, endDate, partition, false)
|
|
s.Require().NoError(err)
|
|
|
|
err = s.manager.SeedHistoryArchiveTorrent(community.ID())
|
|
s.Require().NoError(err)
|
|
s.Require().Len(s.manager.torrentTasks, 1)
|
|
|
|
metaInfoHash := s.manager.torrentTasks[community.IDString()]
|
|
|
|
s.manager.UnseedHistoryArchiveTorrent(community.ID())
|
|
_, ok := s.manager.torrentClient.Torrent(metaInfoHash)
|
|
s.Require().Equal(ok, false)
|
|
}
|
|
|
|
func (s *ManagerSuite) TestCheckChannelPermissions_NoPermissions() {
|
|
|
|
m, _, tm := s.setupManagerForTokenPermissions()
|
|
|
|
var chainID uint64 = 5
|
|
contractAddresses := make(map[uint64]string)
|
|
contractAddresses[chainID] = "0x3d6afaa395c31fcd391fe3d562e75fe9e8ec7e6a"
|
|
|
|
accountChainIDsCombination := []*AccountChainIDsCombination{
|
|
&AccountChainIDsCombination{
|
|
Address: gethcommon.HexToAddress("0xD6b912e09E797D291E8D0eA3D3D17F8000e01c32"),
|
|
ChainIDs: []uint64{chainID},
|
|
},
|
|
}
|
|
|
|
var viewOnlyPermissions = make([]*CommunityTokenPermission, 0)
|
|
var viewAndPostPermissions = make([]*CommunityTokenPermission, 0)
|
|
|
|
tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), 0)
|
|
resp, err := m.checkChannelPermissions(viewOnlyPermissions, viewAndPostPermissions, accountChainIDsCombination, false)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(resp)
|
|
|
|
// Both viewOnly and viewAndPost permissions are expected to be satisfied
|
|
// because we call `checkChannelPermissions()` with no permissions to check
|
|
s.Require().True(resp.ViewOnlyPermissions.Satisfied)
|
|
s.Require().True(resp.ViewAndPostPermissions.Satisfied)
|
|
}
|
|
|
|
func (s *ManagerSuite) TestCheckChannelPermissions_ViewOnlyPermissions() {
|
|
|
|
m, _, tm := s.setupManagerForTokenPermissions()
|
|
|
|
var chainID uint64 = 5
|
|
contractAddresses := make(map[uint64]string)
|
|
contractAddresses[chainID] = "0x3d6afaa395c31fcd391fe3d562e75fe9e8ec7e6a"
|
|
var decimals uint64 = 18
|
|
|
|
accountChainIDsCombination := []*AccountChainIDsCombination{
|
|
&AccountChainIDsCombination{
|
|
Address: gethcommon.HexToAddress("0xD6b912e09E797D291E8D0eA3D3D17F8000e01c32"),
|
|
ChainIDs: []uint64{chainID},
|
|
},
|
|
}
|
|
|
|
var tokenCriteria = []*protobuf.TokenCriteria{
|
|
&protobuf.TokenCriteria{
|
|
ContractAddresses: contractAddresses,
|
|
Symbol: "STT",
|
|
Type: protobuf.CommunityTokenType_ERC20,
|
|
Name: "Status Test Token",
|
|
Amount: "1.000000000000000000",
|
|
Decimals: decimals,
|
|
},
|
|
}
|
|
|
|
var viewOnlyPermissions = []*CommunityTokenPermission{
|
|
&CommunityTokenPermission{
|
|
CommunityTokenPermission: &protobuf.CommunityTokenPermission{
|
|
Id: "some-id",
|
|
Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
|
|
TokenCriteria: tokenCriteria,
|
|
ChatIds: []string{"test-channel-id", "test-channel-id-2"},
|
|
},
|
|
},
|
|
}
|
|
|
|
var viewAndPostPermissions = make([]*CommunityTokenPermission, 0)
|
|
|
|
tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), 0)
|
|
resp, err := m.checkChannelPermissions(viewOnlyPermissions, viewAndPostPermissions, accountChainIDsCombination, false)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(resp)
|
|
|
|
s.Require().False(resp.ViewOnlyPermissions.Satisfied)
|
|
// if viewOnly permissions are not satisfied then viewAndPost
|
|
// permissions shouldn't be satisfied either
|
|
s.Require().False(resp.ViewAndPostPermissions.Satisfied)
|
|
|
|
// Set response to exactly the right one
|
|
tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), int64(1*math.Pow(10, float64(decimals))))
|
|
resp, err = m.checkChannelPermissions(viewOnlyPermissions, viewAndPostPermissions, accountChainIDsCombination, false)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(resp)
|
|
|
|
s.Require().True(resp.ViewOnlyPermissions.Satisfied)
|
|
s.Require().False(resp.ViewAndPostPermissions.Satisfied)
|
|
}
|
|
|
|
func (s *ManagerSuite) TestCheckChannelPermissions_ViewAndPostPermissions() {
|
|
|
|
m, _, tm := s.setupManagerForTokenPermissions()
|
|
|
|
var chainID uint64 = 5
|
|
contractAddresses := make(map[uint64]string)
|
|
contractAddresses[chainID] = "0x3d6afaa395c31fcd391fe3d562e75fe9e8ec7e6a"
|
|
var decimals uint64 = 18
|
|
|
|
accountChainIDsCombination := []*AccountChainIDsCombination{
|
|
&AccountChainIDsCombination{
|
|
Address: gethcommon.HexToAddress("0xD6b912e09E797D291E8D0eA3D3D17F8000e01c32"),
|
|
ChainIDs: []uint64{chainID},
|
|
},
|
|
}
|
|
|
|
var tokenCriteria = []*protobuf.TokenCriteria{
|
|
&protobuf.TokenCriteria{
|
|
ContractAddresses: contractAddresses,
|
|
Symbol: "STT",
|
|
Type: protobuf.CommunityTokenType_ERC20,
|
|
Name: "Status Test Token",
|
|
Amount: "1.000000000000000000",
|
|
Decimals: decimals,
|
|
},
|
|
}
|
|
|
|
var viewAndPostPermissions = []*CommunityTokenPermission{
|
|
&CommunityTokenPermission{
|
|
CommunityTokenPermission: &protobuf.CommunityTokenPermission{
|
|
Id: "some-id",
|
|
Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
|
|
TokenCriteria: tokenCriteria,
|
|
ChatIds: []string{"test-channel-id", "test-channel-id-2"},
|
|
},
|
|
},
|
|
}
|
|
|
|
var viewOnlyPermissions = make([]*CommunityTokenPermission, 0)
|
|
|
|
tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), 0)
|
|
resp, err := m.checkChannelPermissions(viewOnlyPermissions, viewAndPostPermissions, accountChainIDsCombination, false)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(resp)
|
|
|
|
s.Require().False(resp.ViewAndPostPermissions.Satisfied)
|
|
// viewOnly permissions are flagged as not satisfied because we have no viewOnly
|
|
// permissions on this channel and the viewAndPost permission is not satisfied either
|
|
s.Require().False(resp.ViewOnlyPermissions.Satisfied)
|
|
|
|
// Set response to exactly the right one
|
|
tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), int64(1*math.Pow(10, float64(decimals))))
|
|
resp, err = m.checkChannelPermissions(viewOnlyPermissions, viewAndPostPermissions, accountChainIDsCombination, false)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(resp)
|
|
|
|
s.Require().True(resp.ViewAndPostPermissions.Satisfied)
|
|
// if viewAndPost is satisfied then viewOnly should be automatically satisfied
|
|
s.Require().True(resp.ViewOnlyPermissions.Satisfied)
|
|
}
|
|
|
|
func (s *ManagerSuite) TestCheckChannelPermissions_ViewAndPostPermissionsCombination() {
|
|
|
|
m, _, tm := s.setupManagerForTokenPermissions()
|
|
|
|
var chainID uint64 = 5
|
|
contractAddresses := make(map[uint64]string)
|
|
contractAddresses[chainID] = "0x3d6afaa395c31fcd391fe3d562e75fe9e8ec7e6a"
|
|
var decimals uint64 = 18
|
|
|
|
accountChainIDsCombination := []*AccountChainIDsCombination{
|
|
&AccountChainIDsCombination{
|
|
Address: gethcommon.HexToAddress("0xD6b912e09E797D291E8D0eA3D3D17F8000e01c32"),
|
|
ChainIDs: []uint64{chainID},
|
|
},
|
|
}
|
|
|
|
var viewOnlyTokenCriteria = []*protobuf.TokenCriteria{
|
|
&protobuf.TokenCriteria{
|
|
ContractAddresses: contractAddresses,
|
|
Symbol: "STT",
|
|
Type: protobuf.CommunityTokenType_ERC20,
|
|
Name: "Status Test Token",
|
|
Amount: "1.000000000000000000",
|
|
Decimals: decimals,
|
|
},
|
|
}
|
|
|
|
var viewOnlyPermissions = []*CommunityTokenPermission{
|
|
&CommunityTokenPermission{
|
|
CommunityTokenPermission: &protobuf.CommunityTokenPermission{
|
|
Id: "some-id",
|
|
Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
|
|
TokenCriteria: viewOnlyTokenCriteria,
|
|
ChatIds: []string{"test-channel-id", "test-channel-id-2"},
|
|
},
|
|
},
|
|
}
|
|
|
|
testContractAddresses := make(map[uint64]string)
|
|
testContractAddresses[chainID] = "0x123"
|
|
|
|
// Set up token criteria that won't be satisfied
|
|
var viewAndPostTokenCriteria = []*protobuf.TokenCriteria{
|
|
&protobuf.TokenCriteria{
|
|
ContractAddresses: testContractAddresses,
|
|
Symbol: "TEST",
|
|
Type: protobuf.CommunityTokenType_ERC20,
|
|
Name: "TEST token",
|
|
Amount: "1.000000000000000000",
|
|
Decimals: decimals,
|
|
},
|
|
}
|
|
|
|
var viewAndPostPermissions = []*CommunityTokenPermission{
|
|
&CommunityTokenPermission{
|
|
CommunityTokenPermission: &protobuf.CommunityTokenPermission{
|
|
Id: "some-id",
|
|
Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
|
|
TokenCriteria: viewAndPostTokenCriteria,
|
|
ChatIds: []string{"test-channel-id", "test-channel-id-2"},
|
|
},
|
|
},
|
|
}
|
|
|
|
// Set response for viewOnly permissions
|
|
tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), int64(1*math.Pow(10, float64(decimals))))
|
|
// Set resopnse for viewAndPost permissions
|
|
tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(testContractAddresses[chainID]), 0)
|
|
|
|
resp, err := m.checkChannelPermissions(viewOnlyPermissions, viewAndPostPermissions, accountChainIDsCombination, false)
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(resp)
|
|
|
|
// viewOnly permission should be satisfied, even though viewAndPost is not satisfied
|
|
s.Require().True(resp.ViewOnlyPermissions.Satisfied)
|
|
s.Require().False(resp.ViewAndPostPermissions.Satisfied)
|
|
}
|
|
|
|
func (s *ManagerSuite) TestCheckAllChannelsPermissions_EmptyPermissions() {
|
|
|
|
m, _, _ := s.setupManagerForTokenPermissions()
|
|
|
|
createRequest := &requests.CreateCommunity{
|
|
Name: "channel permission community",
|
|
Description: "some description",
|
|
Membership: protobuf.CommunityPermissions_AUTO_ACCEPT,
|
|
}
|
|
community, err := m.CreateCommunity(createRequest, true)
|
|
s.Require().NoError(err)
|
|
|
|
// create community chats
|
|
chat := &protobuf.CommunityChat{
|
|
Identity: &protobuf.ChatIdentity{
|
|
DisplayName: "chat1",
|
|
Description: "description",
|
|
},
|
|
Permissions: &protobuf.CommunityPermissions{
|
|
Access: protobuf.CommunityPermissions_AUTO_ACCEPT,
|
|
},
|
|
Members: make(map[string]*protobuf.CommunityMember),
|
|
}
|
|
|
|
changes, err := m.CreateChat(community.ID(), chat, true, "")
|
|
s.Require().NoError(err)
|
|
|
|
var chatID string
|
|
for cid := range changes.ChatsAdded {
|
|
chatID = community.IDString() + cid
|
|
}
|
|
|
|
response, err := m.CheckAllChannelsPermissions(community.ID(), []gethcommon.Address{
|
|
gethcommon.HexToAddress("0xD6b912e09E797D291E8D0eA3D3D17F8000e01c32"),
|
|
})
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(response)
|
|
|
|
s.Require().Len(response.Channels, 1)
|
|
// we expect both, viewOnly and viewAndPost permissions to be satisfied
|
|
// as there aren't any permissions on this channel
|
|
s.Require().True(response.Channels[chatID].ViewOnlyPermissions.Satisfied)
|
|
s.Require().True(response.Channels[chatID].ViewAndPostPermissions.Satisfied)
|
|
s.Require().Len(response.Channels[chatID].ViewOnlyPermissions.Permissions, 0)
|
|
s.Require().Len(response.Channels[chatID].ViewAndPostPermissions.Permissions, 0)
|
|
}
|
|
|
|
func (s *ManagerSuite) TestCheckAllChannelsPermissions() {
|
|
|
|
m, _, tm := s.setupManagerForTokenPermissions()
|
|
|
|
var chatID1 string
|
|
var chatID2 string
|
|
|
|
// create community
|
|
createRequest := &requests.CreateCommunity{
|
|
Name: "channel permission community",
|
|
Description: "some description",
|
|
Membership: protobuf.CommunityPermissions_AUTO_ACCEPT,
|
|
}
|
|
community, err := m.CreateCommunity(createRequest, true)
|
|
s.Require().NoError(err)
|
|
|
|
// create first community chat
|
|
chat := &protobuf.CommunityChat{
|
|
Identity: &protobuf.ChatIdentity{
|
|
DisplayName: "chat1",
|
|
Description: "description",
|
|
},
|
|
Permissions: &protobuf.CommunityPermissions{
|
|
Access: protobuf.CommunityPermissions_AUTO_ACCEPT,
|
|
},
|
|
Members: make(map[string]*protobuf.CommunityMember),
|
|
}
|
|
|
|
changes, err := m.CreateChat(community.ID(), chat, true, "")
|
|
s.Require().NoError(err)
|
|
|
|
for chatID := range changes.ChatsAdded {
|
|
chatID1 = community.IDString() + chatID
|
|
}
|
|
|
|
// create second community chat
|
|
chat = &protobuf.CommunityChat{
|
|
Identity: &protobuf.ChatIdentity{
|
|
DisplayName: "chat2",
|
|
Description: "description",
|
|
},
|
|
Permissions: &protobuf.CommunityPermissions{
|
|
Access: protobuf.CommunityPermissions_AUTO_ACCEPT,
|
|
},
|
|
Members: make(map[string]*protobuf.CommunityMember),
|
|
}
|
|
|
|
changes, err = m.CreateChat(community.ID(), chat, true, "")
|
|
s.Require().NoError(err)
|
|
|
|
for chatID := range changes.ChatsAdded {
|
|
chatID2 = community.IDString() + chatID
|
|
}
|
|
|
|
var chainID uint64 = 5
|
|
contractAddresses := make(map[uint64]string)
|
|
contractAddresses[chainID] = "0x3d6afaa395c31fcd391fe3d562e75fe9e8ec7e6a"
|
|
var decimals uint64 = 18
|
|
|
|
accountChainIDsCombination := []*AccountChainIDsCombination{
|
|
&AccountChainIDsCombination{
|
|
Address: gethcommon.HexToAddress("0xD6b912e09E797D291E8D0eA3D3D17F8000e01c32"),
|
|
ChainIDs: []uint64{chainID},
|
|
},
|
|
}
|
|
|
|
var tokenCriteria = []*protobuf.TokenCriteria{
|
|
&protobuf.TokenCriteria{
|
|
ContractAddresses: contractAddresses,
|
|
Symbol: "STT",
|
|
Type: protobuf.CommunityTokenType_ERC20,
|
|
Name: "Status Test Token",
|
|
Amount: "1.000000000000000000",
|
|
Decimals: decimals,
|
|
},
|
|
}
|
|
|
|
// create view only permission
|
|
viewOnlyPermission := &requests.CreateCommunityTokenPermission{
|
|
CommunityID: community.ID(),
|
|
Type: protobuf.CommunityTokenPermission_CAN_VIEW_CHANNEL,
|
|
TokenCriteria: tokenCriteria,
|
|
ChatIds: []string{chatID1, chatID2},
|
|
}
|
|
|
|
_, changes, err = m.CreateCommunityTokenPermission(viewOnlyPermission)
|
|
s.Require().NoError(err)
|
|
|
|
var viewOnlyPermissionID string
|
|
for permissionID := range changes.TokenPermissionsAdded {
|
|
viewOnlyPermissionID = permissionID
|
|
}
|
|
|
|
response, err := m.CheckAllChannelsPermissions(community.ID(), []gethcommon.Address{
|
|
gethcommon.HexToAddress("0xD6b912e09E797D291E8D0eA3D3D17F8000e01c32"),
|
|
})
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(response)
|
|
|
|
// we've added to chats to the community, so there should be 2 items
|
|
s.Require().Len(response.Channels, 2)
|
|
|
|
// viewOnly permissions should not be satisfied because the account doesn't
|
|
// have the necessary funds
|
|
|
|
// channel1
|
|
s.Require().False(response.Channels[chatID1].ViewOnlyPermissions.Satisfied)
|
|
s.Require().Len(response.Channels[chatID1].ViewOnlyPermissions.Permissions, 1)
|
|
s.Require().Len(response.Channels[chatID1].ViewOnlyPermissions.Permissions[viewOnlyPermissionID].Criteria, 1)
|
|
s.Require().False(response.Channels[chatID1].ViewOnlyPermissions.Permissions[viewOnlyPermissionID].Criteria[0])
|
|
|
|
// channel2
|
|
s.Require().False(response.Channels[chatID2].ViewOnlyPermissions.Satisfied)
|
|
s.Require().Len(response.Channels[chatID2].ViewOnlyPermissions.Permissions, 1)
|
|
s.Require().Len(response.Channels[chatID2].ViewOnlyPermissions.Permissions[viewOnlyPermissionID].Criteria, 1)
|
|
s.Require().False(response.Channels[chatID2].ViewOnlyPermissions.Permissions[viewOnlyPermissionID].Criteria[0])
|
|
|
|
// viewAndPost permissions are flagged as not satisfied either because
|
|
// viewOnly permission is not satisfied and there are no viewAndPost permissions
|
|
|
|
// channel1
|
|
s.Require().False(response.Channels[chatID1].ViewAndPostPermissions.Satisfied)
|
|
s.Require().Len(response.Channels[chatID1].ViewAndPostPermissions.Permissions, 0)
|
|
|
|
// channel2
|
|
s.Require().False(response.Channels[chatID2].ViewAndPostPermissions.Satisfied)
|
|
s.Require().Len(response.Channels[chatID2].ViewAndPostPermissions.Permissions, 0)
|
|
|
|
// now change balance such that viewOnly permission should be satisfied
|
|
tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), int64(1*math.Pow(10, float64(decimals))))
|
|
|
|
response, err = m.CheckAllChannelsPermissions(community.ID(), []gethcommon.Address{
|
|
gethcommon.HexToAddress("0xD6b912e09E797D291E8D0eA3D3D17F8000e01c32"),
|
|
})
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(response)
|
|
s.Require().Len(response.Channels, 2)
|
|
|
|
// viewOnly permissions should be satisfied for both channels while
|
|
// viewAndPost permissions should not be satisfied (as there aren't any)
|
|
|
|
// channel1
|
|
s.Require().True(response.Channels[chatID1].ViewOnlyPermissions.Satisfied)
|
|
s.Require().Len(response.Channels[chatID1].ViewOnlyPermissions.Permissions, 1)
|
|
s.Require().Len(response.Channels[chatID1].ViewOnlyPermissions.Permissions[viewOnlyPermissionID].Criteria, 1)
|
|
s.Require().True(response.Channels[chatID1].ViewOnlyPermissions.Permissions[viewOnlyPermissionID].Criteria[0])
|
|
|
|
s.Require().False(response.Channels[chatID1].ViewAndPostPermissions.Satisfied)
|
|
s.Require().Len(response.Channels[chatID1].ViewAndPostPermissions.Permissions, 0)
|
|
|
|
// channel2
|
|
s.Require().True(response.Channels[chatID2].ViewOnlyPermissions.Satisfied)
|
|
s.Require().Len(response.Channels[chatID2].ViewOnlyPermissions.Permissions, 1)
|
|
s.Require().Len(response.Channels[chatID2].ViewOnlyPermissions.Permissions[viewOnlyPermissionID].Criteria, 1)
|
|
s.Require().True(response.Channels[chatID2].ViewOnlyPermissions.Permissions[viewOnlyPermissionID].Criteria[0])
|
|
|
|
s.Require().False(response.Channels[chatID2].ViewAndPostPermissions.Satisfied)
|
|
s.Require().Len(response.Channels[chatID2].ViewAndPostPermissions.Permissions, 0)
|
|
|
|
// next, create viewAndPost permission
|
|
// create view only permission
|
|
viewAndPostPermission := &requests.CreateCommunityTokenPermission{
|
|
CommunityID: community.ID(),
|
|
Type: protobuf.CommunityTokenPermission_CAN_VIEW_AND_POST_CHANNEL,
|
|
TokenCriteria: tokenCriteria,
|
|
ChatIds: []string{chatID1, chatID2},
|
|
}
|
|
|
|
_, changes, err = m.CreateCommunityTokenPermission(viewAndPostPermission)
|
|
s.Require().NoError(err)
|
|
|
|
var viewAndPostPermissionID string
|
|
for permissionID := range changes.TokenPermissionsAdded {
|
|
viewAndPostPermissionID = permissionID
|
|
}
|
|
|
|
// now change balance such that viewAndPost permission is not satisfied
|
|
tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), 0)
|
|
|
|
response, err = m.CheckAllChannelsPermissions(community.ID(), []gethcommon.Address{
|
|
gethcommon.HexToAddress("0xD6b912e09E797D291E8D0eA3D3D17F8000e01c32"),
|
|
})
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(response)
|
|
s.Require().Len(response.Channels, 2)
|
|
|
|
// Both, viewOnly and viewAndPost permissions exist on channel1 and channel2
|
|
// but shouldn't be satisfied
|
|
|
|
// channel1
|
|
s.Require().False(response.Channels[chatID1].ViewOnlyPermissions.Satisfied)
|
|
s.Require().Len(response.Channels[chatID1].ViewOnlyPermissions.Permissions, 1)
|
|
s.Require().Len(response.Channels[chatID1].ViewOnlyPermissions.Permissions[viewOnlyPermissionID].Criteria, 1)
|
|
s.Require().False(response.Channels[chatID1].ViewOnlyPermissions.Permissions[viewOnlyPermissionID].Criteria[0])
|
|
|
|
s.Require().False(response.Channels[chatID1].ViewAndPostPermissions.Satisfied)
|
|
s.Require().Len(response.Channels[chatID1].ViewAndPostPermissions.Permissions, 1)
|
|
s.Require().Len(response.Channels[chatID1].ViewAndPostPermissions.Permissions[viewAndPostPermissionID].Criteria, 1)
|
|
s.Require().False(response.Channels[chatID1].ViewAndPostPermissions.Permissions[viewAndPostPermissionID].Criteria[0])
|
|
|
|
// channel2
|
|
s.Require().False(response.Channels[chatID2].ViewOnlyPermissions.Satisfied)
|
|
s.Require().Len(response.Channels[chatID2].ViewOnlyPermissions.Permissions, 1)
|
|
s.Require().Len(response.Channels[chatID2].ViewOnlyPermissions.Permissions[viewOnlyPermissionID].Criteria, 1)
|
|
s.Require().False(response.Channels[chatID2].ViewOnlyPermissions.Permissions[viewOnlyPermissionID].Criteria[0])
|
|
|
|
s.Require().False(response.Channels[chatID2].ViewAndPostPermissions.Satisfied)
|
|
s.Require().Len(response.Channels[chatID2].ViewAndPostPermissions.Permissions, 1)
|
|
s.Require().Len(response.Channels[chatID2].ViewAndPostPermissions.Permissions[viewAndPostPermissionID].Criteria, 1)
|
|
s.Require().False(response.Channels[chatID2].ViewAndPostPermissions.Permissions[viewAndPostPermissionID].Criteria[0])
|
|
|
|
// now change balance such that both, viewOnly and viewAndPost permission, are satisfied
|
|
tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), int64(1*math.Pow(10, float64(decimals))))
|
|
|
|
response, err = m.CheckAllChannelsPermissions(community.ID(), []gethcommon.Address{
|
|
gethcommon.HexToAddress("0xD6b912e09E797D291E8D0eA3D3D17F8000e01c32"),
|
|
})
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(response)
|
|
s.Require().Len(response.Channels, 2)
|
|
|
|
// Both, viewOnly and viewAndPost permissions exist on channel1 and channel2
|
|
// and are satisfied
|
|
|
|
// channel1
|
|
s.Require().True(response.Channels[chatID1].ViewOnlyPermissions.Satisfied)
|
|
s.Require().Len(response.Channels[chatID1].ViewOnlyPermissions.Permissions, 1)
|
|
s.Require().Len(response.Channels[chatID1].ViewOnlyPermissions.Permissions[viewOnlyPermissionID].Criteria, 1)
|
|
s.Require().True(response.Channels[chatID1].ViewOnlyPermissions.Permissions[viewOnlyPermissionID].Criteria[0])
|
|
|
|
s.Require().True(response.Channels[chatID1].ViewAndPostPermissions.Satisfied)
|
|
s.Require().Len(response.Channels[chatID1].ViewAndPostPermissions.Permissions, 1)
|
|
s.Require().Len(response.Channels[chatID1].ViewAndPostPermissions.Permissions[viewAndPostPermissionID].Criteria, 1)
|
|
s.Require().True(response.Channels[chatID1].ViewAndPostPermissions.Permissions[viewAndPostPermissionID].Criteria[0])
|
|
|
|
// channel2
|
|
s.Require().True(response.Channels[chatID2].ViewOnlyPermissions.Satisfied)
|
|
s.Require().Len(response.Channels[chatID2].ViewOnlyPermissions.Permissions, 1)
|
|
s.Require().Len(response.Channels[chatID2].ViewOnlyPermissions.Permissions[viewOnlyPermissionID].Criteria, 1)
|
|
s.Require().True(response.Channels[chatID2].ViewOnlyPermissions.Permissions[viewOnlyPermissionID].Criteria[0])
|
|
|
|
s.Require().True(response.Channels[chatID2].ViewAndPostPermissions.Satisfied)
|
|
s.Require().Len(response.Channels[chatID2].ViewAndPostPermissions.Permissions, 1)
|
|
s.Require().Len(response.Channels[chatID2].ViewAndPostPermissions.Permissions[viewAndPostPermissionID].Criteria, 1)
|
|
s.Require().True(response.Channels[chatID2].ViewAndPostPermissions.Permissions[viewAndPostPermissionID].Criteria[0])
|
|
|
|
// next, delete viewOnly permission so we can check the viewAndPost permission-only case
|
|
deleteViewOnlyPermission := &requests.DeleteCommunityTokenPermission{
|
|
CommunityID: community.ID(),
|
|
PermissionID: viewOnlyPermissionID,
|
|
}
|
|
_, _, err = m.DeleteCommunityTokenPermission(deleteViewOnlyPermission)
|
|
s.Require().NoError(err)
|
|
|
|
response, err = m.CheckAllChannelsPermissions(community.ID(), []gethcommon.Address{
|
|
gethcommon.HexToAddress("0xD6b912e09E797D291E8D0eA3D3D17F8000e01c32"),
|
|
})
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(response)
|
|
s.Require().Len(response.Channels, 2)
|
|
|
|
// Both, channel1 and channel2 now have viewAndPost only permissions that should
|
|
// be satisfied, there's no viewOnly permission anymore the response should mark it
|
|
// as satisfied as well
|
|
|
|
// channel1
|
|
s.Require().True(response.Channels[chatID1].ViewAndPostPermissions.Satisfied)
|
|
s.Require().Len(response.Channels[chatID1].ViewAndPostPermissions.Permissions, 1)
|
|
s.Require().Len(response.Channels[chatID1].ViewAndPostPermissions.Permissions[viewAndPostPermissionID].Criteria, 1)
|
|
s.Require().True(response.Channels[chatID1].ViewAndPostPermissions.Permissions[viewAndPostPermissionID].Criteria[0])
|
|
|
|
s.Require().True(response.Channels[chatID1].ViewOnlyPermissions.Satisfied)
|
|
s.Require().Len(response.Channels[chatID1].ViewOnlyPermissions.Permissions, 0)
|
|
|
|
// channel2
|
|
s.Require().True(response.Channels[chatID2].ViewAndPostPermissions.Satisfied)
|
|
s.Require().Len(response.Channels[chatID2].ViewAndPostPermissions.Permissions, 1)
|
|
s.Require().Len(response.Channels[chatID2].ViewAndPostPermissions.Permissions[viewAndPostPermissionID].Criteria, 1)
|
|
s.Require().True(response.Channels[chatID2].ViewAndPostPermissions.Permissions[viewAndPostPermissionID].Criteria[0])
|
|
|
|
s.Require().True(response.Channels[chatID2].ViewOnlyPermissions.Satisfied)
|
|
s.Require().Len(response.Channels[chatID2].ViewOnlyPermissions.Permissions, 0)
|
|
|
|
// now change balance such that viewAndPost permission is no longer satisfied
|
|
tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), 0)
|
|
|
|
response, err = m.CheckAllChannelsPermissions(community.ID(), []gethcommon.Address{
|
|
gethcommon.HexToAddress("0xD6b912e09E797D291E8D0eA3D3D17F8000e01c32"),
|
|
})
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(response)
|
|
s.Require().Len(response.Channels, 2)
|
|
|
|
// because viewAndPost permission is not satisfied and there are no viewOnly permissions
|
|
// on the channels, the response should mark the viewOnly permissions as not satisfied as well
|
|
|
|
// channel1
|
|
s.Require().False(response.Channels[chatID1].ViewAndPostPermissions.Satisfied)
|
|
s.Require().Len(response.Channels[chatID1].ViewAndPostPermissions.Permissions, 1)
|
|
s.Require().Len(response.Channels[chatID1].ViewAndPostPermissions.Permissions[viewAndPostPermissionID].Criteria, 1)
|
|
s.Require().False(response.Channels[chatID1].ViewAndPostPermissions.Permissions[viewAndPostPermissionID].Criteria[0])
|
|
|
|
s.Require().False(response.Channels[chatID1].ViewOnlyPermissions.Satisfied)
|
|
s.Require().Len(response.Channels[chatID1].ViewOnlyPermissions.Permissions, 0)
|
|
|
|
// channel2
|
|
s.Require().False(response.Channels[chatID2].ViewAndPostPermissions.Satisfied)
|
|
s.Require().Len(response.Channels[chatID2].ViewAndPostPermissions.Permissions, 1)
|
|
s.Require().Len(response.Channels[chatID2].ViewAndPostPermissions.Permissions[viewAndPostPermissionID].Criteria, 1)
|
|
s.Require().False(response.Channels[chatID2].ViewAndPostPermissions.Permissions[viewAndPostPermissionID].Criteria[0])
|
|
|
|
s.Require().False(response.Channels[chatID2].ViewOnlyPermissions.Satisfied)
|
|
s.Require().Len(response.Channels[chatID2].ViewOnlyPermissions.Permissions, 0)
|
|
}
|
|
|
|
func buildTorrentConfig() params.TorrentConfig {
|
|
torrentConfig := params.TorrentConfig{
|
|
Enabled: true,
|
|
DataDir: os.TempDir() + "/archivedata",
|
|
TorrentDir: os.TempDir() + "/torrents",
|
|
Port: 0,
|
|
}
|
|
return torrentConfig
|
|
}
|
|
|
|
func buildMessage(timestamp time.Time, topic types.TopicType, hash []byte) types.Message {
|
|
message := types.Message{
|
|
Sig: []byte{1},
|
|
Timestamp: uint32(timestamp.Unix()),
|
|
Topic: topic,
|
|
Payload: []byte{1},
|
|
Padding: []byte{1},
|
|
Hash: hash,
|
|
}
|
|
return message
|
|
}
|
|
|
|
func (s *ManagerSuite) buildCommunityWithChat() (*Community, string, error) {
|
|
createRequest := &requests.CreateCommunity{
|
|
Name: "status",
|
|
Description: "status community description",
|
|
Membership: protobuf.CommunityPermissions_AUTO_ACCEPT,
|
|
}
|
|
community, err := s.manager.CreateCommunity(createRequest, true)
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
chat := &protobuf.CommunityChat{
|
|
Identity: &protobuf.ChatIdentity{
|
|
DisplayName: "added-chat",
|
|
Description: "description",
|
|
},
|
|
Permissions: &protobuf.CommunityPermissions{
|
|
Access: protobuf.CommunityPermissions_AUTO_ACCEPT,
|
|
},
|
|
Members: make(map[string]*protobuf.CommunityMember),
|
|
}
|
|
changes, err := s.manager.CreateChat(community.ID(), chat, true, "")
|
|
if err != nil {
|
|
return nil, "", err
|
|
}
|
|
|
|
chatID := ""
|
|
for cID := range changes.ChatsAdded {
|
|
chatID = cID
|
|
break
|
|
}
|
|
return community, chatID, nil
|
|
}
|
|
|
|
type testOwnerVerifier struct {
|
|
called int
|
|
ownersMap map[string]string
|
|
}
|
|
|
|
func (t *testOwnerVerifier) SafeGetSignerPubKey(ctx context.Context, chainID uint64, communityID string) (string, error) {
|
|
t.called++
|
|
return t.ownersMap[communityID], nil
|
|
}
|
|
|
|
func (s *ManagerSuite) TestCommunityQueue() {
|
|
|
|
owner, err := crypto.GenerateKey()
|
|
s.Require().NoError(err)
|
|
|
|
verifier := &testOwnerVerifier{}
|
|
m := s.buildManager(verifier)
|
|
|
|
createRequest := &requests.CreateCommunity{
|
|
Name: "status",
|
|
Description: "status community description",
|
|
Membership: protobuf.CommunityPermissions_AUTO_ACCEPT,
|
|
}
|
|
community, err := s.manager.CreateCommunity(createRequest, true)
|
|
s.Require().NoError(err)
|
|
|
|
// set verifier public key
|
|
verifier.ownersMap = make(map[string]string)
|
|
verifier.ownersMap[community.IDString()] = common.PubkeyToHex(&owner.PublicKey)
|
|
|
|
description := community.config.CommunityDescription
|
|
|
|
// safety check
|
|
s.Require().Equal(uint64(0), CommunityDescriptionTokenOwnerChainID(description))
|
|
|
|
// set up permissions
|
|
description.TokenPermissions = make(map[string]*protobuf.CommunityTokenPermission)
|
|
description.TokenPermissions["some-id"] = &protobuf.CommunityTokenPermission{
|
|
Type: protobuf.CommunityTokenPermission_BECOME_TOKEN_OWNER,
|
|
Id: "some-token-id",
|
|
TokenCriteria: []*protobuf.TokenCriteria{
|
|
&protobuf.TokenCriteria{
|
|
ContractAddresses: map[uint64]string{
|
|
2: "some-address",
|
|
},
|
|
},
|
|
}}
|
|
|
|
// Should have now a token owner
|
|
s.Require().Equal(uint64(2), CommunityDescriptionTokenOwnerChainID(description))
|
|
|
|
payload, err := community.MarshaledDescription()
|
|
s.Require().NoError(err)
|
|
|
|
payload, err = v1.WrapMessageV1(payload, protobuf.ApplicationMetadataMessage_COMMUNITY_DESCRIPTION, owner)
|
|
s.Require().NoError(err)
|
|
|
|
// Create a signer, that is not the owner
|
|
notTheOwner, err := crypto.GenerateKey()
|
|
s.Require().NoError(err)
|
|
|
|
subscription := m.Subscribe()
|
|
|
|
response, err := m.HandleCommunityDescriptionMessage(¬TheOwner.PublicKey, description, payload, nil, nil)
|
|
s.Require().NoError(err)
|
|
|
|
// No response, as it should be queued
|
|
s.Require().Nil(response)
|
|
|
|
published := false
|
|
|
|
for !published {
|
|
select {
|
|
case event := <-subscription:
|
|
if event.TokenCommunityValidated == nil {
|
|
continue
|
|
}
|
|
published = true
|
|
case <-time.After(2 * time.Second):
|
|
s.FailNow("no subscription")
|
|
}
|
|
}
|
|
|
|
// Check it's not called multiple times
|
|
s.Require().Equal(1, verifier.called)
|
|
// Cleans up the communities to validate
|
|
communitiesToValidate, err := m.persistence.getCommunitiesToValidate()
|
|
s.Require().NoError(err)
|
|
s.Require().Empty(communitiesToValidate)
|
|
}
|
|
|
|
// 1) We create a community
|
|
// 2) We have 2 owners, but only new owner is returned by the contract
|
|
// 3) We receive the old owner community first
|
|
// 4) We receive the new owner community second
|
|
// 5) We start the queue
|
|
// 6) We should only process 4, and ignore anything else if that is successful, as that's the most recent
|
|
|
|
func (s *ManagerSuite) TestCommunityQueueMultipleDifferentSigners() {
|
|
|
|
newOwner, err := crypto.GenerateKey()
|
|
s.Require().NoError(err)
|
|
|
|
oldOwner, err := crypto.GenerateKey()
|
|
s.Require().NoError(err)
|
|
|
|
verifier := &testOwnerVerifier{}
|
|
m := s.buildManager(verifier)
|
|
|
|
createRequest := &requests.CreateCommunity{
|
|
Name: "status",
|
|
Description: "status community description",
|
|
Membership: protobuf.CommunityPermissions_AUTO_ACCEPT,
|
|
}
|
|
community, err := s.manager.CreateCommunity(createRequest, true)
|
|
s.Require().NoError(err)
|
|
|
|
// set verifier public key
|
|
verifier.ownersMap = make(map[string]string)
|
|
verifier.ownersMap[community.IDString()] = common.PubkeyToHex(&newOwner.PublicKey)
|
|
|
|
description := community.config.CommunityDescription
|
|
|
|
// safety check
|
|
s.Require().Equal(uint64(0), CommunityDescriptionTokenOwnerChainID(description))
|
|
|
|
// set up permissions
|
|
description.TokenPermissions = make(map[string]*protobuf.CommunityTokenPermission)
|
|
description.TokenPermissions["some-id"] = &protobuf.CommunityTokenPermission{
|
|
Type: protobuf.CommunityTokenPermission_BECOME_TOKEN_OWNER,
|
|
Id: "some-token-id",
|
|
TokenCriteria: []*protobuf.TokenCriteria{
|
|
&protobuf.TokenCriteria{
|
|
ContractAddresses: map[uint64]string{
|
|
2: "some-address",
|
|
},
|
|
},
|
|
}}
|
|
|
|
// Should have now a token owner
|
|
s.Require().Equal(uint64(2), CommunityDescriptionTokenOwnerChainID(description))
|
|
|
|
// We nil owner verifier so that messages won't be processed
|
|
m.ownerVerifier = nil
|
|
|
|
// Send message from old owner first
|
|
|
|
payload, err := community.MarshaledDescription()
|
|
s.Require().NoError(err)
|
|
|
|
payload, err = v1.WrapMessageV1(payload, protobuf.ApplicationMetadataMessage_COMMUNITY_DESCRIPTION, oldOwner)
|
|
s.Require().NoError(err)
|
|
|
|
subscription := m.Subscribe()
|
|
|
|
response, err := m.HandleCommunityDescriptionMessage(&oldOwner.PublicKey, description, payload, nil, nil)
|
|
s.Require().NoError(err)
|
|
|
|
// No response, as it should be queued
|
|
s.Require().Nil(response)
|
|
|
|
// Send message from new owner now
|
|
|
|
community.config.CommunityDescription.Clock++
|
|
|
|
clock2 := community.config.CommunityDescription.Clock
|
|
|
|
payload, err = community.MarshaledDescription()
|
|
s.Require().NoError(err)
|
|
|
|
payload, err = v1.WrapMessageV1(payload, protobuf.ApplicationMetadataMessage_COMMUNITY_DESCRIPTION, newOwner)
|
|
s.Require().NoError(err)
|
|
|
|
response, err = m.HandleCommunityDescriptionMessage(&newOwner.PublicKey, description, payload, nil, nil)
|
|
s.Require().NoError(err)
|
|
|
|
// No response, as it should be queued
|
|
s.Require().Nil(response)
|
|
|
|
count, err := m.persistence.getCommunitiesToValidateCount()
|
|
s.Require().NoError(err)
|
|
s.Require().Equal(2, count)
|
|
|
|
communitiesToValidate, err := m.persistence.getCommunitiesToValidate()
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(communitiesToValidate)
|
|
s.Require().NotNil(communitiesToValidate[community.IDString()])
|
|
s.Require().Len(communitiesToValidate[community.IDString()], 2)
|
|
|
|
// We set owner verifier so that we start processing the queue
|
|
m.ownerVerifier = verifier
|
|
|
|
published := false
|
|
|
|
for !published {
|
|
select {
|
|
case event := <-subscription:
|
|
if event.TokenCommunityValidated == nil {
|
|
continue
|
|
}
|
|
published = true
|
|
case <-time.After(2 * time.Second):
|
|
s.FailNow("no subscription")
|
|
}
|
|
}
|
|
|
|
// Check it's not called multiple times, since we should be checking newest first
|
|
s.Require().Equal(1, verifier.called)
|
|
// Cleans up the communities to validate
|
|
communitiesToValidate, err = m.persistence.getCommunitiesToValidate()
|
|
s.Require().NoError(err)
|
|
s.Require().Empty(communitiesToValidate)
|
|
|
|
// Check clock of community is of the last community description
|
|
fetchedCommunity, err := m.GetByID(community.ID())
|
|
s.Require().NoError(err)
|
|
s.Require().Equal(clock2, fetchedCommunity.config.CommunityDescription.Clock)
|
|
|
|
}
|
|
|
|
// 1) We create a community
|
|
// 2) We have 2 owners, but only old owner is returned by the contract
|
|
// 3) We receive the old owner community first
|
|
// 4) We receive the new owner community second (that could be a malicious user)
|
|
// 5) We start the queue
|
|
// 6) We should process both, but ignore the last community description
|
|
|
|
func (s *ManagerSuite) TestCommunityQueueMultipleDifferentSignersIgnoreIfNotReturned() {
|
|
|
|
newOwner, err := crypto.GenerateKey()
|
|
s.Require().NoError(err)
|
|
|
|
oldOwner, err := crypto.GenerateKey()
|
|
s.Require().NoError(err)
|
|
|
|
verifier := &testOwnerVerifier{}
|
|
m := s.buildManager(verifier)
|
|
|
|
createRequest := &requests.CreateCommunity{
|
|
Name: "status",
|
|
Description: "status community description",
|
|
Membership: protobuf.CommunityPermissions_AUTO_ACCEPT,
|
|
}
|
|
community, err := s.manager.CreateCommunity(createRequest, true)
|
|
s.Require().NoError(err)
|
|
|
|
// set verifier public key
|
|
verifier.ownersMap = make(map[string]string)
|
|
verifier.ownersMap[community.IDString()] = common.PubkeyToHex(&oldOwner.PublicKey)
|
|
|
|
description := community.config.CommunityDescription
|
|
|
|
// safety check
|
|
s.Require().Equal(uint64(0), CommunityDescriptionTokenOwnerChainID(description))
|
|
|
|
// set up permissions
|
|
description.TokenPermissions = make(map[string]*protobuf.CommunityTokenPermission)
|
|
description.TokenPermissions["some-id"] = &protobuf.CommunityTokenPermission{
|
|
Type: protobuf.CommunityTokenPermission_BECOME_TOKEN_OWNER,
|
|
Id: "some-token-id",
|
|
TokenCriteria: []*protobuf.TokenCriteria{
|
|
&protobuf.TokenCriteria{
|
|
ContractAddresses: map[uint64]string{
|
|
2: "some-address",
|
|
},
|
|
},
|
|
}}
|
|
|
|
// Should have now a token owner
|
|
s.Require().Equal(uint64(2), CommunityDescriptionTokenOwnerChainID(description))
|
|
|
|
// We nil owner verifier so that messages won't be processed
|
|
m.ownerVerifier = nil
|
|
|
|
clock1 := community.config.CommunityDescription.Clock
|
|
// Send message from old owner first
|
|
|
|
payload, err := community.MarshaledDescription()
|
|
s.Require().NoError(err)
|
|
|
|
payload, err = v1.WrapMessageV1(payload, protobuf.ApplicationMetadataMessage_COMMUNITY_DESCRIPTION, oldOwner)
|
|
s.Require().NoError(err)
|
|
|
|
subscription := m.Subscribe()
|
|
|
|
response, err := m.HandleCommunityDescriptionMessage(&oldOwner.PublicKey, description, payload, nil, nil)
|
|
s.Require().NoError(err)
|
|
|
|
// No response, as it should be queued
|
|
s.Require().Nil(response)
|
|
|
|
// Send message from new owner now
|
|
|
|
community.config.CommunityDescription.Clock++
|
|
|
|
payload, err = community.MarshaledDescription()
|
|
s.Require().NoError(err)
|
|
|
|
payload, err = v1.WrapMessageV1(payload, protobuf.ApplicationMetadataMessage_COMMUNITY_DESCRIPTION, newOwner)
|
|
s.Require().NoError(err)
|
|
|
|
response, err = m.HandleCommunityDescriptionMessage(&newOwner.PublicKey, description, payload, nil, nil)
|
|
s.Require().NoError(err)
|
|
|
|
// No response, as it should be queued
|
|
s.Require().Nil(response)
|
|
|
|
count, err := m.persistence.getCommunitiesToValidateCount()
|
|
s.Require().NoError(err)
|
|
s.Require().Equal(2, count)
|
|
|
|
communitiesToValidate, err := m.persistence.getCommunitiesToValidate()
|
|
s.Require().NoError(err)
|
|
s.Require().NotNil(communitiesToValidate)
|
|
s.Require().NotNil(communitiesToValidate[community.IDString()])
|
|
s.Require().Len(communitiesToValidate[community.IDString()], 2)
|
|
|
|
// We set owner verifier so that we start processing the queue
|
|
m.ownerVerifier = verifier
|
|
|
|
published := false
|
|
|
|
for !published {
|
|
select {
|
|
case event := <-subscription:
|
|
if event.TokenCommunityValidated == nil {
|
|
continue
|
|
}
|
|
published = true
|
|
case <-time.After(2 * time.Second):
|
|
s.FailNow("no subscription")
|
|
}
|
|
}
|
|
|
|
// Check it's not called multiple times, since we should be checking newest first
|
|
s.Require().Equal(2, verifier.called)
|
|
// Cleans up the communities to validate
|
|
communitiesToValidate, err = m.persistence.getCommunitiesToValidate()
|
|
s.Require().NoError(err)
|
|
s.Require().Empty(communitiesToValidate)
|
|
|
|
// Check clock of community is of the first community description
|
|
fetchedCommunity, err := m.GetByID(community.ID())
|
|
s.Require().NoError(err)
|
|
s.Require().Equal(clock1, fetchedCommunity.config.CommunityDescription.Clock)
|
|
}
|