fix_: Publish token actions to privileged members

Send information to owners and token masters about operations: burn, airdrop, remote destruct.
Add CommunityTokenActionSignal to signalize client side.

Fix #13371
This commit is contained in:
Michal Iskierko 2024-05-24 12:34:36 +02:00 committed by Michał Iskierko
parent aec567fd9f
commit b38a9f5878
10 changed files with 983 additions and 660 deletions

View File

@ -225,6 +225,7 @@ type CommunityTokensServiceInterface interface {
GetAssetContractData(chainID uint64, contractAddress string) (*AssetContractData, error)
SafeGetSignerPubKey(ctx context.Context, chainID uint64, communityID string) (string, error)
DeploymentSignatureDigest(chainID uint64, addressFrom string, communityID string) ([]byte, error)
ProcessCommunityTokenAction(message *protobuf.CommunityTokenAction) error
}
type DefaultTokenManager struct {

View File

@ -149,6 +149,10 @@ func (c *CollectiblesServiceMock) DeploymentSignatureDigest(chainID uint64, addr
return gethcommon.Hex2Bytes("ccbb375343347491706cf4b43796f7b96ccc89c9e191a8b78679daeba1684ec7"), nil
}
func (s *CollectiblesServiceMock) ProcessCommunityTokenAction(message *protobuf.CommunityTokenAction) error {
return nil
}
type testCommunitiesMessengerConfig struct {
testMessengerConfig

View File

@ -590,6 +590,10 @@ func (m *Messenger) HandleCommunityEncryptionKeysRequest(state *ReceivedMessageS
return m.handleCommunityEncryptionKeysRequest(community, signer)
}
func (m *Messenger) HandleCommunityTokenAction(state *ReceivedMessageState, message *protobuf.CommunityTokenAction, statusMessage *v1protocol.StatusMessage) error {
return m.communityTokensService.ProcessCommunityTokenAction(message)
}
func (m *Messenger) handleCommunityEncryptionKeysRequest(community *communities.Community, signer *ecdsa.PublicKey) error {
if !community.HasMember(signer) {
return communities.ErrMemberNotFound
@ -1607,6 +1611,57 @@ func (m *Messenger) EditSharedAddressesForCommunity(request *requests.EditShared
return response, nil
}
func (m *Messenger) PublishTokenActionToPrivilegedMembers(communityID []byte, chainID uint64, contractAddress string, actionType protobuf.CommunityTokenAction_ActionType) error {
community, err := m.communitiesManager.GetByID(communityID)
if err != nil {
return err
}
tokenActionProto := &protobuf.CommunityTokenAction{
ChainId: chainID,
ContractAddress: contractAddress,
ActionType: actionType,
}
payload, err := proto.Marshal(tokenActionProto)
if err != nil {
return err
}
rawMessage := common.RawMessage{
Payload: payload,
CommunityID: community.ID(),
ResendType: common.ResendTypeRawMessage,
ResendMethod: common.ResendMethodSendPrivate,
MessageType: protobuf.ApplicationMetadataMessage_COMMUNITY_TOKEN_ACTION,
PubsubTopic: community.PubsubTopic(),
}
skipMembers := make(map[string]struct{})
skipMembers[common.PubkeyToHex(&m.identity.PublicKey)] = struct{}{}
privilegedMembers := community.GetFilteredPrivilegedMembers(skipMembers)
allRecipients := privilegedMembers[protobuf.CommunityMember_ROLE_OWNER]
allRecipients = append(allRecipients, privilegedMembers[protobuf.CommunityMember_ROLE_TOKEN_MASTER]...)
for _, recipient := range allRecipients {
_, err := m.sender.SendPrivate(context.Background(), recipient, &rawMessage)
if err != nil {
return err
}
}
if len(allRecipients) > 0 {
rawMessage.Recipients = allRecipients
if _, err = m.UpsertRawMessageToWatch(&rawMessage); err != nil {
return err
}
}
return nil
}
func (m *Messenger) GetRevealedAccounts(communityID types.HexBytes, memberPk string) ([]*protobuf.RevealedAccount, error) {
return m.communitiesManager.GetRevealedAddresses(communityID, memberPk)
}

View File

@ -255,6 +255,9 @@ func (m *Messenger) dispatchToHandler(messageState *ReceivedMessageState, protoB
case protobuf.ApplicationMetadataMessage_COMMUNITY_ENCRYPTION_KEYS_REQUEST:
return m.handleCommunityEncryptionKeysRequestProtobuf(messageState, protoBytes, msg, filter)
case protobuf.ApplicationMetadataMessage_COMMUNITY_TOKEN_ACTION:
return m.handleCommunityTokenActionProtobuf(messageState, protoBytes, msg, filter)
default:
m.logger.Info("protobuf type not found", zap.String("type", string(msg.ApplicationLayer.Type)))
@ -1832,4 +1835,17 @@ func (m *Messenger) handleCommunityEncryptionKeysRequestProtobuf(messageState *R
}
func (m *Messenger) handleCommunityTokenActionProtobuf(messageState *ReceivedMessageState, protoBytes []byte, msg *v1protocol.StatusMessage, filter transport.Filter) error {
m.logger.Info("handling CommunityTokenAction")
p := &protobuf.CommunityTokenAction{}
err := proto.Unmarshal(protoBytes, p)
if err != nil {
return err
}
m.outputToCSV(msg.TransportLayer.Message.Timestamp, msg.ApplicationLayer.ID, messageState.CurrentMessageState.Contact.ID, filter.ContentTopic, filter.ChatID, msg.ApplicationLayer.Type, p)
return m.HandleCommunityTokenAction(messageState, p, msg)
}

View File

@ -109,6 +109,7 @@ const (
ApplicationMetadataMessage_DELETE_COMMUNITY_MEMBER_MESSAGES ApplicationMetadataMessage_Type = 85
ApplicationMetadataMessage_COMMUNITY_UPDATE_GRANT ApplicationMetadataMessage_Type = 86
ApplicationMetadataMessage_COMMUNITY_ENCRYPTION_KEYS_REQUEST ApplicationMetadataMessage_Type = 87
ApplicationMetadataMessage_COMMUNITY_TOKEN_ACTION ApplicationMetadataMessage_Type = 88
)
// Enum value maps for ApplicationMetadataMessage_Type.
@ -197,6 +198,7 @@ var (
85: "DELETE_COMMUNITY_MEMBER_MESSAGES",
86: "COMMUNITY_UPDATE_GRANT",
87: "COMMUNITY_ENCRYPTION_KEYS_REQUEST",
88: "COMMUNITY_TOKEN_ACTION",
}
ApplicationMetadataMessage_Type_value = map[string]int32{
"UNKNOWN": 0,
@ -282,6 +284,7 @@ var (
"DELETE_COMMUNITY_MEMBER_MESSAGES": 85,
"COMMUNITY_UPDATE_GRANT": 86,
"COMMUNITY_ENCRYPTION_KEYS_REQUEST": 87,
"COMMUNITY_TOKEN_ACTION": 88,
}
)
@ -383,7 +386,7 @@ var File_application_metadata_message_proto protoreflect.FileDescriptor
var file_application_metadata_message_proto_rawDesc = []byte{
0x0a, 0x22, 0x61, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x6d, 0x65,
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x5f, 0x6d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x70,
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x22, 0x8a,
0x72, 0x6f, 0x74, 0x6f, 0x12, 0x08, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x22, 0xa6,
0x16, 0x0a, 0x1a, 0x41, 0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65,
0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x12, 0x1c, 0x0a,
0x09, 0x73, 0x69, 0x67, 0x6e, 0x61, 0x74, 0x75, 0x72, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0c,
@ -393,7 +396,7 @@ var file_application_metadata_message_proto_rawDesc = []byte{
0x01, 0x28, 0x0e, 0x32, 0x29, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x41,
0x70, 0x70, 0x6c, 0x69, 0x63, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61,
0x74, 0x61, 0x4d, 0x65, 0x73, 0x73, 0x61, 0x67, 0x65, 0x2e, 0x54, 0x79, 0x70, 0x65, 0x52, 0x04,
0x74, 0x79, 0x70, 0x65, 0x22, 0xf4, 0x14, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a,
0x74, 0x79, 0x70, 0x65, 0x22, 0x90, 0x15, 0x0a, 0x04, 0x54, 0x79, 0x70, 0x65, 0x12, 0x0b, 0x0a,
0x07, 0x55, 0x4e, 0x4b, 0x4e, 0x4f, 0x57, 0x4e, 0x10, 0x00, 0x12, 0x10, 0x0a, 0x0c, 0x43, 0x48,
0x41, 0x54, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x10, 0x01, 0x12, 0x12, 0x0a, 0x0e,
0x43, 0x4f, 0x4e, 0x54, 0x41, 0x43, 0x54, 0x5f, 0x55, 0x50, 0x44, 0x41, 0x54, 0x45, 0x10, 0x02,
@ -550,19 +553,20 @@ var file_application_metadata_message_proto_rawDesc = []byte{
0x50, 0x44, 0x41, 0x54, 0x45, 0x5f, 0x47, 0x52, 0x41, 0x4e, 0x54, 0x10, 0x56, 0x12, 0x25, 0x0a,
0x21, 0x43, 0x4f, 0x4d, 0x4d, 0x55, 0x4e, 0x49, 0x54, 0x59, 0x5f, 0x45, 0x4e, 0x43, 0x52, 0x59,
0x50, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x4b, 0x45, 0x59, 0x53, 0x5f, 0x52, 0x45, 0x51, 0x55, 0x45,
0x53, 0x54, 0x10, 0x57, 0x22, 0x04, 0x08, 0x0e, 0x10, 0x0e, 0x22, 0x04, 0x08, 0x41, 0x10, 0x41,
0x22, 0x04, 0x08, 0x42, 0x10, 0x42, 0x22, 0x04, 0x08, 0x47, 0x10, 0x47, 0x2a, 0x1d, 0x53, 0x59,
0x4e, 0x43, 0x5f, 0x49, 0x4e, 0x53, 0x54, 0x41, 0x4c, 0x4c, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f,
0x50, 0x55, 0x42, 0x4c, 0x49, 0x43, 0x5f, 0x43, 0x48, 0x41, 0x54, 0x2a, 0x22, 0x53, 0x59, 0x4e,
0x53, 0x54, 0x10, 0x57, 0x12, 0x1a, 0x0a, 0x16, 0x43, 0x4f, 0x4d, 0x4d, 0x55, 0x4e, 0x49, 0x54,
0x59, 0x5f, 0x54, 0x4f, 0x4b, 0x45, 0x4e, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x4f, 0x4e, 0x10, 0x58,
0x22, 0x04, 0x08, 0x0e, 0x10, 0x0e, 0x22, 0x04, 0x08, 0x41, 0x10, 0x41, 0x22, 0x04, 0x08, 0x42,
0x10, 0x42, 0x22, 0x04, 0x08, 0x47, 0x10, 0x47, 0x2a, 0x1d, 0x53, 0x59, 0x4e, 0x43, 0x5f, 0x49,
0x4e, 0x53, 0x54, 0x41, 0x4c, 0x4c, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x50, 0x55, 0x42, 0x4c,
0x49, 0x43, 0x5f, 0x43, 0x48, 0x41, 0x54, 0x2a, 0x22, 0x53, 0x59, 0x4e, 0x43, 0x5f, 0x41, 0x43,
0x54, 0x49, 0x56, 0x49, 0x54, 0x59, 0x5f, 0x43, 0x45, 0x4e, 0x54, 0x45, 0x52, 0x5f, 0x4e, 0x4f,
0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x2a, 0x27, 0x53, 0x59, 0x4e,
0x43, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x49, 0x54, 0x59, 0x5f, 0x43, 0x45, 0x4e, 0x54, 0x45,
0x52, 0x5f, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x53, 0x2a,
0x27, 0x53, 0x59, 0x4e, 0x43, 0x5f, 0x41, 0x43, 0x54, 0x49, 0x56, 0x49, 0x54, 0x59, 0x5f, 0x43,
0x45, 0x4e, 0x54, 0x45, 0x52, 0x5f, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x49,
0x4f, 0x4e, 0x5f, 0x53, 0x54, 0x41, 0x54, 0x45, 0x2a, 0x21, 0x43, 0x4f, 0x4d, 0x4d, 0x55, 0x4e,
0x49, 0x54, 0x59, 0x5f, 0x45, 0x56, 0x45, 0x4e, 0x54, 0x53, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41,
0x47, 0x45, 0x5f, 0x52, 0x45, 0x4a, 0x45, 0x43, 0x54, 0x45, 0x44, 0x42, 0x0d, 0x5a, 0x0b, 0x2e,
0x2f, 0x3b, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74,
0x6f, 0x33,
0x52, 0x5f, 0x4e, 0x4f, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x49, 0x4f, 0x4e, 0x5f, 0x53,
0x54, 0x41, 0x54, 0x45, 0x2a, 0x21, 0x43, 0x4f, 0x4d, 0x4d, 0x55, 0x4e, 0x49, 0x54, 0x59, 0x5f,
0x45, 0x56, 0x45, 0x4e, 0x54, 0x53, 0x5f, 0x4d, 0x45, 0x53, 0x53, 0x41, 0x47, 0x45, 0x5f, 0x52,
0x45, 0x4a, 0x45, 0x43, 0x54, 0x45, 0x44, 0x42, 0x0d, 0x5a, 0x0b, 0x2e, 0x2f, 0x3b, 0x70, 0x72,
0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
}
var (

View File

@ -106,5 +106,6 @@ message ApplicationMetadataMessage {
DELETE_COMMUNITY_MEMBER_MESSAGES = 85;
COMMUNITY_UPDATE_GRANT = 86;
COMMUNITY_ENCRYPTION_KEYS_REQUEST = 87;
COMMUNITY_TOKEN_ACTION = 88;
}
}

File diff suppressed because it is too large Load Diff

View File

@ -46,6 +46,19 @@ message CommunityTokenMetadata {
uint32 decimals = 7;
}
message CommunityTokenAction {
enum ActionType {
UNKNOWN_ACTION_TYPE = 0;
AIRDROP = 1;
BURN = 2;
REMOTE_DESTRUCT = 3;
}
uint64 chain_id = 1;
string contract_address = 2;
ActionType action_type = 3;
}
message CommunityPermissions {
enum Access {
UNKNOWN_ACCESS = 0;

View File

@ -145,11 +145,31 @@ func (s *Service) handleWalletEvent(event walletevent.Event) {
}
func (s *Service) handleAirdropCommunityToken(status string, pendingTransaction *transactions.PendingTransaction) (*token.CommunityToken, error) {
return s.Messenger.GetCommunityTokenByChainAndAddress(int(pendingTransaction.ChainID), pendingTransaction.To.String())
communityToken, err := s.Messenger.GetCommunityTokenByChainAndAddress(int(pendingTransaction.ChainID), pendingTransaction.To.String())
if communityToken == nil {
return nil, fmt.Errorf("token does not exist in database: chainId=%v, address=%v", pendingTransaction.ChainID, pendingTransaction.To.String())
} else {
publishErr := s.publishTokenActionToPrivilegedMembers(communityToken.CommunityID, uint64(communityToken.ChainID),
communityToken.Address, protobuf.CommunityTokenAction_AIRDROP)
if publishErr != nil {
log.Warn("can't publish airdrop action")
}
}
return communityToken, err
}
func (s *Service) handleRemoteDestructCollectible(status string, pendingTransaction *transactions.PendingTransaction) (*token.CommunityToken, error) {
return s.Messenger.GetCommunityTokenByChainAndAddress(int(pendingTransaction.ChainID), pendingTransaction.To.String())
communityToken, err := s.Messenger.GetCommunityTokenByChainAndAddress(int(pendingTransaction.ChainID), pendingTransaction.To.String())
if communityToken == nil {
return nil, fmt.Errorf("token does not exist in database: chainId=%v, address=%v", pendingTransaction.ChainID, pendingTransaction.To.String())
} else {
publishErr := s.publishTokenActionToPrivilegedMembers(communityToken.CommunityID, uint64(communityToken.ChainID),
communityToken.Address, protobuf.CommunityTokenAction_REMOTE_DESTRUCT)
if publishErr != nil {
log.Warn("can't publish remote destruct action")
}
}
return communityToken, err
}
func (s *Service) handleBurnCommunityToken(status string, pendingTransaction *transactions.PendingTransaction) (*token.CommunityToken, error) {
@ -166,7 +186,18 @@ func (s *Service) handleBurnCommunityToken(status string, pendingTransaction *tr
}
}
return s.Messenger.GetCommunityTokenByChainAndAddress(int(pendingTransaction.ChainID), pendingTransaction.To.String())
communityToken, err := s.Messenger.GetCommunityTokenByChainAndAddress(int(pendingTransaction.ChainID), pendingTransaction.To.String())
if communityToken == nil {
return nil, fmt.Errorf("token does not exist in database: chainId=%v, address=%v", pendingTransaction.ChainID, pendingTransaction.To.String())
} else {
publishErr := s.publishTokenActionToPrivilegedMembers(communityToken.CommunityID, uint64(communityToken.ChainID),
communityToken.Address, protobuf.CommunityTokenAction_BURN)
if publishErr != nil {
log.Warn("can't publish burn action")
}
}
return communityToken, err
}
func (s *Service) handleDeployOwnerToken(status string, pendingTransaction *transactions.PendingTransaction) (*token.CommunityToken, *token.CommunityToken, error) {
@ -443,6 +474,33 @@ func (s *Service) DeploymentSignatureDigest(chainID uint64, addressFrom string,
return s.manager.DeploymentSignatureDigest(chainID, addressFrom, communityID)
}
func (s *Service) ProcessCommunityTokenAction(message *protobuf.CommunityTokenAction) error {
communityToken, err := s.Messenger.GetCommunityTokenByChainAndAddress(int(message.ChainId), message.ContractAddress)
if err != nil {
return err
}
if communityToken == nil {
return fmt.Errorf("can't find community token in database: chain %v, address %v", message.ChainId, message.ContractAddress)
}
if message.ActionType == protobuf.CommunityTokenAction_BURN {
// get new max supply and update database
newMaxSupply, err := s.maxSupply(context.Background(), uint64(communityToken.ChainID), communityToken.Address)
if err != nil {
return nil
}
err = s.Messenger.UpdateCommunityTokenSupply(communityToken.ChainID, communityToken.Address, &bigint.BigInt{Int: newMaxSupply})
if err != nil {
return err
}
communityToken, _ = s.Messenger.GetCommunityTokenByChainAndAddress(int(message.ChainId), message.ContractAddress)
}
signal.SendCommunityTokenActionSignal(communityToken, message.ActionType)
return nil
}
func (s *Service) SetSignerPubKey(ctx context.Context, chainID uint64, contractAddress string, txArgs transactions.SendTxArgs, password string, newSignerPubKey string) (string, error) {
if len(newSignerPubKey) <= 0 {
@ -666,3 +724,11 @@ func (s *Service) ReTrackOwnerTokenDeploymentTransaction(ctx context.Context, ch
}
return err
}
func (s *Service) publishTokenActionToPrivilegedMembers(communityID string, chainID uint64, contractAddress string, actionType protobuf.CommunityTokenAction_ActionType) error {
decodedCommunityID, err := types.DecodeHex(communityID)
if err != nil {
return err
}
return s.Messenger.PublishTokenActionToPrivilegedMembers(decodedCommunityID, chainID, contractAddress, actionType)
}

View File

@ -3,6 +3,7 @@ package signal
import (
"github.com/ethereum/go-ethereum/common"
"github.com/status-im/status-go/protocol/communities/token"
"github.com/status-im/status-go/protocol/protobuf"
)
const (
@ -10,6 +11,10 @@ const (
// EventCommunityTokenTransactionStatusChanged is triggered when community token contract
// transaction changed its status
EventCommunityTokenTransactionStatusChanged = "communityToken.communityTokenTransactionStatusChanged"
// EventCommunityTokenAction is triggered when the app receives a message that
// owner or some other token master did some token action, like: airdrop, burn, remote destruct
EventCommunityTokenAction = "communityToken.communityTokenAction"
)
type CommunityTokenTransactionSignal struct {
@ -34,3 +39,15 @@ func SendCommunityTokenTransactionStatusSignal(transactionType string, success b
ErrorString: errorString,
})
}
type CommunityTokenActionSignal struct {
CommunityToken *token.CommunityToken `json:"communityToken"` // community token changed by the other owner/master
ActionType protobuf.CommunityTokenAction_ActionType `json:"actionType"` // type od action made by the other owner/master
}
func SendCommunityTokenActionSignal(communityToken *token.CommunityToken, actionType protobuf.CommunityTokenAction_ActionType) {
send(EventCommunityTokenAction, CommunityTokenActionSignal{
CommunityToken: communityToken,
ActionType: actionType,
})
}