chore: Added TokenMaster permission token type (#3848)

This commit is contained in:
Mykhailo Prakhov 2023-08-04 12:28:46 +02:00 committed by GitHub
parent 078f71a235
commit 3bd972dec4
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 994 additions and 607 deletions

File diff suppressed because it is too large Load Diff

View File

@ -836,11 +836,25 @@ func (o *Community) RemoveUserFromOrg(pk *ecdsa.PublicKey) (*protobuf.CommunityD
func (o *Community) AddCommunityTokensMetadata(token *protobuf.CommunityTokenMetadata) (*protobuf.CommunityDescription, error) { func (o *Community) AddCommunityTokensMetadata(token *protobuf.CommunityTokenMetadata) (*protobuf.CommunityDescription, error) {
o.mutex.Lock() o.mutex.Lock()
defer o.mutex.Unlock() defer o.mutex.Unlock()
if !o.IsControlNode() {
return nil, ErrNotControlNode isControlNode := o.IsControlNode()
allowedToSendTokenEvents := o.IsOwnerWithoutCommunityKey() || o.IsTokenMaster()
if !isControlNode && !allowedToSendTokenEvents {
return nil, ErrInvalidManageTokensPermission
} }
if allowedToSendTokenEvents {
err := o.addNewCommunityEvent(o.ToAddTokenMetadataCommunityEvent(token))
if err != nil {
return nil, err
}
}
o.config.CommunityDescription.CommunityTokensMetadata = append(o.config.CommunityDescription.CommunityTokensMetadata, token) o.config.CommunityDescription.CommunityTokensMetadata = append(o.config.CommunityDescription.CommunityTokensMetadata, token)
o.increaseClock()
if isControlNode {
o.increaseClock()
}
return o.config.CommunityDescription, nil return o.config.CommunityDescription, nil
} }
@ -1151,6 +1165,10 @@ func (o *Community) IsOwnerWithoutCommunityKey() bool {
return o.config.PrivateKey == nil && o.IsMemberOwner(o.config.MemberIdentity) return o.config.PrivateKey == nil && o.IsMemberOwner(o.config.MemberIdentity)
} }
func (o *Community) IsTokenMaster() bool {
return o.IsMemberTokenMaster(o.config.MemberIdentity)
}
func (o *Community) GetPrivilegedMembers() []*ecdsa.PublicKey { func (o *Community) GetPrivilegedMembers() []*ecdsa.PublicKey {
privilegedMembers := make([]*ecdsa.PublicKey, 0) privilegedMembers := make([]*ecdsa.PublicKey, 0)
members := o.GetMemberPubkeys() members := o.GetMemberPubkeys()

View File

@ -173,6 +173,14 @@ func (o *Community) ToCommunityRequestToJoinRejectCommunityEvent(changes *Commun
} }
} }
func (o *Community) ToAddTokenMetadataCommunityEvent(tokenMetadata *protobuf.CommunityTokenMetadata) *CommunityEvent {
return &CommunityEvent{
CommunityEventClock: o.NewCommunityEventClock(),
Type: protobuf.CommunityEvent_COMMUNITY_TOKEN_ADD,
TokenMetadata: tokenMetadata,
}
}
func (o *Community) UpdateCommunityByEvents(communityEventMessage *CommunityEventsMessage) (*CommunityChanges, error) { func (o *Community) UpdateCommunityByEvents(communityEventMessage *CommunityEventsMessage) (*CommunityChanges, error) {
o.mutex.Lock() o.mutex.Lock()
defer o.mutex.Unlock() defer o.mutex.Unlock()
@ -364,6 +372,9 @@ func (o *Community) updateCommunityDescriptionByCommunityEvent(communityEvent Co
return err return err
} }
o.unbanUserFromCommunity(pk) o.unbanUserFromCommunity(pk)
case protobuf.CommunityEvent_COMMUNITY_TOKEN_ADD:
o.config.CommunityDescription.CommunityTokensMetadata = append(o.config.CommunityDescription.CommunityTokensMetadata, communityEvent.TokenMetadata)
} }
return nil return nil
} }

View File

@ -22,6 +22,7 @@ type CommunityEvent struct {
MembersAdded map[string]*protobuf.CommunityMember `json:"membersAdded,omitempty"` MembersAdded map[string]*protobuf.CommunityMember `json:"membersAdded,omitempty"`
RejectedRequestsToJoin map[string]*protobuf.CommunityRequestToJoin `json:"rejectedRequestsToJoin,omitempty"` RejectedRequestsToJoin map[string]*protobuf.CommunityRequestToJoin `json:"rejectedRequestsToJoin,omitempty"`
AcceptedRequestsToJoin map[string]*protobuf.CommunityRequestToJoin `json:"acceptedRequestsToJoin,omitempty"` AcceptedRequestsToJoin map[string]*protobuf.CommunityRequestToJoin `json:"acceptedRequestsToJoin,omitempty"`
TokenMetadata *protobuf.CommunityTokenMetadata `json:"tokenMetadata,omitempty"`
RawPayload []byte `json:"rawPayload"` RawPayload []byte `json:"rawPayload"`
} }
@ -37,6 +38,7 @@ func (e *CommunityEvent) ToProtobuf() *protobuf.CommunityEvent {
MembersAdded: e.MembersAdded, MembersAdded: e.MembersAdded,
RejectedRequestsToJoin: e.RejectedRequestsToJoin, RejectedRequestsToJoin: e.RejectedRequestsToJoin,
AcceptedRequestsToJoin: e.AcceptedRequestsToJoin, AcceptedRequestsToJoin: e.AcceptedRequestsToJoin,
TokenMetadata: e.TokenMetadata,
} }
} }
@ -59,6 +61,7 @@ func CommunityEventFromProtobuf(raw []byte) (*CommunityEvent, error) {
MembersAdded: decodedEvent.MembersAdded, MembersAdded: decodedEvent.MembersAdded,
RejectedRequestsToJoin: decodedEvent.RejectedRequestsToJoin, RejectedRequestsToJoin: decodedEvent.RejectedRequestsToJoin,
AcceptedRequestsToJoin: decodedEvent.AcceptedRequestsToJoin, AcceptedRequestsToJoin: decodedEvent.AcceptedRequestsToJoin,
TokenMetadata: decodedEvent.TokenMetadata,
RawPayload: encodedEvent, RawPayload: encodedEvent,
}, nil }, nil
} }
@ -212,6 +215,11 @@ func validateCommunityEvent(communityEvent *CommunityEvent) error {
if len(communityEvent.MemberToAction) == 0 { if len(communityEvent.MemberToAction) == 0 {
return errors.New("invalid community member unban event") return errors.New("invalid community member unban event")
} }
case protobuf.CommunityEvent_COMMUNITY_TOKEN_ADD:
if communityEvent.TokenMetadata == nil || len(communityEvent.TokenMetadata.ContractAddresses) == 0 {
return errors.New("invalid add community token event")
}
} }
return nil return nil
} }

View File

@ -40,3 +40,4 @@ var ErrMemberWalletNotFound = errors.New("member wallet not found")
var ErrNotEnoughPermissions = errors.New("not enough permissions for this community") var ErrNotEnoughPermissions = errors.New("not enough permissions for this community")
var ErrCannotRemoveOwnerOrAdmin = errors.New("not allowed to remove admin or owner") var ErrCannotRemoveOwnerOrAdmin = errors.New("not allowed to remove admin or owner")
var ErrCannotBanOwnerOrAdmin = errors.New("not allowed to ban admin or owner") var ErrCannotBanOwnerOrAdmin = errors.New("not allowed to ban admin or owner")
var ErrInvalidManageTokensPermission = errors.New("no privileges to manage tokens")

View File

@ -620,12 +620,12 @@ func (m *Manager) EditCommunityTokenPermission(request *requests.EditCommunityTo
return community, changes, nil return community, changes, nil
} }
func (m *Manager) ReevaluateMembers(community *Community, removeAdmins bool) error { func (m *Manager) ReevaluateMembers(community *Community) error {
becomeMemberPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_MEMBER) becomeMemberPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_MEMBER)
becomeAdminPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_ADMIN) becomeAdminPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_ADMIN)
becomeTokenMasterPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER)
hasMemberPermissions := len(becomeMemberPermissions) > 0 hasMemberPermissions := len(becomeMemberPermissions) > 0
hasAdminPermissions := len(becomeAdminPermissions) > 0
for memberKey, member := range community.Members() { for memberKey, member := range community.Members() {
memberPubKey, err := common.HexToPubkey(memberKey) memberPubKey, err := common.HexToPubkey(memberKey)
@ -637,12 +637,13 @@ func (m *Manager) ReevaluateMembers(community *Community, removeAdmins bool) err
continue continue
} }
isTokenMaster := community.IsMemberTokenMaster(memberPubKey)
isAdmin := community.IsMemberAdmin(memberPubKey) isAdmin := community.IsMemberAdmin(memberPubKey)
memberHasWallet := len(member.RevealedAccounts) > 0 memberHasWallet := len(member.RevealedAccounts) > 0
// Check if user was not treated as an admin without wallet in open community // Check if user has privilege role without sharing the account to controlNode
// or user treated as a member without wallet in closed community // or user treated as a member without wallet in closed community
if !memberHasWallet && (hasMemberPermissions || isAdmin) { if !memberHasWallet && (hasMemberPermissions || isAdmin || isTokenMaster) {
_, err = community.RemoveUserFromOrg(memberPubKey) _, err = community.RemoveUserFromOrg(memberPubKey)
if err != nil { if err != nil {
return err return err
@ -652,49 +653,32 @@ func (m *Manager) ReevaluateMembers(community *Community, removeAdmins bool) err
accountsAndChainIDs := revealedAccountsToAccountsAndChainIDsCombination(member.RevealedAccounts) accountsAndChainIDs := revealedAccountsToAccountsAndChainIDsCombination(member.RevealedAccounts)
// Check if user is still an admin or can become an admin and do update of member role isTokenMaster, err = m.ReevaluatePrivelegedMember(community, becomeTokenMasterPermissions, accountsAndChainIDs, memberPubKey,
removeAdminRole := false protobuf.CommunityMember_ROLE_TOKEN_MASTER, isTokenMaster)
if hasAdminPermissions {
permissionResponse, err := m.checkPermissionToJoin(becomeAdminPermissions, accountsAndChainIDs, true) if err != nil {
if err != nil { return err
return err
} else if permissionResponse.Satisfied && !isAdmin {
_, err = community.AddRoleToMember(memberPubKey, protobuf.CommunityMember_ROLE_ADMIN)
if err != nil {
return err
}
isAdmin = true
} else if !permissionResponse.Satisfied && isAdmin {
removeAdminRole = true
isAdmin = false
}
} }
// Remove admin role if user does not pass admin permissions or we do not have admin permissions but have an admin role if isTokenMaster {
if removeAdminRole || isAdmin && removeAdmins { // Skip further validation if user has TokenMaster permissions
_, err = community.RemoveRoleFromMember(memberPubKey, protobuf.CommunityMember_ROLE_ADMIN) continue
if err != nil { }
return err
} isAdmin, err = m.ReevaluatePrivelegedMember(community, becomeAdminPermissions, accountsAndChainIDs, memberPubKey,
isAdmin = false protobuf.CommunityMember_ROLE_ADMIN, isAdmin)
if err != nil {
return err
} }
if isAdmin { if isAdmin {
// Make sure admin is added to every channel // Skip further validation if user has Admin permissions
for channelID := range community.Chats() {
if !community.IsMemberInChat(memberPubKey, channelID) {
_, err = community.AddMemberToChat(channelID, memberPubKey, []protobuf.CommunityMember_Roles{protobuf.CommunityMember_ROLE_ADMIN})
if err != nil {
return err
}
}
}
// Skip further validation if user has admin permissions
continue continue
} }
if hasMemberPermissions { if hasMemberPermissions {
permissionResponse, err := m.checkPermissionToJoin(becomeMemberPermissions, accountsAndChainIDs, true) permissionResponse, err := m.checkPermissions(becomeMemberPermissions, accountsAndChainIDs, true)
if err != nil { if err != nil {
return err return err
} }
@ -776,7 +760,7 @@ func (m *Manager) ReevaluateMembersPeriodically(communityID types.HexBytes) {
m.periodicMembersReevaluationTasks.Delete(communityID.String()) m.periodicMembersReevaluationTasks.Delete(communityID.String())
} }
err = m.ReevaluateMembers(community, true) err = m.ReevaluateMembers(community)
if err != nil { if err != nil {
m.logger.Debug("failed to check member permissions", zap.Error(err)) m.logger.Debug("failed to check member permissions", zap.Error(err))
} }
@ -1525,8 +1509,10 @@ func (m *Manager) CheckPermissionToJoin(id []byte, addresses []gethcommon.Addres
becomeAdminPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_ADMIN) becomeAdminPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_ADMIN)
becomeMemberPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_MEMBER) becomeMemberPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_MEMBER)
becomeTokenMasterPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER)
permissionsToJoin := append(becomeAdminPermissions, becomeMemberPermissions...) permissionsToJoin := append(becomeAdminPermissions, becomeMemberPermissions...)
permissionsToJoin = append(permissionsToJoin, becomeTokenMasterPermissions...)
allChainIDs, err := m.tokenManager.GetAllChainIDs() allChainIDs, err := m.tokenManager.GetAllChainIDs()
if err != nil { if err != nil {
@ -1545,28 +1531,32 @@ func (m *Manager) CheckPermissionToJoin(id []byte, addresses []gethcommon.Addres
} }
return response, nil return response, nil
} }
return m.checkPermissionToJoin(permissionsToJoin, accountsAndChainIDs, false) return m.checkPermissions(permissionsToJoin, accountsAndChainIDs, false)
} }
func (m *Manager) accountsSatisfyPermissionsToJoin(community *Community, accounts []*protobuf.RevealedAccount) (satisfy bool, isAdmin bool, err error) { func (m *Manager) accountsSatisfyPermissionsToJoin(community *Community, accounts []*protobuf.RevealedAccount) (bool, protobuf.CommunityMember_Roles, error) {
accountsAndChainIDs := revealedAccountsToAccountsAndChainIDsCombination(accounts) accountsAndChainIDs := revealedAccountsToAccountsAndChainIDsCombination(accounts)
becomeAdminPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_ADMIN) becomeAdminPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_ADMIN)
becomeMemberPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_MEMBER) becomeMemberPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_MEMBER)
becomeTokenMasterPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER)
if len(becomeAdminPermissions) > 0 && m.accountsHasAdminPermission(becomeAdminPermissions, accountsAndChainIDs) { if m.accountsHasPrivilegedPermission(becomeTokenMasterPermissions, accountsAndChainIDs) {
return true, true, nil return true, protobuf.CommunityMember_ROLE_TOKEN_MASTER, nil
}
if m.accountsHasPrivilegedPermission(becomeAdminPermissions, accountsAndChainIDs) {
return true, protobuf.CommunityMember_ROLE_ADMIN, nil
} }
if len(becomeMemberPermissions) > 0 { if len(becomeMemberPermissions) > 0 {
permissionResponse, err := m.checkPermissionToJoin(becomeMemberPermissions, accountsAndChainIDs, true) permissionResponse, err := m.checkPermissions(becomeMemberPermissions, accountsAndChainIDs, true)
if err != nil { if err != nil {
return false, false, err return false, protobuf.CommunityMember_ROLE_NONE, err
} }
return permissionResponse.Satisfied, false, nil return permissionResponse.Satisfied, protobuf.CommunityMember_ROLE_NONE, nil
} }
return true, false, nil return true, protobuf.CommunityMember_ROLE_NONE, nil
} }
func (m *Manager) accountsSatisfyPermissionsToJoinChannels(community *Community, accounts []*protobuf.RevealedAccount) (map[string]*protobuf.CommunityChat, error) { func (m *Manager) accountsSatisfyPermissionsToJoinChannels(community *Community, accounts []*protobuf.RevealedAccount) (map[string]*protobuf.CommunityChat, error) {
@ -1611,7 +1601,7 @@ func (m *Manager) AcceptRequestToJoin(request *requests.AcceptRequestToJoinCommu
return nil, err return nil, err
} }
permissionsSatisfied, isAdmin, err := m.accountsSatisfyPermissionsToJoin(community, revealedAccounts) permissionsSatisfied, role, err := m.accountsSatisfyPermissionsToJoin(community, revealedAccounts)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1621,8 +1611,8 @@ func (m *Manager) AcceptRequestToJoin(request *requests.AcceptRequestToJoinCommu
} }
memberRoles := []protobuf.CommunityMember_Roles{} memberRoles := []protobuf.CommunityMember_Roles{}
if isAdmin { if role != protobuf.CommunityMember_ROLE_NONE {
memberRoles = []protobuf.CommunityMember_Roles{protobuf.CommunityMember_ROLE_ADMIN} memberRoles = []protobuf.CommunityMember_Roles{role}
} }
pk, err := common.HexToPubkey(dbRequest.PublicKey) pk, err := common.HexToPubkey(dbRequest.PublicKey)
@ -1906,7 +1896,7 @@ type AccountChainIDsCombination struct {
ChainIDs []uint64 `json:"chainIds"` ChainIDs []uint64 `json:"chainIds"`
} }
func (c *CheckPermissionToJoinResponse) calculateSatisfied() { func (c *CheckPermissionsResponse) calculateSatisfied() {
if len(c.Permissions) == 0 { if len(c.Permissions) == 0 {
c.Satisfied = true c.Satisfied = true
return return
@ -1945,10 +1935,6 @@ func calculateChainIDsSet(accountsAndChainIDs []*AccountChainIDsCombination, req
return revealedAccountsChainIDs return revealedAccountsChainIDs
} }
func (m *Manager) checkPermissionToJoin(permissions []*protobuf.CommunityTokenPermission, accountsAndChainIDs []*AccountChainIDsCombination, shortcircuit bool) (*CheckPermissionToJoinResponse, error) {
return m.checkPermissions(permissions, accountsAndChainIDs, shortcircuit)
}
// checkPermissions will retrieve balances and check whether the user has // checkPermissions will retrieve balances and check whether the user has
// permission to join the community, if shortcircuit is true, it will stop as soon // permission to join the community, if shortcircuit is true, it will stop as soon
// as we know the answer // as we know the answer
@ -4065,11 +4051,11 @@ func revealedAccountsToAccountsAndChainIDsCombination(revealedAccounts []*protob
return accountsAndChainIDs return accountsAndChainIDs
} }
func (m *Manager) accountsHasAdminPermission(becomeAdminPermissions []*protobuf.CommunityTokenPermission, accounts []*AccountChainIDsCombination) bool { func (m *Manager) accountsHasPrivilegedPermission(privilegedPermissions []*protobuf.CommunityTokenPermission, accounts []*AccountChainIDsCombination) bool {
if len(becomeAdminPermissions) > 0 { if len(privilegedPermissions) > 0 {
permissionResponse, err := m.checkPermissionToJoin(becomeAdminPermissions, accounts, true) permissionResponse, err := m.checkPermissions(privilegedPermissions, accounts, true)
if err != nil { if err != nil {
m.logger.Warn("check admin permission failed: %v", zap.Error(err)) m.logger.Warn("check privileged permission failed: %v", zap.Error(err))
return false return false
} }
return permissionResponse.Satisfied return permissionResponse.Satisfied
@ -4129,3 +4115,51 @@ func (m *Manager) fixupChannelMembers() error {
return nil return nil
} }
func (m *Manager) ReevaluatePrivelegedMember(community *Community, tokenPermissions []*protobuf.CommunityTokenPermission,
accountsAndChainIDs []*AccountChainIDsCombination, memberPubKey *ecdsa.PublicKey,
privilegedRole protobuf.CommunityMember_Roles, alreadyHasPrivilegedRole bool) (bool, error) {
hasPrivilegedRolePermissions := len(tokenPermissions) > 0
removeCurrentRole := false
if hasPrivilegedRolePermissions {
permissionResponse, err := m.checkPermissions(tokenPermissions, accountsAndChainIDs, true)
if err != nil {
return alreadyHasPrivilegedRole, err
} else if permissionResponse.Satisfied && !alreadyHasPrivilegedRole {
_, err = community.AddRoleToMember(memberPubKey, privilegedRole)
if err != nil {
return alreadyHasPrivilegedRole, err
}
alreadyHasPrivilegedRole = true
} else if !permissionResponse.Satisfied && alreadyHasPrivilegedRole {
removeCurrentRole = true
alreadyHasPrivilegedRole = false
}
}
// Remove privileged role if user does not pass role permissions check or
// Community does not have permissions but user has a role
if removeCurrentRole || (!hasPrivilegedRolePermissions && alreadyHasPrivilegedRole) {
_, err := community.RemoveRoleFromMember(memberPubKey, privilegedRole)
if err != nil {
return alreadyHasPrivilegedRole, err
}
alreadyHasPrivilegedRole = false
}
if alreadyHasPrivilegedRole {
// Make sure privileged user is added to every channel
for channelID := range community.Chats() {
if !community.IsMemberInChat(memberPubKey, channelID) {
_, err := community.AddMemberToChat(channelID, memberPubKey, []protobuf.CommunityMember_Roles{privilegedRole})
if err != nil {
return alreadyHasPrivilegedRole, err
}
}
}
}
return alreadyHasPrivilegedRole, nil
}

View File

@ -200,14 +200,14 @@ func (s *ManagerSuite) TestRetrieveTokens() {
} }
// Set response to exactly the right one // Set response to exactly the right one
tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), int64(1*math.Pow(10, float64(decimals)))) tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), int64(1*math.Pow(10, float64(decimals))))
resp, err := m.checkPermissionToJoin(permissions, accountChainIDsCombination, false) resp, err := m.checkPermissions(permissions, accountChainIDsCombination, false)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NotNil(resp) s.Require().NotNil(resp)
s.Require().True(resp.Satisfied) s.Require().True(resp.Satisfied)
// Set response to 0 // Set response to 0
tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), 0) tm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), 0)
resp, err = m.checkPermissionToJoin(permissions, accountChainIDsCombination, false) resp, err = m.checkPermissions(permissions, accountChainIDsCombination, false)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NotNil(resp) s.Require().NotNil(resp)
s.Require().False(resp.Satisfied) s.Require().False(resp.Satisfied)
@ -249,7 +249,7 @@ func (s *ManagerSuite) TestRetrieveCollectibles() {
// Set response to exactly the right one // Set response to exactly the right one
tokenBalances = []thirdparty.TokenBalance{tokenBalance(tokenID, 1)} tokenBalances = []thirdparty.TokenBalance{tokenBalance(tokenID, 1)}
cm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), tokenBalances) cm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), tokenBalances)
resp, err := m.checkPermissionToJoin(permissions, accountChainIDsCombination, false) resp, err := m.checkPermissions(permissions, accountChainIDsCombination, false)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NotNil(resp) s.Require().NotNil(resp)
s.Require().True(resp.Satisfied) s.Require().True(resp.Satisfied)
@ -257,7 +257,7 @@ func (s *ManagerSuite) TestRetrieveCollectibles() {
// Set balances to 0 // Set balances to 0
tokenBalances = []thirdparty.TokenBalance{} tokenBalances = []thirdparty.TokenBalance{}
cm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), tokenBalances) cm.setResponse(chainID, accountChainIDsCombination[0].Address, gethcommon.HexToAddress(contractAddresses[chainID]), tokenBalances)
resp, err = m.checkPermissionToJoin(permissions, accountChainIDsCombination, false) resp, err = m.checkPermissions(permissions, accountChainIDsCombination, false)
s.Require().NoError(err) s.Require().NoError(err)
s.Require().NotNil(resp) s.Require().NotNil(resp)
s.Require().False(resp.Satisfied) s.Require().False(resp.Satisfied)

View File

@ -264,14 +264,9 @@ func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestOwnerPinMessage() {
testEventSenderPinMessage(s, community) testEventSenderPinMessage(s, community)
} }
func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestOwnerMintToken() { func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestOwnerAddCommunityToken() {
setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER) community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER)
// TODO owner test: Mint Tokens (rescticted) testEventSenderAddedCommunityToken(s, community)
}
func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestOwnerAirdropTokens() {
setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_OWNER)
// TODO owner test: Airdrop Tokens (restricted)
} }
func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestMemberReceiveOwnerEventsWhenControlNodeOffline() { func (s *OwnerWithoutCommunityKeyCommunityEventsSuite) TestMemberReceiveOwnerEventsWhenControlNodeOffline() {

View File

@ -188,14 +188,9 @@ func (s *TokenMasterCommunityEventsSuite) TestTokenMasterPinMessage() {
testEventSenderPinMessage(s, community) testEventSenderPinMessage(s, community)
} }
func (s *TokenMasterCommunityEventsSuite) TestTokenMasterMintToken() { func (s *TokenMasterCommunityEventsSuite) TestTokenMasterAddCommunityToken() {
setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_TOKEN_MASTER) community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_TOKEN_MASTER)
// TODO TokenMaster test: Mint Tokens testEventSenderAddedCommunityToken(s, community)
}
func (s *TokenMasterCommunityEventsSuite) TestTokenMasterAirdropTokens() {
setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_TOKEN_MASTER)
// TODO TokenMaster test: Airdrop Tokens
} }
func (s *TokenMasterCommunityEventsSuite) TestMemberReceiveTokenMasterEventsWhenControlNodeOffline() { func (s *TokenMasterCommunityEventsSuite) TestMemberReceiveTokenMasterEventsWhenControlNodeOffline() {

View File

@ -3,6 +3,7 @@ package protocol
import ( import (
"context" "context"
"errors" "errors"
"math/big"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
@ -11,6 +12,7 @@ import (
"github.com/status-im/status-go/protocol/communities" "github.com/status-im/status-go/protocol/communities"
"github.com/status-im/status-go/protocol/protobuf" "github.com/status-im/status-go/protocol/protobuf"
"github.com/status-im/status-go/protocol/requests" "github.com/status-im/status-go/protocol/requests"
"github.com/status-im/status-go/services/wallet/bigint"
) )
type CommunityEventsTestsInterface interface { type CommunityEventsTestsInterface interface {
@ -1229,3 +1231,46 @@ func testEventSenderCannotEditBecomeAdminPermission(base CommunityEventsTestsInt
s.Require().Error(err) s.Require().Error(err)
s.Require().Nil(response) s.Require().Nil(response)
} }
func testEventSenderAddedCommunityToken(base CommunityEventsTestsInterface, community *communities.Community) {
tokenERC721 := &communities.CommunityToken{
CommunityID: community.IDString(),
TokenType: protobuf.CommunityTokenType_ERC721,
Address: "0x123",
Name: "StatusToken",
Symbol: "STT",
Description: "desc",
Supply: &bigint.BigInt{Int: big.NewInt(123)},
InfiniteSupply: false,
Transferable: true,
RemoteSelfDestruct: true,
ChainID: 1,
DeployState: communities.Deployed,
Base64Image: "ABCD",
}
s := base.GetSuite()
_, err := base.GetEventSender().SaveCommunityToken(tokenERC721, nil)
s.Require().NoError(err)
err = base.GetEventSender().AddCommunityToken(tokenERC721.CommunityID, tokenERC721.ChainID, tokenERC721.Address)
s.Require().NoError(err)
checkTokenAdded := func(response *MessengerResponse) error {
modifiedCommmunity, err := getModifiedCommunity(response, community.IDString())
if err != nil {
return err
}
for _, tokenMetadata := range modifiedCommmunity.CommunityTokensMetadata() {
if tokenMetadata.Name == tokenERC721.Name {
return nil
}
}
return errors.New("Token was not found")
}
checkClientsReceivedAdminEvent(base, WaitCommunityCondition, checkTokenAdded)
}

View File

@ -2,6 +2,7 @@ package protocol
import ( import (
"crypto/ecdsa" "crypto/ecdsa"
"math/big"
"testing" "testing"
"github.com/stretchr/testify/suite" "github.com/stretchr/testify/suite"
@ -11,8 +12,10 @@ import (
"github.com/status-im/status-go/eth-node/crypto" "github.com/status-im/status-go/eth-node/crypto"
"github.com/status-im/status-go/eth-node/types" "github.com/status-im/status-go/eth-node/types"
"github.com/status-im/status-go/protocol/common" "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/protobuf"
"github.com/status-im/status-go/protocol/tt" "github.com/status-im/status-go/protocol/tt"
"github.com/status-im/status-go/services/wallet/bigint"
"github.com/status-im/status-go/waku" "github.com/status-im/status-go/waku"
) )
@ -191,14 +194,30 @@ func (s *AdminCommunityEventsSuite) TestAdminPinMessage() {
testEventSenderPinMessage(s, community) testEventSenderPinMessage(s, community)
} }
func (s *AdminCommunityEventsSuite) TestAdminMintToken() { func (s *AdminCommunityEventsSuite) TestAdminAddCommunityToken() {
setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN) community := setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN)
// TODO admin test: Mint Tokens (rescticted)
}
func (s *AdminCommunityEventsSuite) TestAdminAirdropTokens() { tokenERC721 := &communities.CommunityToken{
setUpCommunityAndRoles(s, protobuf.CommunityMember_ROLE_ADMIN) CommunityID: community.IDString(),
// TODO admin test: Airdrop Tokens (restricted) TokenType: protobuf.CommunityTokenType_ERC721,
Address: "0x123",
Name: "StatusToken",
Symbol: "STT",
Description: "desc",
Supply: &bigint.BigInt{Int: big.NewInt(123)},
InfiniteSupply: false,
Transferable: true,
RemoteSelfDestruct: true,
ChainID: 1,
DeployState: communities.Deployed,
Base64Image: "ABCD",
}
_, err := s.admin.SaveCommunityToken(tokenERC721, nil)
s.Require().NoError(err)
err = s.admin.AddCommunityToken(tokenERC721.CommunityID, tokenERC721.ChainID, tokenERC721.Address)
s.Require().Error(err)
} }
func (s *AdminCommunityEventsSuite) TestMemberReceiveAdminEventsWhenOwnerOffline() { func (s *AdminCommunityEventsSuite) TestMemberReceiveAdminEventsWhenOwnerOffline() {

View File

@ -3,6 +3,7 @@ package protocol
import ( import (
"bytes" "bytes"
"context" "context"
"crypto/ecdsa"
"errors" "errors"
"math/big" "math/big"
"sync" "sync"
@ -898,7 +899,7 @@ func (s *MessengerCommunitiesTokenPermissionsSuite) TestViewChannelPermissions()
// in production it will happen automatically, by periodic check // in production it will happen automatically, by periodic check
community, err = s.owner.communitiesManager.GetByID(community.ID()) community, err = s.owner.communitiesManager.GetByID(community.ID())
s.Require().NoError(err) s.Require().NoError(err)
err = s.owner.communitiesManager.ReevaluateMembers(community, true) err = s.owner.communitiesManager.ReevaluateMembers(community)
s.Require().NoError(err) s.Require().NoError(err)
err = <-waitOnChannelKeyToBeDistributedToBob err = <-waitOnChannelKeyToBeDistributedToBob
@ -936,3 +937,246 @@ func (s *MessengerCommunitiesTokenPermissionsSuite) TestViewChannelPermissions()
s.Require().Len(response.Messages(), 1) s.Require().Len(response.Messages(), 1)
s.Require().Equal(msg.Text, response.Messages()[0].Text) s.Require().Equal(msg.Text, response.Messages()[0].Text)
} }
func (s *MessengerCommunitiesTokenPermissionsSuite) testReevaluateMemberPrivilegedRoleInOpenCommunity(permissionType protobuf.CommunityTokenPermission_Type) {
community, _ := s.createCommunity()
createTokenPermission := &requests.CreateCommunityTokenPermission{
CommunityID: community.ID(),
Type: permissionType,
TokenCriteria: []*protobuf.TokenCriteria{
&protobuf.TokenCriteria{
Type: protobuf.CommunityTokenType_ERC20,
ContractAddresses: map[uint64]string{testChainID1: "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)
s.Require().True(response.Communities()[0].HasTokenPermissions())
waitOnCommunityPermissionCreated := s.waitOnCommunitiesEvent(s.owner, func(sub *communities.Subscription) bool {
return sub.Community.HasTokenPermissions()
})
err = <-waitOnCommunityPermissionCreated
s.Require().NoError(err)
community, err = s.owner.communitiesManager.GetByID(community.ID())
s.Require().NoError(err)
s.Require().True(community.HasTokenPermissions())
s.advertiseCommunityTo(community, s.alice)
var tokenPermission *protobuf.CommunityTokenPermission
for _, tokenPermission = range community.TokenPermissions() {
break
}
s.makeAddressSatisfyTheCriteria(testChainID1, aliceAddress1, tokenPermission.TokenCriteria[0])
// join community as a privileged user
s.joinCommunity(community, s.alice, alicePassword, []string{aliceAddress1})
community, err = s.owner.communitiesManager.GetByID(community.ID())
s.Require().NoError(err)
s.Require().True(checkRoleBasedOnThePermissionType(permissionType, &s.alice.identity.PublicKey, community))
// the control node re-evaluates the roles of the participants, checking that the privileged user has not lost his role
err = s.owner.communitiesManager.ReevaluateMembers(community)
s.Require().NoError(err)
community, err = s.owner.communitiesManager.GetByID(community.ID())
s.Require().NoError(err)
s.Require().True(checkRoleBasedOnThePermissionType(permissionType, &s.alice.identity.PublicKey, community))
// remove privileged token permission and reevaluate member permissions
deleteTokenPermission := &requests.DeleteCommunityTokenPermission{
CommunityID: community.ID(),
PermissionID: tokenPermission.Id,
}
response, err = s.owner.DeleteCommunityTokenPermission(deleteTokenPermission)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Communities(), 1)
s.Require().False(response.Communities()[0].HasTokenPermissions())
community, err = s.owner.communitiesManager.GetByID(community.ID())
s.Require().NoError(err)
s.Require().False(community.HasTokenPermissions())
err = s.owner.communitiesManager.ReevaluateMembers(community)
s.Require().NoError(err)
community, err = s.owner.communitiesManager.GetByID(community.ID())
s.Require().NoError(err)
s.Require().True(community.HasMember(&s.alice.identity.PublicKey))
s.Require().False(checkRoleBasedOnThePermissionType(permissionType, &s.alice.identity.PublicKey, community))
}
func (s *MessengerCommunitiesTokenPermissionsSuite) TestReevaluateMemberAdminRoleInOpenCommunity() {
s.testReevaluateMemberPrivilegedRoleInOpenCommunity(protobuf.CommunityTokenPermission_BECOME_ADMIN)
}
func (s *MessengerCommunitiesTokenPermissionsSuite) TestReevaluateMemberTokenMasterRoleInOpenCommunity() {
s.testReevaluateMemberPrivilegedRoleInOpenCommunity(protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER)
}
func (s *MessengerCommunitiesTokenPermissionsSuite) testReevaluateMemberPrivilegedRoleInClosedCommunity(permissionType protobuf.CommunityTokenPermission_Type) {
community, _ := s.createCommunity()
createTokenPermission := &requests.CreateCommunityTokenPermission{
CommunityID: community.ID(),
Type: permissionType,
TokenCriteria: []*protobuf.TokenCriteria{
&protobuf.TokenCriteria{
Type: protobuf.CommunityTokenType_ERC20,
ContractAddresses: map[uint64]string{testChainID1: "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)
s.Require().True(response.Communities()[0].HasTokenPermissions())
createTokenMemberPermission := &requests.CreateCommunityTokenPermission{
CommunityID: community.ID(),
Type: protobuf.CommunityTokenPermission_BECOME_MEMBER,
TokenCriteria: []*protobuf.TokenCriteria{
&protobuf.TokenCriteria{
Type: protobuf.CommunityTokenType_ERC20,
ContractAddresses: map[uint64]string{testChainID1: "0x124"},
Symbol: "TEST2",
Amount: "100",
Decimals: uint64(18),
},
},
}
response, err = s.owner.CreateCommunityTokenPermission(createTokenMemberPermission)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Communities(), 1)
s.Require().True(response.Communities()[0].HasTokenPermissions())
waitOnCommunityPermissionCreated := s.waitOnCommunitiesEvent(s.owner, func(sub *communities.Subscription) bool {
return len(sub.Community.TokenPermissions()) == 2
})
err = <-waitOnCommunityPermissionCreated
s.Require().NoError(err)
community, err = s.owner.communitiesManager.GetByID(community.ID())
s.Require().NoError(err)
s.Require().Len(community.TokenPermissions(), 2)
s.advertiseCommunityTo(community, s.alice)
var tokenPermission *protobuf.CommunityTokenPermission
var tokenMemberPermission *protobuf.CommunityTokenPermission
for _, permission := range community.TokenPermissions() {
if permission.Type == protobuf.CommunityTokenPermission_BECOME_MEMBER {
tokenMemberPermission = permission
} else {
tokenPermission = permission
}
}
s.makeAddressSatisfyTheCriteria(testChainID1, aliceAddress1, tokenPermission.TokenCriteria[0])
s.makeAddressSatisfyTheCriteria(testChainID1, aliceAddress1, tokenMemberPermission.TokenCriteria[0])
// join community as a privileged user
s.joinCommunity(community, s.alice, alicePassword, []string{aliceAddress1})
community, err = s.owner.communitiesManager.GetByID(community.ID())
s.Require().NoError(err)
s.Require().True(checkRoleBasedOnThePermissionType(permissionType, &s.alice.identity.PublicKey, community))
// the control node reevaluates the roles of the participants, checking that the privileged user has not lost his role
err = s.owner.communitiesManager.ReevaluateMembers(community)
s.Require().NoError(err)
community, err = s.owner.communitiesManager.GetByID(community.ID())
s.Require().NoError(err)
s.Require().True(checkRoleBasedOnThePermissionType(permissionType, &s.alice.identity.PublicKey, community))
deleteTokenPermission := &requests.DeleteCommunityTokenPermission{
CommunityID: community.ID(),
PermissionID: tokenPermission.Id,
}
// remove privileged token permission and reevaluate member permissions
response, err = s.owner.DeleteCommunityTokenPermission(deleteTokenPermission)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Communities(), 1)
s.Require().Len(response.Communities()[0].TokenPermissions(), 1)
community, err = s.owner.communitiesManager.GetByID(community.ID())
s.Require().NoError(err)
s.Require().Len(response.Communities()[0].TokenPermissions(), 1)
err = s.owner.communitiesManager.ReevaluateMembers(community)
s.Require().NoError(err)
community, err = s.owner.communitiesManager.GetByID(community.ID())
s.Require().NoError(err)
s.Require().True(community.HasMember(&s.alice.identity.PublicKey))
s.Require().False(checkRoleBasedOnThePermissionType(permissionType, &s.alice.identity.PublicKey, community))
// delete member permissions and reevaluate user permissions
deleteMemberTokenPermission := &requests.DeleteCommunityTokenPermission{
CommunityID: community.ID(),
PermissionID: tokenMemberPermission.Id,
}
response, err = s.owner.DeleteCommunityTokenPermission(deleteMemberTokenPermission)
s.Require().NoError(err)
s.Require().NotNil(response)
s.Require().Len(response.Communities(), 1)
s.Require().Len(response.Communities()[0].TokenPermissions(), 0)
community, err = s.owner.communitiesManager.GetByID(community.ID())
s.Require().NoError(err)
s.Require().Len(response.Communities()[0].TokenPermissions(), 0)
err = s.owner.communitiesManager.ReevaluateMembers(community)
s.Require().NoError(err)
community, err = s.owner.communitiesManager.GetByID(community.ID())
s.Require().NoError(err)
s.Require().True(community.HasMember(&s.alice.identity.PublicKey))
s.Require().False(checkRoleBasedOnThePermissionType(permissionType, &s.alice.identity.PublicKey, community))
}
func (s *MessengerCommunitiesTokenPermissionsSuite) TestReevaluateMemberAdminRoleInClosedCommunity() {
s.testReevaluateMemberPrivilegedRoleInClosedCommunity(protobuf.CommunityTokenPermission_BECOME_ADMIN)
}
func (s *MessengerCommunitiesTokenPermissionsSuite) TestReevaluateMemberTokenMasterRoleInClosedCommunity() {
s.testReevaluateMemberPrivilegedRoleInClosedCommunity(protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER)
}
func checkRoleBasedOnThePermissionType(permissionType protobuf.CommunityTokenPermission_Type, member *ecdsa.PublicKey, community *communities.Community) bool {
switch permissionType {
case protobuf.CommunityTokenPermission_BECOME_ADMIN:
return community.IsMemberAdmin(member)
case protobuf.CommunityTokenPermission_BECOME_TOKEN_MASTER:
return community.IsMemberTokenMaster(member)
default:
panic("Unknown permission, please, update the test")
}
}

View File

@ -1708,7 +1708,7 @@ func (m *Messenger) CreateCommunityTokenPermission(request *requests.CreateCommu
if community.IsControlNode() { if community.IsControlNode() {
// check existing member permission once, then check periodically // check existing member permission once, then check periodically
go func() { go func() {
err := m.communitiesManager.ReevaluateMembers(community, true) err := m.communitiesManager.ReevaluateMembers(community)
if err != nil { if err != nil {
m.logger.Debug("failed to check member permissions", zap.Error(err)) m.logger.Debug("failed to check member permissions", zap.Error(err))
} }
@ -1740,7 +1740,7 @@ func (m *Messenger) EditCommunityTokenPermission(request *requests.EditCommunity
// We do this in a separate routine to not block this function // We do this in a separate routine to not block this function
if community.IsControlNode() { if community.IsControlNode() {
go func() { go func() {
err := m.communitiesManager.ReevaluateMembers(community, true) err := m.communitiesManager.ReevaluateMembers(community)
if err != nil { if err != nil {
m.logger.Debug("failed to check member permissions", zap.Error(err)) m.logger.Debug("failed to check member permissions", zap.Error(err))
} }
@ -1768,10 +1768,7 @@ func (m *Messenger) DeleteCommunityTokenPermission(request *requests.DeleteCommu
// We do this in a separate routine to not block this function // We do this in a separate routine to not block this function
if community.IsControlNode() { if community.IsControlNode() {
go func() { go func() {
becomeAdminPermissions := community.TokenPermissionsByType(protobuf.CommunityTokenPermission_BECOME_ADMIN) err = m.communitiesManager.ReevaluateMembers(community)
// Make sure that we remove admins roles if we remove admin permissions
err = m.communitiesManager.ReevaluateMembers(community, len(becomeAdminPermissions) == 0)
if err != nil { if err != nil {
m.logger.Debug("failed to check member permissions", zap.Error(err)) m.logger.Debug("failed to check member permissions", zap.Error(err))
} }

View File

@ -96,6 +96,7 @@ const (
CommunityTokenPermission_BECOME_MEMBER CommunityTokenPermission_Type = 2 CommunityTokenPermission_BECOME_MEMBER CommunityTokenPermission_Type = 2
CommunityTokenPermission_CAN_VIEW_CHANNEL CommunityTokenPermission_Type = 3 CommunityTokenPermission_CAN_VIEW_CHANNEL CommunityTokenPermission_Type = 3
CommunityTokenPermission_CAN_VIEW_AND_POST_CHANNEL CommunityTokenPermission_Type = 4 CommunityTokenPermission_CAN_VIEW_AND_POST_CHANNEL CommunityTokenPermission_Type = 4
CommunityTokenPermission_BECOME_TOKEN_MASTER CommunityTokenPermission_Type = 5
) )
var CommunityTokenPermission_Type_name = map[int32]string{ var CommunityTokenPermission_Type_name = map[int32]string{
@ -104,6 +105,7 @@ var CommunityTokenPermission_Type_name = map[int32]string{
2: "BECOME_MEMBER", 2: "BECOME_MEMBER",
3: "CAN_VIEW_CHANNEL", 3: "CAN_VIEW_CHANNEL",
4: "CAN_VIEW_AND_POST_CHANNEL", 4: "CAN_VIEW_AND_POST_CHANNEL",
5: "BECOME_TOKEN_MASTER",
} }
var CommunityTokenPermission_Type_value = map[string]int32{ var CommunityTokenPermission_Type_value = map[string]int32{
@ -112,6 +114,7 @@ var CommunityTokenPermission_Type_value = map[string]int32{
"BECOME_MEMBER": 2, "BECOME_MEMBER": 2,
"CAN_VIEW_CHANNEL": 3, "CAN_VIEW_CHANNEL": 3,
"CAN_VIEW_AND_POST_CHANNEL": 4, "CAN_VIEW_AND_POST_CHANNEL": 4,
"BECOME_TOKEN_MASTER": 5,
} }
func (x CommunityTokenPermission_Type) String() string { func (x CommunityTokenPermission_Type) String() string {
@ -1734,136 +1737,136 @@ func init() {
} }
var fileDescriptor_f937943d74c1cd8b = []byte{ var fileDescriptor_f937943d74c1cd8b = []byte{
// 2086 bytes of a gzipped FileDescriptorProto // 2096 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x4f, 0x73, 0x23, 0x47, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xcc, 0x58, 0x4f, 0x73, 0x23, 0x47,
0x15, 0xcf, 0xe8, 0x8f, 0x2d, 0x3d, 0x49, 0xb6, 0xdc, 0xd9, 0xb5, 0xc7, 0xde, 0x75, 0x56, 0x19, 0x15, 0xcf, 0x48, 0xb2, 0x2d, 0x3d, 0x59, 0xb6, 0xdc, 0xbb, 0x6b, 0x8f, 0xbd, 0xeb, 0xac, 0x33,
0xa0, 0x70, 0x42, 0xa1, 0x4d, 0x0c, 0x14, 0x5b, 0x09, 0x24, 0xd1, 0xca, 0xc3, 0x46, 0xec, 0x6a, 0x40, 0xe1, 0x84, 0xc2, 0x49, 0x0c, 0x14, 0x5b, 0x09, 0x24, 0xd1, 0xca, 0xc3, 0x46, 0xec, 0x6a,
0xe4, 0xb4, 0xe4, 0x2c, 0xa4, 0x80, 0xa9, 0xf6, 0x4c, 0xdb, 0xee, 0x5a, 0x69, 0x46, 0x4c, 0xb7, 0xe4, 0xb4, 0xe4, 0x2c, 0xa4, 0x80, 0xa9, 0xf6, 0x4c, 0xdb, 0xee, 0x5a, 0x69, 0x46, 0x4c, 0xb7,
0x0c, 0xe2, 0x10, 0xaa, 0x28, 0x3e, 0x01, 0x9f, 0x80, 0x2a, 0x0e, 0xdc, 0xf8, 0x06, 0x14, 0x07, 0x0c, 0xe2, 0x00, 0x55, 0x14, 0x9f, 0x80, 0x0b, 0x57, 0xaa, 0x38, 0x70, 0xe3, 0x1b, 0x50, 0x1c,
0xee, 0xdc, 0xb9, 0xc1, 0x8d, 0x23, 0x27, 0xce, 0x54, 0x77, 0xcf, 0x8c, 0x66, 0x64, 0x69, 0xff, 0xb8, 0x73, 0xe7, 0xc6, 0x91, 0x23, 0x27, 0x2e, 0x5c, 0xa8, 0xfe, 0x33, 0xa3, 0x19, 0x59, 0xda,
0x10, 0xa8, 0xe2, 0xa4, 0x79, 0xaf, 0x5f, 0xbf, 0x7e, 0xfd, 0xde, 0xef, 0xbd, 0x7e, 0x4f, 0xb0, 0x3f, 0x04, 0xaa, 0x38, 0x69, 0xde, 0xeb, 0xd7, 0xaf, 0x5f, 0xbf, 0xf7, 0x7b, 0xaf, 0xdf, 0x13,
0xe3, 0x85, 0x93, 0xc9, 0x2c, 0x60, 0x82, 0x51, 0xde, 0x9e, 0x46, 0xa1, 0x08, 0x51, 0x45, 0xfd, 0x6c, 0x05, 0xf1, 0x68, 0x34, 0x89, 0x98, 0x60, 0x94, 0x1f, 0x8d, 0x93, 0x58, 0xc4, 0xa8, 0xaa,
0x9c, 0xcf, 0x2e, 0x0e, 0x5e, 0xf7, 0xae, 0x88, 0x70, 0x99, 0x4f, 0x03, 0xc1, 0xc4, 0x5c, 0x2f, 0x7e, 0xce, 0x27, 0x17, 0x7b, 0xb7, 0x82, 0x2b, 0x22, 0x7c, 0x16, 0xd2, 0x48, 0x30, 0x31, 0xd5,
0x1f, 0xd4, 0x68, 0x30, 0x9b, 0xc4, 0xb2, 0xd6, 0x35, 0x94, 0x1f, 0x45, 0x24, 0x10, 0xe8, 0x4d, 0xcb, 0x7b, 0x75, 0x1a, 0x4d, 0x46, 0x46, 0xd6, 0xb9, 0x86, 0x95, 0x47, 0x09, 0x89, 0x04, 0x7a,
0xa8, 0x27, 0x9a, 0xe6, 0x2e, 0xf3, 0x4d, 0xa3, 0x65, 0x1c, 0xd5, 0x71, 0x2d, 0xe5, 0xf5, 0x7c, 0x03, 0xd6, 0x53, 0x4d, 0x53, 0x9f, 0x85, 0xb6, 0x75, 0x60, 0x1d, 0xae, 0xe3, 0x7a, 0xc6, 0xeb,
0x74, 0x07, 0xaa, 0x13, 0x3a, 0x39, 0xa7, 0x91, 0x5c, 0x2f, 0xa8, 0xf5, 0x8a, 0x66, 0xf4, 0x7c, 0x84, 0xe8, 0x2e, 0xd4, 0x46, 0x74, 0x74, 0x4e, 0x13, 0xb9, 0x5e, 0x52, 0xeb, 0x55, 0xcd, 0xe8,
0xb4, 0x07, 0x9b, 0xf1, 0x61, 0x66, 0xb1, 0x65, 0x1c, 0x55, 0xf1, 0x86, 0x24, 0x7b, 0x3e, 0xba, 0x84, 0x68, 0x07, 0xd6, 0xcc, 0x61, 0x76, 0xf9, 0xc0, 0x3a, 0xac, 0xe1, 0x55, 0x49, 0x76, 0x42,
0x05, 0x65, 0x6f, 0x1c, 0x7a, 0xcf, 0xcc, 0x52, 0xcb, 0x38, 0x2a, 0x61, 0x4d, 0x58, 0x7f, 0x2c, 0x74, 0x1b, 0x56, 0x82, 0x61, 0x1c, 0x3c, 0xb3, 0x2b, 0x07, 0xd6, 0x61, 0x05, 0x6b, 0xc2, 0xf9,
0xc0, 0x76, 0x37, 0xd1, 0xdd, 0x57, 0x4a, 0xd0, 0xb7, 0xa0, 0x1c, 0x85, 0x63, 0xca, 0x4d, 0xa3, 0x63, 0x09, 0x36, 0xdb, 0xa9, 0xee, 0xae, 0x52, 0x82, 0xbe, 0x01, 0x2b, 0x49, 0x3c, 0xa4, 0xdc,
0x55, 0x3c, 0xda, 0x3a, 0xbe, 0xd7, 0x4e, 0xee, 0xd1, 0x5e, 0x92, 0x6c, 0x63, 0x29, 0x86, 0xb5, 0xb6, 0x0e, 0xca, 0x87, 0x1b, 0xc7, 0xf7, 0x8f, 0xd2, 0x7b, 0x1c, 0xcd, 0x49, 0x1e, 0x61, 0x29,
0x34, 0xfa, 0x1e, 0xec, 0x44, 0xf4, 0x9a, 0x92, 0x31, 0xf5, 0x5d, 0xe2, 0x79, 0xe1, 0x2c, 0x10, 0x86, 0xb5, 0x34, 0xfa, 0x0e, 0x6c, 0x25, 0xf4, 0x9a, 0x92, 0x21, 0x0d, 0x7d, 0x12, 0x04, 0xf1,
0xdc, 0x2c, 0xb4, 0x8a, 0x47, 0xb5, 0xe3, 0xfd, 0x85, 0x0a, 0x1c, 0x8b, 0x74, 0xb4, 0x04, 0x6e, 0x24, 0x12, 0xdc, 0x2e, 0x1d, 0x94, 0x0f, 0xeb, 0xc7, 0xbb, 0x33, 0x15, 0xd8, 0x88, 0xb4, 0xb4,
0x46, 0x79, 0x06, 0x47, 0x6f, 0xc3, 0xce, 0x98, 0x70, 0xe1, 0xce, 0xa6, 0x3e, 0x11, 0xd4, 0xd5, 0x04, 0x6e, 0x26, 0x45, 0x06, 0x47, 0x6f, 0xc1, 0xd6, 0x90, 0x70, 0xe1, 0x4f, 0xc6, 0x21, 0x11,
0x46, 0x17, 0x95, 0xd1, 0xdb, 0x72, 0xe1, 0x4c, 0xf1, 0xbb, 0xca, 0xfc, 0x5f, 0x42, 0x59, 0xd9, 0xd4, 0xd7, 0x46, 0x97, 0x95, 0xd1, 0x9b, 0x72, 0xe1, 0x4c, 0xf1, 0xdb, 0xca, 0xfc, 0x5f, 0xc0,
0x80, 0x1a, 0x50, 0xc5, 0x83, 0x27, 0xb6, 0xeb, 0x0c, 0x1c, 0xbb, 0xf9, 0x1a, 0xda, 0x02, 0x50, 0x8a, 0xb2, 0x01, 0x35, 0xa0, 0x86, 0x7b, 0x4f, 0x5c, 0xdf, 0xeb, 0x79, 0x6e, 0xf3, 0x35, 0xb4,
0xe4, 0xe0, 0xa9, 0x63, 0xe3, 0xa6, 0x81, 0x6e, 0xc3, 0x8e, 0xa2, 0xfb, 0x1d, 0xa7, 0xf3, 0xc8, 0x01, 0xa0, 0xc8, 0xde, 0x53, 0xcf, 0xc5, 0x4d, 0x0b, 0xdd, 0x81, 0x2d, 0x45, 0x77, 0x5b, 0x5e,
0x76, 0xcf, 0x86, 0x36, 0x1e, 0x36, 0x0b, 0x68, 0x1f, 0x6e, 0x6b, 0xf6, 0xe0, 0xc4, 0xc6, 0x9d, 0xeb, 0x91, 0xeb, 0x9f, 0xf5, 0x5d, 0xdc, 0x6f, 0x96, 0xd0, 0x2e, 0xdc, 0xd1, 0xec, 0xde, 0x89,
0x91, 0xed, 0x76, 0x07, 0xce, 0xc8, 0x76, 0x46, 0xcd, 0x62, 0xaa, 0xa1, 0x73, 0xd2, 0xef, 0x39, 0x8b, 0x5b, 0x03, 0xd7, 0x6f, 0xf7, 0xbc, 0x81, 0xeb, 0x0d, 0x9a, 0xe5, 0x4c, 0x43, 0xeb, 0xa4,
0xcd, 0x52, 0xaa, 0x61, 0x34, 0x78, 0x6c, 0x3b, 0x6e, 0xbf, 0x33, 0x1c, 0xd9, 0xb8, 0x59, 0xb6, 0xdb, 0xf1, 0x9a, 0x95, 0x4c, 0xc3, 0xa0, 0xf7, 0xd8, 0xf5, 0xfc, 0x6e, 0xab, 0x3f, 0x70, 0x71,
0x7e, 0x55, 0x84, 0xdd, 0xd4, 0x2b, 0xa3, 0xf0, 0x19, 0x0d, 0xfa, 0x54, 0x10, 0x9f, 0x08, 0x82, 0x73, 0xc5, 0xf9, 0x65, 0x19, 0xb6, 0x33, 0xaf, 0x0c, 0xe2, 0x67, 0x34, 0xea, 0x52, 0x41, 0x42,
0x2e, 0x00, 0x79, 0x61, 0x20, 0x22, 0xe2, 0x09, 0x97, 0xf8, 0x7e, 0x44, 0x39, 0x8f, 0x7d, 0x5a, 0x22, 0x08, 0xba, 0x00, 0x14, 0xc4, 0x91, 0x48, 0x48, 0x20, 0x7c, 0x12, 0x86, 0x09, 0xe5, 0xdc,
0x3b, 0xfe, 0xf6, 0x0a, 0x9f, 0xe6, 0x76, 0xb7, 0xbb, 0xf1, 0xd6, 0x4e, 0xb2, 0xd3, 0x0e, 0x44, 0xf8, 0xb4, 0x7e, 0xfc, 0xcd, 0x05, 0x3e, 0x2d, 0xec, 0x3e, 0x6a, 0x9b, 0xad, 0xad, 0x74, 0xa7,
0x34, 0xc7, 0x3b, 0xde, 0x32, 0x1f, 0xb5, 0xa0, 0xe6, 0x53, 0xee, 0x45, 0x6c, 0x2a, 0x58, 0x18, 0x1b, 0x89, 0x64, 0x8a, 0xb7, 0x82, 0x79, 0x3e, 0x3a, 0x80, 0x7a, 0x48, 0x79, 0x90, 0xb0, 0xb1,
0x28, 0x40, 0x54, 0x71, 0x96, 0x25, 0x43, 0xcf, 0x26, 0xe4, 0x92, 0xc6, 0x88, 0xd0, 0x04, 0x7a, 0x60, 0x71, 0xa4, 0x00, 0x51, 0xc3, 0x79, 0x96, 0x0c, 0x3d, 0x1b, 0x91, 0x4b, 0x6a, 0x10, 0xa1,
0x0f, 0xaa, 0x42, 0x1e, 0x39, 0x9a, 0x4f, 0xa9, 0x02, 0xc5, 0xd6, 0xf1, 0xdd, 0x75, 0x66, 0x49, 0x09, 0xf4, 0x1e, 0xd4, 0x84, 0x3c, 0x72, 0x30, 0x1d, 0x53, 0x05, 0x8a, 0x8d, 0xe3, 0x7b, 0xcb,
0x19, 0xbc, 0x10, 0x47, 0xbb, 0xb0, 0xc1, 0xe7, 0x93, 0xf3, 0x70, 0x6c, 0x96, 0x35, 0xc8, 0x34, 0xcc, 0x92, 0x32, 0x78, 0x26, 0x8e, 0xb6, 0x61, 0x95, 0x4f, 0x47, 0xe7, 0xf1, 0xd0, 0x5e, 0xd1,
0x85, 0x10, 0x94, 0x02, 0x32, 0xa1, 0xe6, 0x86, 0xe2, 0xaa, 0x6f, 0x74, 0x00, 0x15, 0x9f, 0x7a, 0x20, 0xd3, 0x14, 0x42, 0x50, 0x89, 0xc8, 0x88, 0xda, 0xab, 0x8a, 0xab, 0xbe, 0xd1, 0x1e, 0x54,
0x6c, 0x42, 0xc6, 0xdc, 0xdc, 0x6c, 0x19, 0x47, 0x0d, 0x9c, 0xd2, 0x07, 0x27, 0xd2, 0x7b, 0xab, 0x43, 0x1a, 0xb0, 0x11, 0x19, 0x72, 0x7b, 0xed, 0xc0, 0x3a, 0x6c, 0xe0, 0x8c, 0xde, 0x3b, 0x91,
0x2e, 0x8a, 0x9a, 0x50, 0x7c, 0x46, 0xe7, 0x0a, 0xfe, 0x25, 0x2c, 0x3f, 0xe5, 0x2d, 0xae, 0xc9, 0xde, 0x5b, 0x74, 0x51, 0xd4, 0x84, 0xf2, 0x33, 0x3a, 0x55, 0xf0, 0xaf, 0x60, 0xf9, 0x29, 0x6f,
0x78, 0x46, 0xe3, 0x1b, 0x6a, 0xe2, 0xbd, 0xc2, 0x03, 0xc3, 0xfa, 0x9b, 0x01, 0xb7, 0x52, 0x7b, 0x71, 0x4d, 0x86, 0x13, 0x6a, 0x6e, 0xa8, 0x89, 0xf7, 0x4a, 0x0f, 0x2c, 0xe7, 0x6f, 0x16, 0xdc,
0x4f, 0x69, 0x34, 0x61, 0x9c, 0xb3, 0x30, 0xe0, 0x68, 0x1f, 0x2a, 0x34, 0xe0, 0x6e, 0x18, 0x8c, 0xce, 0xec, 0x3d, 0xa5, 0xc9, 0x88, 0x71, 0xce, 0xe2, 0x88, 0xa3, 0x5d, 0xa8, 0xd2, 0x88, 0xfb,
0xb5, 0xa6, 0x0a, 0xde, 0xa4, 0x01, 0x1f, 0x04, 0xe3, 0x39, 0x32, 0x61, 0x73, 0x1a, 0xb1, 0x6b, 0x71, 0x34, 0xd4, 0x9a, 0xaa, 0x78, 0x8d, 0x46, 0xbc, 0x17, 0x0d, 0xa7, 0xc8, 0x86, 0xb5, 0x71,
0x22, 0xb4, 0xbe, 0x0a, 0x4e, 0x48, 0xf4, 0x5d, 0xd8, 0x20, 0x9e, 0x47, 0x39, 0x57, 0xee, 0xda, 0xc2, 0xae, 0x89, 0xd0, 0xfa, 0xaa, 0x38, 0x25, 0xd1, 0xb7, 0x61, 0x95, 0x04, 0x01, 0xe5, 0x5c,
0x3a, 0xfe, 0xca, 0x0a, 0xa7, 0x64, 0x0e, 0x69, 0x77, 0x94, 0x30, 0x8e, 0x37, 0x59, 0x23, 0xd8, 0xb9, 0x6b, 0xe3, 0xf8, 0x4b, 0x0b, 0x9c, 0x92, 0x3b, 0xe4, 0xa8, 0xa5, 0x84, 0xb1, 0xd9, 0xe4,
0xd0, 0x1c, 0x84, 0x60, 0xeb, 0xcc, 0x79, 0xec, 0x0c, 0x9e, 0x3a, 0x6e, 0xa7, 0xdb, 0xb5, 0x87, 0x0c, 0x60, 0x55, 0x73, 0x10, 0x82, 0x8d, 0x33, 0xef, 0xb1, 0xd7, 0x7b, 0xea, 0xf9, 0xad, 0x76,
0xc3, 0xe6, 0x6b, 0x68, 0x07, 0x1a, 0xce, 0xc0, 0xed, 0xdb, 0xfd, 0x87, 0x36, 0x1e, 0x7e, 0xdc, 0xdb, 0xed, 0xf7, 0x9b, 0xaf, 0xa1, 0x2d, 0x68, 0x78, 0x3d, 0xbf, 0xeb, 0x76, 0x1f, 0xba, 0xb8,
0x3b, 0x6d, 0x1a, 0xe8, 0x75, 0xd8, 0xee, 0x39, 0x9f, 0xf6, 0x46, 0x9d, 0x51, 0x6f, 0xe0, 0xb8, 0xff, 0x71, 0xe7, 0xb4, 0x69, 0xa1, 0x5b, 0xb0, 0xd9, 0xf1, 0x3e, 0xed, 0x0c, 0x5a, 0x83, 0x4e,
0x03, 0xe7, 0xc9, 0x0f, 0x9b, 0x05, 0x09, 0xbf, 0x81, 0xe3, 0x62, 0xfb, 0x93, 0x33, 0x7b, 0x38, 0xcf, 0xf3, 0x7b, 0xde, 0x93, 0xef, 0x37, 0x4b, 0x12, 0x7e, 0x3d, 0xcf, 0xc7, 0xee, 0x27, 0x67,
0x6a, 0x16, 0xad, 0x5f, 0x17, 0xa1, 0xa1, 0x22, 0xd1, 0x8d, 0x98, 0xa0, 0x11, 0x23, 0xe8, 0xc7, 0x6e, 0x7f, 0xd0, 0x2c, 0x3b, 0xbf, 0x2a, 0x43, 0x43, 0x45, 0xa2, 0x9d, 0x30, 0x41, 0x13, 0x46,
0xcf, 0x81, 0x57, 0x7b, 0x61, 0x72, 0x6e, 0xd3, 0x2b, 0xa0, 0xea, 0x1d, 0x28, 0x09, 0x09, 0x8c, 0xd0, 0x0f, 0x9f, 0x03, 0xaf, 0xa3, 0x99, 0xc9, 0x85, 0x4d, 0xaf, 0x80, 0xaa, 0x77, 0xa0, 0x22,
0xc2, 0x4b, 0x00, 0x43, 0x49, 0x66, 0x30, 0x51, 0x5c, 0x89, 0x89, 0x52, 0x06, 0x13, 0xbb, 0xb0, 0x24, 0x30, 0x4a, 0x2f, 0x01, 0x0c, 0x25, 0x99, 0xc3, 0x44, 0x79, 0x21, 0x26, 0x2a, 0x39, 0x4c,
0x41, 0x26, 0x32, 0xdd, 0x13, 0xfc, 0x68, 0x4a, 0x96, 0x36, 0x05, 0x32, 0x97, 0xf9, 0xdc, 0xdc, 0x6c, 0xc3, 0x2a, 0x19, 0xc9, 0x74, 0x4f, 0xf1, 0xa3, 0x29, 0x59, 0xda, 0x14, 0xc8, 0x7c, 0x16,
0x68, 0x15, 0x8f, 0x4a, 0xb8, 0xa2, 0x18, 0x3d, 0x9f, 0xa3, 0x7b, 0x50, 0x93, 0xd1, 0x9c, 0x12, 0x72, 0x7b, 0xf5, 0xa0, 0x7c, 0x58, 0xc1, 0x55, 0xc5, 0xe8, 0x84, 0x1c, 0xdd, 0x87, 0xba, 0x8c,
0x21, 0x68, 0x14, 0x28, 0x2c, 0x55, 0x31, 0xd0, 0x80, 0x9f, 0x6a, 0x4e, 0x0e, 0x69, 0x15, 0x05, 0xe6, 0x98, 0x08, 0x41, 0x93, 0x48, 0x61, 0xa9, 0x86, 0x81, 0x46, 0xfc, 0x54, 0x73, 0x0a, 0x48,
0x9c, 0xff, 0x36, 0xd2, 0xfe, 0x5e, 0x00, 0x33, 0xef, 0x80, 0x05, 0x12, 0xd0, 0x16, 0x14, 0xe2, 0xab, 0x2a, 0xe0, 0xfc, 0xb7, 0x91, 0xf6, 0xaf, 0x12, 0xd8, 0x45, 0x07, 0xcc, 0x90, 0x80, 0x36,
0x82, 0x5d, 0xc5, 0x05, 0xe6, 0xa3, 0xf7, 0x73, 0x2e, 0xfc, 0xea, 0x3a, 0x17, 0x2e, 0x34, 0xb4, 0xa0, 0x64, 0x0a, 0x76, 0x0d, 0x97, 0x58, 0x88, 0xde, 0x2f, 0xb8, 0xf0, 0xcb, 0xcb, 0x5c, 0x38,
0x33, 0xde, 0xfc, 0x00, 0xb6, 0xb4, 0x27, 0xbc, 0x38, 0x76, 0x66, 0x51, 0x85, 0x76, 0x6f, 0x4d, 0xd3, 0x70, 0x94, 0xf3, 0xe6, 0x07, 0xb0, 0xa1, 0x3d, 0x11, 0x98, 0xd8, 0xd9, 0x65, 0x15, 0xda,
0x68, 0x71, 0x43, 0xe4, 0xe0, 0xb1, 0x0f, 0x95, 0xf8, 0x1d, 0xe0, 0x66, 0xa9, 0x55, 0x3c, 0xaa, 0x9d, 0x25, 0xa1, 0xc5, 0x0d, 0x51, 0x80, 0xc7, 0x2e, 0x54, 0xcd, 0x3b, 0xc0, 0xed, 0xca, 0x41,
0xe2, 0x4d, 0xfd, 0x10, 0x70, 0x74, 0x08, 0xc0, 0xb8, 0x9b, 0xa0, 0xbf, 0xac, 0xd0, 0x5f, 0x65, 0xf9, 0xb0, 0x86, 0xd7, 0xf4, 0x43, 0xc0, 0xd1, 0x3e, 0x00, 0xe3, 0x7e, 0x8a, 0xfe, 0x15, 0x85,
0xfc, 0x54, 0x33, 0xac, 0xcf, 0xa1, 0xa4, 0x72, 0xfc, 0x2e, 0x98, 0x09, 0x7c, 0x75, 0xd1, 0x3b, 0xfe, 0x1a, 0xe3, 0xa7, 0x9a, 0xe1, 0xfc, 0xc6, 0x82, 0x8a, 0x4a, 0xf2, 0x7b, 0x60, 0xa7, 0xf8,
0xb5, 0x71, 0xbf, 0x37, 0x1c, 0xf6, 0x06, 0x4e, 0xf3, 0x35, 0xd4, 0x84, 0xfa, 0x43, 0xbb, 0x3b, 0xd5, 0x55, 0xef, 0xd4, 0xc5, 0xdd, 0x4e, 0xbf, 0xdf, 0xe9, 0x79, 0xcd, 0xd7, 0x50, 0x13, 0xd6,
0xe8, 0x27, 0x15, 0xd2, 0x90, 0xd0, 0x8e, 0x39, 0x1a, 0xde, 0xcd, 0x02, 0xba, 0x05, 0xcd, 0x6e, 0x1f, 0xba, 0xed, 0x5e, 0x37, 0x2d, 0x91, 0x96, 0xc4, 0xb6, 0xe1, 0x68, 0x7c, 0x37, 0x4b, 0xe8,
0xc7, 0x71, 0x3f, 0xed, 0xd9, 0x4f, 0xdd, 0xee, 0xc7, 0x1d, 0xc7, 0xb1, 0x9f, 0x34, 0x8b, 0xe8, 0x36, 0x34, 0xdb, 0x2d, 0xcf, 0xff, 0xb4, 0xe3, 0x3e, 0xf5, 0xdb, 0x1f, 0xb7, 0x3c, 0xcf, 0x7d,
0x10, 0xf6, 0x53, 0x6e, 0xc7, 0x39, 0x71, 0x4f, 0x07, 0xc3, 0x51, 0xba, 0x5c, 0xb2, 0xfe, 0x55, 0xd2, 0x2c, 0xa3, 0x7d, 0xd8, 0xcd, 0xb8, 0x2d, 0xef, 0xc4, 0x3f, 0xed, 0xf5, 0x07, 0xd9, 0x72,
0xcd, 0x64, 0xf3, 0x49, 0xbe, 0x8c, 0xe9, 0xc7, 0xc0, 0xc8, 0xbc, 0x60, 0xc8, 0x86, 0x4d, 0xfd, 0x05, 0xed, 0xc0, 0x2d, 0xa3, 0x67, 0xae, 0xd8, 0xfe, 0xb3, 0x96, 0xcb, 0xf3, 0x93, 0x62, 0x81,
0xf8, 0x25, 0x8f, 0xcd, 0xd7, 0x56, 0x38, 0x3a, 0xa3, 0xa6, 0xad, 0xdf, 0xae, 0x18, 0xf9, 0xc9, 0xd3, 0xcf, 0x84, 0x95, 0x7b, 0xdb, 0x90, 0x0b, 0x6b, 0xfa, 0x59, 0x4c, 0x9f, 0xa1, 0xaf, 0x2c,
0x5e, 0xf4, 0x11, 0xd4, 0xa6, 0x8b, 0xa4, 0x56, 0x10, 0xae, 0x1d, 0xbf, 0xf1, 0xfc, 0xd4, 0xc7, 0x08, 0x41, 0x4e, 0xcd, 0x91, 0x7e, 0xd5, 0x4c, 0x4e, 0xa4, 0x7b, 0xd1, 0x47, 0x50, 0x1f, 0xcf,
0xd9, 0x2d, 0xe8, 0x18, 0x2a, 0xc9, 0x0b, 0xaf, 0x9c, 0x5a, 0x3b, 0xde, 0xcd, 0x6c, 0x57, 0xbe, 0xd2, 0x5d, 0x81, 0xbb, 0x7e, 0xfc, 0xfa, 0xf3, 0x8b, 0x02, 0xce, 0x6f, 0x41, 0xc7, 0x50, 0x4d,
0xd7, 0xab, 0x38, 0x95, 0x43, 0x1f, 0x42, 0x59, 0x46, 0x45, 0x63, 0xbd, 0x76, 0xfc, 0xd6, 0x0b, 0xdf, 0x7e, 0xe5, 0xee, 0xfa, 0xf1, 0x76, 0x6e, 0xbb, 0x8a, 0x8a, 0x5e, 0xc5, 0x99, 0x1c, 0xfa,
0x4c, 0x97, 0x5a, 0x62, 0xc3, 0xf5, 0x3e, 0x19, 0xe6, 0x73, 0x12, 0xb8, 0x63, 0xc6, 0x85, 0xb9, 0x10, 0x56, 0x64, 0xbc, 0x74, 0x16, 0xd4, 0x8f, 0xdf, 0x7c, 0x81, 0xe9, 0x52, 0x8b, 0x31, 0x5c,
0xa9, 0xc3, 0x7c, 0x4e, 0x82, 0x27, 0x8c, 0x0b, 0xe4, 0x00, 0x78, 0x44, 0xd0, 0xcb, 0x30, 0x62, 0xef, 0x93, 0x00, 0x38, 0x27, 0x91, 0x3f, 0x64, 0x5c, 0xd8, 0x6b, 0x1a, 0x00, 0xe7, 0x24, 0x7a,
0x54, 0xe6, 0xc3, 0x52, 0x61, 0x58, 0x7d, 0x40, 0xba, 0x41, 0x9f, 0x92, 0xd1, 0x80, 0x1e, 0x80, 0xc2, 0xb8, 0x40, 0x1e, 0x40, 0x40, 0x04, 0xbd, 0x8c, 0x13, 0x46, 0x65, 0xa6, 0xcc, 0x95, 0x8c,
0x49, 0x22, 0xef, 0x8a, 0x5d, 0x53, 0x77, 0x42, 0x2e, 0x03, 0x2a, 0xc6, 0x2c, 0x78, 0x16, 0x3f, 0xc5, 0x07, 0x64, 0x1b, 0xf4, 0x29, 0x39, 0x0d, 0xe8, 0x01, 0xd8, 0x24, 0x09, 0xae, 0xd8, 0x35,
0xcf, 0x55, 0x15, 0x91, 0xdd, 0x78, 0xbd, 0x9f, 0x2e, 0xab, 0x57, 0x1a, 0x3d, 0x82, 0x2d, 0xe2, 0xf5, 0x47, 0xe4, 0x32, 0xa2, 0x62, 0xc8, 0xa2, 0x67, 0xe6, 0xe1, 0xae, 0xa9, 0x88, 0x6c, 0x9b,
0x4f, 0x58, 0xe0, 0x72, 0x2a, 0x04, 0x0b, 0x2e, 0xb9, 0x09, 0xca, 0x3f, 0xad, 0x15, 0xd6, 0x74, 0xf5, 0x6e, 0xb6, 0xac, 0xde, 0x6f, 0xf4, 0x08, 0x36, 0x48, 0x38, 0x62, 0x91, 0xcf, 0xa9, 0x10,
0xa4, 0xe0, 0x30, 0x96, 0xc3, 0x0d, 0x92, 0x25, 0xd1, 0x97, 0xa0, 0xc1, 0x02, 0x11, 0x85, 0xee, 0x2c, 0xba, 0xe4, 0x36, 0x28, 0xff, 0x1c, 0x2c, 0xb0, 0xa6, 0x25, 0x05, 0xfb, 0x46, 0x0e, 0x37,
0x84, 0x72, 0x2e, 0x1f, 0xb4, 0x9a, 0x4a, 0xb6, 0xba, 0x62, 0xf6, 0x35, 0x4f, 0x0a, 0x85, 0xb3, 0x48, 0x9e, 0x44, 0x5f, 0x80, 0x06, 0x8b, 0x44, 0x12, 0xfb, 0x23, 0xca, 0xb9, 0x7c, 0xea, 0xea,
0xac, 0x50, 0x5d, 0x0b, 0x29, 0x66, 0x22, 0x74, 0x17, 0xaa, 0x34, 0xf0, 0xa2, 0xf9, 0x54, 0x50, 0x2a, 0x0d, 0xd7, 0x15, 0xb3, 0xab, 0x79, 0x52, 0x28, 0x9e, 0xe4, 0x85, 0xd6, 0xb5, 0x90, 0x62,
0xdf, 0x6c, 0xe8, 0x14, 0x48, 0x19, 0xb2, 0x64, 0x09, 0x72, 0xc9, 0xcd, 0x2d, 0xe5, 0x51, 0xf5, 0xa6, 0x42, 0xf7, 0xa0, 0x46, 0xa3, 0x20, 0x99, 0x8e, 0x05, 0x0d, 0xed, 0x86, 0x4e, 0x8e, 0x8c,
0x8d, 0x08, 0xec, 0xe8, 0x84, 0xcc, 0xc2, 0x64, 0x5b, 0x79, 0xf5, 0x9b, 0x2f, 0xf0, 0xea, 0x52, 0x21, 0x8b, 0x99, 0x20, 0x97, 0xdc, 0xde, 0x50, 0x1e, 0x55, 0xdf, 0x88, 0xc0, 0x96, 0x4e, 0xd5,
0x9a, 0xc7, 0xbe, 0x6d, 0x8a, 0x25, 0x36, 0xfa, 0x11, 0xec, 0x2f, 0x7a, 0x3f, 0xb5, 0xca, 0xdd, 0x3c, 0x4c, 0x36, 0x95, 0x57, 0xbf, 0xfe, 0x02, 0xaf, 0xce, 0x15, 0x00, 0xe3, 0xdb, 0xa6, 0x98,
0x49, 0xdc, 0x10, 0x98, 0x4d, 0x75, 0x54, 0xeb, 0x45, 0x8d, 0x03, 0xde, 0xf3, 0x72, 0x7c, 0x9e, 0x63, 0xa3, 0x1f, 0xc0, 0xee, 0xac, 0x2b, 0x54, 0xab, 0xdc, 0x1f, 0x99, 0x56, 0xc1, 0x6e, 0xaa,
0xf6, 0x23, 0xef, 0xc0, 0x2d, 0xe2, 0x09, 0x15, 0x3e, 0x8d, 0x79, 0x57, 0x35, 0x5c, 0xe6, 0x8e, 0xa3, 0x0e, 0x5e, 0xd4, 0x52, 0xe0, 0x9d, 0xa0, 0xc0, 0xe7, 0x59, 0xa7, 0xf2, 0x0e, 0xdc, 0x26,
0x8a, 0x1d, 0xd2, 0x6b, 0x71, 0x72, 0x74, 0xe5, 0xca, 0xc1, 0x19, 0xd4, 0xb3, 0xc9, 0x92, 0xad, 0x81, 0x50, 0xe1, 0xd3, 0x98, 0xf7, 0x55, 0x2b, 0x66, 0x6f, 0xa9, 0xd8, 0x21, 0xbd, 0x66, 0x92,
0x94, 0x55, 0x5d, 0x29, 0xef, 0x67, 0x2b, 0x65, 0xae, 0xcf, 0x5b, 0x6a, 0x15, 0x33, 0x45, 0xf4, 0xa3, 0x2d, 0x57, 0xf6, 0xce, 0x60, 0x3d, 0x9f, 0x2c, 0xf9, 0x1a, 0x5a, 0xd3, 0x35, 0xf4, 0xed,
0xe0, 0x13, 0x80, 0x05, 0x90, 0x57, 0x28, 0xfd, 0x7a, 0x5e, 0xe9, 0xde, 0x0a, 0xa5, 0x72, 0x7f, 0x7c, 0x0d, 0x2d, 0x74, 0x80, 0x73, 0x4d, 0x64, 0xae, 0xbc, 0xee, 0x7d, 0x02, 0x30, 0x03, 0xf2,
0x56, 0xe5, 0x67, 0xb0, 0xbd, 0x04, 0xdd, 0x15, 0x7a, 0xdf, 0xcd, 0xeb, 0xbd, 0xb3, 0x4a, 0xaf, 0x02, 0xa5, 0x5f, 0x2d, 0x2a, 0xdd, 0x59, 0xa0, 0x54, 0xee, 0xcf, 0xab, 0xfc, 0x0c, 0x36, 0xe7,
0x56, 0x32, 0xcf, 0xea, 0xbe, 0x84, 0xdb, 0x2b, 0x03, 0xb8, 0xe2, 0x84, 0x07, 0xf9, 0x13, 0xac, 0xa0, 0xbb, 0x40, 0xef, 0xbb, 0x45, 0xbd, 0x77, 0x17, 0xe9, 0xd5, 0x4a, 0xa6, 0x79, 0xdd, 0x97,
0x17, 0x97, 0xfc, 0xec, 0xe3, 0xf2, 0x93, 0x4c, 0x2b, 0x99, 0x4b, 0x03, 0x74, 0x02, 0xf7, 0xa6, 0x70, 0x67, 0x61, 0x00, 0x17, 0x9c, 0xf0, 0xa0, 0x78, 0x82, 0xf3, 0xe2, 0xc7, 0x20, 0xff, 0xec,
0x2c, 0x48, 0x00, 0xed, 0x92, 0xf1, 0x38, 0x8d, 0x21, 0x0d, 0xc8, 0xf9, 0x98, 0xfa, 0x71, 0x7b, 0xfc, 0x28, 0xd7, 0x64, 0x16, 0xd2, 0x00, 0x9d, 0xc0, 0xfd, 0x31, 0x8b, 0x52, 0x40, 0xfb, 0x64,
0x73, 0x67, 0xca, 0x82, 0x18, 0xe2, 0x9d, 0xf1, 0x38, 0x0d, 0x9e, 0x12, 0xb1, 0xfe, 0x5a, 0x80, 0x38, 0xcc, 0x62, 0x48, 0x23, 0x72, 0x3e, 0xa4, 0xa1, 0x69, 0x7c, 0xee, 0x8e, 0x59, 0x64, 0x20,
0x46, 0xce, 0x83, 0xe8, 0x83, 0x45, 0xed, 0xd4, 0x8d, 0xc3, 0x97, 0xd7, 0xf8, 0xfa, 0xe5, 0x8a, 0xde, 0x1a, 0x0e, 0xb3, 0xe0, 0x29, 0x11, 0xe7, 0xaf, 0x25, 0x68, 0x14, 0x3c, 0x88, 0x3e, 0x98,
0x66, 0xe1, 0x8b, 0x15, 0xcd, 0xe2, 0x4b, 0x16, 0xcd, 0x7b, 0x50, 0x8b, 0xcb, 0x92, 0x9a, 0x90, 0xd5, 0x4e, 0xdd, 0x52, 0x7c, 0x71, 0x89, 0xaf, 0x5f, 0xae, 0x68, 0x96, 0x3e, 0x5f, 0xd1, 0x2c,
0x74, 0x5f, 0x91, 0x54, 0x2a, 0x39, 0x20, 0x1d, 0x40, 0x65, 0x1a, 0x72, 0xa6, 0xda, 0x61, 0x59, 0xbf, 0x64, 0xd1, 0xbc, 0x0f, 0x75, 0x53, 0x96, 0xd4, 0xec, 0xa4, 0x3b, 0x8e, 0xb4, 0x52, 0xc9,
0x89, 0xcb, 0x38, 0xa5, 0xff, 0x47, 0x98, 0xb6, 0x7c, 0xd8, 0xb9, 0x01, 0xa2, 0x65, 0x43, 0x8d, 0xd1, 0x69, 0x0f, 0xaa, 0xe3, 0x98, 0x33, 0xd5, 0x28, 0xcb, 0x4a, 0xbc, 0x82, 0x33, 0xfa, 0x7f,
0x1b, 0x86, 0x26, 0xad, 0x51, 0x21, 0xdf, 0x2e, 0xa7, 0xc6, 0x17, 0xf3, 0xc6, 0x5b, 0xbf, 0x37, 0x84, 0x69, 0x27, 0x84, 0xad, 0x1b, 0x20, 0x9a, 0x37, 0xd4, 0xba, 0x61, 0x68, 0xda, 0x34, 0x95,
0xe0, 0xf5, 0xf4, 0x98, 0x5e, 0x70, 0xcd, 0x04, 0x51, 0x2f, 0xe3, 0x43, 0x38, 0xfc, 0x59, 0x44, 0x8a, 0x8d, 0x74, 0x66, 0x7c, 0xb9, 0x68, 0xbc, 0xf3, 0x7b, 0x0b, 0x6e, 0x65, 0xc7, 0x74, 0xa2,
0xa6, 0x53, 0xea, 0xbb, 0x8b, 0x02, 0x92, 0x1d, 0x0a, 0xf4, 0x14, 0x79, 0x27, 0x16, 0x5a, 0xf7, 0x6b, 0x26, 0x88, 0x7a, 0x19, 0x1f, 0xc2, 0xfe, 0x4f, 0x12, 0x32, 0x1e, 0xd3, 0xd0, 0x9f, 0x15,
0xba, 0x5e, 0xca, 0x09, 0x34, 0x9e, 0x28, 0x35, 0xb1, 0x7e, 0x9c, 0x3c, 0x04, 0x98, 0xce, 0xce, 0x90, 0xfc, 0xb8, 0xa0, 0xe7, 0xcb, 0xbb, 0x46, 0x68, 0xd9, 0xeb, 0x7a, 0x29, 0x67, 0x53, 0x33,
0xc7, 0xcc, 0x73, 0xa5, 0xfb, 0x4a, 0x6a, 0x4f, 0x55, 0x73, 0x1e, 0xd3, 0xb9, 0xf5, 0x1b, 0x03, 0x6b, 0x6a, 0x62, 0xf9, 0xa0, 0xb9, 0x0f, 0x30, 0x9e, 0x9c, 0x0f, 0x59, 0xe0, 0x4b, 0xf7, 0x55,
0xb6, 0x97, 0x46, 0x3d, 0xd9, 0x72, 0xc7, 0x8d, 0x6a, 0xec, 0x8a, 0x84, 0x94, 0xd5, 0x98, 0xb3, 0xd4, 0x9e, 0x9a, 0xe6, 0x3c, 0xa6, 0x53, 0xe7, 0xd7, 0x16, 0x6c, 0xce, 0x0d, 0x81, 0xb2, 0x19,
0xcb, 0x80, 0x88, 0x59, 0x44, 0xe3, 0xf3, 0x17, 0x0c, 0xd9, 0x14, 0x7a, 0x57, 0x84, 0xe9, 0xa6, 0x37, 0x2d, 0xac, 0x71, 0x45, 0x4a, 0xca, 0x6a, 0xcc, 0xd9, 0x65, 0x44, 0xc4, 0x24, 0xa1, 0xe6,
0xb0, 0xa8, 0x9b, 0x42, 0xc5, 0x90, 0xcd, 0xcc, 0xdb, 0xd0, 0x64, 0xbc, 0xc3, 0x22, 0x3f, 0x0a, 0xfc, 0x19, 0x43, 0xb6, 0x8b, 0xc1, 0x15, 0x61, 0xba, 0x5d, 0x2c, 0xeb, 0x76, 0x51, 0x31, 0x64,
0xa7, 0x71, 0x63, 0xa7, 0xac, 0xa9, 0xe0, 0x1b, 0x7c, 0xeb, 0x9f, 0x46, 0x26, 0xc3, 0x30, 0xfd, 0x9b, 0xf3, 0x16, 0x34, 0x19, 0x6f, 0xb1, 0x24, 0x4c, 0xe2, 0xb1, 0x69, 0xf9, 0x94, 0x35, 0x55,
0xe9, 0x8c, 0x72, 0x31, 0x0a, 0xbf, 0x1f, 0xb2, 0x75, 0xbd, 0x45, 0x3c, 0x3f, 0x64, 0x62, 0x24, 0x7c, 0x83, 0xef, 0xfc, 0xc3, 0xca, 0x65, 0x18, 0xa6, 0x3f, 0x9e, 0x50, 0x2e, 0x06, 0xf1, 0x77,
0xe7, 0x07, 0x47, 0x86, 0x69, 0xad, 0x63, 0x96, 0x07, 0xf8, 0xd2, 0xcd, 0x01, 0xfe, 0x4d, 0xa8, 0x63, 0xb6, 0xac, 0xb7, 0x30, 0x93, 0x45, 0x2e, 0x46, 0x72, 0xb2, 0xf0, 0x64, 0x98, 0x96, 0x3a,
0xfb, 0x8c, 0x4f, 0xc7, 0x64, 0xae, 0x55, 0x97, 0xe3, 0x91, 0x4d, 0xf3, 0x94, 0xfa, 0x95, 0xc3, 0x66, 0x7e, 0xb4, 0xaf, 0xdc, 0x1c, 0xed, 0xdf, 0x80, 0xf5, 0x90, 0xf1, 0xf1, 0x90, 0x4c, 0xb5,
0xf4, 0xc6, 0x2b, 0x0f, 0xd3, 0xd6, 0x6f, 0x0d, 0x38, 0x4c, 0xaf, 0x6c, 0xfb, 0x4c, 0xe0, 0xe5, 0xea, 0x15, 0x33, 0xcc, 0x69, 0x9e, 0x52, 0xbf, 0x70, 0xcc, 0x5e, 0x7d, 0xe5, 0x31, 0xdb, 0xf9,
0x71, 0x7b, 0xf5, 0xcd, 0x97, 0x6f, 0x51, 0xb8, 0x79, 0x8b, 0x95, 0x26, 0x16, 0x5f, 0xdd, 0xc4, 0xad, 0x05, 0xfb, 0xd9, 0x95, 0xdd, 0x90, 0x09, 0x3c, 0x3f, 0x88, 0x2f, 0xbe, 0xf9, 0xfc, 0x2d,
0x3f, 0x18, 0x70, 0x37, 0x93, 0x3b, 0x81, 0x47, 0xc7, 0xff, 0xd7, 0xb1, 0xb1, 0xfe, 0x61, 0xc0, 0x4a, 0x37, 0x6f, 0xb1, 0xd0, 0xc4, 0xf2, 0xab, 0x9b, 0xf8, 0x07, 0x0b, 0xee, 0xe5, 0x72, 0x27,
0x1b, 0xab, 0x61, 0x84, 0x29, 0x9f, 0x86, 0x01, 0xa7, 0x6b, 0x4c, 0xfe, 0x0e, 0x54, 0xd3, 0xa3, 0x0a, 0xe8, 0xf0, 0xff, 0x3a, 0x36, 0xce, 0xdf, 0x2d, 0x78, 0x7d, 0x31, 0x8c, 0x30, 0xe5, 0xe3,
0x9e, 0x53, 0x2c, 0x33, 0x59, 0x89, 0x17, 0x1b, 0x64, 0x61, 0x90, 0x23, 0xa6, 0xea, 0x58, 0x8a, 0x38, 0xe2, 0x74, 0x89, 0xc9, 0xdf, 0x82, 0x5a, 0x76, 0xd4, 0x73, 0x8a, 0x65, 0x2e, 0x2b, 0xf1,
0x0a, 0xe1, 0x29, 0xbd, 0x48, 0xde, 0x52, 0x36, 0x79, 0x97, 0xaf, 0x5b, 0xbe, 0x79, 0xdd, 0x43, 0x6c, 0x83, 0x2c, 0x0c, 0x72, 0xf8, 0x54, 0x1d, 0x4b, 0x59, 0x21, 0x3c, 0xa3, 0x67, 0xc9, 0x5b,
0x00, 0xdd, 0xcc, 0xb9, 0xb3, 0x88, 0xc5, 0x63, 0x7b, 0x55, 0x73, 0xce, 0x22, 0x66, 0x61, 0xd8, 0xc9, 0x27, 0xef, 0xfc, 0x75, 0x57, 0x6e, 0x5e, 0x77, 0x1f, 0x40, 0x37, 0x73, 0xfe, 0x24, 0x61,
0xbb, 0x79, 0xd3, 0x27, 0x94, 0x5c, 0xd3, 0xff, 0x18, 0x37, 0xd6, 0x0f, 0xe0, 0xcd, 0x4c, 0x21, 0x66, 0xa0, 0xaf, 0x69, 0xce, 0x59, 0xc2, 0x1c, 0x0c, 0x3b, 0x37, 0x6f, 0xfa, 0x84, 0x92, 0x6b,
0xd5, 0x6f, 0xd5, 0x72, 0xdf, 0xb8, 0x46, 0x7b, 0xde, 0xda, 0xc2, 0xb2, 0xb5, 0x7f, 0x32, 0xa0, 0xfa, 0x1f, 0xe3, 0xc6, 0xf9, 0x1e, 0xbc, 0x91, 0x2b, 0xa4, 0xfa, 0xad, 0x9a, 0xef, 0x1b, 0x97,
0xf6, 0x94, 0x3c, 0x9b, 0x25, 0x4d, 0x5e, 0x13, 0x8a, 0x9c, 0x5d, 0xc6, 0xc5, 0x4f, 0x7e, 0xca, 0x68, 0x2f, 0x5a, 0x5b, 0x9a, 0xb7, 0xf6, 0x4f, 0x16, 0xd4, 0x9f, 0x92, 0x67, 0x93, 0xb4, 0xc9,
0x42, 0x23, 0xd8, 0x84, 0x72, 0x41, 0x26, 0x53, 0xb5, 0xbf, 0x84, 0x17, 0x0c, 0x79, 0xa8, 0x08, 0x6b, 0x42, 0x99, 0xb3, 0x4b, 0x53, 0xfc, 0xe4, 0xa7, 0x2c, 0x34, 0x82, 0x8d, 0x28, 0x17, 0x64,
0xa7, 0xcc, 0x53, 0xee, 0xad, 0x63, 0x4d, 0xa8, 0x7f, 0x0a, 0xc8, 0x7c, 0x1c, 0x92, 0x04, 0x2f, 0x34, 0x56, 0xfb, 0x2b, 0x78, 0xc6, 0x90, 0x87, 0x8a, 0x78, 0xcc, 0x02, 0xe5, 0xde, 0x75, 0xac,
0x09, 0xa9, 0x57, 0x7c, 0x9f, 0x05, 0x97, 0xb1, 0x6b, 0x13, 0x52, 0x16, 0xf6, 0x2b, 0xc2, 0xaf, 0x09, 0xf5, 0x1f, 0x02, 0x99, 0x0e, 0x63, 0x92, 0xe2, 0x25, 0x25, 0xf5, 0x4a, 0x18, 0xb2, 0xe8,
0x94, 0x43, 0xeb, 0x58, 0x7d, 0x23, 0x0b, 0xea, 0xe2, 0x8a, 0x45, 0xfe, 0x29, 0x89, 0xa4, 0x1f, 0xd2, 0xb8, 0x36, 0x25, 0x65, 0x61, 0xbf, 0x22, 0xfc, 0x4a, 0x39, 0x74, 0x1d, 0xab, 0x6f, 0xe4,
0xe2, 0xf9, 0x35, 0xc7, 0xb3, 0x3e, 0x87, 0x83, 0xcc, 0x05, 0x12, 0xb7, 0x24, 0x1d, 0x9c, 0x09, 0xc0, 0xba, 0xb8, 0x62, 0x49, 0x78, 0x4a, 0x12, 0xe9, 0x07, 0x33, 0xd9, 0x16, 0x78, 0xce, 0xcf,
0x9b, 0xd7, 0x34, 0xe2, 0x49, 0x41, 0x6f, 0xe0, 0x84, 0x94, 0xe7, 0x5d, 0x44, 0xe1, 0x24, 0xbe, 0x61, 0x2f, 0x77, 0x81, 0xd4, 0x2d, 0x69, 0x07, 0x67, 0xc3, 0xda, 0x35, 0x4d, 0x78, 0x5a, 0xd0,
0x92, 0xfa, 0x96, 0xe3, 0xa8, 0x08, 0xe3, 0x3f, 0xce, 0x0a, 0x22, 0x94, 0xe7, 0xcb, 0x31, 0x9f, 0x1b, 0x38, 0x25, 0xe5, 0x79, 0x17, 0x49, 0x3c, 0x32, 0x57, 0x52, 0xdf, 0x72, 0x50, 0x15, 0xb1,
0x06, 0x62, 0xa4, 0x2e, 0x29, 0xa7, 0xc2, 0x3a, 0xce, 0xf1, 0xac, 0xdf, 0x19, 0x80, 0x6e, 0x1a, 0xf9, 0x4b, 0xad, 0x24, 0x62, 0x79, 0x7e, 0x10, 0x47, 0x82, 0x46, 0x62, 0xa0, 0x2e, 0x29, 0xe7,
0xf0, 0x9c, 0x83, 0x3f, 0x82, 0x4a, 0xda, 0xa1, 0x6a, 0x44, 0x67, 0x5a, 0x88, 0xf5, 0x57, 0xc1, 0xc5, 0x75, 0x5c, 0xe0, 0x39, 0xbf, 0xb3, 0x00, 0xdd, 0x34, 0xe0, 0x39, 0x07, 0x7f, 0x04, 0xd5,
0xe9, 0x2e, 0xf4, 0xae, 0xd4, 0xa0, 0x64, 0x92, 0xea, 0x71, 0x7b, 0xa5, 0x06, 0x9c, 0x8a, 0x59, 0xac, 0x43, 0xd5, 0x88, 0xce, 0xb5, 0x10, 0xcb, 0xaf, 0x82, 0xb3, 0x5d, 0xe8, 0x5d, 0xa9, 0x41,
0x7f, 0x36, 0xe0, 0xde, 0x4d, 0xdd, 0xbd, 0xc0, 0xa7, 0x3f, 0x7f, 0x09, 0x5f, 0x7d, 0x71, 0x93, 0xc9, 0xa4, 0xd5, 0xe3, 0xce, 0x42, 0x0d, 0x38, 0x13, 0x73, 0xfe, 0x6c, 0xc1, 0xfd, 0x9b, 0xba,
0x77, 0x61, 0x23, 0xbc, 0xb8, 0xe0, 0x54, 0xc4, 0xde, 0x8d, 0x29, 0x19, 0x05, 0xce, 0x7e, 0x41, 0x3b, 0x51, 0x48, 0x7f, 0xfa, 0x12, 0xbe, 0xfa, 0xfc, 0x26, 0x6f, 0xc3, 0x6a, 0x7c, 0x71, 0xc1,
0xe3, 0x7f, 0x58, 0xd5, 0xf7, 0x32, 0x46, 0x4a, 0x29, 0x46, 0xac, 0xbf, 0x18, 0xb0, 0xb7, 0xe6, 0xa9, 0x30, 0xde, 0x35, 0x94, 0x8c, 0x02, 0x67, 0x3f, 0xa3, 0xe6, 0xbf, 0x57, 0xf5, 0x3d, 0x8f,
0x16, 0xe8, 0x31, 0x54, 0xe2, 0x59, 0x2a, 0xe9, 0xcc, 0xee, 0x3f, 0xcf, 0x46, 0xb5, 0xa9, 0x1d, 0x91, 0x4a, 0x86, 0x11, 0xe7, 0x2f, 0x16, 0xec, 0x2c, 0xb9, 0x05, 0x7a, 0x0c, 0x55, 0x33, 0x4b,
0x13, 0x71, 0x93, 0x96, 0x2a, 0x38, 0xb8, 0x80, 0x46, 0x6e, 0x69, 0x45, 0xcf, 0xf3, 0x61, 0xbe, 0xa5, 0x9d, 0xd9, 0xdb, 0xcf, 0xb3, 0x51, 0x6d, 0x3a, 0x32, 0x84, 0x69, 0xd2, 0x32, 0x05, 0x7b,
0xe7, 0x79, 0xeb, 0x85, 0x87, 0xa5, 0x5e, 0x59, 0xf4, 0x40, 0x0f, 0x1b, 0x9f, 0xd5, 0xda, 0xf7, 0x17, 0xd0, 0x28, 0x2c, 0x2d, 0xe8, 0x79, 0x3e, 0x2c, 0xf6, 0x3c, 0x6f, 0xbe, 0xf0, 0xb0, 0xcc,
0xdf, 0x4f, 0x76, 0x9e, 0x6f, 0xa8, 0xaf, 0x6f, 0xfc, 0x3b, 0x00, 0x00, 0xff, 0xff, 0xf2, 0xe9, 0x2b, 0xb3, 0x1e, 0xe8, 0x61, 0xe3, 0xb3, 0xfa, 0xd1, 0xdb, 0xef, 0xa7, 0x3b, 0xcf, 0x57, 0xd5,
0x47, 0xc4, 0x1a, 0x17, 0x00, 0x00, 0xd7, 0xd7, 0xfe, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x7d, 0x68, 0x3c, 0x26, 0x34, 0x17, 0x00, 0x00,
} }

View File

@ -70,6 +70,7 @@ message CommunityTokenPermission {
BECOME_MEMBER = 2; BECOME_MEMBER = 2;
CAN_VIEW_CHANNEL = 3; CAN_VIEW_CHANNEL = 3;
CAN_VIEW_AND_POST_CHANNEL = 4; CAN_VIEW_AND_POST_CHANNEL = 4;
BECOME_TOKEN_MASTER = 5;
} }
string id = 1; string id = 1;

View File

@ -40,6 +40,7 @@ const (
CommunityEvent_COMMUNITY_MEMBER_KICK CommunityEvent_EventType = 14 CommunityEvent_COMMUNITY_MEMBER_KICK CommunityEvent_EventType = 14
CommunityEvent_COMMUNITY_MEMBER_BAN CommunityEvent_EventType = 15 CommunityEvent_COMMUNITY_MEMBER_BAN CommunityEvent_EventType = 15
CommunityEvent_COMMUNITY_MEMBER_UNBAN CommunityEvent_EventType = 16 CommunityEvent_COMMUNITY_MEMBER_UNBAN CommunityEvent_EventType = 16
CommunityEvent_COMMUNITY_TOKEN_ADD CommunityEvent_EventType = 17
) )
var CommunityEvent_EventType_name = map[int32]string{ var CommunityEvent_EventType_name = map[int32]string{
@ -60,6 +61,7 @@ var CommunityEvent_EventType_name = map[int32]string{
14: "COMMUNITY_MEMBER_KICK", 14: "COMMUNITY_MEMBER_KICK",
15: "COMMUNITY_MEMBER_BAN", 15: "COMMUNITY_MEMBER_BAN",
16: "COMMUNITY_MEMBER_UNBAN", 16: "COMMUNITY_MEMBER_UNBAN",
17: "COMMUNITY_TOKEN_ADD",
} }
var CommunityEvent_EventType_value = map[string]int32{ var CommunityEvent_EventType_value = map[string]int32{
@ -80,6 +82,7 @@ var CommunityEvent_EventType_value = map[string]int32{
"COMMUNITY_MEMBER_KICK": 14, "COMMUNITY_MEMBER_KICK": 14,
"COMMUNITY_MEMBER_BAN": 15, "COMMUNITY_MEMBER_BAN": 15,
"COMMUNITY_MEMBER_UNBAN": 16, "COMMUNITY_MEMBER_UNBAN": 16,
"COMMUNITY_TOKEN_ADD": 17,
} }
func (x CommunityEvent_EventType) String() string { func (x CommunityEvent_EventType) String() string {
@ -101,6 +104,7 @@ type CommunityEvent struct {
MembersAdded map[string]*CommunityMember `protobuf:"bytes,8,rep,name=membersAdded,proto3" json:"membersAdded,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` MembersAdded map[string]*CommunityMember `protobuf:"bytes,8,rep,name=membersAdded,proto3" json:"membersAdded,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
RejectedRequestsToJoin map[string]*CommunityRequestToJoin `protobuf:"bytes,9,rep,name=rejectedRequestsToJoin,proto3" json:"rejectedRequestsToJoin,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` RejectedRequestsToJoin map[string]*CommunityRequestToJoin `protobuf:"bytes,9,rep,name=rejectedRequestsToJoin,proto3" json:"rejectedRequestsToJoin,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
AcceptedRequestsToJoin map[string]*CommunityRequestToJoin `protobuf:"bytes,10,rep,name=acceptedRequestsToJoin,proto3" json:"acceptedRequestsToJoin,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` AcceptedRequestsToJoin map[string]*CommunityRequestToJoin `protobuf:"bytes,10,rep,name=acceptedRequestsToJoin,proto3" json:"acceptedRequestsToJoin,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"`
TokenMetadata *CommunityTokenMetadata `protobuf:"bytes,11,opt,name=token_metadata,json=tokenMetadata,proto3" json:"token_metadata,omitempty"`
XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_NoUnkeyedLiteral struct{} `json:"-"`
XXX_unrecognized []byte `json:"-"` XXX_unrecognized []byte `json:"-"`
XXX_sizecache int32 `json:"-"` XXX_sizecache int32 `json:"-"`
@ -201,6 +205,13 @@ func (m *CommunityEvent) GetAcceptedRequestsToJoin() map[string]*CommunityReques
return nil return nil
} }
func (m *CommunityEvent) GetTokenMetadata() *CommunityTokenMetadata {
if m != nil {
return m.TokenMetadata
}
return nil
}
type CommunityConfig struct { type CommunityConfig struct {
Identity *ChatIdentity `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"` Identity *ChatIdentity `protobuf:"bytes,1,opt,name=identity,proto3" json:"identity,omitempty"`
Permissions *CommunityPermissions `protobuf:"bytes,2,opt,name=permissions,proto3" json:"permissions,omitempty"` Permissions *CommunityPermissions `protobuf:"bytes,2,opt,name=permissions,proto3" json:"permissions,omitempty"`
@ -482,66 +493,69 @@ func init() {
} }
var fileDescriptor_52ed23dfc73918ab = []byte{ var fileDescriptor_52ed23dfc73918ab = []byte{
// 974 bytes of a gzipped FileDescriptorProto // 1010 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0x5d, 0x6f, 0xdb, 0x36, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x56, 0xdd, 0x6e, 0xdb, 0x36,
0x14, 0x9d, 0x63, 0xe7, 0xc3, 0x57, 0x8e, 0xa3, 0xb2, 0x8b, 0xab, 0xb8, 0x6b, 0xe6, 0x7a, 0x7b, 0x14, 0x9e, 0x63, 0xe7, 0xc7, 0x47, 0xb6, 0xa3, 0x32, 0x8b, 0xe3, 0xb8, 0x6b, 0xe6, 0x7a, 0xbb,
0x30, 0x86, 0x21, 0xc5, 0xb2, 0xa1, 0x28, 0xd6, 0x97, 0x29, 0x32, 0x91, 0x2a, 0xa9, 0xe5, 0x8c, 0x30, 0x86, 0x21, 0xc5, 0xb2, 0xa1, 0x28, 0xd6, 0x9b, 0x29, 0x32, 0x91, 0x2a, 0xa9, 0xe5, 0x8c,
0x51, 0x30, 0xb4, 0x2f, 0x04, 0x23, 0xb1, 0x89, 0x96, 0x58, 0xf2, 0x4c, 0xba, 0x80, 0x5f, 0xf7, 0x51, 0x30, 0xb4, 0x37, 0x04, 0x23, 0xb1, 0x89, 0x96, 0x58, 0xf2, 0x4c, 0xba, 0x80, 0x6f, 0xf7,
0x27, 0xf6, 0x32, 0xec, 0x27, 0xf4, 0x37, 0x0e, 0xa2, 0x24, 0x4b, 0x8a, 0x95, 0x74, 0x0f, 0x7b, 0x04, 0x7b, 0x80, 0x61, 0x8f, 0x30, 0xec, 0x11, 0x07, 0x51, 0x92, 0x25, 0x25, 0x72, 0xba, 0x8b,
0x49, 0xc8, 0x7b, 0xce, 0x3d, 0xe7, 0x92, 0xb8, 0xe2, 0x35, 0x74, 0xbc, 0x68, 0x32, 0x99, 0x87, 0xdd, 0xd8, 0xe2, 0xf9, 0xbe, 0xf3, 0x7d, 0x87, 0x14, 0xc9, 0x23, 0x68, 0xbb, 0xe1, 0x64, 0x32,
0x81, 0x5c, 0xd0, 0xf9, 0xd4, 0x67, 0x92, 0x1f, 0x4c, 0x67, 0x91, 0x8c, 0xd0, 0x96, 0xfa, 0x77, 0x0f, 0x7c, 0xb9, 0xa0, 0xf3, 0xa9, 0xc7, 0x24, 0x3f, 0x9c, 0xce, 0x42, 0x19, 0xa2, 0x2d, 0xf5,
0x39, 0xff, 0xd0, 0x7d, 0xec, 0x5d, 0x33, 0x49, 0x03, 0x9f, 0x87, 0x32, 0x90, 0x8b, 0x04, 0xee, 0x77, 0x35, 0xff, 0xd0, 0xdd, 0x71, 0x6f, 0x98, 0xa4, 0xbe, 0xc7, 0x03, 0xe9, 0xcb, 0x45, 0x0c,
0x3e, 0xca, 0xd2, 0x02, 0x2e, 0x92, 0x50, 0xff, 0x53, 0x0b, 0xda, 0x56, 0x26, 0x86, 0x3f, 0xf2, 0x77, 0x9f, 0xa4, 0x69, 0x3e, 0x17, 0x71, 0xa8, 0xff, 0x47, 0x13, 0x5a, 0x66, 0x2a, 0x86, 0x3f,
0x50, 0xa2, 0x43, 0xd8, 0xcd, 0xe5, 0x79, 0x1c, 0xa2, 0xde, 0x6d, 0xe4, 0xdd, 0x18, 0xb5, 0x5e, 0xf2, 0x40, 0xa2, 0x23, 0xd8, 0xcd, 0xe4, 0x79, 0x14, 0xa2, 0xee, 0x5d, 0xe8, 0xde, 0x76, 0x2a,
0x6d, 0xd0, 0x20, 0x8f, 0xbd, 0x12, 0xdd, 0x8a, 0x21, 0xf4, 0x12, 0x1a, 0x72, 0x31, 0xe5, 0xc6, 0xbd, 0xca, 0xa0, 0x46, 0x76, 0xdc, 0x02, 0xdd, 0x8c, 0x20, 0xf4, 0x12, 0x6a, 0x72, 0x31, 0xe5,
0x5a, 0xaf, 0x36, 0x68, 0x1f, 0xf6, 0x0f, 0xb2, 0x3a, 0x0e, 0xca, 0xda, 0x07, 0xea, 0xaf, 0xbb, 0x9d, 0xb5, 0x5e, 0x65, 0xd0, 0x3a, 0xea, 0x1f, 0xa6, 0x75, 0x1c, 0x16, 0xb5, 0x0f, 0xd5, 0xaf,
0x98, 0x72, 0xa2, 0xf8, 0x68, 0x08, 0x7a, 0xee, 0xe5, 0x45, 0xe1, 0x87, 0xe0, 0xca, 0xa8, 0xf7, 0xb3, 0x98, 0x72, 0xa2, 0xf8, 0x68, 0x08, 0x7a, 0xe6, 0xe5, 0x86, 0xc1, 0x07, 0xff, 0xba, 0x53,
0x6a, 0x03, 0xed, 0x70, 0xaf, 0x42, 0xc3, 0x52, 0x04, 0xb2, 0xe3, 0x95, 0x03, 0x68, 0x04, 0xba, 0xed, 0x55, 0x06, 0xda, 0xd1, 0x7e, 0x89, 0x86, 0xa9, 0x08, 0x64, 0xdb, 0x2d, 0x06, 0xd0, 0x08,
0x8c, 0x6e, 0x78, 0x48, 0xa7, 0x7c, 0x36, 0x09, 0x84, 0x08, 0xa2, 0xd0, 0x68, 0x28, 0x95, 0xaa, 0x74, 0x19, 0xde, 0xf2, 0x80, 0x4e, 0xf9, 0x6c, 0xe2, 0x0b, 0xe1, 0x87, 0x41, 0xa7, 0xa6, 0x54,
0x4a, 0xdc, 0x98, 0x7a, 0xb6, 0x64, 0x92, 0x1d, 0x59, 0x0e, 0xa0, 0xd7, 0xb0, 0xed, 0x31, 0xc9, 0xca, 0x2a, 0x71, 0x22, 0xea, 0xf9, 0x92, 0x49, 0xb6, 0x65, 0x31, 0x80, 0x5e, 0x43, 0xd3, 0x65,
0xaf, 0xa2, 0xd9, 0x82, 0xfa, 0x4c, 0x32, 0x63, 0x5d, 0x69, 0x75, 0x0a, 0x5a, 0x29, 0x3c, 0x64, 0x92, 0x5f, 0x87, 0xb3, 0x05, 0xf5, 0x98, 0x64, 0x9d, 0x75, 0xa5, 0xd5, 0xce, 0x69, 0x25, 0xf0,
0x92, 0x91, 0x96, 0x57, 0xd8, 0xa1, 0x57, 0xd0, 0xf2, 0xae, 0x59, 0x18, 0xf2, 0xdb, 0x24, 0x77, 0x90, 0x49, 0x46, 0x1a, 0x6e, 0x6e, 0x84, 0x5e, 0x41, 0xc3, 0xbd, 0x61, 0x41, 0xc0, 0xef, 0xe2,
0x43, 0xe5, 0xee, 0x16, 0x72, 0x13, 0x54, 0xa5, 0x6a, 0x5e, 0xbe, 0x41, 0x03, 0xd0, 0x27, 0x7c, 0xdc, 0x0d, 0x95, 0xbb, 0x9b, 0xcb, 0x8d, 0x51, 0x95, 0xaa, 0xb9, 0xd9, 0x00, 0x0d, 0x40, 0x9f,
0x72, 0xc9, 0x67, 0x54, 0x46, 0x94, 0x79, 0x32, 0x3e, 0xc5, 0x66, 0xaf, 0x36, 0x68, 0x92, 0x76, 0xf0, 0xc9, 0x15, 0x9f, 0x51, 0x19, 0x52, 0xe6, 0xca, 0x68, 0x16, 0x9b, 0xbd, 0xca, 0xa0, 0x4e,
0x12, 0x77, 0x23, 0x53, 0x45, 0x91, 0x03, 0xad, 0x24, 0x22, 0x4c, 0xdf, 0xe7, 0xbe, 0xb1, 0xd5, 0x5a, 0x71, 0xdc, 0x09, 0x0d, 0x15, 0x45, 0x36, 0x34, 0xe2, 0x88, 0x30, 0x3c, 0x8f, 0x7b, 0x9d,
0xab, 0x0f, 0xb4, 0xc3, 0xef, 0xee, 0xbd, 0xf5, 0x51, 0x81, 0x8c, 0x43, 0x39, 0x5b, 0x90, 0x52, 0xad, 0x5e, 0x75, 0xa0, 0x1d, 0x7d, 0xb3, 0x72, 0xd5, 0x47, 0x39, 0x32, 0x0e, 0xe4, 0x6c, 0x41,
0x3e, 0xba, 0x85, 0xce, 0x8c, 0xff, 0xce, 0x3d, 0xc9, 0x7d, 0xc2, 0xff, 0x98, 0x73, 0x21, 0x85, 0x0a, 0xf9, 0xe8, 0x0e, 0xda, 0x33, 0xfe, 0x2b, 0x77, 0x25, 0xf7, 0x08, 0xff, 0x6d, 0xce, 0x85,
0x1b, 0x9d, 0x44, 0x41, 0x68, 0x34, 0x95, 0xf2, 0x4f, 0xf7, 0x2a, 0x93, 0xca, 0xb4, 0xc4, 0xe3, 0x14, 0x4e, 0x78, 0x1a, 0xfa, 0x41, 0xa7, 0xae, 0x94, 0x7f, 0x58, 0xa9, 0x4c, 0x4a, 0xd3, 0x62,
0x1e, 0xcd, 0xd8, 0x8d, 0x79, 0x1e, 0x9f, 0xae, 0xba, 0xc1, 0x67, 0xdc, 0xcc, 0xca, 0xb4, 0xd4, 0x8f, 0x15, 0x9a, 0x91, 0x1b, 0x73, 0x5d, 0x3e, 0x7d, 0xe8, 0x06, 0x9f, 0x70, 0x33, 0x4a, 0xd3,
0xad, 0x5a, 0xb3, 0xfb, 0x1e, 0x1e, 0xad, 0x1c, 0x1f, 0xe9, 0x50, 0xbf, 0xe1, 0x0b, 0xd5, 0xd0, 0x12, 0xb7, 0x72, 0x4d, 0x74, 0x02, 0xad, 0x78, 0x6f, 0x4c, 0xb8, 0x64, 0xea, 0x8d, 0x68, 0xea,
0x4d, 0x12, 0x2f, 0xd1, 0x0b, 0x58, 0xff, 0xc8, 0x6e, 0xe7, 0x49, 0x07, 0x57, 0x77, 0x5f, 0x22, 0x8d, 0xf4, 0x56, 0xed, 0x8c, 0x51, 0xc2, 0x23, 0x4d, 0x99, 0x1f, 0x76, 0xdf, 0xc3, 0x93, 0x07,
0x43, 0x12, 0xde, 0xcf, 0x6b, 0xaf, 0x6a, 0xdd, 0x1b, 0x78, 0xfa, 0xc0, 0x05, 0x54, 0xb8, 0xbc, 0xeb, 0x88, 0x74, 0xa8, 0xde, 0xf2, 0x85, 0x3a, 0x19, 0x75, 0x12, 0x3d, 0xa2, 0x17, 0xb0, 0xfe,
0x2c, 0xbb, 0xf4, 0x2a, 0x5c, 0x52, 0xa1, 0x44, 0xe7, 0x8e, 0xd9, 0x03, 0xe7, 0xff, 0x7f, 0xcd, 0x91, 0xdd, 0xcd, 0xe3, 0xa3, 0x50, 0xbe, 0x8d, 0x63, 0x19, 0x12, 0xf3, 0x7e, 0x5c, 0x7b, 0x55,
0xfa, 0x7f, 0x35, 0xa0, 0xb9, 0xfc, 0x56, 0x91, 0x06, 0x9b, 0x17, 0xce, 0xa9, 0x33, 0xfe, 0xcd, 0xe9, 0xde, 0xc2, 0xd3, 0x47, 0x56, 0xb2, 0xc4, 0xe5, 0x65, 0xd1, 0xa5, 0x6c, 0x32, 0x89, 0x50,
0xd1, 0xbf, 0x40, 0x08, 0xda, 0xd6, 0x78, 0x34, 0xba, 0x70, 0x6c, 0xf7, 0x1d, 0xc5, 0x43, 0xdb, 0xac, 0x73, 0xcf, 0xec, 0x91, 0x85, 0xfc, 0x7f, 0xcd, 0xfa, 0xff, 0xd4, 0xa0, 0xbe, 0x3c, 0xf4,
0xd5, 0x6b, 0xe8, 0x7b, 0x18, 0xe4, 0xb1, 0x11, 0x1e, 0x1d, 0x61, 0x42, 0xdd, 0xf1, 0x29, 0x76, 0x48, 0x83, 0xcd, 0x4b, 0xfb, 0xcc, 0x1e, 0xff, 0x62, 0xeb, 0x9f, 0x21, 0x04, 0x2d, 0x73, 0x3c,
0xe8, 0x19, 0x26, 0x23, 0xfb, 0xfc, 0xdc, 0x1e, 0x3b, 0xd4, 0x7a, 0x63, 0x3a, 0xc7, 0x58, 0x5f, 0x1a, 0x5d, 0xda, 0x96, 0xf3, 0x8e, 0xe2, 0xa1, 0xe5, 0xe8, 0x15, 0xf4, 0x2d, 0x0c, 0xb2, 0xd8,
0xfb, 0x6f, 0xec, 0x21, 0x7e, 0x8b, 0x5d, 0xac, 0xd7, 0xd1, 0x33, 0xd8, 0xcb, 0xd9, 0x96, 0xe9, 0x08, 0x8f, 0x8e, 0x31, 0xa1, 0xce, 0xf8, 0x0c, 0xdb, 0xf4, 0x1c, 0x93, 0x91, 0x75, 0x71, 0x61,
0xe2, 0xe3, 0x31, 0x79, 0x47, 0x2d, 0x82, 0x4d, 0x17, 0xeb, 0x8d, 0x7b, 0xe0, 0x34, 0x7b, 0x1d, 0x8d, 0x6d, 0x6a, 0xbe, 0x31, 0xec, 0x13, 0xac, 0xaf, 0xfd, 0x37, 0xf6, 0x10, 0xbf, 0xc5, 0x0e,
0x3d, 0x85, 0x27, 0x15, 0xb0, 0x2a, 0x7b, 0x03, 0x7d, 0x05, 0x46, 0x01, 0x7c, 0x63, 0x3a, 0x0e, 0xd6, 0xab, 0xe8, 0x19, 0xec, 0x67, 0x6c, 0xd3, 0x70, 0xf0, 0xc9, 0x98, 0xbc, 0xa3, 0x26, 0xc1,
0x7e, 0x9b, 0x29, 0x6f, 0x56, 0xa3, 0xa9, 0xf0, 0x16, 0xea, 0x42, 0x67, 0x15, 0x55, 0xba, 0x4d, 0x86, 0x83, 0xf5, 0xda, 0x0a, 0x38, 0xc9, 0x5e, 0x47, 0x4f, 0x61, 0xaf, 0x04, 0x56, 0x65, 0x6f,
0xb4, 0x0f, 0xdd, 0x0a, 0x53, 0x82, 0xc7, 0x64, 0x88, 0x89, 0x0e, 0x77, 0x6a, 0x4e, 0x73, 0x33, 0xa0, 0x2f, 0xa0, 0x93, 0x03, 0xdf, 0x18, 0xb6, 0x8d, 0xdf, 0xa6, 0xca, 0x9b, 0xe5, 0x68, 0x22,
0x58, 0x43, 0xdf, 0x42, 0x2f, 0x87, 0x09, 0xfe, 0xf5, 0x02, 0x9f, 0xbb, 0xd4, 0x1d, 0xd3, 0x93, 0xbc, 0x85, 0xba, 0xd0, 0x7e, 0x88, 0x2a, 0xdd, 0x3a, 0x3a, 0x80, 0x6e, 0x89, 0x29, 0xc1, 0x63,
0xb1, 0xed, 0x50, 0xd3, 0xb2, 0xf0, 0x99, 0xab, 0xb7, 0x1e, 0x66, 0x11, 0x7c, 0x82, 0x2d, 0x57, 0x32, 0xc4, 0x44, 0x87, 0x7b, 0x35, 0x27, 0xb9, 0x29, 0xac, 0xa1, 0xaf, 0xa1, 0x97, 0xc1, 0x04,
0xdf, 0x46, 0x7b, 0xb0, 0xbb, 0x72, 0xd7, 0xa7, 0xb6, 0x75, 0xaa, 0xb7, 0x91, 0x01, 0x5f, 0xae, 0xff, 0x7c, 0x89, 0x2f, 0x1c, 0xea, 0x8c, 0xe9, 0xe9, 0xd8, 0xb2, 0xa9, 0x61, 0x9a, 0xf8, 0xdc,
0x40, 0x47, 0xa6, 0xa3, 0xef, 0x94, 0xcf, 0x96, 0x22, 0x17, 0x4e, 0x8c, 0xe9, 0xfd, 0x4f, 0x6b, 0xd1, 0x1b, 0x8f, 0xb3, 0x08, 0x3e, 0xc5, 0xa6, 0xa3, 0x37, 0xd1, 0x3e, 0xec, 0x3e, 0x58, 0xeb,
0xb0, 0x73, 0xe7, 0x41, 0x46, 0x87, 0xb0, 0x95, 0x4d, 0x1a, 0xd5, 0x80, 0xe5, 0xb7, 0xf2, 0x9a, 0x33, 0xcb, 0x3c, 0xd3, 0x5b, 0xa8, 0x03, 0x9f, 0x3f, 0x80, 0x8e, 0x0d, 0x5b, 0xdf, 0x2e, 0xce,
0x49, 0x3b, 0x45, 0xc9, 0x92, 0x87, 0x7e, 0x01, 0x2d, 0x7f, 0xad, 0x45, 0xda, 0xa3, 0xfb, 0x15, 0x2d, 0x41, 0x2e, 0xed, 0x08, 0xd3, 0xd1, 0x1e, 0xec, 0x64, 0x58, 0xfc, 0xd6, 0x8c, 0xe1, 0x50,
0x3d, 0x9a, 0x3f, 0xcc, 0x82, 0x14, 0x53, 0xd0, 0x31, 0xb4, 0x99, 0x3f, 0x09, 0x42, 0x2a, 0xb8, 0x7f, 0xd2, 0xff, 0x7b, 0x0d, 0xb6, 0xef, 0x5d, 0xf9, 0xe8, 0x08, 0xb6, 0xd2, 0x5e, 0xa6, 0x76,
0x94, 0x41, 0x78, 0x25, 0xd2, 0xc9, 0x51, 0xd5, 0xe8, 0x66, 0x4c, 0x3c, 0x4f, 0x79, 0x64, 0x9b, 0x66, 0xf1, 0x36, 0xbe, 0x61, 0xd2, 0x4a, 0x50, 0xb2, 0xe4, 0xa1, 0x9f, 0x40, 0xcb, 0xfa, 0x81,
0x15, 0xb7, 0xe8, 0x1b, 0xd8, 0x0e, 0x42, 0x39, 0x8b, 0xe8, 0x84, 0x0b, 0xc1, 0xae, 0xb8, 0x9a, 0x48, 0x36, 0xef, 0x41, 0xc9, 0xe6, 0xcd, 0xae, 0x7e, 0x41, 0xf2, 0x29, 0xd1, 0xdd, 0xc1, 0xbc,
0x1d, 0x4d, 0xd2, 0x52, 0xc1, 0x51, 0x12, 0x8b, 0x49, 0xd1, 0xbc, 0x48, 0x5a, 0x4f, 0x48, 0x2a, 0x89, 0x1f, 0x50, 0xc1, 0xa5, 0xf4, 0x83, 0x6b, 0x91, 0xf4, 0xa6, 0xb2, 0x13, 0x60, 0x44, 0xc4,
0x98, 0x91, 0x10, 0x34, 0x24, 0xbb, 0x12, 0xc6, 0x46, 0xaf, 0x3e, 0x68, 0x12, 0xb5, 0xee, 0xff, 0x8b, 0x84, 0x47, 0x9a, 0x2c, 0x3f, 0x44, 0x5f, 0x41, 0xd3, 0x0f, 0xe4, 0x2c, 0xa4, 0x13, 0x2e,
0x59, 0x83, 0x56, 0x71, 0x5e, 0xa0, 0xaf, 0x41, 0x5b, 0x8e, 0x97, 0xc0, 0x4f, 0xbf, 0x58, 0xc8, 0x04, 0xbb, 0xe6, 0xaa, 0x3b, 0xd5, 0x49, 0x43, 0x05, 0x47, 0x71, 0x2c, 0x22, 0x85, 0xf3, 0x3c,
0x42, 0xb6, 0x1f, 0xab, 0x84, 0x6c, 0x92, 0x7c, 0xb7, 0x4d, 0xa2, 0xd6, 0xe8, 0xf9, 0x72, 0xac, 0x69, 0x3d, 0x26, 0xa9, 0x60, 0x4a, 0x42, 0x50, 0x93, 0xec, 0x5a, 0x74, 0x36, 0x7a, 0xd5, 0x41,
0x08, 0x1a, 0xf8, 0xf1, 0x51, 0x63, 0x87, 0x6c, 0x7e, 0x08, 0xdb, 0x17, 0xa8, 0x0b, 0x5b, 0xd3, 0x9d, 0xa8, 0xe7, 0xfe, 0xef, 0x15, 0x68, 0xe4, 0x3b, 0x12, 0xfa, 0x12, 0xb4, 0x65, 0x03, 0xf3,
0x48, 0x04, 0x32, 0x9b, 0x7e, 0xeb, 0x64, 0xb9, 0xef, 0xff, 0x5d, 0x03, 0xad, 0x30, 0x78, 0x3e, 0xbd, 0xe4, 0x28, 0x43, 0x1a, 0xb2, 0xbc, 0x48, 0x25, 0x60, 0x93, 0xf8, 0x40, 0xd7, 0x89, 0x7a,
0x5f, 0xc3, 0x33, 0x80, 0x6c, 0x8c, 0x05, 0x7e, 0x5a, 0x49, 0x33, 0x8d, 0xd8, 0x7e, 0xc9, 0xab, 0x46, 0xcf, 0x97, 0x8d, 0x4b, 0x50, 0xdf, 0x8b, 0xa6, 0x1a, 0x39, 0xa4, 0x1d, 0x4a, 0x58, 0x9e,
0x5e, 0xf6, 0x42, 0x3f, 0xc0, 0x66, 0x4a, 0x4c, 0x87, 0xf0, 0x93, 0xaa, 0x51, 0x7e, 0xcd, 0x24, 0x40, 0x5d, 0xd8, 0x9a, 0x86, 0xc2, 0x97, 0x69, 0x7f, 0x5d, 0x27, 0xcb, 0x71, 0xff, 0xcf, 0x0a,
0xc9, 0x78, 0xfd, 0x7f, 0x6a, 0xd0, 0x29, 0xbf, 0xf5, 0x22, 0xbb, 0xd2, 0xf8, 0xe0, 0xcb, 0x5f, 0x68, 0xb9, 0xd6, 0xf6, 0xe9, 0x1a, 0x9e, 0x01, 0xa4, 0x8d, 0xd2, 0xf7, 0x92, 0x4a, 0xea, 0x49,
0x08, 0x69, 0xa9, 0x2d, 0xa2, 0x2d, 0x63, 0xb6, 0x8f, 0x6c, 0x78, 0xae, 0x7e, 0xa6, 0x08, 0x7a, 0xc4, 0xf2, 0x0a, 0x5e, 0xd5, 0xa2, 0x17, 0xfa, 0x0e, 0x36, 0x13, 0x62, 0xd2, 0xe6, 0xf7, 0xca,
0xc9, 0x04, 0xa7, 0x39, 0xdd, 0xe7, 0xc2, 0x9b, 0x05, 0x53, 0x55, 0xe5, 0x9a, 0xca, 0xdb, 0x4f, 0x3e, 0x16, 0x6e, 0x98, 0x24, 0x29, 0xaf, 0xff, 0x57, 0x05, 0xda, 0xc5, 0x6e, 0x22, 0xd2, 0x25,
0x88, 0x47, 0x4c, 0xf0, 0xa5, 0xdf, 0x30, 0x67, 0xa1, 0x0e, 0x6c, 0x24, 0x0c, 0x75, 0xc1, 0x2d, 0x8d, 0x26, 0xbe, 0xfc, 0x06, 0x49, 0x4a, 0x6d, 0x10, 0x6d, 0x19, 0xb3, 0x3c, 0x64, 0xc1, 0x73,
0x92, 0xee, 0x8e, 0xb6, 0xdf, 0x6b, 0x07, 0x2f, 0x5e, 0x67, 0xc7, 0xb8, 0xdc, 0x50, 0xab, 0x1f, 0xf5, 0x21, 0x24, 0xe8, 0x15, 0x13, 0x9c, 0x66, 0x74, 0x8f, 0x0b, 0x77, 0xe6, 0x4f, 0x55, 0x95,
0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x6c, 0xdd, 0xb4, 0xc1, 0x88, 0x09, 0x00, 0x00, 0x6b, 0x2a, 0xef, 0x20, 0x26, 0x1e, 0x33, 0xc1, 0x97, 0x7e, 0xc3, 0x8c, 0x85, 0xda, 0xb0, 0x11,
0x33, 0xd4, 0x02, 0x37, 0x48, 0x32, 0x3a, 0x6e, 0xbe, 0xd7, 0x0e, 0x5f, 0xbc, 0x4e, 0xa7, 0x71,
0xb5, 0xa1, 0x9e, 0xbe, 0xff, 0x37, 0x00, 0x00, 0xff, 0xff, 0xe3, 0x67, 0xf7, 0x49, 0xea, 0x09,
0x00, 0x00,
} }

View File

@ -17,6 +17,7 @@ message CommunityEvent {
map<string,CommunityMember> membersAdded = 8; map<string,CommunityMember> membersAdded = 8;
map<string,CommunityRequestToJoin> rejectedRequestsToJoin = 9; map<string,CommunityRequestToJoin> rejectedRequestsToJoin = 9;
map<string,CommunityRequestToJoin> acceptedRequestsToJoin = 10; map<string,CommunityRequestToJoin> acceptedRequestsToJoin = 10;
CommunityTokenMetadata token_metadata = 11;
enum EventType { enum EventType {
UNKNOWN = 0; UNKNOWN = 0;
@ -36,6 +37,7 @@ message CommunityEvent {
COMMUNITY_MEMBER_KICK = 14; COMMUNITY_MEMBER_KICK = 14;
COMMUNITY_MEMBER_BAN = 15; COMMUNITY_MEMBER_BAN = 15;
COMMUNITY_MEMBER_UNBAN = 16; COMMUNITY_MEMBER_UNBAN = 16;
COMMUNITY_TOKEN_ADD = 17;
} }
} }