From 2d1edc9f4a4355abc43a7c5bcb88d0724bc2a67c Mon Sep 17 00:00:00 2001 From: Peter <> Date: Tue, 16 Jul 2019 15:24:19 +0100 Subject: [PATCH] Ton fixes --- submodules/MtProtoKit/TON/TON.h | 14 +- submodules/MtProtoKit/TON/TON.mm | 154 ++++++++++++++++-- .../TelegramUI/ChatController.swift | 112 ++++++++++++- 3 files changed, 261 insertions(+), 19 deletions(-) diff --git a/submodules/MtProtoKit/TON/TON.h b/submodules/MtProtoKit/TON/TON.h index 760fb17934..a8fa40f193 100644 --- a/submodules/MtProtoKit/TON/TON.h +++ b/submodules/MtProtoKit/TON/TON.h @@ -4,6 +4,12 @@ NS_ASSUME_NONNULL_BEGIN @class MTSignal; +@interface TONError : NSObject + +@property (nonatomic, strong, readonly) NSString *text; + +@end + @interface TONKey : NSObject @property (nonatomic, strong, readonly) NSString *publicKey; @@ -13,7 +19,7 @@ NS_ASSUME_NONNULL_BEGIN @end -@interface TONTestGiverAccountState : NSObject +@interface TONAccountState : NSObject @property (nonatomic, readonly) int64_t balance; @property (nonatomic, readonly) int32_t seqno; @@ -29,8 +35,12 @@ NS_ASSUME_NONNULL_BEGIN - (MTSignal *)createKeyWithLocalPassword:(NSString *)localPassword mnemonicPassword:(NSString *)mnemonicPassword; - (MTSignal *)getTestWalletAccountAddressWithPublicKey:(NSString *)publicKey; - (MTSignal *)getTestGiverAccountState; -- (MTSignal *)testGiverSendGramsWithAccountState:(TONTestGiverAccountState *)accountState accountAddress:(NSString *)accountAddress amount:(int64_t)amount; +- (MTSignal *)testGiverSendGramsWithAccountState:(TONAccountState *)accountState accountAddress:(NSString *)accountAddress amount:(int64_t)amount; - (MTSignal *)getAccountStateWithAddress:(NSString *)accountAddress; +- (MTSignal *)sendGramsFromKey:(TONKey *)key localPassword:(NSString *)localPassword fromAddress:(NSString *)fromAddress toAddress:(NSString *)address amount:(int64_t)amount; +- (MTSignal *)exportKey:(TONKey *)key localPassword:(NSString *)localPassword; +- (MTSignal *)importKeyWithLocalPassword:(NSString *)localPassword mnemonicPassword:(NSString *)mnemonicPassword wordList:(NSArray *)wordList; +- (MTSignal *)deleteKeyWithPublicKey:(NSString *)publicKey; @end diff --git a/submodules/MtProtoKit/TON/TON.mm b/submodules/MtProtoKit/TON/TON.mm index 2046b9668b..9f58c445ac 100644 --- a/submodules/MtProtoKit/TON/TON.mm +++ b/submodules/MtProtoKit/TON/TON.mm @@ -19,7 +19,7 @@ @end -@implementation TONTestGiverAccountState +@implementation TONAccountState - (instancetype)initWithBalance:(int64_t)balance seqno:(int32_t)seqno { self = [super init]; @@ -72,12 +72,6 @@ using tonlib_api::make_object; @end -@interface TONError : NSObject - -@property (nonatomic, strong, readonly) NSString *text; - -@end - @implementation TONError - (instancetype)initWithText:(NSString *)text { @@ -239,7 +233,7 @@ using tonlib_api::make_object; [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; } else if (object->get_id() == tonlib_api::testGiver_accountState::ID) { auto result = tonlib_api::move_object_as(object); - [subscriber putNext:[[TONTestGiverAccountState alloc] initWithBalance:result->balance_ seqno:result->seqno_]]; + [subscriber putNext:[[TONAccountState alloc] initWithBalance:result->balance_ seqno:result->seqno_]]; [subscriber putCompletion]; } else { assert(false); @@ -254,7 +248,7 @@ using tonlib_api::make_object; }] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]]; } -- (MTSignal *)testGiverSendGramsWithAccountState:(TONTestGiverAccountState *)accountState accountAddress:(NSString *)accountAddress amount:(int64_t)amount { +- (MTSignal *)testGiverSendGramsWithAccountState:(TONAccountState *)accountState accountAddress:(NSString *)accountAddress amount:(int64_t)amount { return [[[[MTSignal alloc] initWithGenerator:^id(MTSubscriber *subscriber) { uint64_t requestId = _nextRequestId; _nextRequestId += 1; @@ -287,7 +281,11 @@ using tonlib_api::make_object; [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; } else if (object->get_id() == tonlib_api::generic_accountStateUninited::ID) { auto result = tonlib_api::move_object_as(object); - [subscriber putNext:@(result->account_state_->balance_)]; + [subscriber putNext:[[TONAccountState alloc] initWithBalance:result->account_state_->balance_ seqno:-1]]; + [subscriber putCompletion]; + } else if (object->get_id() == tonlib_api::generic_accountStateTestWallet::ID) { + auto result = tonlib_api::move_object_as(object); + [subscriber putNext:[[TONAccountState alloc] initWithBalance:result->account_state_->balance_ seqno:result->account_state_->seqno_]]; [subscriber putCompletion]; } else { assert(false); @@ -302,4 +300,140 @@ using tonlib_api::make_object; }] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]]; } +- (MTSignal *)sendGramsFromKey:(TONKey *)key localPassword:(NSString *)localPassword fromAddress:(NSString *)fromAddress toAddress:(NSString *)address amount:(int64_t)amount { + return [[[[MTSignal alloc] initWithGenerator:^id(MTSubscriber *subscriber) { + uint64_t requestId = _nextRequestId; + _nextRequestId += 1; + + _requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr &object) { + if (object->get_id() == tonlib_api::error::ID) { + auto error = tonlib_api::move_object_as(object); + [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; + } else { + [subscriber putCompletion]; + } + }]; + + NSData *publicKeyData = [[NSData alloc] initWithBase64EncodedString:key.publicKey options:0]; + std::string publicKeyString((uint8_t *)publicKeyData.bytes, (uint8_t *)publicKeyData.bytes + publicKeyData.length); + + NSData *secretData = [[NSData alloc] initWithBase64EncodedString:key.secret options:0]; + std::string secretString((uint8_t *)secretData.bytes, (uint8_t *)secretData.bytes + secretData.length); + + NSData *localPasswordData = [localPassword dataUsingEncoding:NSUTF8StringEncoding]; + std::string localPasswordString((uint8_t *)localPasswordData.bytes, (uint8_t *)localPasswordData.bytes + localPasswordData.length); + + auto query = make_object(make_object(make_object(publicKeyString, secretString), localPasswordString), make_object(fromAddress.UTF8String), make_object(address.UTF8String), amount); + _client->send({ requestId, std::move(query) }); + + return [[MTBlockDisposable alloc] initWithBlock:^{ + }]; + }] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]]; +} + +- (MTSignal *)exportKey:(TONKey *)key localPassword:(NSString *)localPassword { + return [[[[MTSignal alloc] initWithGenerator:^id(MTSubscriber *subscriber) { + uint64_t requestId = _nextRequestId; + _nextRequestId += 1; + + _requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr &object) { + if (object->get_id() == tonlib_api::error::ID) { + auto error = tonlib_api::move_object_as(object); + [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; + } else if (object->get_id() == tonlib_api::exportedKey::ID) { + auto result = tonlib_api::move_object_as(object); + NSMutableArray *wordList = [[NSMutableArray alloc] init]; + for (auto it : result->word_list_) { + [wordList addObject:[[NSString alloc] initWithUTF8String:it.c_str()]]; + } + [subscriber putNext:wordList]; + [subscriber putCompletion]; + } else { + assert(false); + } + }]; + + NSData *publicKeyData = [[NSData alloc] initWithBase64EncodedString:key.publicKey options:0]; + std::string publicKeyString((uint8_t *)publicKeyData.bytes, (uint8_t *)publicKeyData.bytes + publicKeyData.length); + + NSData *secretData = [[NSData alloc] initWithBase64EncodedString:key.secret options:0]; + std::string secretString((uint8_t *)secretData.bytes, (uint8_t *)secretData.bytes + secretData.length); + + NSData *localPasswordData = [localPassword dataUsingEncoding:NSUTF8StringEncoding]; + std::string localPasswordString((uint8_t *)localPasswordData.bytes, (uint8_t *)localPasswordData.bytes + localPasswordData.length); + + auto query = make_object(make_object(make_object(publicKeyString, secretString), localPasswordString)); + _client->send({ requestId, std::move(query) }); + + return [[MTBlockDisposable alloc] initWithBlock:^{ + }]; + }] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]]; +} + +- (MTSignal *)importKeyWithLocalPassword:(NSString *)localPassword mnemonicPassword:(NSString *)mnemonicPassword wordList:(NSArray *)wordList { + return [[[[MTSignal alloc] initWithGenerator:^id(MTSubscriber *subscriber) { + uint64_t requestId = _nextRequestId; + _nextRequestId += 1; + + _requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr &object) { + if (object->get_id() == tonlib_api::error::ID) { + auto error = tonlib_api::move_object_as(object); + [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; + } else if (object->get_id() == tonlib_api::key::ID) { + auto result = tonlib_api::move_object_as(object); + NSString *publicKey = [[[NSData alloc] initWithBytes:result->public_key_.data() length:result->public_key_.length()] base64EncodedStringWithOptions:0]; + NSString *secret = [[[NSData alloc] initWithBytes:result->secret_.data() length:result->secret_.length()] base64EncodedStringWithOptions:0]; + [subscriber putNext:[[TONKey alloc] initWithPublicKey:publicKey secret:secret]]; + [subscriber putCompletion]; + } else { + assert(false); + } + }]; + + NSData *localPasswordData = [localPassword dataUsingEncoding:NSUTF8StringEncoding]; + std::string localPasswordString((uint8_t *)localPasswordData.bytes, (uint8_t *)localPasswordData.bytes + localPasswordData.length); + + NSData *mnemonicPasswordData = [mnemonicPassword dataUsingEncoding:NSUTF8StringEncoding]; + std::string mnemonicPasswordString((uint8_t *)mnemonicPasswordData.bytes, (uint8_t *)mnemonicPasswordData.bytes + mnemonicPasswordData.length); + + std::vector wordVector; + for (NSString *word in wordList) { + NSData *wordData = [word dataUsingEncoding:NSUTF8StringEncoding]; + std::string wordString((uint8_t *)wordData.bytes, (uint8_t *)wordData.bytes + wordData.length); + wordVector.push_back(wordString); + } + + auto query = make_object(localPasswordString, mnemonicPasswordString, make_object(std::move(wordVector))); + _client->send({ requestId, std::move(query) }); + + return [[MTBlockDisposable alloc] initWithBlock:^{ + }]; + }] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]]; +} + +- (MTSignal *)deleteKeyWithPublicKey:(NSString *)publicKey { + return [[[[MTSignal alloc] initWithGenerator:^id(MTSubscriber *subscriber) { + uint64_t requestId = _nextRequestId; + _nextRequestId += 1; + + _requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr &object) { + if (object->get_id() == tonlib_api::error::ID) { + auto error = tonlib_api::move_object_as(object); + [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; + } else { + [subscriber putCompletion]; + } + }]; + + NSData *publicKeyData = [[NSData alloc] initWithBase64EncodedString:publicKey options:0]; + std::string publicKeyString((uint8_t *)publicKeyData.bytes, (uint8_t *)publicKeyData.bytes + publicKeyData.length); + + auto query = make_object(publicKeyString); + _client->send({ requestId, std::move(query) }); + + return [[MTBlockDisposable alloc] initWithBlock:^{ + }]; + }] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]]; +} + @end diff --git a/submodules/TelegramUI/TelegramUI/ChatController.swift b/submodules/TelegramUI/TelegramUI/ChatController.swift index 46bc3ea0df..ac40eda1d2 100644 --- a/submodules/TelegramUI/TelegramUI/ChatController.swift +++ b/submodules/TelegramUI/TelegramUI/ChatController.swift @@ -2378,13 +2378,13 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget, let _ = transaction.addMessages([message], location: .UpperHistoryBlock) }).start() - let _ = strongSelf.context.ton?.getAccountState(withAddress: components[2]).start(next: { balance in - guard let balance = balance as? Int64 else { + let _ = strongSelf.context.ton?.getAccountState(withAddress: components[2]).start(next: { state in + guard let state = state as? TONAccountState else { return } let timestamp = strongSelf.context.account.network.getApproximateRemoteTimestamp() let _ = (strongSelf.context.account.postbox.transaction { transaction in - let message1 = StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: nil, groupingKey: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: peerId, text: "balance: \(balance)", attributes: [], media: []) + let message1 = StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: nil, groupingKey: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: peerId, text: "balance: \(state.balance)", attributes: [], media: []) let _ = transaction.addMessages([message1], location: .UpperHistoryBlock) }).start() }) @@ -2398,7 +2398,7 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget, }).start() let _ = strongSelf.context.ton?.getTestGiverAccountState().start(next: { state in - guard let state = state as? TONTestGiverAccountState else { + guard let state = state as? TONAccountState else { return } let _ = strongSelf.context.ton?.testGiverSendGrams(with: state, accountAddress: components[2], amount: amount).start(next: nil, completed: { @@ -2411,6 +2411,70 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget, }) } } else if components[1] == "sendgrams" { + if components.count >= 7, let amount = Int64(components[6]) { + let timestamp = strongSelf.context.account.network.getApproximateRemoteTimestamp() + let _ = (strongSelf.context.account.postbox.transaction { transaction in + let message = StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: nil, groupingKey: nil, timestamp: timestamp, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: strongSelf.context.account.peerId, text: message.text, attributes: [], media: []) + let _ = transaction.addMessages([message], location: .UpperHistoryBlock) + }).start() + + let _ = strongSelf.context.ton?.getTestWalletAccountAddress(withPublicKey: components[2]).start(next: { localAddress in + guard let localAddress = localAddress as? String else { + return + } + let _ = strongSelf.context.ton?.sendGrams(from: TONKey(publicKey: components[2], secret: components[3]), localPassword: components[4], fromAddress: localAddress, toAddress: components[5], amount: amount).start(next: nil, error: { error in + guard let error = error as? TONError else { + return + } + let _ = (strongSelf.context.account.postbox.transaction { transaction in + let message1 = StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: nil, groupingKey: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: peerId, text: "error: \(error.text)", attributes: [], media: []) + let _ = transaction.addMessages([message1], location: .UpperHistoryBlock) + }).start() + }, completed: { + let timestamp = strongSelf.context.account.network.getApproximateRemoteTimestamp() + let _ = (strongSelf.context.account.postbox.transaction { transaction in + let message1 = StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: nil, groupingKey: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: peerId, text: "sent", attributes: [], media: []) + let _ = transaction.addMessages([message1], location: .UpperHistoryBlock) + }).start() + }) + }) + } + } else if components[1] == "exportkey" { + if components.count >= 5 { + let timestamp = strongSelf.context.account.network.getApproximateRemoteTimestamp() + let _ = (strongSelf.context.account.postbox.transaction { transaction in + let message = StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: nil, groupingKey: nil, timestamp: timestamp, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: strongSelf.context.account.peerId, text: message.text, attributes: [], media: []) + let _ = transaction.addMessages([message], location: .UpperHistoryBlock) + }).start() + + let _ = strongSelf.context.ton?.export(TONKey(publicKey: components[2], secret: components[3]), localPassword: components[4]).start(next: { wordList in + guard let wordList = wordList as? [String] else { + return + } + let timestamp = strongSelf.context.account.network.getApproximateRemoteTimestamp() + let _ = (strongSelf.context.account.postbox.transaction { transaction in + let message1 = StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: nil, groupingKey: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: peerId, text: "word list:", attributes: [], media: []) + var listText = "" + for word in wordList { + if !listText.isEmpty { + listText.append(" ") + } + listText.append(word) + } + let message2 = StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: nil, groupingKey: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: peerId, text: "\(listText)", attributes: [], media: []) + let _ = transaction.addMessages([message1, message2], location: .UpperHistoryBlock) + }).start() + }, error: { error in + guard let error = error as? TONError else { + return + } + let _ = (strongSelf.context.account.postbox.transaction { transaction in + let message1 = StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: nil, groupingKey: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: peerId, text: "error: \(error.text)", attributes: [], media: []) + let _ = transaction.addMessages([message1], location: .UpperHistoryBlock) + }).start() + }, completed: nil) + } + } else if components[1] == "importkey" { if components.count >= 4 { let timestamp = strongSelf.context.account.network.getApproximateRemoteTimestamp() let _ = (strongSelf.context.account.postbox.transaction { transaction in @@ -2418,13 +2482,47 @@ public final class ChatController: TelegramController, GalleryHiddenMediaTarget, let _ = transaction.addMessages([message], location: .UpperHistoryBlock) }).start() - let _ = strongSelf.context.ton?.getAccountState(withAddress: components[2]).start(next: { balance in - guard let balance = balance as? Int64 else { + let _ = strongSelf.context.ton?.importKey(withLocalPassword: components[2], mnemonicPassword: components[3], wordList: Array(components[4...])).start(next: { key in + guard let key = key as? TONKey else { return } let timestamp = strongSelf.context.account.network.getApproximateRemoteTimestamp() let _ = (strongSelf.context.account.postbox.transaction { transaction in - let message1 = StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: nil, groupingKey: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: peerId, text: "balance: \(balance)", attributes: [], media: []) + let message1 = StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: nil, groupingKey: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: peerId, text: "imported key (public, secret):", attributes: [], media: []) + let message2 = StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: nil, groupingKey: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: peerId, text: "\(key.publicKey)", attributes: [], media: []) + let message3 = StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: nil, groupingKey: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: peerId, text: "\(key.secret)", attributes: [], media: []) + let _ = transaction.addMessages([message1, message2, message3], location: .UpperHistoryBlock) + }).start() + }, error: { error in + guard let error = error as? TONError else { + return + } + let _ = (strongSelf.context.account.postbox.transaction { transaction in + let message1 = StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: nil, groupingKey: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: peerId, text: "error: \(error.text)", attributes: [], media: []) + let _ = transaction.addMessages([message1], location: .UpperHistoryBlock) + }).start() + }, completed: nil) + } + } else if components[1] == "deletekey" { + if components.count >= 3 { + let timestamp = strongSelf.context.account.network.getApproximateRemoteTimestamp() + let _ = (strongSelf.context.account.postbox.transaction { transaction in + let message = StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: nil, groupingKey: nil, timestamp: timestamp, flags: [], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: strongSelf.context.account.peerId, text: message.text, attributes: [], media: []) + let _ = transaction.addMessages([message], location: .UpperHistoryBlock) + }).start() + + let _ = strongSelf.context.ton?.deleteKey(withPublicKey: components[2]).start(next: nil, error: { error in + guard let error = error as? TONError else { + return + } + let _ = (strongSelf.context.account.postbox.transaction { transaction in + let message1 = StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: nil, groupingKey: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: peerId, text: "error: \(error.text)", attributes: [], media: []) + let _ = transaction.addMessages([message1], location: .UpperHistoryBlock) + }).start() + }, completed: { + let timestamp = strongSelf.context.account.network.getApproximateRemoteTimestamp() + let _ = (strongSelf.context.account.postbox.transaction { transaction in + let message1 = StoreMessage(peerId: peerId, namespace: Namespaces.Message.Local, globallyUniqueId: nil, groupingKey: nil, timestamp: timestamp, flags: [.Incoming], tags: [], globalTags: [], localTags: [], forwardInfo: nil, authorId: peerId, text: "deleted", attributes: [], media: []) let _ = transaction.addMessages([message1], location: .UpperHistoryBlock) }).start() })