Update tonlib [skip ci]

This commit is contained in:
Peter 2019-09-23 22:30:37 +04:00
parent fa3bb5d9c5
commit beab635aba
41 changed files with 807 additions and 266 deletions

View File

@ -43,8 +43,9 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) int64_t value; @property (nonatomic, readonly) int64_t value;
@property (nonatomic, strong, readonly) NSString * _Nonnull source; @property (nonatomic, strong, readonly) NSString * _Nonnull source;
@property (nonatomic, strong, readonly) NSString * _Nonnull destination; @property (nonatomic, strong, readonly) NSString * _Nonnull destination;
@property (nonatomic, strong, readonly) NSString * _Nonnull textMessage;
- (instancetype)initWithValue:(int64_t)value source:(NSString * _Nonnull)source destination:(NSString * _Nonnull)destination; - (instancetype)initWithValue:(int64_t)value source:(NSString * _Nonnull)source destination:(NSString * _Nonnull)destination textMessage:(NSString * _Nonnull)textMessage;
@end @end
@ -80,7 +81,7 @@ NS_ASSUME_NONNULL_BEGIN
- (MTSignal *)getTestGiverAddress; - (MTSignal *)getTestGiverAddress;
- (MTSignal *)testGiverSendGramsWithAccountState:(TONAccountState *)accountState accountAddress:(NSString *)accountAddress amount:(int64_t)amount; - (MTSignal *)testGiverSendGramsWithAccountState:(TONAccountState *)accountState accountAddress:(NSString *)accountAddress amount:(int64_t)amount;
- (MTSignal *)getAccountStateWithAddress:(NSString *)accountAddress; - (MTSignal *)getAccountStateWithAddress:(NSString *)accountAddress;
- (MTSignal *)sendGramsFromKey:(TONKey *)key localPassword:(NSData *)localPassword fromAddress:(NSString *)fromAddress toAddress:(NSString *)address amount:(int64_t)amount; - (MTSignal *)sendGramsFromKey:(TONKey *)key localPassword:(NSData *)localPassword fromAddress:(NSString *)fromAddress toAddress:(NSString *)address amount:(int64_t)amount textMessage:(NSString *)textMessage;
- (MTSignal *)exportKey:(TONKey *)key localPassword:(NSData *)localPassword; - (MTSignal *)exportKey:(TONKey *)key localPassword:(NSData *)localPassword;
- (MTSignal *)importKeyWithLocalPassword:(NSData *)localPassword mnemonicPassword:(NSData *)mnemonicPassword wordList:(NSArray<NSString *> *)wordList; - (MTSignal *)importKeyWithLocalPassword:(NSData *)localPassword mnemonicPassword:(NSData *)mnemonicPassword wordList:(NSArray<NSString *> *)wordList;
- (MTSignal *)deleteKeyWithPublicKey:(NSString *)publicKey; - (MTSignal *)deleteKeyWithPublicKey:(NSString *)publicKey;

View File

@ -43,10 +43,14 @@ static TONTransactionMessage * _Nullable parseTransactionMessage(tonlib_api::obj
} }
NSString *source = readString(message->source_); NSString *source = readString(message->source_);
NSString *destination = readString(message->source_); NSString *destination = readString(message->source_);
NSString *textMessage = readString(message->message_);
if (source == nil || destination == nil) { if (source == nil || destination == nil) {
return nil; return nil;
} }
return [[TONTransactionMessage alloc] initWithValue:message->value_ source:source destination:destination]; if (textMessage == nil) {
textMessage = @"";
}
return [[TONTransactionMessage alloc] initWithValue:message->value_ source:source destination:destination textMessage:textMessage];
} }
@implementation TONKey @implementation TONKey
@ -91,12 +95,13 @@ static TONTransactionMessage * _Nullable parseTransactionMessage(tonlib_api::obj
@implementation TONTransactionMessage @implementation TONTransactionMessage
- (instancetype)initWithValue:(int64_t)value source:(NSString * _Nonnull)source destination:(NSString * _Nonnull)destination { - (instancetype)initWithValue:(int64_t)value source:(NSString * _Nonnull)source destination:(NSString * _Nonnull)destination textMessage:(NSString * _Nonnull)textMessage {
self = [super init]; self = [super init];
if (self != nil) { if (self != nil) {
_value = value; _value = value;
_source = source; _source = source;
_destination = destination; _destination = destination;
_textMessage = textMessage;
} }
return self; return self;
} }
@ -449,7 +454,12 @@ typedef enum {
} }
}]; }];
auto query = make_object<tonlib_api::testGiver_sendGrams>(make_object<tonlib_api::accountAddress>(accountAddress.UTF8String), accountState.seqno, amount); auto query = make_object<tonlib_api::testGiver_sendGrams>(make_object<tonlib_api::accountAddress>(
accountAddress.UTF8String),
accountState.seqno,
amount,
std::string()
);
_client->send({ requestId, std::move(query) }); _client->send({ requestId, std::move(query) });
return [[MTBlockDisposable alloc] initWithBlock:^{ return [[MTBlockDisposable alloc] initWithBlock:^{
@ -493,13 +503,18 @@ typedef enum {
}] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]]; }] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]];
} }
- (MTSignal *)sendGramsFromKey:(TONKey *)key localPassword:(NSData *)localPassword fromAddress:(NSString *)fromAddress toAddress:(NSString *)address amount:(int64_t)amount { - (MTSignal *)sendGramsFromKey:(TONKey *)key localPassword:(NSData *)localPassword fromAddress:(NSString *)fromAddress toAddress:(NSString *)address amount:(int64_t)amount textMessage:(NSString * _Nonnull)textMessage {
return [[[[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) { return [[[[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
NSData *publicKeyData = [key.publicKey dataUsingEncoding:NSUTF8StringEncoding]; NSData *publicKeyData = [key.publicKey dataUsingEncoding:NSUTF8StringEncoding];
if (publicKeyData == nil) { if (publicKeyData == nil) {
[subscriber putError:[[TONError alloc] initWithText:@"Error encoding UTF8 string in sendGramsFromKey"]]; [subscriber putError:[[TONError alloc] initWithText:@"Error encoding UTF8 string in sendGramsFromKey"]];
return [[MTBlockDisposable alloc] initWithBlock:^{}]; return [[MTBlockDisposable alloc] initWithBlock:^{}];
} }
NSData *textMessageData = [textMessage dataUsingEncoding:NSUTF8StringEncoding];
if (textMessageData == nil) {
[subscriber putError:[[TONError alloc] initWithText:@"Error encoding UTF8 string in sendGramsFromKey"]];
return [[MTBlockDisposable alloc] initWithBlock:^{}];
}
uint64_t requestId = _nextRequestId; uint64_t requestId = _nextRequestId;
_nextRequestId += 1; _nextRequestId += 1;
@ -523,7 +538,8 @@ typedef enum {
), ),
make_object<tonlib_api::accountAddress>(fromAddress.UTF8String), make_object<tonlib_api::accountAddress>(fromAddress.UTF8String),
make_object<tonlib_api::accountAddress>(address.UTF8String), make_object<tonlib_api::accountAddress>(address.UTF8String),
amount amount,
makeString(textMessageData)
); );
_client->send({ requestId, std::move(query) }); _client->send({ requestId, std::move(query) });

View File

@ -69,10 +69,33 @@ public final class TonInstance {
}) })
} }
fileprivate func createWallet(keychain: TonKeychain, serverSalt: Data) -> Signal<(WalletInfo, [String]), NoError> { fileprivate func exportKey(key: TONKey, serverSalt: Data) -> Signal<[String], NoError> {
return Signal { subscriber in return Signal { subscriber in
let disposable = MetaDisposable() let disposable = MetaDisposable()
self.impl.with { impl in
impl.withInstance { ton in
let cancel = ton.export(key, localPassword: serverSalt).start(next: { wordList in
guard let wordList = wordList as? [String] else {
assertionFailure()
return
}
subscriber.putNext(wordList)
subscriber.putCompletion()
})
disposable.set(ActionDisposable {
cancel?.dispose()
})
}
}
return disposable
}
}
fileprivate func createWallet(keychain: TonKeychain, serverSalt: Data) -> Signal<(WalletInfo, [String]), NoError> {
return Signal { subscriber in
let disposable = MetaDisposable()
self.impl.with { impl in self.impl.with { impl in
impl.withInstance { ton in impl.withInstance { ton in
let cancel = ton.createKey(withLocalPassword: serverSalt, mnemonicPassword: Data()).start(next: { key in let cancel = ton.createKey(withLocalPassword: serverSalt, mnemonicPassword: Data()).start(next: { key in
@ -85,13 +108,11 @@ public final class TonInstance {
assertionFailure() assertionFailure()
return return
} }
let cancel = ton.export(key, localPassword: serverSalt).start(next: { wordList in let _ = self.exportKey(key: key, serverSalt: serverSalt).start(next: { wordList in
guard let wordList = wordList as? [String] else {
assertionFailure()
return
}
subscriber.putNext((WalletInfo(publicKey: WalletPublicKey(rawValue: key.publicKey), encryptedSecret: EncryptedWalletSecret(rawValue: encryptedSecretData)), wordList)) subscriber.putNext((WalletInfo(publicKey: WalletPublicKey(rawValue: key.publicKey), encryptedSecret: EncryptedWalletSecret(rawValue: encryptedSecretData)), wordList))
subscriber.putCompletion() subscriber.putCompletion()
}, error: { _ in
preconditionFailure()
}) })
}, error: { _ in }, error: { _ in
}, completed: { }, completed: {
@ -195,30 +216,20 @@ public final class TonInstance {
} }
} }
fileprivate func walletBalance(publicKey: WalletPublicKey) -> Signal<WalletBalance, NoError> { fileprivate func getWalletState(address: String) -> Signal<WalletState, NoError> {
return Signal { subscriber in return Signal { subscriber in
let disposable = MetaDisposable() let disposable = MetaDisposable()
self.impl.with { impl in self.impl.with { impl in
impl.withInstance { ton in impl.withInstance { ton in
let cancel = ton.getTestWalletAccountAddress(withPublicKey: publicKey.rawValue).start(next: { address in let cancel = ton.getAccountState(withAddress: address).start(next: { state in
guard let address = address as? String else { guard let state = state as? TONAccountState else {
return return
} }
let cancel = ton.getAccountState(withAddress: address).start(next: { state in subscriber.putNext(WalletState(balance: state.balance, lastTransactionId: state.lastTransactionId.flatMap(WalletTransactionId.init(tonTransactionId:))))
guard let state = state as? TONAccountState else {
return
}
subscriber.putNext(WalletBalance(rawValue: state.balance))
}, error: { _ in
}, completed: {
subscriber.putCompletion()
})
disposable.set(ActionDisposable {
cancel?.dispose()
})
}, error: { _ in }, error: { _ in
}, completed: { }, completed: {
subscriber.putCompletion()
}) })
disposable.set(ActionDisposable { disposable.set(ActionDisposable {
cancel?.dispose() cancel?.dispose()
@ -282,17 +293,39 @@ public final class TonInstance {
} }
} }
fileprivate func getGramsFromTestGiver(address: String, amount: Int64) -> Signal<Void, GetGramsFromTestGiverError> { fileprivate func getTestGiverAccountState() -> Signal<TONAccountState?, NoError> {
return Signal { subscriber in return Signal { subscriber in
let disposable = MetaDisposable() let disposable = MetaDisposable()
self.impl.with { impl in self.impl.with { impl in
impl.withInstance { ton in impl.withInstance { ton in
let cancel = ton.getTestGiverAccountState().start(next: { state in let cancel = ton.getTestGiverAccountState().start(next: { state in
guard let state = state as? TONAccountState else { subscriber.putNext(state as? TONAccountState)
subscriber.putError(.generic) subscriber.putCompletion()
return }, error: { _ in
} subscriber.putNext(nil)
subscriber.putCompletion()
}, completed: {
})
}
}
return disposable
}
}
fileprivate func getGramsFromTestGiver(address: String, amount: Int64) -> Signal<Void, GetGramsFromTestGiverError> {
return self.getTestGiverAccountState()
|> introduceError(GetGramsFromTestGiverError.self)
|> mapToSignal { state in
guard let state = state else {
return .fail(.generic)
}
return Signal { subscriber in
let disposable = MetaDisposable()
self.impl.with { impl in
impl.withInstance { ton in
let cancel = ton.testGiverSendGrams(with: state, accountAddress: address, amount: amount).start(next: { _ in let cancel = ton.testGiverSendGrams(with: state, accountAddress: address, amount: amount).start(next: { _ in
}, error: { _ in }, error: { _ in
subscriber.putError(.generic) subscriber.putError(.generic)
@ -302,21 +335,15 @@ public final class TonInstance {
disposable.set(ActionDisposable { disposable.set(ActionDisposable {
cancel?.dispose() cancel?.dispose()
}) })
}, error: { _ in }
subscriber.putError(.generic)
}, completed: {
})
disposable.set(ActionDisposable {
cancel?.dispose()
})
} }
return disposable
} }
return disposable
} }
} }
fileprivate func sendGramsFromWallet(keychain: TonKeychain, serverSalt: Data, walletInfo: WalletInfo, fromAddress: String, toAddress: String, amount: Int64) -> Signal<Never, SendGramsFromWalletError> { fileprivate func sendGramsFromWallet(keychain: TonKeychain, serverSalt: Data, walletInfo: WalletInfo, fromAddress: String, toAddress: String, amount: Int64, textMessage: String) -> Signal<Never, SendGramsFromWalletError> {
return keychain.decrypt(walletInfo.encryptedSecret.rawValue) return keychain.decrypt(walletInfo.encryptedSecret.rawValue)
|> introduceError(SendGramsFromWalletError.self) |> introduceError(SendGramsFromWalletError.self)
|> mapToSignal { decryptedSecret -> Signal<Never, SendGramsFromWalletError> in |> mapToSignal { decryptedSecret -> Signal<Never, SendGramsFromWalletError> in
@ -328,7 +355,7 @@ public final class TonInstance {
self.impl.with { impl in self.impl.with { impl in
impl.withInstance { ton in impl.withInstance { ton in
let cancel = ton.sendGrams(from: TONKey(publicKey: walletInfo.publicKey.rawValue, secret: decryptedSecret), localPassword: serverSalt, fromAddress: fromAddress, toAddress: toAddress, amount: amount).start(next: { _ in let cancel = ton.sendGrams(from: TONKey(publicKey: walletInfo.publicKey.rawValue, secret: decryptedSecret), localPassword: serverSalt, fromAddress: fromAddress, toAddress: toAddress, amount: amount, textMessage: textMessage).start(next: { _ in
preconditionFailure() preconditionFailure()
}, error: { _ in }, error: { _ in
subscriber.putError(.generic) subscriber.putError(.generic)
@ -526,11 +553,13 @@ public func walletRestoreWords(network: Network, walletInfo: WalletInfo, tonInst
} }
} }
public struct WalletBalance: Hashable { public struct WalletState: Equatable {
public var rawValue: Int64 public let balance: Int64
public let lastTransactionId: WalletTransactionId?
public init(rawValue: Int64) { public init(balance: Int64, lastTransactionId: WalletTransactionId?) {
self.rawValue = rawValue self.balance = balance
self.lastTransactionId = lastTransactionId
} }
} }
@ -542,8 +571,8 @@ public func testGiverWalletAddress(tonInstance: TonInstance) -> Signal<String, N
return tonInstance.testGiverWalletAddress() return tonInstance.testGiverWalletAddress()
} }
public func currentWalletBalance(publicKey: WalletPublicKey, tonInstance: TonInstance) -> Signal<WalletBalance, NoError> { public func getWalletState(address: String, tonInstance: TonInstance) -> Signal<WalletState, NoError> {
return tonInstance.walletBalance(publicKey: publicKey) return tonInstance.getWalletState(address: address)
} }
public enum GetGramsFromTestGiverError { public enum GetGramsFromTestGiverError {
@ -559,7 +588,7 @@ public enum SendGramsFromWalletError {
case secretDecryptionFailed case secretDecryptionFailed
} }
public func sendGramsFromWallet(network: Network, tonInstance: TonInstance, keychain: TonKeychain, walletInfo: WalletInfo, toAddress: String, amount: Int64) -> Signal<Never, SendGramsFromWalletError> { public func sendGramsFromWallet(network: Network, tonInstance: TonInstance, keychain: TonKeychain, walletInfo: WalletInfo, toAddress: String, amount: Int64, textMessage: String) -> Signal<Never, SendGramsFromWalletError> {
return getServerWalletSalt(network: network) return getServerWalletSalt(network: network)
|> mapError { _ -> SendGramsFromWalletError in |> mapError { _ -> SendGramsFromWalletError in
return .generic return .generic
@ -568,7 +597,7 @@ public func sendGramsFromWallet(network: Network, tonInstance: TonInstance, keyc
return walletAddress(publicKey: walletInfo.publicKey, tonInstance: tonInstance) return walletAddress(publicKey: walletInfo.publicKey, tonInstance: tonInstance)
|> introduceError(SendGramsFromWalletError.self) |> introduceError(SendGramsFromWalletError.self)
|> mapToSignal { fromAddress in |> mapToSignal { fromAddress in
return tonInstance.sendGramsFromWallet(keychain: keychain, serverSalt: serverSalt, walletInfo: walletInfo, fromAddress: fromAddress, toAddress: toAddress, amount: amount) return tonInstance.sendGramsFromWallet(keychain: keychain, serverSalt: serverSalt, walletInfo: walletInfo, fromAddress: fromAddress, toAddress: toAddress, amount: amount, textMessage: textMessage)
} }
} }
} }
@ -589,11 +618,13 @@ public final class WalletTransactionMessage: Equatable {
public let value: Int64 public let value: Int64
public let source: String public let source: String
public let destination: String public let destination: String
public let textMessage: String
init(value: Int64, source: String, destination: String) { init(value: Int64, source: String, destination: String, textMessage: String) {
self.value = value self.value = value
self.source = source self.source = source
self.destination = destination self.destination = destination
self.textMessage = textMessage
} }
public static func ==(lhs: WalletTransactionMessage, rhs: WalletTransactionMessage) -> Bool { public static func ==(lhs: WalletTransactionMessage, rhs: WalletTransactionMessage) -> Bool {
@ -604,7 +635,10 @@ public final class WalletTransactionMessage: Equatable {
return false return false
} }
if lhs.destination != rhs.destination { if lhs.destination != rhs.destination {
return false; return false
}
if lhs.textMessage != rhs.textMessage {
return false
} }
return true return true
} }
@ -612,7 +646,7 @@ public final class WalletTransactionMessage: Equatable {
private extension WalletTransactionMessage { private extension WalletTransactionMessage {
convenience init(tonTransactionMessage: TONTransactionMessage) { convenience init(tonTransactionMessage: TONTransactionMessage) {
self.init(value: tonTransactionMessage.value, source: tonTransactionMessage.source, destination: tonTransactionMessage.destination) self.init(value: tonTransactionMessage.value, source: tonTransactionMessage.source, destination: tonTransactionMessage.destination, textMessage: tonTransactionMessage.textMessage)
} }
} }

View File

@ -87,8 +87,8 @@ public final class WalletInfoScreen: ViewController {
} }
private final class WalletInfoBalanceNode: ASDisplayNode { private final class WalletInfoBalanceNode: ASDisplayNode {
private let balanceTextNode: ImmediateTextNode let balanceTextNode: ImmediateTextNode
private let balanceIconNode: ASImageNode let balanceIconNode: ASImageNode
var balance: String = " " { var balance: String = " " {
didSet { didSet {
@ -96,6 +96,8 @@ private final class WalletInfoBalanceNode: ASDisplayNode {
} }
} }
var isLoading: Bool = true
init(theme: PresentationTheme) { init(theme: PresentationTheme) {
self.balanceTextNode = ImmediateTextNode() self.balanceTextNode = ImmediateTextNode()
self.balanceTextNode.displaysAsynchronously = false self.balanceTextNode.displaysAsynchronously = false
@ -123,9 +125,14 @@ private final class WalletInfoBalanceNode: ASDisplayNode {
let balanceOrigin = CGPoint(x: floor((width - balanceTextSize.width - balanceIconSpacing - balanceIconSize.width / 2.0) / 2.0), y: 0.0) let balanceOrigin = CGPoint(x: floor((width - balanceTextSize.width - balanceIconSpacing - balanceIconSize.width / 2.0) / 2.0), y: 0.0)
let balanceTextFrame = CGRect(origin: balanceOrigin, size: balanceTextSize) let balanceTextFrame = CGRect(origin: balanceOrigin, size: balanceTextSize)
let balanceIconFrame = CGRect(origin: CGPoint(x: balanceTextFrame.maxX + balanceIconSpacing, y: balanceTextFrame.minY + floor((balanceTextFrame.height - balanceIconSize.height) / 2.0)), size: balanceIconSize) let balanceIconFrame: CGRect
if self.isLoading {
balanceIconFrame = CGRect(origin: CGPoint(x: floor((width - balanceIconSize.width) / 2.0), y: balanceTextFrame.minY + floor((balanceTextFrame.height - balanceIconSize.height) / 2.0)), size: balanceIconSize)
} else {
balanceIconFrame = CGRect(origin: CGPoint(x: balanceTextFrame.maxX + balanceIconSpacing, y: balanceTextFrame.minY + floor((balanceTextFrame.height - balanceIconSize.height) / 2.0)), size: balanceIconSize)
}
transition.updateFrameAdditive(node: self.balanceTextNode, frame: balanceTextFrame) transition.updateFrameAdditive(node: self.balanceTextNode, frame: balanceTextFrame)
transition.updateFrameAdditive(node: self.balanceIconNode, frame: balanceIconFrame) transition.updateFrame(node: self.balanceIconNode, frame: balanceIconFrame)
return balanceTextSize.height return balanceTextSize.height
} }
@ -245,10 +252,10 @@ private final class WalletInfoHeaderNode: ASDisplayNode {
} }
} }
if self.balance == nil { if self.balance == nil {
self.balanceNode.isHidden = true self.balanceNode.balanceTextNode.isHidden = true
self.balanceSubtitleNode.isHidden = true self.balanceSubtitleNode.isHidden = true
} else { } else {
self.balanceNode.isHidden = false self.balanceNode.balanceTextNode.isHidden = false
self.balanceSubtitleNode.isHidden = false self.balanceSubtitleNode.isHidden = false
} }
transition.updateFrame(node: self.receiveButtonNode, frame: receiveButtonFrame) transition.updateFrame(node: self.receiveButtonNode, frame: receiveButtonFrame)
@ -272,7 +279,8 @@ private final class WalletInfoHeaderNode: ASDisplayNode {
func animateIn() { func animateIn() {
self.sendButtonNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) self.sendButtonNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.receiveButtonNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) self.receiveButtonNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.balanceNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) self.balanceNode.isLoading = false
self.balanceNode.balanceTextNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
self.balanceSubtitleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) self.balanceSubtitleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
} }
} }
@ -366,6 +374,8 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
private var currentEntries: [WalletInfoListEntry]? private var currentEntries: [WalletInfoListEntry]?
private var isReady: Bool = false
init(account: Account, tonContext: TonContext, presentationData: PresentationData, walletInfo: WalletInfo, address: String, sendAction: @escaping () -> Void, receiveAction: @escaping () -> Void, openTransaction: @escaping (WalletTransaction) -> Void) { init(account: Account, tonContext: TonContext, presentationData: PresentationData, walletInfo: WalletInfo, address: String, sendAction: @escaping () -> Void, receiveAction: @escaping () -> Void, openTransaction: @escaping (WalletTransaction) -> Void) {
self.account = account self.account = account
self.tonContext = tonContext self.tonContext = tonContext
@ -379,24 +389,28 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
self.listNode = ListView() self.listNode = ListView()
self.listNode.verticalScrollIndicatorColor = self.presentationData.theme.list.scrollIndicatorColor self.listNode.verticalScrollIndicatorColor = self.presentationData.theme.list.scrollIndicatorColor
self.listNode.verticalScrollIndicatorFollowsOverscroll = true self.listNode.verticalScrollIndicatorFollowsOverscroll = true
self.listNode.isHidden = true
super.init() super.init()
self.backgroundColor = .white self.backgroundColor = .white
self.balanceDisposable.set((currentWalletBalance(publicKey: walletInfo.publicKey, tonInstance: tonContext.instance) self.balanceDisposable.set((getWalletState(address: address, tonInstance: tonContext.instance)
|> deliverOnMainQueue).start(next: { [weak self] value in |> deliverOnMainQueue).start(next: { [weak self] value in
guard let strongSelf = self else { guard let strongSelf = self else {
return return
} }
let firstTime = strongSelf.headerNode.balance == nil strongSelf.headerNode.balanceNode.balance = formatBalanceText(max(0, value.balance))
strongSelf.headerNode.balanceNode.balance = formatBalanceText(max(0, value.rawValue)) strongSelf.headerNode.balance = max(0, value.balance)
strongSelf.headerNode.balance = max(0, value.rawValue)
if let (layout, navigationHeight) = strongSelf.validLayout { if let (layout, navigationHeight) = strongSelf.validLayout {
strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate) strongSelf.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate)
} }
if firstTime {
strongSelf.headerNode.animateIn() let wasReady = strongSelf.isReady
strongSelf.isReady = strongSelf.headerNode.balance != nil && strongSelf.currentEntries != nil
if strongSelf.isReady && !wasReady {
strongSelf.animateReadyIn()
} }
})) }))
@ -410,7 +424,9 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
strongSelf.listOffset = offset strongSelf.listOffset = offset
strongSelf.headerNode.update(size: strongSelf.headerNode.bounds.size, navigationHeight: navigationHeight, offset: offset, transition: listTransition) if strongSelf.isReady {
strongSelf.headerNode.update(size: strongSelf.headerNode.bounds.size, navigationHeight: navigationHeight, offset: offset, transition: listTransition)
}
} }
self.listNode.visibleBottomContentOffsetChanged = { [weak self] offset in self.listNode.visibleBottomContentOffsetChanged = { [weak self] offset in
@ -464,11 +480,22 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
let headerHeight: CGFloat = navigationHeight + 260.0 let headerHeight: CGFloat = navigationHeight + 260.0
let topInset: CGFloat = headerHeight let topInset: CGFloat = headerHeight
let headerFrame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: headerHeight)) let visualHeaderHeight: CGFloat
transition.updateFrame(node: self.headerNode, frame: headerFrame) let visualHeaderOffset: CGFloat
self.headerNode.update(size: headerFrame.size, navigationHeight: navigationHeight, offset: self.listOffset ?? 0.0, transition: transition) if !self.isReady {
visualHeaderHeight = layout.size.height
visualHeaderOffset = visualHeaderHeight
} else {
visualHeaderHeight = headerHeight
visualHeaderOffset = self.listOffset ?? 0.0
}
let visualListOffset = visualHeaderHeight - headerHeight
transition.updateFrame(node: self.listNode, frame: CGRect(origin: CGPoint(), size: layout.size)) let headerFrame = CGRect(origin: CGPoint(), size: CGSize(width: layout.size.width, height: visualHeaderHeight))
transition.updateFrame(node: self.headerNode, frame: headerFrame)
self.headerNode.update(size: headerFrame.size, navigationHeight: navigationHeight, offset: visualHeaderOffset, transition: transition)
transition.updateFrame(node: self.listNode, frame: CGRect(origin: CGPoint(x: 0.0, y: visualListOffset), size: layout.size))
var duration: Double = 0.0 var duration: Double = 0.0
var curve: UInt = 0 var curve: UInt = 0
@ -599,8 +626,11 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
self.enqueuedTransactions.append(transaction) self.enqueuedTransactions.append(transaction)
self.dequeueTransaction() self.dequeueTransaction()
if isFirst { let wasReady = self.isReady
self.listNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) self.isReady = self.headerNode.balance != nil && self.currentEntries != nil
if self.isReady && !wasReady {
self.animateReadyIn()
} }
} }
@ -619,6 +649,14 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
self.listNode.transaction(deleteIndices: transaction.deletions, insertIndicesAndItems: transaction.insertions, updateIndicesAndItems: transaction.updates, options: options, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { _ in self.listNode.transaction(deleteIndices: transaction.deletions, insertIndicesAndItems: transaction.insertions, updateIndicesAndItems: transaction.updates, options: options, updateSizeAndInsets: nil, updateOpaqueState: nil, completion: { _ in
}) })
} }
private func animateReadyIn() {
self.listNode.isHidden = false
self.headerNode.animateIn()
if let (layout, navigationHeight) = self.validLayout {
self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .animated(duration: 0.5, curve: .spring))
}
}
} }
func formatBalanceText(_ value: Int64) -> String { func formatBalanceText(_ value: Int64) -> String {

View File

@ -355,7 +355,7 @@ func walletSendScreen(context: AccountContext, tonContext: TonContext, walletInf
} }
}) })
let balance: Signal<WalletBalance?, NoError> = Signal.single(WalletBalance(rawValue: 2500)) let balance: Signal<WalletState?, NoError> = Signal.single(WalletState(balance: 2500, lastTransactionId: nil))
var focusItemTag: ItemListItemTag? var focusItemTag: ItemListItemTag?
if address == nil { if address == nil {
@ -373,14 +373,14 @@ func walletSendScreen(context: AccountContext, tonContext: TonContext, walletInf
let amount = amountValue(state.amount) let amount = amountValue(state.amount)
var sendEnabled = false var sendEnabled = false
if let balance = balance { if let balance = balance {
sendEnabled = isValidAddress(state.address, exactLength: true) && amount > 0 && amount <= balance.rawValue sendEnabled = isValidAddress(state.address, exactLength: true) && amount > 0 && amount <= balance.balance
} }
let rightNavigationButton = ItemListNavigationButton(content: .text("Send"), style: .bold, enabled: sendEnabled, action: { let rightNavigationButton = ItemListNavigationButton(content: .text("Send"), style: .bold, enabled: sendEnabled, action: {
arguments.proceed() arguments.proceed()
}) })
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text("Send Grams"), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false) let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text("Send Grams"), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back), animateChanges: false)
let listState = ItemListNodeState(entries: walletSendScreenEntries(presentationData: presentationData, balance: balance?.rawValue, state: state), style: .blocks, focusItemTag: focusItemTag, animateChanges: false) let listState = ItemListNodeState(entries: walletSendScreenEntries(presentationData: presentationData, balance: balance?.balance, state: state), style: .blocks, focusItemTag: focusItemTag, animateChanges: false)
return (controllerState, (listState, arguments)) return (controllerState, (listState, arguments))
} }

View File

@ -176,6 +176,113 @@ td::Status AdnlOutboundConnection::process_packet(td::BufferSlice data) {
return td::Status::OK(); return td::Status::OK();
} }
void AdnlExtMultiClientImpl::start_up() {
for (auto &id : ids_) {
add_server(id.first, id.second, [](td::Result<td::Unit> R) {});
}
ids_.clear();
}
void AdnlExtMultiClientImpl::add_server(AdnlNodeIdFull dst, td::IPAddress dst_addr, td::Promise<td::Unit> promise) {
for (auto &c : clients_) {
if (c.second->addr == dst_addr) {
promise.set_error(td::Status::Error(ErrorCode::error, "duplicate ip"));
return;
}
}
auto g = ++generation_;
auto cli = std::make_unique<Client>(AdnlExtClient::create(dst, dst_addr, make_callback(g)), dst, dst_addr, g);
clients_[g] = std::move(cli);
}
void AdnlExtMultiClientImpl::del_server(td::IPAddress dst_addr, td::Promise<td::Unit> promise) {
for (auto &c : clients_) {
if (c.second->addr == dst_addr) {
if (c.second->ready) {
total_ready_--;
if (!total_ready_) {
callback_->on_stop_ready();
}
}
clients_.erase(c.first);
promise.set_value(td::Unit());
return;
}
}
promise.set_error(td::Status::Error(ErrorCode::error, "ip not found"));
}
void AdnlExtMultiClientImpl::send_query(std::string name, td::BufferSlice data, td::Timestamp timeout,
td::Promise<td::BufferSlice> promise) {
if (total_ready_ == 0) {
promise.set_error(td::Status::Error(ErrorCode::notready, "conn not ready"));
return;
}
std::vector<td::uint32> vec;
for (auto &c : clients_) {
if (c.second->ready) {
vec.push_back(c.first);
}
}
CHECK(vec.size() == total_ready_);
auto &c = clients_[vec[td::Random::fast(0, td::narrow_cast<td::uint32>(vec.size() - 1))]];
td::actor::send_closure(c->client, &AdnlExtClient::send_query, std::move(name), std::move(data), timeout,
std::move(promise));
}
void AdnlExtMultiClientImpl::client_ready(td::uint32 idx, bool value) {
auto it = clients_.find(idx);
if (it == clients_.end()) {
return;
}
auto &c = it->second;
if (c->ready == value) {
return;
}
c->ready = value;
if (value) {
total_ready_++;
if (total_ready_ == 1) {
callback_->on_ready();
}
} else {
total_ready_--;
if (total_ready_ == 0) {
callback_->on_stop_ready();
}
}
}
std::unique_ptr<AdnlExtClient::Callback> AdnlExtMultiClientImpl::make_callback(td::uint32 g) {
class Cb : public Callback {
public:
Cb(td::actor::ActorId<AdnlExtMultiClientImpl> id, td::uint32 idx) : id_(id), idx_(idx) {
}
void on_ready() override {
td::actor::send_closure(id_, &AdnlExtMultiClientImpl::client_ready, idx_, true);
}
void on_stop_ready() override {
td::actor::send_closure(id_, &AdnlExtMultiClientImpl::client_ready, idx_, false);
}
private:
td::actor::ActorId<AdnlExtMultiClientImpl> id_;
td::uint32 idx_;
};
return std::make_unique<Cb>(actor_id(this), g);
}
td::actor::ActorOwn<AdnlExtMultiClient> AdnlExtMultiClient::create(
std::vector<std::pair<AdnlNodeIdFull, td::IPAddress>> ids, std::unique_ptr<AdnlExtClient::Callback> callback) {
return td::actor::create_actor<AdnlExtMultiClientImpl>("extmulticlient", std::move(ids), std::move(callback));
}
} // namespace adnl } // namespace adnl
} // namespace ton } // namespace ton

View File

@ -43,6 +43,14 @@ class AdnlExtClient : public td::actor::Actor {
std::unique_ptr<AdnlExtClient::Callback> callback); std::unique_ptr<AdnlExtClient::Callback> callback);
}; };
class AdnlExtMultiClient : public AdnlExtClient {
public:
virtual void add_server(AdnlNodeIdFull dst, td::IPAddress dst_addr, td::Promise<td::Unit> promise) = 0;
virtual void del_server(td::IPAddress dst_addr, td::Promise<td::Unit> promise) = 0;
static td::actor::ActorOwn<AdnlExtMultiClient> create(std::vector<std::pair<AdnlNodeIdFull, td::IPAddress>> ids,
std::unique_ptr<AdnlExtClient::Callback> callback);
};
} // namespace adnl } // namespace adnl
} // namespace ton } // namespace ton

View File

@ -140,6 +140,52 @@ class AdnlExtClientImpl : public AdnlExtClient {
void try_stop(); void try_stop();
}; };
class AdnlExtMultiClientImpl : public AdnlExtMultiClient {
public:
AdnlExtMultiClientImpl(std::vector<std::pair<AdnlNodeIdFull, td::IPAddress>> ids,
std::unique_ptr<AdnlExtClient::Callback> callback)
: ids_(std::move(ids)), callback_(std::move(callback)) {
}
void start_up() override;
void add_server(AdnlNodeIdFull dst, td::IPAddress dst_addr, td::Promise<td::Unit> promise) override;
void del_server(td::IPAddress dst_addr, td::Promise<td::Unit> promise) override;
void check_ready(td::Promise<td::Unit> promise) override {
if (total_ready_ > 0) {
promise.set_value(td::Unit());
} else {
promise.set_error(td::Status::Error(ErrorCode::notready, "conn not ready"));
}
}
void send_query(std::string name, td::BufferSlice data, td::Timestamp timeout,
td::Promise<td::BufferSlice> promise) override;
void client_ready(td::uint32 idx, bool value);
private:
std::unique_ptr<Callback> make_callback(td::uint32 g);
struct Client {
Client(td::actor::ActorOwn<AdnlExtClient> client, AdnlNodeIdFull pubkey, td::IPAddress addr, td::uint32 generation)
: client(std::move(client)), pubkey(std::move(pubkey)), addr(addr), generation(generation), ready(false) {
}
td::actor::ActorOwn<AdnlExtClient> client;
AdnlNodeIdFull pubkey;
td::IPAddress addr;
td::uint32 generation;
bool ready = false;
};
td::uint32 total_ready_ = 0;
td::uint32 generation_ = 0;
std::map<td::uint32, std::unique_ptr<Client>> clients_;
std::vector<std::pair<AdnlNodeIdFull, td::IPAddress>> ids_;
std::unique_ptr<AdnlExtClient::Callback> callback_;
};
} // namespace adnl } // namespace adnl
} // namespace ton } // namespace ton

View File

@ -25,15 +25,40 @@
namespace td { namespace td {
Timer::Timer() : start_time_(Time::now()) { Timer::Timer(bool is_paused) : is_paused_(is_paused) {
if (is_paused_) {
start_time_ = 0;
} else {
start_time_ = Time::now();
}
}
void Timer::pause() {
if (is_paused_) {
return;
}
elapsed_ += Time::now() - start_time_;
is_paused_ = true;
}
void Timer::resume() {
if (!is_paused_) {
return;
}
start_time_ = Time::now();
is_paused_ = false;
} }
double Timer::elapsed() const { double Timer::elapsed() const {
return Time::now() - start_time_; double res = elapsed_;
if (!is_paused_) {
res += Time::now() - start_time_;
}
return res;
} }
StringBuilder &operator<<(StringBuilder &string_builder, const Timer &timer) { StringBuilder &operator<<(StringBuilder &string_builder, const Timer &timer) {
return string_builder << "in " << Time::now() - timer.start_time_; return string_builder << format::as_time(timer.elapsed());
} }
PerfWarningTimer::PerfWarningTimer(string name, double max_duration) PerfWarningTimer::PerfWarningTimer(string name, double max_duration)

View File

@ -24,14 +24,22 @@ namespace td {
class Timer { class Timer {
public: public:
Timer(); Timer() : Timer(false) {
}
explicit Timer(bool is_paused);
Timer(const Timer &other) = default;
Timer &operator=(const Timer &other) = default;
double elapsed() const; double elapsed() const;
void pause();
void resume();
private: private:
friend StringBuilder &operator<<(StringBuilder &string_builder, const Timer &timer); friend StringBuilder &operator<<(StringBuilder &string_builder, const Timer &timer);
double elapsed_{0};
double start_time_; double start_time_;
bool is_paused_{false};
}; };
class PerfWarningTimer { class PerfWarningTimer {

View File

@ -53,17 +53,20 @@ SecureString create_empty<SecureString>(size_t size) {
template <class T> template <class T>
Result<T> read_file_impl(CSlice path, int64 size, int64 offset) { Result<T> read_file_impl(CSlice path, int64 size, int64 offset) {
TRY_RESULT(from_file, FileFd::open(path, FileFd::Read)); TRY_RESULT(from_file, FileFd::open(path, FileFd::Read));
TRY_RESULT(file_size, from_file.get_size());
if (offset < 0 || offset > file_size) {
return Status::Error("Failed to read file: invalid offset");
}
if (size == -1) { if (size == -1) {
TRY_RESULT(file_size, from_file.get_size()); size = file_size - offset;
size = file_size; } else if (size >= 0) {
if (size + offset > file_size) {
size = file_size - offset;
}
} }
if (size < 0) { if (size < 0) {
return Status::Error("Failed to read file: invalid size"); return Status::Error("Failed to read file: invalid size");
} }
if (offset < 0 || offset > size) {
return Status::Error("Failed to read file: invalid offset");
}
size -= offset;
auto content = create_empty<T>(narrow_cast<size_t>(size)); auto content = create_empty<T>(narrow_cast<size_t>(size));
TRY_RESULT(got_size, from_file.pread(as_mutable_slice(content), offset)); TRY_RESULT(got_size, from_file.pread(as_mutable_slice(content), offset));
if (got_size != static_cast<size_t>(size)) { if (got_size != static_cast<size_t>(size)) {

View File

@ -7643,21 +7643,21 @@ engine_validator_config::engine_validator_config()
, dht_() , dht_()
, validators_() , validators_()
, fullnode_() , fullnode_()
, fullnodeslave_() , fullnodeslaves_()
, fullnodemasters_() , fullnodemasters_()
, liteservers_() , liteservers_()
, control_() , control_()
, gc_() , gc_()
{} {}
engine_validator_config::engine_validator_config(std::int32_t out_port_, std::vector<object_ptr<engine_Addr>> &&addrs_, std::vector<object_ptr<engine_adnl>> &&adnl_, std::vector<object_ptr<engine_dht>> &&dht_, std::vector<object_ptr<engine_validator>> &&validators_, td::Bits256 const &fullnode_, object_ptr<engine_validator_fullNodeSlave> &&fullnodeslave_, std::vector<object_ptr<engine_validator_fullNodeMaster>> &&fullnodemasters_, std::vector<object_ptr<engine_liteServer>> &&liteservers_, std::vector<object_ptr<engine_controlInterface>> &&control_, object_ptr<engine_gc> &&gc_) engine_validator_config::engine_validator_config(std::int32_t out_port_, std::vector<object_ptr<engine_Addr>> &&addrs_, std::vector<object_ptr<engine_adnl>> &&adnl_, std::vector<object_ptr<engine_dht>> &&dht_, std::vector<object_ptr<engine_validator>> &&validators_, td::Bits256 const &fullnode_, std::vector<object_ptr<engine_validator_fullNodeSlave>> &&fullnodeslaves_, std::vector<object_ptr<engine_validator_fullNodeMaster>> &&fullnodemasters_, std::vector<object_ptr<engine_liteServer>> &&liteservers_, std::vector<object_ptr<engine_controlInterface>> &&control_, object_ptr<engine_gc> &&gc_)
: out_port_(out_port_) : out_port_(out_port_)
, addrs_(std::move(addrs_)) , addrs_(std::move(addrs_))
, adnl_(std::move(adnl_)) , adnl_(std::move(adnl_))
, dht_(std::move(dht_)) , dht_(std::move(dht_))
, validators_(std::move(validators_)) , validators_(std::move(validators_))
, fullnode_(fullnode_) , fullnode_(fullnode_)
, fullnodeslave_(std::move(fullnodeslave_)) , fullnodeslaves_(std::move(fullnodeslaves_))
, fullnodemasters_(std::move(fullnodemasters_)) , fullnodemasters_(std::move(fullnodemasters_))
, liteservers_(std::move(liteservers_)) , liteservers_(std::move(liteservers_))
, control_(std::move(control_)) , control_(std::move(control_))
@ -7678,7 +7678,7 @@ engine_validator_config::engine_validator_config(td::TlParser &p)
, dht_(TlFetchVector<TlFetchObject<engine_dht>>::parse(p)) , dht_(TlFetchVector<TlFetchObject<engine_dht>>::parse(p))
, validators_(TlFetchVector<TlFetchObject<engine_validator>>::parse(p)) , validators_(TlFetchVector<TlFetchObject<engine_validator>>::parse(p))
, fullnode_(TlFetchInt256::parse(p)) , fullnode_(TlFetchInt256::parse(p))
, fullnodeslave_(TlFetchObject<engine_validator_fullNodeSlave>::parse(p)) , fullnodeslaves_(TlFetchVector<TlFetchObject<engine_validator_fullNodeSlave>>::parse(p))
, fullnodemasters_(TlFetchVector<TlFetchObject<engine_validator_fullNodeMaster>>::parse(p)) , fullnodemasters_(TlFetchVector<TlFetchObject<engine_validator_fullNodeMaster>>::parse(p))
, liteservers_(TlFetchVector<TlFetchObject<engine_liteServer>>::parse(p)) , liteservers_(TlFetchVector<TlFetchObject<engine_liteServer>>::parse(p))
, control_(TlFetchVector<TlFetchObject<engine_controlInterface>>::parse(p)) , control_(TlFetchVector<TlFetchObject<engine_controlInterface>>::parse(p))
@ -7694,7 +7694,7 @@ void engine_validator_config::store(td::TlStorerCalcLength &s) const {
TlStoreVector<TlStoreObject>::store(dht_, s); TlStoreVector<TlStoreObject>::store(dht_, s);
TlStoreVector<TlStoreObject>::store(validators_, s); TlStoreVector<TlStoreObject>::store(validators_, s);
TlStoreBinary::store(fullnode_, s); TlStoreBinary::store(fullnode_, s);
TlStoreObject::store(fullnodeslave_, s); TlStoreVector<TlStoreObject>::store(fullnodeslaves_, s);
TlStoreVector<TlStoreObject>::store(fullnodemasters_, s); TlStoreVector<TlStoreObject>::store(fullnodemasters_, s);
TlStoreVector<TlStoreObject>::store(liteservers_, s); TlStoreVector<TlStoreObject>::store(liteservers_, s);
TlStoreVector<TlStoreObject>::store(control_, s); TlStoreVector<TlStoreObject>::store(control_, s);
@ -7709,7 +7709,7 @@ void engine_validator_config::store(td::TlStorerUnsafe &s) const {
TlStoreVector<TlStoreObject>::store(dht_, s); TlStoreVector<TlStoreObject>::store(dht_, s);
TlStoreVector<TlStoreObject>::store(validators_, s); TlStoreVector<TlStoreObject>::store(validators_, s);
TlStoreBinary::store(fullnode_, s); TlStoreBinary::store(fullnode_, s);
TlStoreObject::store(fullnodeslave_, s); TlStoreVector<TlStoreObject>::store(fullnodeslaves_, s);
TlStoreVector<TlStoreObject>::store(fullnodemasters_, s); TlStoreVector<TlStoreObject>::store(fullnodemasters_, s);
TlStoreVector<TlStoreObject>::store(liteservers_, s); TlStoreVector<TlStoreObject>::store(liteservers_, s);
TlStoreVector<TlStoreObject>::store(control_, s); TlStoreVector<TlStoreObject>::store(control_, s);
@ -7725,7 +7725,7 @@ void engine_validator_config::store(td::TlStorerToString &s, const char *field_n
{ const std::vector<object_ptr<engine_dht>> &v = dht_; const std::uint32_t multiplicity = static_cast<std::uint32_t>(v.size()); const auto vector_name = "vector[" + td::to_string(multiplicity)+ "]"; s.store_class_begin("dht", vector_name.c_str()); for (std::uint32_t i = 0; i < multiplicity; i++) { if (v[i] == nullptr) { s.store_field("", "null"); } else { v[i]->store(s, ""); } } s.store_class_end(); } { const std::vector<object_ptr<engine_dht>> &v = dht_; const std::uint32_t multiplicity = static_cast<std::uint32_t>(v.size()); const auto vector_name = "vector[" + td::to_string(multiplicity)+ "]"; s.store_class_begin("dht", vector_name.c_str()); for (std::uint32_t i = 0; i < multiplicity; i++) { if (v[i] == nullptr) { s.store_field("", "null"); } else { v[i]->store(s, ""); } } s.store_class_end(); }
{ const std::vector<object_ptr<engine_validator>> &v = validators_; const std::uint32_t multiplicity = static_cast<std::uint32_t>(v.size()); const auto vector_name = "vector[" + td::to_string(multiplicity)+ "]"; s.store_class_begin("validators", vector_name.c_str()); for (std::uint32_t i = 0; i < multiplicity; i++) { if (v[i] == nullptr) { s.store_field("", "null"); } else { v[i]->store(s, ""); } } s.store_class_end(); } { const std::vector<object_ptr<engine_validator>> &v = validators_; const std::uint32_t multiplicity = static_cast<std::uint32_t>(v.size()); const auto vector_name = "vector[" + td::to_string(multiplicity)+ "]"; s.store_class_begin("validators", vector_name.c_str()); for (std::uint32_t i = 0; i < multiplicity; i++) { if (v[i] == nullptr) { s.store_field("", "null"); } else { v[i]->store(s, ""); } } s.store_class_end(); }
s.store_field("fullnode", fullnode_); s.store_field("fullnode", fullnode_);
if (fullnodeslave_ == nullptr) { s.store_field("fullnodeslave", "null"); } else { fullnodeslave_->store(s, "fullnodeslave"); } { const std::vector<object_ptr<engine_validator_fullNodeSlave>> &v = fullnodeslaves_; const std::uint32_t multiplicity = static_cast<std::uint32_t>(v.size()); const auto vector_name = "vector[" + td::to_string(multiplicity)+ "]"; s.store_class_begin("fullnodeslaves", vector_name.c_str()); for (std::uint32_t i = 0; i < multiplicity; i++) { if (v[i] == nullptr) { s.store_field("", "null"); } else { v[i]->store(s, ""); } } s.store_class_end(); }
{ const std::vector<object_ptr<engine_validator_fullNodeMaster>> &v = fullnodemasters_; const std::uint32_t multiplicity = static_cast<std::uint32_t>(v.size()); const auto vector_name = "vector[" + td::to_string(multiplicity)+ "]"; s.store_class_begin("fullnodemasters", vector_name.c_str()); for (std::uint32_t i = 0; i < multiplicity; i++) { if (v[i] == nullptr) { s.store_field("", "null"); } else { v[i]->store(s, ""); } } s.store_class_end(); } { const std::vector<object_ptr<engine_validator_fullNodeMaster>> &v = fullnodemasters_; const std::uint32_t multiplicity = static_cast<std::uint32_t>(v.size()); const auto vector_name = "vector[" + td::to_string(multiplicity)+ "]"; s.store_class_begin("fullnodemasters", vector_name.c_str()); for (std::uint32_t i = 0; i < multiplicity; i++) { if (v[i] == nullptr) { s.store_field("", "null"); } else { v[i]->store(s, ""); } } s.store_class_end(); }
{ const std::vector<object_ptr<engine_liteServer>> &v = liteservers_; const std::uint32_t multiplicity = static_cast<std::uint32_t>(v.size()); const auto vector_name = "vector[" + td::to_string(multiplicity)+ "]"; s.store_class_begin("liteservers", vector_name.c_str()); for (std::uint32_t i = 0; i < multiplicity; i++) { if (v[i] == nullptr) { s.store_field("", "null"); } else { v[i]->store(s, ""); } } s.store_class_end(); } { const std::vector<object_ptr<engine_liteServer>> &v = liteservers_; const std::uint32_t multiplicity = static_cast<std::uint32_t>(v.size()); const auto vector_name = "vector[" + td::to_string(multiplicity)+ "]"; s.store_class_begin("liteservers", vector_name.c_str()); for (std::uint32_t i = 0; i < multiplicity; i++) { if (v[i] == nullptr) { s.store_field("", "null"); } else { v[i]->store(s, ""); } } s.store_class_end(); }
{ const std::vector<object_ptr<engine_controlInterface>> &v = control_; const std::uint32_t multiplicity = static_cast<std::uint32_t>(v.size()); const auto vector_name = "vector[" + td::to_string(multiplicity)+ "]"; s.store_class_begin("control", vector_name.c_str()); for (std::uint32_t i = 0; i < multiplicity; i++) { if (v[i] == nullptr) { s.store_field("", "null"); } else { v[i]->store(s, ""); } } s.store_class_end(); } { const std::vector<object_ptr<engine_controlInterface>> &v = control_; const std::uint32_t multiplicity = static_cast<std::uint32_t>(v.size()); const auto vector_name = "vector[" + td::to_string(multiplicity)+ "]"; s.store_class_begin("control", vector_name.c_str()); for (std::uint32_t i = 0; i < multiplicity; i++) { if (v[i] == nullptr) { s.store_field("", "null"); } else { v[i]->store(s, ""); } } s.store_class_end(); }

View File

@ -4214,7 +4214,7 @@ class engine_validator_config final : public Object {
std::vector<object_ptr<engine_dht>> dht_; std::vector<object_ptr<engine_dht>> dht_;
std::vector<object_ptr<engine_validator>> validators_; std::vector<object_ptr<engine_validator>> validators_;
td::Bits256 fullnode_; td::Bits256 fullnode_;
object_ptr<engine_validator_fullNodeSlave> fullnodeslave_; std::vector<object_ptr<engine_validator_fullNodeSlave>> fullnodeslaves_;
std::vector<object_ptr<engine_validator_fullNodeMaster>> fullnodemasters_; std::vector<object_ptr<engine_validator_fullNodeMaster>> fullnodemasters_;
std::vector<object_ptr<engine_liteServer>> liteservers_; std::vector<object_ptr<engine_liteServer>> liteservers_;
std::vector<object_ptr<engine_controlInterface>> control_; std::vector<object_ptr<engine_controlInterface>> control_;
@ -4222,9 +4222,9 @@ class engine_validator_config final : public Object {
engine_validator_config(); engine_validator_config();
engine_validator_config(std::int32_t out_port_, std::vector<object_ptr<engine_Addr>> &&addrs_, std::vector<object_ptr<engine_adnl>> &&adnl_, std::vector<object_ptr<engine_dht>> &&dht_, std::vector<object_ptr<engine_validator>> &&validators_, td::Bits256 const &fullnode_, object_ptr<engine_validator_fullNodeSlave> &&fullnodeslave_, std::vector<object_ptr<engine_validator_fullNodeMaster>> &&fullnodemasters_, std::vector<object_ptr<engine_liteServer>> &&liteservers_, std::vector<object_ptr<engine_controlInterface>> &&control_, object_ptr<engine_gc> &&gc_); engine_validator_config(std::int32_t out_port_, std::vector<object_ptr<engine_Addr>> &&addrs_, std::vector<object_ptr<engine_adnl>> &&adnl_, std::vector<object_ptr<engine_dht>> &&dht_, std::vector<object_ptr<engine_validator>> &&validators_, td::Bits256 const &fullnode_, std::vector<object_ptr<engine_validator_fullNodeSlave>> &&fullnodeslaves_, std::vector<object_ptr<engine_validator_fullNodeMaster>> &&fullnodemasters_, std::vector<object_ptr<engine_liteServer>> &&liteservers_, std::vector<object_ptr<engine_controlInterface>> &&control_, object_ptr<engine_gc> &&gc_);
static const std::int32_t ID = 17126390; static const std::int32_t ID = -826140252;
std::int32_t get_id() const final { std::int32_t get_id() const final {
return ID; return ID;
} }

View File

@ -625,7 +625,7 @@ Result<int32> tl_constructor_from_string(ton_api::Object *object, const std::str
{"engine.adnlProxy.config", 1848000769}, {"engine.adnlProxy.config", 1848000769},
{"engine.adnlProxy.port", -117344950}, {"engine.adnlProxy.port", -117344950},
{"engine.dht.config", -197295930}, {"engine.dht.config", -197295930},
{"engine.validator.config", 17126390}, {"engine.validator.config", -826140252},
{"engine.validator.controlQueryError", 1999018527}, {"engine.validator.controlQueryError", 1999018527},
{"engine.validator.dhtServerStatus", -1323440290}, {"engine.validator.dhtServerStatus", -1323440290},
{"engine.validator.dhtServersStatus", 725155112}, {"engine.validator.dhtServersStatus", 725155112},
@ -3099,9 +3099,9 @@ Status from_json(ton_api::engine_validator_config &to, JsonObject &from) {
} }
} }
{ {
TRY_RESULT(value, get_json_object_field(from, "fullnodeslave", JsonValue::Type::Null, true)); TRY_RESULT(value, get_json_object_field(from, "fullnodeslaves", JsonValue::Type::Null, true));
if (value.type() != JsonValue::Type::Null) { if (value.type() != JsonValue::Type::Null) {
TRY_STATUS(from_json(to.fullnodeslave_, value)); TRY_STATUS(from_json(to.fullnodeslaves_, value));
} }
} }
{ {
@ -6711,9 +6711,7 @@ void to_json(JsonValueScope &jv, const ton_api::engine_validator_config &object)
jo << ctie("dht", ToJson(object.dht_)); jo << ctie("dht", ToJson(object.dht_));
jo << ctie("validators", ToJson(object.validators_)); jo << ctie("validators", ToJson(object.validators_));
jo << ctie("fullnode", ToJson(object.fullnode_)); jo << ctie("fullnode", ToJson(object.fullnode_));
if (object.fullnodeslave_) { jo << ctie("fullnodeslaves", ToJson(object.fullnodeslaves_));
jo << ctie("fullnodeslave", ToJson(object.fullnodeslave_));
}
jo << ctie("fullnodemasters", ToJson(object.fullnodemasters_)); jo << ctie("fullnodemasters", ToJson(object.fullnodemasters_));
jo << ctie("liteservers", ToJson(object.liteservers_)); jo << ctie("liteservers", ToJson(object.liteservers_));
jo << ctie("control", ToJson(object.control_)); jo << ctie("control", ToJson(object.control_));

View File

@ -413,12 +413,14 @@ raw_message::raw_message()
: source_() : source_()
, destination_() , destination_()
, value_() , value_()
, message_()
{} {}
raw_message::raw_message(std::string const &source_, std::string const &destination_, std::int64_t value_) raw_message::raw_message(std::string const &source_, std::string const &destination_, std::int64_t value_, std::string const &message_)
: source_(std::move(source_)) : source_(std::move(source_))
, destination_(std::move(destination_)) , destination_(std::move(destination_))
, value_(value_) , value_(value_)
, message_(std::move(message_))
{} {}
const std::int32_t raw_message::ID; const std::int32_t raw_message::ID;
@ -429,6 +431,7 @@ void raw_message::store(td::TlStorerToString &s, const char *field_name) const {
s.store_field("source", source_); s.store_field("source", source_);
s.store_field("destination", destination_); s.store_field("destination", destination_);
s.store_field("value", value_); s.store_field("value", value_);
s.store_bytes_field("message", message_);
s.store_class_end(); s.store_class_end();
} }
} }
@ -741,13 +744,15 @@ generic_sendGrams::generic_sendGrams()
, source_() , source_()
, destination_() , destination_()
, amount_() , amount_()
, message_()
{} {}
generic_sendGrams::generic_sendGrams(object_ptr<inputKey> &&private_key_, object_ptr<accountAddress> &&source_, object_ptr<accountAddress> &&destination_, std::int64_t amount_) generic_sendGrams::generic_sendGrams(object_ptr<inputKey> &&private_key_, object_ptr<accountAddress> &&source_, object_ptr<accountAddress> &&destination_, std::int64_t amount_, std::string const &message_)
: private_key_(std::move(private_key_)) : private_key_(std::move(private_key_))
, source_(std::move(source_)) , source_(std::move(source_))
, destination_(std::move(destination_)) , destination_(std::move(destination_))
, amount_(amount_) , amount_(amount_)
, message_(std::move(message_))
{} {}
const std::int32_t generic_sendGrams::ID; const std::int32_t generic_sendGrams::ID;
@ -759,6 +764,7 @@ void generic_sendGrams::store(td::TlStorerToString &s, const char *field_name) c
if (source_ == nullptr) { s.store_field("source", "null"); } else { source_->store(s, "source"); } if (source_ == nullptr) { s.store_field("source", "null"); } else { source_->store(s, "source"); }
if (destination_ == nullptr) { s.store_field("destination", "null"); } else { destination_->store(s, "destination"); } if (destination_ == nullptr) { s.store_field("destination", "null"); } else { destination_->store(s, "destination"); }
s.store_field("amount", amount_); s.store_field("amount", amount_);
s.store_bytes_field("message", message_);
s.store_class_end(); s.store_class_end();
} }
} }
@ -1058,12 +1064,14 @@ testGiver_sendGrams::testGiver_sendGrams()
: destination_() : destination_()
, seqno_() , seqno_()
, amount_() , amount_()
, message_()
{} {}
testGiver_sendGrams::testGiver_sendGrams(object_ptr<accountAddress> &&destination_, std::int32_t seqno_, std::int64_t amount_) testGiver_sendGrams::testGiver_sendGrams(object_ptr<accountAddress> &&destination_, std::int32_t seqno_, std::int64_t amount_, std::string const &message_)
: destination_(std::move(destination_)) : destination_(std::move(destination_))
, seqno_(seqno_) , seqno_(seqno_)
, amount_(amount_) , amount_(amount_)
, message_(std::move(message_))
{} {}
const std::int32_t testGiver_sendGrams::ID; const std::int32_t testGiver_sendGrams::ID;
@ -1074,6 +1082,7 @@ void testGiver_sendGrams::store(td::TlStorerToString &s, const char *field_name)
if (destination_ == nullptr) { s.store_field("destination", "null"); } else { destination_->store(s, "destination"); } if (destination_ == nullptr) { s.store_field("destination", "null"); } else { destination_->store(s, "destination"); }
s.store_field("seqno", seqno_); s.store_field("seqno", seqno_);
s.store_field("amount", amount_); s.store_field("amount", amount_);
s.store_bytes_field("message", message_);
s.store_class_end(); s.store_class_end();
} }
} }
@ -1137,13 +1146,15 @@ testWallet_sendGrams::testWallet_sendGrams()
, destination_() , destination_()
, seqno_() , seqno_()
, amount_() , amount_()
, message_()
{} {}
testWallet_sendGrams::testWallet_sendGrams(object_ptr<inputKey> &&private_key_, object_ptr<accountAddress> &&destination_, std::int32_t seqno_, std::int64_t amount_) testWallet_sendGrams::testWallet_sendGrams(object_ptr<inputKey> &&private_key_, object_ptr<accountAddress> &&destination_, std::int32_t seqno_, std::int64_t amount_, std::string const &message_)
: private_key_(std::move(private_key_)) : private_key_(std::move(private_key_))
, destination_(std::move(destination_)) , destination_(std::move(destination_))
, seqno_(seqno_) , seqno_(seqno_)
, amount_(amount_) , amount_(amount_)
, message_(std::move(message_))
{} {}
const std::int32_t testWallet_sendGrams::ID; const std::int32_t testWallet_sendGrams::ID;
@ -1155,6 +1166,7 @@ void testWallet_sendGrams::store(td::TlStorerToString &s, const char *field_name
if (destination_ == nullptr) { s.store_field("destination", "null"); } else { destination_->store(s, "destination"); } if (destination_ == nullptr) { s.store_field("destination", "null"); } else { destination_->store(s, "destination"); }
s.store_field("seqno", seqno_); s.store_field("seqno", seqno_);
s.store_field("amount", amount_); s.store_field("amount", amount_);
s.store_bytes_field("message", message_);
s.store_class_end(); s.store_class_end();
} }
} }

View File

@ -442,12 +442,13 @@ class raw_message final : public Object {
std::string source_; std::string source_;
std::string destination_; std::string destination_;
std::int64_t value_; std::int64_t value_;
std::string message_;
raw_message(); raw_message();
raw_message(std::string const &source_, std::string const &destination_, std::int64_t value_); raw_message(std::string const &source_, std::string const &destination_, std::int64_t value_, std::string const &message_);
static const std::int32_t ID = -1131081640; static const std::int32_t ID = -259956097;
std::int32_t get_id() const final { std::int32_t get_id() const final {
return ID; return ID;
} }
@ -717,12 +718,13 @@ class generic_sendGrams final : public Function {
object_ptr<accountAddress> source_; object_ptr<accountAddress> source_;
object_ptr<accountAddress> destination_; object_ptr<accountAddress> destination_;
std::int64_t amount_; std::int64_t amount_;
std::string message_;
generic_sendGrams(); generic_sendGrams();
generic_sendGrams(object_ptr<inputKey> &&private_key_, object_ptr<accountAddress> &&source_, object_ptr<accountAddress> &&destination_, std::int64_t amount_); generic_sendGrams(object_ptr<inputKey> &&private_key_, object_ptr<accountAddress> &&source_, object_ptr<accountAddress> &&destination_, std::int64_t amount_, std::string const &message_);
static const std::int32_t ID = 799772985; static const std::int32_t ID = 1523427648;
std::int32_t get_id() const final { std::int32_t get_id() const final {
return ID; return ID;
} }
@ -1012,12 +1014,13 @@ class testGiver_sendGrams final : public Function {
object_ptr<accountAddress> destination_; object_ptr<accountAddress> destination_;
std::int32_t seqno_; std::int32_t seqno_;
std::int64_t amount_; std::int64_t amount_;
std::string message_;
testGiver_sendGrams(); testGiver_sendGrams();
testGiver_sendGrams(object_ptr<accountAddress> &&destination_, std::int32_t seqno_, std::int64_t amount_); testGiver_sendGrams(object_ptr<accountAddress> &&destination_, std::int32_t seqno_, std::int64_t amount_, std::string const &message_);
static const std::int32_t ID = -178493799; static const std::int32_t ID = -1361914347;
std::int32_t get_id() const final { std::int32_t get_id() const final {
return ID; return ID;
} }
@ -1087,12 +1090,13 @@ class testWallet_sendGrams final : public Function {
object_ptr<accountAddress> destination_; object_ptr<accountAddress> destination_;
std::int32_t seqno_; std::int32_t seqno_;
std::int64_t amount_; std::int64_t amount_;
std::string message_;
testWallet_sendGrams(); testWallet_sendGrams();
testWallet_sendGrams(object_ptr<inputKey> &&private_key_, object_ptr<accountAddress> &&destination_, std::int32_t seqno_, std::int64_t amount_); testWallet_sendGrams(object_ptr<inputKey> &&private_key_, object_ptr<accountAddress> &&destination_, std::int32_t seqno_, std::int64_t amount_, std::string const &message_);
static const std::int32_t ID = -1716705044; static const std::int32_t ID = 43200674;
std::int32_t get_id() const final { std::int32_t get_id() const final {
return ID; return ID;
} }

View File

@ -60,7 +60,7 @@ Result<int32> tl_constructor_from_string(tonlib_api::Object *object, const std::
{"internal.transactionId", -989527262}, {"internal.transactionId", -989527262},
{"raw.accountState", 461615898}, {"raw.accountState", 461615898},
{"raw.initialAccountState", 777456197}, {"raw.initialAccountState", 777456197},
{"raw.message", -1131081640}, {"raw.message", -259956097},
{"raw.transaction", -1159530820}, {"raw.transaction", -1159530820},
{"raw.transactions", 240548986}, {"raw.transactions", 240548986},
{"testGiver.accountState", 860930426}, {"testGiver.accountState", 860930426},
@ -84,7 +84,7 @@ Result<int32> tl_constructor_from_string(tonlib_api::Function *object, const std
{"exportKey", 399723440}, {"exportKey", 399723440},
{"exportPemKey", -2047752448}, {"exportPemKey", -2047752448},
{"generic.getAccountState", -657000446}, {"generic.getAccountState", -657000446},
{"generic.sendGrams", 799772985}, {"generic.sendGrams", 1523427648},
{"getBip39Hints", -1889640982}, {"getBip39Hints", -1889640982},
{"importEncryptedKey", 656724958}, {"importEncryptedKey", 656724958},
{"importKey", -1607900903}, {"importKey", -1607900903},
@ -100,11 +100,11 @@ Result<int32> tl_constructor_from_string(tonlib_api::Function *object, const std
{"runTests", -2039925427}, {"runTests", -2039925427},
{"testGiver.getAccountAddress", -540100768}, {"testGiver.getAccountAddress", -540100768},
{"testGiver.getAccountState", 267738275}, {"testGiver.getAccountState", 267738275},
{"testGiver.sendGrams", -178493799}, {"testGiver.sendGrams", -1361914347},
{"testWallet.getAccountAddress", -1557748223}, {"testWallet.getAccountAddress", -1557748223},
{"testWallet.getAccountState", 654082364}, {"testWallet.getAccountState", 654082364},
{"testWallet.init", 419055225}, {"testWallet.init", 419055225},
{"testWallet.sendGrams", -1716705044} {"testWallet.sendGrams", 43200674}
}; };
auto it = m.find(str); auto it = m.find(str);
if (it == m.end()) { if (it == m.end()) {
@ -377,6 +377,12 @@ Status from_json(tonlib_api::raw_message &to, JsonObject &from) {
TRY_STATUS(from_json(to.value_, value)); TRY_STATUS(from_json(to.value_, value));
} }
} }
{
TRY_RESULT(value, get_json_object_field(from, "message", JsonValue::Type::Null, true));
if (value.type() != JsonValue::Type::Null) {
TRY_STATUS(from_json_bytes(to.message_, value));
}
}
return Status::OK(); return Status::OK();
} }
Status from_json(tonlib_api::raw_transaction &to, JsonObject &from) { Status from_json(tonlib_api::raw_transaction &to, JsonObject &from) {
@ -638,6 +644,12 @@ Status from_json(tonlib_api::generic_sendGrams &to, JsonObject &from) {
TRY_STATUS(from_json(to.amount_, value)); TRY_STATUS(from_json(to.amount_, value));
} }
} }
{
TRY_RESULT(value, get_json_object_field(from, "message", JsonValue::Type::Null, true));
if (value.type() != JsonValue::Type::Null) {
TRY_STATUS(from_json_bytes(to.message_, value));
}
}
return Status::OK(); return Status::OK();
} }
Status from_json(tonlib_api::getBip39Hints &to, JsonObject &from) { Status from_json(tonlib_api::getBip39Hints &to, JsonObject &from) {
@ -848,6 +860,12 @@ Status from_json(tonlib_api::testGiver_sendGrams &to, JsonObject &from) {
TRY_STATUS(from_json(to.amount_, value)); TRY_STATUS(from_json(to.amount_, value));
} }
} }
{
TRY_RESULT(value, get_json_object_field(from, "message", JsonValue::Type::Null, true));
if (value.type() != JsonValue::Type::Null) {
TRY_STATUS(from_json_bytes(to.message_, value));
}
}
return Status::OK(); return Status::OK();
} }
Status from_json(tonlib_api::testWallet_getAccountAddress &to, JsonObject &from) { Status from_json(tonlib_api::testWallet_getAccountAddress &to, JsonObject &from) {
@ -902,6 +920,12 @@ Status from_json(tonlib_api::testWallet_sendGrams &to, JsonObject &from) {
TRY_STATUS(from_json(to.amount_, value)); TRY_STATUS(from_json(to.amount_, value));
} }
} }
{
TRY_RESULT(value, get_json_object_field(from, "message", JsonValue::Type::Null, true));
if (value.type() != JsonValue::Type::Null) {
TRY_STATUS(from_json_bytes(to.message_, value));
}
}
return Status::OK(); return Status::OK();
} }
void to_json(JsonValueScope &jv, const tonlib_api::accountAddress &object) { void to_json(JsonValueScope &jv, const tonlib_api::accountAddress &object) {
@ -1043,6 +1067,7 @@ void to_json(JsonValueScope &jv, const tonlib_api::raw_message &object) {
jo << ctie("source", ToJson(object.source_)); jo << ctie("source", ToJson(object.source_));
jo << ctie("destination", ToJson(object.destination_)); jo << ctie("destination", ToJson(object.destination_));
jo << ctie("value", ToJson(JsonInt64{object.value_})); jo << ctie("value", ToJson(JsonInt64{object.value_}));
jo << ctie("message", ToJson(JsonBytes{object.message_}));
} }
void to_json(JsonValueScope &jv, const tonlib_api::raw_transaction &object) { void to_json(JsonValueScope &jv, const tonlib_api::raw_transaction &object) {
auto jo = jv.enter_object(); auto jo = jv.enter_object();
@ -1167,6 +1192,7 @@ void to_json(JsonValueScope &jv, const tonlib_api::generic_sendGrams &object) {
jo << ctie("destination", ToJson(object.destination_)); jo << ctie("destination", ToJson(object.destination_));
} }
jo << ctie("amount", ToJson(JsonInt64{object.amount_})); jo << ctie("amount", ToJson(JsonInt64{object.amount_}));
jo << ctie("message", ToJson(JsonBytes{object.message_}));
} }
void to_json(JsonValueScope &jv, const tonlib_api::getBip39Hints &object) { void to_json(JsonValueScope &jv, const tonlib_api::getBip39Hints &object) {
auto jo = jv.enter_object(); auto jo = jv.enter_object();
@ -1280,6 +1306,7 @@ void to_json(JsonValueScope &jv, const tonlib_api::testGiver_sendGrams &object)
} }
jo << ctie("seqno", ToJson(object.seqno_)); jo << ctie("seqno", ToJson(object.seqno_));
jo << ctie("amount", ToJson(JsonInt64{object.amount_})); jo << ctie("amount", ToJson(JsonInt64{object.amount_}));
jo << ctie("message", ToJson(JsonBytes{object.message_}));
} }
void to_json(JsonValueScope &jv, const tonlib_api::testWallet_getAccountAddress &object) { void to_json(JsonValueScope &jv, const tonlib_api::testWallet_getAccountAddress &object) {
auto jo = jv.enter_object(); auto jo = jv.enter_object();
@ -1313,6 +1340,7 @@ void to_json(JsonValueScope &jv, const tonlib_api::testWallet_sendGrams &object)
} }
jo << ctie("seqno", ToJson(object.seqno_)); jo << ctie("seqno", ToJson(object.seqno_));
jo << ctie("amount", ToJson(JsonInt64{object.amount_})); jo << ctie("amount", ToJson(JsonInt64{object.amount_}));
jo << ctie("message", ToJson(JsonBytes{object.message_}));
} }
} // namespace tonlib_api } // namespace tonlib_api
} // namespace ton } // namespace ton

View File

@ -490,7 +490,7 @@ engine.validator.fullNodeMaster port:int adnl:int256 = engine.validator.FullNode
engine.validator.fullNodeSlave ip:int port:int adnl:PublicKey = engine.validator.FullNodeSlave; engine.validator.fullNodeSlave ip:int port:int adnl:PublicKey = engine.validator.FullNodeSlave;
engine.validator.config out_port:int addrs:(vector engine.Addr) adnl:(vector engine.adnl) engine.validator.config out_port:int addrs:(vector engine.Addr) adnl:(vector engine.adnl)
dht:(vector engine.dht) dht:(vector engine.dht)
validators:(vector engine.validator) fullnode:int256 fullnodeslave:engine.validator.fullNodeSlave validators:(vector engine.validator) fullnode:int256 fullnodeslaves:(vector engine.validator.fullNodeSlave)
fullnodemasters:(vector engine.validator.fullNodeMaster) fullnodemasters:(vector engine.validator.fullNodeMaster)
liteservers:(vector engine.liteServer) control:(vector engine.controlInterface) liteservers:(vector engine.liteServer) control:(vector engine.controlInterface)
gc:engine.gc = engine.validator.Config; gc:engine.gc = engine.validator.Config;

View File

@ -32,7 +32,7 @@ internal.transactionId lt:int64 hash:bytes = internal.TransactionId;
raw.initialAccountState code:bytes data:bytes = raw.InitialAccountState; raw.initialAccountState code:bytes data:bytes = raw.InitialAccountState;
raw.accountState balance:int64 code:bytes data:bytes last_transaction_id:internal.transactionId sync_utime:int53 = raw.AccountState; raw.accountState balance:int64 code:bytes data:bytes last_transaction_id:internal.transactionId sync_utime:int53 = raw.AccountState;
raw.message source:string destination:string value:int64 = raw.Message; raw.message source:string destination:string value:int64 message:bytes = raw.Message;
raw.transaction utime:int53 data:bytes transaction_id:internal.transactionId fee:int64 in_msg:raw.message out_msgs:vector<raw.message> = raw.Transaction; raw.transaction utime:int53 data:bytes transaction_id:internal.transactionId fee:int64 in_msg:raw.message out_msgs:vector<raw.message> = raw.Transaction;
raw.transactions transactions:vector<raw.Transaction> previous_transaction_id:internal.transactionId = raw.Transactions; raw.transactions transactions:vector<raw.Transaction> previous_transaction_id:internal.transactionId = raw.Transactions;
@ -81,15 +81,15 @@ raw.getTransactions account_address:accountAddress from_transaction_id:internal.
testWallet.init private_key:inputKey = Ok; testWallet.init private_key:inputKey = Ok;
testWallet.getAccountAddress initital_account_state:testWallet.initialAccountState = AccountAddress; testWallet.getAccountAddress initital_account_state:testWallet.initialAccountState = AccountAddress;
testWallet.getAccountState account_address:accountAddress = testWallet.AccountState; testWallet.getAccountState account_address:accountAddress = testWallet.AccountState;
testWallet.sendGrams private_key:inputKey destination:accountAddress seqno:int32 amount:int64 = Ok; testWallet.sendGrams private_key:inputKey destination:accountAddress seqno:int32 amount:int64 message:bytes = Ok;
testGiver.getAccountState = testGiver.AccountState; testGiver.getAccountState = testGiver.AccountState;
testGiver.getAccountAddress = AccountAddress; testGiver.getAccountAddress = AccountAddress;
testGiver.sendGrams destination:accountAddress seqno:int32 amount:int64 = Ok; testGiver.sendGrams destination:accountAddress seqno:int32 amount:int64 message:bytes = Ok;
//generic.getAccountAddress initital_account_state:generic.InitialAccountState = AccountAddress; //generic.getAccountAddress initital_account_state:generic.InitialAccountState = AccountAddress;
generic.getAccountState account_address:accountAddress = generic.AccountState; generic.getAccountState account_address:accountAddress = generic.AccountState;
generic.sendGrams private_key:inputKey source:accountAddress destination:accountAddress amount:int64 = Ok; generic.sendGrams private_key:inputKey source:accountAddress destination:accountAddress amount:int64 message:bytes = Ok;
onLiteServerQueryResult id:int64 bytes:bytes = Ok; onLiteServerQueryResult id:int64 bytes:bytes = Ok;
onLiteServerQueryError id:int64 error:error = Ok; onLiteServerQueryError id:int64 error:error = Ok;

View File

@ -13,6 +13,7 @@ set(TONLIB_SOURCE
tonlib/GenericAccount.cpp tonlib/GenericAccount.cpp
tonlib/KeyStorage.cpp tonlib/KeyStorage.cpp
tonlib/LastBlock.cpp tonlib/LastBlock.cpp
tonlib/LastBlockStorage.cpp
tonlib/TestGiver.cpp tonlib/TestGiver.cpp
tonlib/TestWallet.cpp tonlib/TestWallet.cpp
tonlib/TonlibClient.cpp tonlib/TonlibClient.cpp
@ -26,6 +27,7 @@ set(TONLIB_SOURCE
tonlib/GenericAccount.h tonlib/GenericAccount.h
tonlib/KeyStorage.h tonlib/KeyStorage.h
tonlib/LastBlock.h tonlib/LastBlock.h
tonlib/LastBlockStorage.h
tonlib/TestGiver.h tonlib/TestGiver.h
tonlib/TestWallet.h tonlib/TestWallet.h
tonlib/TonlibCallback.h tonlib/TonlibCallback.h

View File

@ -127,8 +127,8 @@ TEST(Tonlib, TestGiver) {
auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data; auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data;
auto res = GenericAccount::create_ext_message(TestGiver::address(), {}, auto res = GenericAccount::create_ext_message(
TestGiver::make_a_gift_message(0, 1000000000ll * 6666 / 1000, address)); TestGiver::address(), {}, TestGiver::make_a_gift_message(0, 1000000000ll * 6666 / 1000, "GIFT", address));
vm::CellSlice(vm::NoVm(), res).print_rec(std::cerr); vm::CellSlice(vm::NoVm(), res).print_rec(std::cerr);
CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == res->get_hash()); CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == res->get_hash());
} }

View File

@ -118,7 +118,7 @@ void transfer_grams(Client& client, std::string from, std::string to, td::int64
auto balance = get_balance(client, to); auto balance = get_balance(client, to);
sync_send(client, tonlib_api::make_object<tonlib_api::generic_sendGrams>( sync_send(client, tonlib_api::make_object<tonlib_api::generic_sendGrams>(
std::move(input_key), tonlib_api::make_object<tonlib_api::accountAddress>(from), std::move(input_key), tonlib_api::make_object<tonlib_api::accountAddress>(from),
tonlib_api::make_object<tonlib_api::accountAddress>(to), amount)) tonlib_api::make_object<tonlib_api::accountAddress>(to), amount, "GIFT"))
.ensure(); .ensure();
while (balance == get_balance(client, to)) { while (balance == get_balance(client, to)) {
client.receive(1); client.receive(1);
@ -270,7 +270,7 @@ int main(int argc, char* argv[]) {
{ {
sync_send(client, make_object<tonlib_api::testGiver_sendGrams>( sync_send(client, make_object<tonlib_api::testGiver_sendGrams>(
make_object<tonlib_api::accountAddress>(wallet_addr.rserialize()), seqno, make_object<tonlib_api::accountAddress>(wallet_addr.rserialize()), seqno,
1000000000ll * 6666 / 1000)) 1000000000ll * 6666 / 1000, "GIFT"))
.ensure(); .ensure();
} }
@ -307,9 +307,10 @@ int main(int argc, char* argv[]) {
} }
{ {
sync_send(client, make_object<tonlib_api::generic_sendGrams>( sync_send(client,
create_input_key(), make_object<tonlib_api::accountAddress>(wallet_addr.rserialize()), make_object<tonlib_api::generic_sendGrams>(
make_object<tonlib_api::accountAddress>(test_giver_address), 1000000000ll * 3333 / 1000)) create_input_key(), make_object<tonlib_api::accountAddress>(wallet_addr.rserialize()),
make_object<tonlib_api::accountAddress>(test_giver_address), 1000000000ll * 3333 / 1000, "GIFT"))
.ensure(); .ensure();
} }
while (true) { while (true) {

View File

@ -1,11 +0,0 @@
#pragma once
#include "tonlib/LastBlock.h"
namespace tonlib {
class BlockchainInfoStorage {
td::Status set_directory(std::string directory);
td::Result<LastBlock::State> get_state(ZeroStateIdExt);
void save_state(LastBlock::State state);
};
} // namespace tonlib

View File

@ -21,10 +21,10 @@
#include "tonlib/LastBlock.h" #include "tonlib/LastBlock.h"
namespace tonlib { namespace tonlib {
void ExtClient::with_last_block(td::Promise<LastBlockInfo> promise) { void ExtClient::with_last_block(td::Promise<LastBlockState> promise) {
auto query_id = last_block_queries_.create(std::move(promise)); auto query_id = last_block_queries_.create(std::move(promise));
td::Promise<LastBlockInfo> P = [query_id, self = this, td::Promise<LastBlockState> P = [query_id, self = this,
actor_id = td::actor::actor_id()](td::Result<LastBlockInfo> result) { actor_id = td::actor::actor_id()](td::Result<LastBlockState> result) {
send_lambda(actor_id, [self, query_id, result = std::move(result)]() mutable { send_lambda(actor_id, [self, query_id, result = std::move(result)]() mutable {
self->last_block_queries_.extract(query_id).set_result(std::move(result)); self->last_block_queries_.extract(query_id).set_result(std::move(result));
}); });

View File

@ -29,7 +29,7 @@
namespace tonlib { namespace tonlib {
class LastBlock; class LastBlock;
struct LastBlockInfo; struct LastBlockState;
struct ExtClientRef { struct ExtClientRef {
td::actor::ActorId<ton::adnl::AdnlExtClient> andl_ext_client_; td::actor::ActorId<ton::adnl::AdnlExtClient> andl_ext_client_;
td::actor::ActorId<LastBlock> last_block_actor_; td::actor::ActorId<LastBlock> last_block_actor_;
@ -50,7 +50,7 @@ class ExtClient {
return client_; return client_;
} }
void with_last_block(td::Promise<LastBlockInfo> promise); void with_last_block(td::Promise<LastBlockState> promise);
template <class QueryT> template <class QueryT>
void send_query(QueryT query, td::Promise<typename QueryT::ReturnType> promise) { void send_query(QueryT query, td::Promise<typename QueryT::ReturnType> promise) {
@ -75,7 +75,7 @@ class ExtClient {
private: private:
ExtClientRef client_; ExtClientRef client_;
td::Container<td::Promise<td::BufferSlice>> queries_; td::Container<td::Promise<td::BufferSlice>> queries_;
td::Container<td::Promise<LastBlockInfo>> last_block_queries_; td::Container<td::Promise<LastBlockState>> last_block_queries_;
void send_raw_query(td::BufferSlice query, td::Promise<td::BufferSlice> promise); void send_raw_query(td::BufferSlice query, td::Promise<td::BufferSlice> promise);
}; };

View File

@ -28,7 +28,7 @@ td::Ref<vm::Cell> GenericAccount::get_init_state(td::Ref<vm::Cell> code, td::Ref
.finalize(); .finalize();
} }
block::StdAddress GenericAccount::get_address(ton::WorkchainId workchain_id, const td::Ref<vm::Cell>& init_state) { block::StdAddress GenericAccount::get_address(ton::WorkchainId workchain_id, const td::Ref<vm::Cell>& init_state) {
return block::StdAddress(workchain_id, init_state->get_hash().bits()); return block::StdAddress(workchain_id, init_state->get_hash().bits(), false);
} }
td::Ref<vm::Cell> GenericAccount::create_ext_message(const block::StdAddress& address, td::Ref<vm::Cell> new_state, td::Ref<vm::Cell> GenericAccount::create_ext_message(const block::StdAddress& address, td::Ref<vm::Cell> new_state,
td::Ref<vm::Cell> body) { td::Ref<vm::Cell> body) {

View File

@ -23,13 +23,23 @@
#include "lite-client/lite-client-common.h" #include "lite-client/lite-client-common.h"
namespace tonlib { namespace tonlib {
LastBlock::LastBlock(ExtClientRef client, State state, td::unique_ptr<Callback> callback)
td::StringBuilder& operator<<(td::StringBuilder& sb, const LastBlockState& state) {
return sb << td::tag("last_block", state.last_block_id.to_str())
<< td::tag("last_key_block", state.last_key_block_id.to_str()) << td::tag("utime", state.utime);
}
LastBlock::LastBlock(ExtClientRef client, LastBlockState state, td::unique_ptr<Callback> callback)
: state_(std::move(state)), callback_(std::move(callback)) { : state_(std::move(state)), callback_(std::move(callback)) {
client_.set_client(client); client_.set_client(client);
} }
void LastBlock::get_last_block(td::Promise<LastBlockInfo> promise) { void LastBlock::get_last_block(td::Promise<LastBlockState> promise) {
if (promises_.empty()) { if (promises_.empty()) {
total_sync_ = td::Timer();
validate_ = td::Timer(true);
queries_ = 0;
LOG(INFO) << "Begin last block synchronization " << state_;
do_get_last_block(); do_get_last_block();
} }
promises_.push_back(std::move(promise)); promises_.push_back(std::move(promise));
@ -41,6 +51,7 @@ void LastBlock::do_get_last_block() {
//return; //return;
//liteServer.getBlockProof mode:# known_block:tonNode.blockIdExt target_block:mode.0?tonNode.blockIdExt = liteServer.PartialBlockProof; //liteServer.getBlockProof mode:# known_block:tonNode.blockIdExt target_block:mode.0?tonNode.blockIdExt = liteServer.PartialBlockProof;
queries_++;
client_.send_query( client_.send_query(
ton::lite_api::liteServer_getBlockProof(0, create_tl_lite_block_id(state_.last_key_block_id), nullptr), ton::lite_api::liteServer_getBlockProof(0, create_tl_lite_block_id(state_.last_key_block_id), nullptr),
[this, from = state_.last_key_block_id](auto r_block_proof) { [this, from = state_.last_key_block_id](auto r_block_proof) {
@ -51,21 +62,30 @@ void LastBlock::do_get_last_block() {
td::Result<bool> LastBlock::process_block_proof( td::Result<bool> LastBlock::process_block_proof(
ton::BlockIdExt from, ton::BlockIdExt from,
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof) { td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof) {
validate_.resume();
SCOPE_EXIT {
validate_.pause();
};
TRY_RESULT(block_proof, std::move(r_block_proof)); TRY_RESULT(block_proof, std::move(r_block_proof));
LOG(ERROR) << to_string(block_proof); LOG(DEBUG) << "Got proof FROM\n" << to_string(block_proof->from_) << "TO\n" << to_string(block_proof->to_);
TRY_RESULT(chain, liteclient::deserialize_proof_chain(std::move(block_proof))); TRY_RESULT(chain, liteclient::deserialize_proof_chain(std::move(block_proof)));
if (chain->from != from) { if (chain->from != from) {
return td::Status::Error(PSLICE() << "block proof chain starts from block " << chain->from.to_str() return td::Status::Error(PSLICE() << "block proof chain starts from block " << chain->from.to_str()
<< ", not from requested block " << from.to_str()); << ", not from requested block " << from.to_str());
} }
TRY_STATUS(chain->validate()); TRY_STATUS(chain->validate());
update_mc_last_block(chain->to); bool is_changed = false;
is_changed |= update_mc_last_block(chain->to);
if (chain->has_key_block) { if (chain->has_key_block) {
update_mc_last_key_block(chain->key_blkid); is_changed |= update_mc_last_key_block(chain->key_blkid);
} }
if (chain->has_utime) { if (chain->has_utime) {
update_utime(chain->last_utime); update_utime(chain->last_utime);
} }
if (is_changed) {
callback_->on_state_changed(state_);
}
return chain->complete; return chain->complete;
} }
@ -73,17 +93,20 @@ void LastBlock::on_block_proof(
ton::BlockIdExt from, ton::BlockIdExt from,
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof) { td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof) {
auto r_is_ready = process_block_proof(from, std::move(r_block_proof)); auto r_is_ready = process_block_proof(from, std::move(r_block_proof));
bool is_ready;
if (r_is_ready.is_error()) { if (r_is_ready.is_error()) {
LOG(WARNING) << "Failed liteServer_getBlockProof " << r_is_ready.error(); LOG(WARNING) << "Error during last block synchronization " << r_is_ready.error();
return; is_ready = true;
} else {
is_ready = r_is_ready.move_as_ok();
} }
auto is_ready = r_is_ready.move_as_ok();
if (is_ready) { if (is_ready) {
LOG(INFO) << "End last block synchronization " << state_ << "\n"
<< " net queries: " << queries_ << "\n"
<< " total: " << total_sync_ << " validation: " << validate_;
for (auto& promise : promises_) { for (auto& promise : promises_) {
LastBlockInfo res; auto state = state_;
res.id = state_.last_block_id; promise.set_value(std::move(state));
res.utime = state_.utime;
promise.set_value(std::move(res));
} }
promises_.clear(); promises_.clear();
} else { } else {
@ -101,10 +124,8 @@ void LastBlock::on_masterchain_info(
LOG(WARNING) << "Failed liteServer_getMasterchainInfo " << r_info.error(); LOG(WARNING) << "Failed liteServer_getMasterchainInfo " << r_info.error();
} }
for (auto& promise : promises_) { for (auto& promise : promises_) {
LastBlockInfo res; auto state = state_;
res.id = state_.last_block_id; promise.set_value(std::move(state));
res.utime = state_.utime;
promise.set_value(std::move(res));
} }
promises_.clear(); promises_.clear();
} }
@ -131,25 +152,30 @@ void LastBlock::update_zero_state(ton::ZeroStateIdExt zero_state_id) {
// One will have to restart ton client // One will have to restart ton client
} }
void LastBlock::update_mc_last_block(ton::BlockIdExt mc_block_id) { bool LastBlock::update_mc_last_block(ton::BlockIdExt mc_block_id) {
if (!mc_block_id.is_valid()) { if (!mc_block_id.is_valid()) {
LOG(ERROR) << "Ignore invalid masterchain block"; LOG(ERROR) << "Ignore invalid masterchain block";
return; return false;
} }
if (!state_.last_block_id.is_valid() || state_.last_block_id.id.seqno < mc_block_id.id.seqno) { if (!state_.last_block_id.is_valid() || state_.last_block_id.id.seqno < mc_block_id.id.seqno) {
state_.last_block_id = mc_block_id; state_.last_block_id = mc_block_id;
LOG(INFO) << "Update masterchain block id: " << state_.last_block_id.to_str(); LOG(INFO) << "Update masterchain block id: " << state_.last_block_id.to_str();
return true;
} }
return false;
} }
void LastBlock::update_mc_last_key_block(ton::BlockIdExt mc_key_block_id) {
bool LastBlock::update_mc_last_key_block(ton::BlockIdExt mc_key_block_id) {
if (!mc_key_block_id.is_valid()) { if (!mc_key_block_id.is_valid()) {
LOG(ERROR) << "Ignore invalid masterchain block"; LOG(ERROR) << "Ignore invalid masterchain block";
return; return false;
} }
if (!state_.last_key_block_id.is_valid() || state_.last_key_block_id.id.seqno < mc_key_block_id.id.seqno) { if (!state_.last_key_block_id.is_valid() || state_.last_key_block_id.id.seqno < mc_key_block_id.id.seqno) {
state_.last_key_block_id = mc_key_block_id; state_.last_key_block_id = mc_key_block_id;
LOG(INFO) << "Update masterchain key block id: " << state_.last_key_block_id.to_str(); LOG(INFO) << "Update masterchain key block id: " << state_.last_key_block_id.to_str();
return true;
} }
return false;
} }
void LastBlock::update_utime(td::int64 utime) { void LastBlock::update_utime(td::int64 utime) {

View File

@ -22,35 +22,114 @@
#include "tonlib/ExtClient.h" #include "tonlib/ExtClient.h"
namespace tonlib { namespace tonlib {
struct LastBlockInfo { td::StringBuilder &operator<<(td::StringBuilder &sb, const LastBlockState &state);
ton::BlockIdExt id; template <unsigned int N, class StorerT>
void store(const td::BitArray<N> &arr, StorerT &storer) {
storer.store_binary(arr);
}
template <unsigned int N, class ParserT>
void parse(td::BitArray<N> &arr, ParserT &parser) {
arr = parser.template fetch_binary<td::BitArray<N>>();
}
template <class StorerT>
void store(const ton::ZeroStateIdExt &zero_state_id, StorerT &storer) {
using td::store;
using tonlib::store;
store(zero_state_id.workchain, storer);
store(zero_state_id.root_hash, storer);
store(zero_state_id.file_hash, storer);
}
template <class ParserT>
void parse(ton::ZeroStateIdExt &zero_state_id, ParserT &parser) {
using td::parse;
using tonlib::parse;
parse(zero_state_id.workchain, parser);
parse(zero_state_id.root_hash, parser);
parse(zero_state_id.file_hash, parser);
}
template <class StorerT>
void store(const ton::BlockId &block_id, StorerT &storer) {
using td::store;
using tonlib::store;
store(block_id.workchain, storer);
store(block_id.shard, storer);
store(block_id.seqno, storer);
}
template <class ParserT>
void parse(ton::BlockId &block_id, ParserT &parser) {
using td::parse;
using tonlib::parse;
parse(block_id.workchain, parser);
parse(block_id.shard, parser);
parse(block_id.seqno, parser);
}
template <class StorerT>
void store(const ton::BlockIdExt &block_id, StorerT &storer) {
using td::store;
using tonlib::store;
store(block_id.id, storer);
store(block_id.root_hash, storer);
store(block_id.file_hash, storer);
}
template <class ParserT>
void parse(ton::BlockIdExt &block_id, ParserT &parser) {
using td::parse;
using tonlib::parse;
parse(block_id.id, parser);
parse(block_id.root_hash, parser);
parse(block_id.file_hash, parser);
}
struct LastBlockState {
ton::ZeroStateIdExt zero_state_id;
ton::BlockIdExt last_key_block_id;
ton::BlockIdExt last_block_id;
td::int64 utime{0}; td::int64 utime{0};
template <class StorerT>
void store(StorerT &storer) const {
using td::store;
using tonlib::store;
store(zero_state_id, storer);
store(last_key_block_id, storer);
store(last_block_id, storer);
store(utime, storer);
}
template <class ParserT>
void parse(ParserT &parser) {
using td::parse;
using tonlib::parse;
parse(zero_state_id, parser);
parse(last_key_block_id, parser);
parse(last_block_id, parser);
parse(utime, parser);
}
}; };
class LastBlock : public td::actor::Actor { class LastBlock : public td::actor::Actor {
public: public:
struct State {
ton::ZeroStateIdExt zero_state_id;
ton::BlockIdExt last_key_block_id;
ton::BlockIdExt last_block_id;
td::int64 utime{0};
};
class Callback { class Callback {
public: public:
virtual ~Callback() { virtual ~Callback() {
} }
virtual void on_state_changes(State state) = 0; virtual void on_state_changed(LastBlockState state) = 0;
}; };
explicit LastBlock(ExtClientRef client, State state, td::unique_ptr<Callback> callback); explicit LastBlock(ExtClientRef client, LastBlockState state, td::unique_ptr<Callback> callback);
void get_last_block(td::Promise<LastBlockInfo> promise); void get_last_block(td::Promise<LastBlockState> promise);
private: private:
ExtClient client_; ExtClient client_;
State state_; LastBlockState state_;
td::unique_ptr<Callback> callback_; td::unique_ptr<Callback> callback_;
std::vector<td::Promise<LastBlockInfo>> promises_; // stats
td::Timer total_sync_;
td::Timer validate_;
td::uint32 queries_;
std::vector<td::Promise<LastBlockState>> promises_;
void do_get_last_block(); void do_get_last_block();
void on_masterchain_info(td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_masterchainInfo>> r_info); void on_masterchain_info(td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_masterchainInfo>> r_info);
@ -62,8 +141,8 @@ class LastBlock : public td::actor::Actor {
void update_zero_state(ton::ZeroStateIdExt zero_state_id); void update_zero_state(ton::ZeroStateIdExt zero_state_id);
void update_mc_last_block(ton::BlockIdExt mc_block_id); bool update_mc_last_block(ton::BlockIdExt mc_block_id);
void update_mc_last_key_block(ton::BlockIdExt mc_key_block_id); bool update_mc_last_key_block(ton::BlockIdExt mc_key_block_id);
void update_utime(td::int64 utime); void update_utime(td::int64 utime);
}; };
} // namespace tonlib } // namespace tonlib

View File

@ -0,0 +1,44 @@
#include "LastBlockStorage.h"
#include "td/utils/as.h"
#include "td/utils/filesystem.h"
#include "td/utils/port/path.h"
#include "td/utils/tl_helpers.h"
namespace tonlib {
td::Status LastBlockStorage::set_directory(std::string directory) {
TRY_RESULT(path, td::realpath(directory));
TRY_RESULT(stat, td::stat(path));
if (!stat.is_dir_) {
return td::Status::Error("not a directory");
}
directory_ = std::move(path);
return td::Status::OK();
}
std::string LastBlockStorage::get_file_name(td::Slice name) {
return directory_ + TD_DIR_SLASH + td::buffer_to_hex(name) + ".blkstate";
}
td::Result<LastBlockState> LastBlockStorage::get_state(td::Slice name) {
TRY_RESULT(data, td::read_file(get_file_name(name)));
if (data.size() < 8) {
return td::Status::Error("too short");
}
if (td::as<td::uint64>(data.data()) != td::crc64(td::Slice(data).substr(8))) {
return td::Status::Error("crc64 mismatch");
}
LastBlockState res;
TRY_STATUS(td::unserialize(res, td::Slice(data).substr(8)));
return res;
}
void LastBlockStorage::save_state(td::Slice name, LastBlockState state) {
auto x = td::serialize(state);
std::string y(x.size() + 8, 0);
td::MutableSlice(y).substr(8).copy_from(x);
td::as<td::uint64>(td::MutableSlice(y).data()) = td::crc64(x);
td::atomic_write_file(get_file_name(name), y);
}
} // namespace tonlib

View File

@ -0,0 +1,16 @@
#pragma once
#include "tonlib/LastBlock.h"
namespace tonlib {
class LastBlockStorage {
public:
td::Status set_directory(std::string directory);
td::Result<LastBlockState> get_state(td::Slice name);
void save_state(td::Slice name, LastBlockState state);
private:
std::string directory_;
std::string get_file_name(td::Slice name);
};
} // namespace tonlib

View File

@ -32,8 +32,9 @@ vm::CellHash TestGiver::get_init_code_hash() {
return vm::CellHash::from_slice(td::base64_decode("wDkZp0yR4xo+9+BnuAPfGVjBzK6FPzqdv2DwRq3z3KE=").move_as_ok()); return vm::CellHash::from_slice(td::base64_decode("wDkZp0yR4xo+9+BnuAPfGVjBzK6FPzqdv2DwRq3z3KE=").move_as_ok());
} }
td::Ref<vm::Cell> TestGiver::make_a_gift_message(td::uint32 seqno, td::uint64 gramms, td::Ref<vm::Cell> TestGiver::make_a_gift_message(td::uint32 seqno, td::uint64 gramms, td::Slice message,
const block::StdAddress& dest_address) { const block::StdAddress& dest_address) {
CHECK(message.size() <= 128);
td::BigInt256 dest_addr; td::BigInt256 dest_addr;
dest_addr.import_bits(dest_address.addr.as_bitslice()); dest_addr.import_bits(dest_address.addr.as_bitslice());
vm::CellBuilder cb; vm::CellBuilder cb;
@ -44,7 +45,7 @@ td::Ref<vm::Cell> TestGiver::make_a_gift_message(td::uint32 seqno, td::uint64 gr
.store_int256(dest_addr, 256); .store_int256(dest_addr, 256);
block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms)); block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms));
auto message_inner = cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("GIFT").finalize(); auto message_inner = cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes(message).finalize();
return vm::CellBuilder().store_long(seqno, 32).store_long(1, 8).store_ref(message_inner).finalize(); return vm::CellBuilder().store_long(seqno, 32).store_long(1, 8).store_ref(message_inner).finalize();
} }
} // namespace tonlib } // namespace tonlib

View File

@ -23,7 +23,7 @@ class TestGiver {
public: public:
static const block::StdAddress& address(); static const block::StdAddress& address();
static vm::CellHash get_init_code_hash(); static vm::CellHash get_init_code_hash();
static td::Ref<vm::Cell> make_a_gift_message(td::uint32 seqno, td::uint64 gramms, static td::Ref<vm::Cell> make_a_gift_message(td::uint32 seqno, td::uint64 gramms, td::Slice message,
const block::StdAddress& dest_address); const block::StdAddress& dest_address);
}; };
} // namespace tonlib } // namespace tonlib

View File

@ -38,7 +38,9 @@ td::Ref<vm::Cell> TestWallet::get_init_message(const td::Ed25519::PrivateKey& pr
} }
td::Ref<vm::Cell> TestWallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, td::Ref<vm::Cell> TestWallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno,
td::int64 gramms, const block::StdAddress& dest_address) { td::int64 gramms, td::Slice message,
const block::StdAddress& dest_address) {
CHECK(message.size() <= 128);
td::BigInt256 dest_addr; td::BigInt256 dest_addr;
dest_addr.import_bits(dest_address.addr.as_bitslice()); dest_addr.import_bits(dest_address.addr.as_bitslice());
vm::CellBuilder cb; vm::CellBuilder cb;
@ -46,11 +48,11 @@ td::Ref<vm::Cell> TestWallet::make_a_gift_message(const td::Ed25519::PrivateKey&
.store_long(dest_address.workchain, 8) .store_long(dest_address.workchain, 8)
.store_int256(dest_addr, 256); .store_int256(dest_addr, 256);
block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms)); block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms));
auto message_inner = cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("GIFT").finalize(); auto message_inner = cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes(message).finalize();
auto message = vm::CellBuilder().store_long(seqno, 32).store_long(1, 8).store_ref(message_inner).finalize(); auto message_outer = vm::CellBuilder().store_long(seqno, 32).store_long(1, 8).store_ref(message_inner).finalize();
std::string seq_no(4, 0); std::string seq_no(4, 0);
auto signature = private_key.sign(message->get_hash().as_slice()).move_as_ok(); auto signature = private_key.sign(message_outer->get_hash().as_slice()).move_as_ok();
return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message)).finalize(); return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize();
} }
td::Ref<vm::Cell> TestWallet::get_init_code() { td::Ref<vm::Cell> TestWallet::get_init_code() {

View File

@ -28,7 +28,8 @@ class TestWallet {
static td::Ref<vm::Cell> get_init_state(const td::Ed25519::PublicKey& public_key); static td::Ref<vm::Cell> get_init_state(const td::Ed25519::PublicKey& public_key);
static td::Ref<vm::Cell> get_init_message(const td::Ed25519::PrivateKey& private_key); static td::Ref<vm::Cell> get_init_message(const td::Ed25519::PrivateKey& private_key);
static td::Ref<vm::Cell> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, static td::Ref<vm::Cell> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno,
td::int64 gramms, const block::StdAddress& dest_address); td::int64 gramms, td::Slice message,
const block::StdAddress& dest_address);
static td::Ref<vm::Cell> get_init_code(); static td::Ref<vm::Cell> get_init_code();
static vm::CellHash get_init_code_hash(); static vm::CellHash get_init_code_hash();

View File

@ -41,8 +41,6 @@
#include "td/utils/tests.h" #include "td/utils/tests.h"
#include "td/utils/port/path.h" #include "td/utils/port/path.h"
namespace ton {} // namespace ton
namespace tonlib { namespace tonlib {
template <class F> template <class F>
@ -152,6 +150,11 @@ class GetTransactionHistory : public td::actor::Actor {
} }
void start_up() override { void start_up() override {
if (lt_ == 0) {
promise_.set_value(block::TransactionList::Info());
stop();
return;
}
client_.send_query( client_.send_query(
ton::lite_api::liteServer_getTransactions( ton::lite_api::liteServer_getTransactions(
count_, ton::create_tl_object<ton::lite_api::liteServer_accountId>(address_.workchain, address_.addr), lt_, count_, ton::create_tl_object<ton::lite_api::liteServer_accountId>(address_.workchain, address_.addr), lt_,
@ -171,7 +174,7 @@ class GetRawAccountState : public td::actor::Actor {
block::StdAddress address_; block::StdAddress address_;
td::Promise<RawAccountState> promise_; td::Promise<RawAccountState> promise_;
ExtClient client_; ExtClient client_;
LastBlockInfo last_block_; LastBlockState last_block_;
void with_account_state(td::Result<ton::tl_object_ptr<ton::lite_api::liteServer_accountState>> r_account_state) { void with_account_state(td::Result<ton::tl_object_ptr<ton::lite_api::liteServer_accountState>> r_account_state) {
promise_.set_result(TRY_VM(do_with_account_state(std::move(r_account_state)))); promise_.set_result(TRY_VM(do_with_account_state(std::move(r_account_state))));
@ -182,7 +185,7 @@ class GetRawAccountState : public td::actor::Actor {
td::Result<ton::tl_object_ptr<ton::lite_api::liteServer_accountState>> r_account_state) { td::Result<ton::tl_object_ptr<ton::lite_api::liteServer_accountState>> r_account_state) {
TRY_RESULT(raw_account_state, std::move(r_account_state)); TRY_RESULT(raw_account_state, std::move(r_account_state));
auto account_state = create_account_state(std::move(raw_account_state)); auto account_state = create_account_state(std::move(raw_account_state));
TRY_RESULT(info, account_state.validate(last_block_.id, address_)); TRY_RESULT(info, account_state.validate(last_block_.last_block_id, address_));
auto serialized_state = account_state.state.clone(); auto serialized_state = account_state.state.clone();
RawAccountState res; RawAccountState res;
res.info = std::move(info); res.info = std::move(info);
@ -225,14 +228,14 @@ class GetRawAccountState : public td::actor::Actor {
} }
void start_up() override { void start_up() override {
client_.with_last_block([self = this](td::Result<LastBlockInfo> r_last_block) { client_.with_last_block([self = this](td::Result<LastBlockState> r_last_block) {
if (r_last_block.is_error()) { if (r_last_block.is_error()) {
return self->check(r_last_block.move_as_error()); return self->check(r_last_block.move_as_error());
} }
self->last_block_ = r_last_block.move_as_ok(); self->last_block_ = r_last_block.move_as_ok();
self->client_.send_query( self->client_.send_query(
ton::lite_api::liteServer_getAccountState(ton::create_tl_lite_block_id(self->last_block_.id), ton::lite_api::liteServer_getAccountState(ton::create_tl_lite_block_id(self->last_block_.last_block_id),
ton::create_tl_object<ton::lite_api::liteServer_accountId>( ton::create_tl_object<ton::lite_api::liteServer_accountId>(
self->address_.workchain, self->address_.addr)), self->address_.workchain, self->address_.addr)),
[self](auto r_state) { self->with_account_state(std::move(r_state)); }); [self](auto r_state) { self->with_account_state(std::move(r_state)); });
@ -307,24 +310,36 @@ void TonlibClient::init_ext_client() {
td::make_unique<Callback>(td::actor::actor_shared())); td::make_unique<Callback>(td::actor::actor_shared()));
} }
} }
void TonlibClient::update_last_block_state(LastBlockState state) {
last_block_storage_.save_state("none", state);
}
void TonlibClient::init_last_block() { void TonlibClient::init_last_block() {
ref_cnt_++; ref_cnt_++;
class Callback : public LastBlock::Callback { class Callback : public LastBlock::Callback {
public: public:
Callback(td::actor::ActorShared<TonlibClient> client) : client_(std::move(client)) { Callback(td::actor::ActorShared<TonlibClient> client) : client_(std::move(client)) {
} }
void on_state_changes(LastBlock::State state) override { void on_state_changed(LastBlockState state) override {
//TODO send_closure(client_, &TonlibClient::update_last_block_state, std::move(state));
} }
private: private:
td::actor::ActorShared<TonlibClient> client_; td::actor::ActorShared<TonlibClient> client_;
}; };
LastBlock::State state; LastBlockState state;
//state.zero_state_id = ton::ZeroStateIdExt(config_.zero_state_id.id.workchain, config_.zero_state_id.root_hash,
//config_.zero_state_id.file_hash), auto r_state = last_block_storage_.get_state("none");
state.last_block_id = config_.zero_state_id; if (r_state.is_error()) {
state.last_key_block_id = config_.zero_state_id; LOG(WARNING) << "Unknown LastBlockState: " << r_state.error();
state.zero_state_id = ton::ZeroStateIdExt(config_.zero_state_id.id.workchain, config_.zero_state_id.root_hash,
config_.zero_state_id.file_hash),
state.last_block_id = config_.zero_state_id;
state.last_key_block_id = config_.zero_state_id;
} else {
state = r_state.move_as_ok();
}
raw_last_block_ = td::actor::create_actor<LastBlock>("LastBlock", get_client_ref(), std::move(state), raw_last_block_ = td::actor::create_actor<LastBlock>("LastBlock", get_client_ref(), std::move(state),
td::make_unique<Callback>(td::actor::actor_shared(this))); td::make_unique<Callback>(td::actor::actor_shared(this)));
@ -469,6 +484,7 @@ td::Status TonlibClient::do_request(const tonlib_api::init& request,
return td::Status::Error(400, "Field options must not be empty"); return td::Status::Error(400, "Field options must not be empty");
} }
TRY_STATUS(key_storage_.set_directory(request.options_->keystore_directory_)); TRY_STATUS(key_storage_.set_directory(request.options_->keystore_directory_));
TRY_STATUS(last_block_storage_.set_directory(request.options_->keystore_directory_));
use_callbacks_for_network_ = request.options_->use_callbacks_for_network_; use_callbacks_for_network_ = request.options_->use_callbacks_for_network_;
if (!request.options_->config_.empty()) { if (!request.options_->config_.empty()) {
TRY_STATUS(set_config(std::move(request.options_->config_))); TRY_STATUS(set_config(std::move(request.options_->config_)));
@ -570,10 +586,25 @@ td::Result<tonlib_api::object_ptr<tonlib_api::raw_message>> to_raw_message_or_th
if (!tlb::csr_unpack(message.info, msg_info)) { if (!tlb::csr_unpack(message.info, msg_info)) {
return td::Status::Error("Failed to unpack CommonMsgInfo::int_msg_info"); return td::Status::Error("Failed to unpack CommonMsgInfo::int_msg_info");
} }
TRY_RESULT(balance, to_balance(msg_info.value)); TRY_RESULT(balance, to_balance(msg_info.value));
TRY_RESULT(src, to_std_address(msg_info.src)); TRY_RESULT(src, to_std_address(msg_info.src));
TRY_RESULT(dest, to_std_address(msg_info.dest)); TRY_RESULT(dest, to_std_address(msg_info.dest));
return tonlib_api::make_object<tonlib_api::raw_message>(std::move(src), std::move(dest), balance); td::Ref<vm::CellSlice> body;
if (message.body->prefetch_long(1) == 0) {
body = std::move(message.body);
body.write().advance(1);
} else {
body = vm::load_cell_slice_ref(message.body->prefetch_ref());
}
std::string body_message;
if (body->size() % 8 == 0) {
body_message = std::string(body->size() / 8, 0);
body->prefetch_bytes(td::MutableSlice(body_message).ubegin(), body->size() / 8);
}
return tonlib_api::make_object<tonlib_api::raw_message>(std::move(src), std::move(dest), balance,
std::move(body_message));
} }
case block::gen::CommonMsgInfo::ext_in_msg_info: { case block::gen::CommonMsgInfo::ext_in_msg_info: {
block::gen::CommonMsgInfo::Record_ext_in_msg_info msg_info; block::gen::CommonMsgInfo::Record_ext_in_msg_info msg_info;
@ -581,7 +612,7 @@ td::Result<tonlib_api::object_ptr<tonlib_api::raw_message>> to_raw_message_or_th
return td::Status::Error("Failed to unpack CommonMsgInfo::ext_in_msg_info"); return td::Status::Error("Failed to unpack CommonMsgInfo::ext_in_msg_info");
} }
TRY_RESULT(dest, to_std_address(msg_info.dest)); TRY_RESULT(dest, to_std_address(msg_info.dest));
return tonlib_api::make_object<tonlib_api::raw_message>("", std::move(dest), 0); return tonlib_api::make_object<tonlib_api::raw_message>("", std::move(dest), 0, "");
} }
case block::gen::CommonMsgInfo::ext_out_msg_info: { case block::gen::CommonMsgInfo::ext_out_msg_info: {
block::gen::CommonMsgInfo::Record_ext_out_msg_info msg_info; block::gen::CommonMsgInfo::Record_ext_out_msg_info msg_info;
@ -589,7 +620,7 @@ td::Result<tonlib_api::object_ptr<tonlib_api::raw_message>> to_raw_message_or_th
return td::Status::Error("Failed to unpack CommonMsgInfo::ext_out_msg_info"); return td::Status::Error("Failed to unpack CommonMsgInfo::ext_out_msg_info");
} }
TRY_RESULT(src, to_std_address(msg_info.src)); TRY_RESULT(src, to_std_address(msg_info.src));
return tonlib_api::make_object<tonlib_api::raw_message>(std::move(src), "", 0); return tonlib_api::make_object<tonlib_api::raw_message>(std::move(src), "", 0, "");
} }
} }
@ -626,7 +657,7 @@ td::Result<tonlib_api::object_ptr<tonlib_api::raw_transaction>> to_raw_transacti
if (is_just == trans.r1.in_msg->fetch_long_eof) { if (is_just == trans.r1.in_msg->fetch_long_eof) {
return td::Status::Error("Failed to parse long"); return td::Status::Error("Failed to parse long");
} }
if (is_just == 1) { if (is_just == -1) {
auto msg = trans.r1.in_msg->prefetch_ref(); auto msg = trans.r1.in_msg->prefetch_ref();
TRY_RESULT(in_msg_copy, to_raw_message(trans.r1.in_msg->prefetch_ref())); TRY_RESULT(in_msg_copy, to_raw_message(trans.r1.in_msg->prefetch_ref()));
in_msg = std::move(in_msg_copy); in_msg = std::move(in_msg_copy);
@ -702,7 +733,7 @@ td::Result<tonlib_api::object_ptr<tonlib_api::generic_AccountState>> to_generic_
RawAccountState&& raw_state) { RawAccountState&& raw_state) {
if (raw_state.code.is_null()) { if (raw_state.code.is_null()) {
return tonlib_api::make_object<tonlib_api::generic_accountStateUninited>( return tonlib_api::make_object<tonlib_api::generic_accountStateUninited>(
tonlib_api::make_object<tonlib_api::uninited_accountState>(raw_state.balance, empty_transaction_id(), tonlib_api::make_object<tonlib_api::uninited_accountState>(raw_state.balance, to_transaction_id(raw_state.info),
raw_state.sync_utime)); raw_state.sync_utime));
} }
@ -827,21 +858,24 @@ td::Status TonlibClient::do_request(const tonlib_api::testWallet_sendGrams& requ
if (!request.private_key_) { if (!request.private_key_) {
return td::Status::Error(400, "Field private_key must not be empty"); return td::Status::Error(400, "Field private_key must not be empty");
} }
if (request.message_.size() > 128) {
return td::Status::Error(400, "Message is too long");
}
TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_)); TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_));
account_address.bounceable = false; account_address.bounceable = false;
TRY_RESULT(input_key, from_tonlib(*request.private_key_)); TRY_RESULT(input_key, from_tonlib(*request.private_key_));
auto address = GenericAccount::get_address( auto address = GenericAccount::get_address(
0 /*zerochain*/, TestWallet::get_init_state(td::Ed25519::PublicKey(input_key.key.public_key.copy()))); 0 /*zerochain*/, TestWallet::get_init_state(td::Ed25519::PublicKey(input_key.key.public_key.copy())));
TRY_RESULT(private_key, key_storage_.load_private_key(std::move(input_key))); TRY_RESULT(private_key, key_storage_.load_private_key(std::move(input_key)));
return do_request( return do_request(tonlib_api::raw_sendMessage(
tonlib_api::raw_sendMessage(tonlib_api::make_object<tonlib_api::accountAddress>(address.rserialize()), "", tonlib_api::make_object<tonlib_api::accountAddress>(address.rserialize()), "",
vm::std_boc_serialize(TestWallet::make_a_gift_message( vm::std_boc_serialize(TestWallet::make_a_gift_message(
td::Ed25519::PrivateKey(std::move(private_key.private_key)), td::Ed25519::PrivateKey(std::move(private_key.private_key)),
request.seqno_, request.amount_, account_address)) request.seqno_, request.amount_, request.message_, account_address))
.move_as_ok() .move_as_ok()
.as_slice() .as_slice()
.str()), .str()),
std::move(promise)); std::move(promise));
} }
td::Status TonlibClient::do_request(tonlib_api::testWallet_getAccountState& request, td::Status TonlibClient::do_request(tonlib_api::testWallet_getAccountState& request,
@ -868,16 +902,19 @@ td::Status TonlibClient::do_request(const tonlib_api::testGiver_sendGrams& reque
if (!request.destination_) { if (!request.destination_) {
return td::Status::Error(400, "Field destination must not be empty"); return td::Status::Error(400, "Field destination must not be empty");
} }
if (request.message_.size() > 128) {
return td::Status::Error(400, "Message is too long");
}
TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_)); TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_));
account_address.bounceable = false; account_address.bounceable = false;
return do_request( return do_request(tonlib_api::raw_sendMessage(
tonlib_api::raw_sendMessage( tonlib_api::make_object<tonlib_api::accountAddress>(TestGiver::address().rserialize()), "",
tonlib_api::make_object<tonlib_api::accountAddress>(TestGiver::address().rserialize()), "", vm::std_boc_serialize(TestGiver::make_a_gift_message(request.seqno_, request.amount_,
vm::std_boc_serialize(TestGiver::make_a_gift_message(request.seqno_, request.amount_, account_address)) request.message_, account_address))
.move_as_ok() .move_as_ok()
.as_slice() .as_slice()
.str()), .str()),
std::move(promise)); std::move(promise));
} }
td::Status TonlibClient::do_request(const tonlib_api::testGiver_getAccountState& request, td::Status TonlibClient::do_request(const tonlib_api::testGiver_getAccountState& request,
@ -920,7 +957,7 @@ td::Status TonlibClient::do_request(tonlib_api::generic_sendGrams& request,
"GetAccountState", client_.get_client(), std::move(account_address), "GetAccountState", client_.get_client(), std::move(account_address),
[promise = std::move(promise), self = this, actor_id = td::actor::actor_id(), [promise = std::move(promise), self = this, actor_id = td::actor::actor_id(),
private_key = std::move(request.private_key_), destination = std::move(request.destination_), private_key = std::move(request.private_key_), destination = std::move(request.destination_),
amount = request.amount_](td::Result<RawAccountState> r_state) mutable { amount = request.amount_, message = std::move(request.message_)](td::Result<RawAccountState> r_state) mutable {
if (r_state.is_error()) { if (r_state.is_error()) {
return promise.set_error(r_state.move_as_error()); return promise.set_error(r_state.move_as_error());
} }
@ -930,40 +967,41 @@ td::Status TonlibClient::do_request(tonlib_api::generic_sendGrams& request,
} }
auto state = rr_state.move_as_ok(); auto state = rr_state.move_as_ok();
downcast_call(*state, downcast_call(*state, td::overloaded(
td::overloaded( [&](tonlib_api::generic_accountStateTestGiver& test_giver_state) {
[&](tonlib_api::generic_accountStateTestGiver& test_giver_state) { send_lambda(actor_id,
send_lambda(actor_id, [promise = std::move(promise), self, [promise = std::move(promise), self,
query = tonlib_api::testGiver_sendGrams( query = tonlib_api::testGiver_sendGrams(
std::move(destination), test_giver_state.account_state_->seqno_, std::move(destination), test_giver_state.account_state_->seqno_,
amount)]() mutable { amount, std::move(message))]() mutable {
LOG(INFO) << "Send " << to_string(query); LOG(INFO) << "Send " << to_string(query);
auto status = self->do_request(query, std::move(promise)); auto status = self->do_request(query, std::move(promise));
if (status.is_error()) { if (status.is_error()) {
CHECK(promise); CHECK(promise);
promise.set_error(std::move(status)); promise.set_error(std::move(status));
} }
}); });
return; return;
}, },
[&](tonlib_api::generic_accountStateTestWallet& test_wallet_state) { [&](tonlib_api::generic_accountStateTestWallet& test_wallet_state) {
send_lambda(actor_id, [promise = std::move(promise), self, send_lambda(actor_id, [promise = std::move(promise), self,
query = tonlib_api::testWallet_sendGrams( query = tonlib_api::testWallet_sendGrams(
std::move(private_key), std::move(destination), std::move(private_key), std::move(destination),
test_wallet_state.account_state_->seqno_, amount)]() mutable { test_wallet_state.account_state_->seqno_, amount,
auto status = self->do_request(query, std::move(promise)); std::move(message))]() mutable {
if (status.is_error()) { auto status = self->do_request(query, std::move(promise));
CHECK(promise); if (status.is_error()) {
promise.set_error(std::move(status)); CHECK(promise);
} promise.set_error(std::move(status));
}); }
}, });
[&](tonlib_api::generic_accountStateUninited&) { },
promise.set_error(td::Status::Error(400, "Account is not inited")); [&](tonlib_api::generic_accountStateUninited&) {
}, promise.set_error(td::Status::Error(400, "Account is not inited"));
[&](tonlib_api::generic_accountStateRaw&) { },
promise.set_error(td::Status::Error(400, "Unknown account type")); [&](tonlib_api::generic_accountStateRaw&) {
})); promise.set_error(td::Status::Error(400, "Unknown account type"));
}));
}) })
.release(); .release();
return td::Status::OK(); return td::Status::OK();

View File

@ -24,6 +24,7 @@
#include "tonlib/ExtClient.h" #include "tonlib/ExtClient.h"
#include "tonlib/ExtClientOutbound.h" #include "tonlib/ExtClientOutbound.h"
#include "tonlib/KeyStorage.h" #include "tonlib/KeyStorage.h"
#include "tonlib/LastBlockStorage.h"
#include "td/actor/actor.h" #include "td/actor/actor.h"
@ -50,6 +51,7 @@ class TonlibClient : public td::actor::Actor {
// KeyStorage // KeyStorage
KeyStorage key_storage_; KeyStorage key_storage_;
LastBlockStorage last_block_storage_;
// network // network
td::actor::ActorOwn<ton::adnl::AdnlExtClient> raw_client_; td::actor::ActorOwn<ton::adnl::AdnlExtClient> raw_client_;
@ -73,6 +75,7 @@ class TonlibClient : public td::actor::Actor {
} }
} }
void update_last_block_state(LastBlockState state);
void on_result(td::uint64 id, object_ptr<tonlib_api::Object> response); void on_result(td::uint64 id, object_ptr<tonlib_api::Object> response);
static bool is_static_request(td::int32 id); static bool is_static_request(td::int32 id);
static bool is_uninited_request(td::int32 id); static bool is_uninited_request(td::int32 id);

View File

@ -6,6 +6,7 @@
#include "td/utils/port/signals.h" #include "td/utils/port/signals.h"
#include "td/utils/port/path.h" #include "td/utils/port/path.h"
#include "td/utils/Random.h" #include "td/utils/Random.h"
#include "td/utils/as.h"
#include "terminal/terminal.h" #include "terminal/terminal.h"
@ -327,6 +328,8 @@ class TonlibCli : public td::actor::Actor {
<< "\n"; << "\n";
for (size_t i = 0; i < keys_.size(); i++) { for (size_t i = 0; i < keys_.size(); i++) {
td::TerminalIO::out() << " #" << i << ": " << keys_[i].public_key << "\n"; td::TerminalIO::out() << " #" << i << ": " << keys_[i].public_key << "\n";
td::TerminalIO::out() << " " << to_account_address(PSLICE() << i, false).move_as_ok().address->account_address_
<< "\n";
} }
} }
@ -598,13 +601,21 @@ class TonlibCli : public td::actor::Actor {
} }
void transfer(Address from, Address to, td::uint64 grams, td::Slice password) { void transfer(Address from, Address to, td::uint64 grams, td::Slice password) {
td::TerminalIO::out() << "Enter message (could be empty)";
cont_ = [this, from = std::move(from), to = std::move(to), grams,
password = password.str()](td::Slice message) mutable {
this->transfer(std::move(from), std::move(to), grams, password, message);
};
return;
}
void transfer(Address from, Address to, td::uint64 grams, td::Slice password, td::Slice message) {
using tonlib_api::make_object; using tonlib_api::make_object;
auto key = !from.secret.empty() auto key = !from.secret.empty()
? make_object<tonlib_api::inputKey>( ? make_object<tonlib_api::inputKey>(
make_object<tonlib_api::key>(from.public_key, from.secret.copy()), td::SecureString(password)) make_object<tonlib_api::key>(from.public_key, from.secret.copy()), td::SecureString(password))
: nullptr; : nullptr;
send_query(make_object<tonlib_api::generic_sendGrams>(std::move(key), std::move(from.address), send_query(make_object<tonlib_api::generic_sendGrams>(std::move(key), std::move(from.address),
std::move(to.address), grams), std::move(to.address), grams, message.str()),
[](auto r_res) { [](auto r_res) {
if (r_res.is_error()) { if (r_res.is_error()) {
td::TerminalIO::out() << "Can't get state: " << r_res.error() << "\n"; td::TerminalIO::out() << "Can't get state: " << r_res.error() << "\n";