From ceacff19c5cd28b9e7b671d9d98dea64364c8c2e Mon Sep 17 00:00:00 2001 From: Peter <> Date: Thu, 26 Sep 2019 21:00:03 +0400 Subject: [PATCH] Wallet improvements --- Telegram-iOS/en.lproj/Localizable.strings | 34 +- .../Telegram_Buck+IntentsExtension.xcscheme | 4 + ...Buck+NotificationContentExtension.xcscheme | 4 + ...Buck+NotificationServiceExtension.xcscheme | 4 + .../Telegram_Buck+ShareExtension.xcscheme | 4 + .../Telegram_Buck+WidgetExtension.xcscheme | 4 + .../xcschemes/Telegram_Buck.xcscheme | 4 + .../ChatListUI/Sources/ChatContextMenus.swift | 2 +- .../Display/Display/DisplayLinkAnimator.swift | 42 + .../Display/Display/KeyboardManager.swift | 20 + .../Navigation/NavigationContainer.swift | 74 +- .../Navigation/NavigationController.swift | 12 +- .../Navigation/NavigationModalContainer.swift | 2 + .../NavigationTransitionCoordinator.swift | 145 ++-- submodules/Display/Display/Spring.swift | 2 +- .../Display/UIViewController+Navigation.h | 1 + .../Display/UIViewController+Navigation.m | 9 + .../Display/Display/WindowContent.swift | 8 +- submodules/MtProtoKit/TON/TON.h | 8 +- submodules/MtProtoKit/TON/TON.mm | 150 +--- .../xcschemes/SwiftSignalKit.xcscheme | 13 + .../SettingsUI/Sources/DebugController.swift | 51 +- .../TelegramCore/TelegramCore/Wallets.swift | 249 ++---- .../Wallet/RefreshIcon.imageset/Contents.json | 12 + .../Wallet/RefreshIcon.imageset/􀊯 2.pdf | Bin 0 -> 171124 bytes .../TelegramUI/ChatControllerNode.swift | 2 +- .../WalletAnimations/SendingGrams.tgs | Bin 0 -> 27574 bytes .../WalletAnimations/WalletCreated.tgs | Bin 0 -> 11992 bytes .../WalletAnimations/WalletEmpty.tgs | Bin 0 -> 20243 bytes .../WalletAnimations/WalletIntroStatic.tgs | Bin 0 -> 5366 bytes .../WalletAnimations/WalletWordCheck.tgs | Bin 0 -> 12985 bytes .../WalletAnimations/WalletWordList.tgs | Bin 0 -> 7178 bytes .../Sources/WalletInfoEmptyNode.swift | 27 +- .../WalletUI/Sources/WalletInfoScreen.swift | 249 +++--- .../Sources/WalletInfoTransactionItem.swift | 19 +- .../Sources/WalletPasscodeScreen.swift | 1 + .../WalletUI/Sources/WalletRefreshNode.swift | 210 +++++ .../Sources/WalletSettingsScreen.swift | 9 +- .../WalletUI/Sources/WalletSplashScreen.swift | 21 +- .../Sources/WalletTransactionInfoScreen.swift | 20 +- .../Sources/WalletWordCheckScreen.swift | 31 +- .../Sources/WalletWordDisplayScreen.swift | 25 +- .../WalletUI.xcodeproj/project.pbxproj | 20 + submodules/ton/tonlib-src/CMakeLists.txt | 4 + .../tonlib-src/crypto/block/check-proof.cpp | 18 +- .../ton/tonlib-src/crypto/block/check-proof.h | 6 +- .../ton/tonlib-src/crypto/fift/IntCtx.h | 1 + .../ton/tonlib-src/crypto/fift/SourceLookup.h | 21 +- .../ton/tonlib-src/crypto/fift/utils.cpp | 6 +- submodules/ton/tonlib-src/crypto/fift/utils.h | 2 +- .../ton/tonlib-src/crypto/fift/words.cpp | 6 +- .../crypto/smartcont/new-wallet-v2.fif | 62 ++ .../tonlib-src/crypto/smartcont/testgiver.fif | 2 +- .../crypto/smartcont/wallet-code.fc | 2 +- .../crypto/smartcont/wallet-code.fif | 2 +- .../tonlib-src/crypto/smartcont/wallet-v2.fif | 48 ++ .../tonlib-src/crypto/smartcont/wallet.fif | 7 +- .../tonlib-src/tdutils/td/utils/tl_storers.h | 13 + .../tl/generate/auto/tl/ton_api.cpp | 773 +++++++++++++++++- .../tonlib-src/tl/generate/auto/tl/ton_api.h | 444 +++++++++- .../tl/generate/auto/tl/ton_api.hpp | 68 ++ .../tl/generate/auto/tl/ton_api_json.cpp | 308 ++++++- .../tl/generate/auto/tl/ton_api_json.h | 34 + .../tl/generate/auto/tl/tonlib_api.cpp | 407 ++++++++- .../tl/generate/auto/tl/tonlib_api.h | 398 ++++++++- .../tl/generate/auto/tl/tonlib_api.hpp | 112 ++- .../tl/generate/auto/tl/tonlib_api_json.cpp | 430 ++++++++-- .../tl/generate/auto/tl/tonlib_api_json.h | 48 +- .../tonlib-src/tl/generate/scheme/ton_api.tl | 23 +- .../tonlib-src/tl/generate/scheme/ton_api.tlo | Bin 51684 -> 53872 bytes .../tl/generate/scheme/tonlib_api.tl | 63 +- .../tl/generate/scheme/tonlib_api.tlo | Bin 9548 -> 11624 bytes .../ton/tonlib-src/tonlib/CMakeLists.txt | 4 + .../ton/tonlib-src/tonlib/test/offline.cpp | 125 ++- .../tonlib/tonlib/GenericAccount.cpp | 2 +- .../tonlib-src/tonlib/tonlib/KeyStorage.cpp | 37 +- .../ton/tonlib-src/tonlib/tonlib/KeyStorage.h | 5 +- .../tonlib/tonlib/LastBlockStorage.cpp | 18 + .../tonlib/tonlib/LastBlockStorage.h | 18 + .../ton/tonlib-src/tonlib/tonlib/Logging.cpp | 137 ++++ .../ton/tonlib-src/tonlib/tonlib/Logging.h | 49 ++ .../tonlib-src/tonlib/tonlib/TestGiver.cpp | 4 +- .../tonlib-src/tonlib/tonlib/TestWallet.cpp | 13 +- .../tonlib-src/tonlib/tonlib/TonlibClient.cpp | 454 ++++++++-- .../tonlib-src/tonlib/tonlib/TonlibClient.h | 32 +- .../ton/tonlib-src/tonlib/tonlib/Wallet.cpp | 89 ++ .../ton/tonlib-src/tonlib/tonlib/Wallet.h | 38 + .../tonlib-src/tonlib/tonlib/tonlib-cli.cpp | 62 +- 88 files changed, 4885 insertions(+), 1002 deletions(-) create mode 100644 submodules/TelegramUI/Images.xcassets/Wallet/RefreshIcon.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Wallet/RefreshIcon.imageset/􀊯 2.pdf create mode 100644 submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/SendingGrams.tgs create mode 100644 submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletCreated.tgs create mode 100644 submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletEmpty.tgs create mode 100644 submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletIntroStatic.tgs create mode 100644 submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletWordCheck.tgs create mode 100644 submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletWordList.tgs create mode 100644 submodules/WalletUI/Sources/WalletRefreshNode.swift create mode 100644 submodules/ton/tonlib-src/crypto/smartcont/new-wallet-v2.fif create mode 100644 submodules/ton/tonlib-src/crypto/smartcont/wallet-v2.fif create mode 100644 submodules/ton/tonlib-src/tonlib/tonlib/Logging.cpp create mode 100644 submodules/ton/tonlib-src/tonlib/tonlib/Logging.h create mode 100644 submodules/ton/tonlib-src/tonlib/tonlib/Wallet.cpp create mode 100644 submodules/ton/tonlib-src/tonlib/tonlib/Wallet.h diff --git a/Telegram-iOS/en.lproj/Localizable.strings b/Telegram-iOS/en.lproj/Localizable.strings index b2d578e6f0..9828490834 100644 --- a/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram-iOS/en.lproj/Localizable.strings @@ -4755,20 +4755,20 @@ Any member of this group will be able to see messages in the channel."; "ChatSearch.ResultsTooltip" = "Tap to view as a list."; -"Updated.JustNow" = "updated just now"; -"Updated.MinutesAgo_0" = "updated %@ minutes ago"; //three to ten -"Updated.MinutesAgo_1" = "updated 1 minute ago"; //one -"Updated.MinutesAgo_2" = "updated 2 minutes ago"; //two -"Updated.MinutesAgo_3_10" = "updated %@ minutes ago"; //three to ten -"Updated.MinutesAgo_many" = "updated %@ minutes ago"; // more than ten -"Updated.MinutesAgo_any" = "updated %@ minutes ago"; // more than ten -"Updated.HoursAgo_0" = "updated %@ hours ago"; -"Updated.HoursAgo_1" = "updated 1 hour ago"; -"Updated.HoursAgo_2" = "updated 2 hours ago"; -"Updated.HoursAgo_3_10" = "updated %@ hours ago"; -"Updated.HoursAgo_any" = "updated %@ hours ago"; -"Updated.HoursAgo_many" = "updated %@ hours ago"; -"Updated.HoursAgo_0" = "updated %@ hours ago"; -"Updated.YesterdayAt" = "updated yesterday at %@"; -"Updated.AtDate" = "updated %@"; -"Updated.TodayAt" = "updated today at %@"; \ No newline at end of file +"Updated.JustNow" = "just now"; +"Updated.MinutesAgo_0" = "%@ minutes ago"; //three to ten +"Updated.MinutesAgo_1" = "1 minute ago"; //one +"Updated.MinutesAgo_2" = "2 minutes ago"; //two +"Updated.MinutesAgo_3_10" = "%@ minutes ago"; //three to ten +"Updated.MinutesAgo_many" = "%@ minutes ago"; // more than ten +"Updated.MinutesAgo_any" = "%@ minutes ago"; // more than ten +"Updated.HoursAgo_0" = "%@ hours ago"; +"Updated.HoursAgo_1" = "1 hour ago"; +"Updated.HoursAgo_2" = "2 hours ago"; +"Updated.HoursAgo_3_10" = "%@ hours ago"; +"Updated.HoursAgo_any" = "%@ hours ago"; +"Updated.HoursAgo_many" = "%@ hours ago"; +"Updated.HoursAgo_0" = "%@ hours ago"; +"Updated.YesterdayAt" = "yesterday at %@"; +"Updated.AtDate" = "%@"; +"Updated.TodayAt" = "today at %@"; diff --git a/Telegram_Buck.xcworkspace/xcshareddata/xcschemes/Telegram_Buck+IntentsExtension.xcscheme b/Telegram_Buck.xcworkspace/xcshareddata/xcschemes/Telegram_Buck+IntentsExtension.xcscheme index ce9b325629..a48bce0372 100644 --- a/Telegram_Buck.xcworkspace/xcshareddata/xcschemes/Telegram_Buck+IntentsExtension.xcscheme +++ b/Telegram_Buck.xcworkspace/xcshareddata/xcschemes/Telegram_Buck+IntentsExtension.xcscheme @@ -44,6 +44,8 @@ shouldUseLaunchSchemeArgsEnv = "YES"> + + + + + + + + + + + + + + + + + + + + + + + + deliverOnMainQueue).start(completed: { f(.default) diff --git a/submodules/Display/Display/DisplayLinkAnimator.swift b/submodules/Display/Display/DisplayLinkAnimator.swift index a7cbd79858..acd8db711f 100644 --- a/submodules/Display/Display/DisplayLinkAnimator.swift +++ b/submodules/Display/Display/DisplayLinkAnimator.swift @@ -65,3 +65,45 @@ public final class DisplayLinkAnimator { } } } + +public final class ConstantDisplayLinkAnimator { + private var displayLink: CADisplayLink! + private let update: () -> Void + private var completed = false + + public var isPaused: Bool = true { + didSet { + if self.isPaused != oldValue { + self.displayLink.isPaused = self.isPaused + } + } + } + + public init(update: @escaping () -> Void) { + self.update = update + + self.displayLink = CADisplayLink(target: DisplayLinkTarget({ [weak self] in + self?.tick() + }), selector: #selector(DisplayLinkTarget.event)) + self.displayLink.isPaused = true + self.displayLink.add(to: RunLoop.main, forMode: .common) + } + + deinit { + self.displayLink.isPaused = true + self.displayLink.invalidate() + } + + public func invalidate() { + self.displayLink.isPaused = true + self.displayLink.invalidate() + } + + @objc private func tick() { + if self.completed { + return + } + self.update() + } +} + diff --git a/submodules/Display/Display/KeyboardManager.swift b/submodules/Display/Display/KeyboardManager.swift index 56769ae4b9..3cea17e8f1 100644 --- a/submodules/Display/Display/KeyboardManager.swift +++ b/submodules/Display/Display/KeyboardManager.swift @@ -144,3 +144,23 @@ class KeyboardManager { self.previousFirstResponderView = firstResponderView } } + +final class KeyboardViewManager { + private let host: StatusBarHost + + init(host: StatusBarHost) { + self.host = host + } + + func update(leftEdge: CGFloat, transition: ContainedViewLayoutTransition) { + guard let keyboardWindow = self.host.keyboardWindow else { + return + } + let previousPosition = keyboardWindow.layer.position + let updatedPosition = CGPoint(x: leftEdge + keyboardWindow.layer.bounds.width / 2.0, y: previousPosition.y) + keyboardWindow.layer.position = updatedPosition + if transition.isAnimated && !previousPosition.x.isEqual(to: updatedPosition.x) { + transition.animatePositionAdditive(layer: keyboardWindow.layer, offset: CGPoint(x: previousPosition.x - updatedPosition.x, y: 0.0)) + } + } +} diff --git a/submodules/Display/Display/Navigation/NavigationContainer.swift b/submodules/Display/Display/Navigation/NavigationContainer.swift index add58d141f..0321302ae4 100644 --- a/submodules/Display/Display/Navigation/NavigationContainer.swift +++ b/submodules/Display/Display/Navigation/NavigationContainer.swift @@ -54,10 +54,12 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate { } private final class TopTransition { + let type: PendingChild.TransitionType let previous: Child let coordinator: NavigationTransitionCoordinator - init(previous: Child, coordinator: NavigationTransitionCoordinator) { + init(type: PendingChild.TransitionType, previous: Child, coordinator: NavigationTransitionCoordinator) { + self.type = type self.previous = previous self.coordinator = coordinator } @@ -77,10 +79,10 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate { private(set) var isReady: Bool = false var isReadyUpdated: (() -> Void)? var controllerRemoved: (ViewController) -> Void - var keyboardManager: KeyboardManager? { + var keyboardViewManager: KeyboardViewManager? { didSet { - if self.keyboardManager !== oldValue { - self.keyboardManager?.surfaces = self.state.top?.value.view.flatMap({ [KeyboardSurface(host: $0)] }) ?? [] + if self.keyboardViewManager !== oldValue { + } } } @@ -139,14 +141,17 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate { } topController.viewWillDisappear(true) - let topView = topController.view! + let topNode = topController.displayNode bottomController.containerLayoutUpdated(layout, transition: .immediate) bottomController.viewWillAppear(true) - let bottomView = bottomController.view! + let bottomNode = bottomController.displayNode - let navigationTransitionCoordinator = NavigationTransitionCoordinator(transition: .Pop, container: self.view, topView: topView, topNavigationBar: topController.navigationBar, bottomView: bottomView, bottomNavigationBar: bottomController.navigationBar, didUpdateProgress: { [weak self] progress, transition in + let navigationTransitionCoordinator = NavigationTransitionCoordinator(transition: .Pop, container: self, topNode: topNode, topNavigationBar: topController.navigationBar, bottomNode: bottomNode, bottomNavigationBar: bottomController.navigationBar, didUpdateProgress: { [weak self] progress, transition, topFrame, bottomFrame in if let strongSelf = self { - strongSelf.keyboardManager?.surfaces = strongSelf.state.top?.value.view.flatMap({ [KeyboardSurface(host: $0)] }) ?? [] + if let top = strongSelf.state.top { + strongSelf.syncKeyboard(leftEdge: top.value.displayNode.frame.minX, transition: transition) + } + //strongSelf.keyboardManager?.surfaces = strongSelf.state.top?.value.view.flatMap({ [KeyboardSurface(host: $0)] }) ?? [] /*for i in 0 ..< strongSelf._viewControllers.count { if let controller = strongSelf._viewControllers[i].controller as? ViewController { if i < strongSelf._viewControllers.count - 1 { @@ -159,13 +164,13 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate { } }) bottomController.displayNode.recursivelyEnsureDisplaySynchronously(true) - self.state.transition = TopTransition(previous: Child(value: bottomController, layout: layout), coordinator: navigationTransitionCoordinator) + self.state.transition = TopTransition(type: .pop, previous: Child(value: bottomController, layout: layout), coordinator: navigationTransitionCoordinator) } case .changed: if let navigationTransitionCoordinator = self.state.transition?.coordinator, !navigationTransitionCoordinator.animatingCompletion { let translation = recognizer.translation(in: self.view).x let progress = max(0.0, min(1.0, translation / self.view.frame.width)) - navigationTransitionCoordinator.progress = progress + navigationTransitionCoordinator.updateProgress(progress, transition: .immediate, completion: {}) } case .ended, .cancelled: if let navigationTransitionCoordinator = self.state.transition?.coordinator, !navigationTransitionCoordinator.animatingCompletion { @@ -274,7 +279,7 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate { if pending.isReady { self.state.pending = nil let previous = self.state.top - previous?.value.view.endEditing(true) + //previous?.value.view.endEditing(true) self.state.top = pending.value self.topTransition(from: previous, to: pending.value, transitionType: pending.transitionType, layout: layout.withUpdatedInputHeight(nil), transition: pending.transition) statusBarTransition = pending.transition @@ -293,7 +298,7 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate { var updatedStatusBarStyle = self.statusBarStyle if let top = self.state.top { - self.applyLayout(layout: layout, to: top, transition: transition) + self.applyLayout(layout: layout, to: top, isMaster: true, transition: transition) updatedStatusBarStyle = top.value.statusBar.statusBarStyle } else { updatedStatusBarStyle = .Ignore @@ -304,13 +309,13 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate { } if self.state.transition == nil { - self.keyboardManager?.surfaces = self.state.top?.value.view.flatMap({ [KeyboardSurface(host: $0)] }) ?? [] + //self.keyboardManager?.surfaces = self.state.top?.value.view.flatMap({ [KeyboardSurface(host: $0)] }) ?? [] } } private func topTransition(from fromValue: Child?, to toValue: Child?, transitionType: PendingChild.TransitionType, layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) { if case .animated = transition, let fromValue = fromValue, let toValue = toValue { - self.keyboardManager?.surfaces = fromValue.value.view.flatMap({ [KeyboardSurface(host: $0)] }) ?? [] + //self.keyboardManager?.surfaces = fromValue.value.view.flatMap({ [KeyboardSurface(host: $0)] }) ?? [] if let currentTransition = self.state.transition { assertionFailure() } @@ -338,7 +343,17 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate { } toValue.value.setIgnoreAppearanceMethodInvocations(false) - let topTransition = TopTransition(previous: fromValue, coordinator: NavigationTransitionCoordinator(transition: mappedTransitionType, container: self.view, topView: topController.view, topNavigationBar: topController.navigationBar, bottomView: bottomController.view, bottomNavigationBar: bottomController.navigationBar, didUpdateProgress: nil)) + let topTransition = TopTransition(type: transitionType, previous: fromValue, coordinator: NavigationTransitionCoordinator(transition: mappedTransitionType, container: self, topNode: topController.displayNode, topNavigationBar: topController.navigationBar, bottomNode: bottomController.displayNode, bottomNavigationBar: bottomController.navigationBar, didUpdateProgress: { [weak self] _, transition, topFrame, bottomFrame in + guard let strongSelf = self else { + return + } + switch transitionType { + case .push: + strongSelf.syncKeyboard(leftEdge: topFrame.minX - bottomFrame.width, transition: transition) + case .pop: + strongSelf.syncKeyboard(leftEdge: topFrame.minX, transition: transition) + } + })) self.state.transition = topTransition topTransition.coordinator.animateCompletion(0.0, completion: { [weak self, weak topTransition] in @@ -353,13 +368,13 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate { topTransition.previous.value.viewDidDisappear(true) if let toValue = strongSelf.state.top, let layout = strongSelf.state.layout { toValue.value.displayNode.frame = CGRect(origin: CGPoint(), size: layout.size) - strongSelf.applyLayout(layout: layout, to: toValue, transition: .immediate) + strongSelf.applyLayout(layout: layout, to: toValue, isMaster: true, transition: .immediate) toValue.value.viewDidAppear(true) - strongSelf.keyboardManager?.surfaces = toValue.value.view.flatMap({ [KeyboardSurface(host: $0)] }) ?? [] + //strongSelf.keyboardManager?.surfaces = toValue.value.view.flatMap({ [KeyboardSurface(host: $0)] }) ?? [] } }) } else { - self.keyboardManager?.surfaces = toValue?.value.view.flatMap({ [KeyboardSurface(host: $0)] }) ?? [] + //self.keyboardManager?.surfaces = toValue?.value.view.flatMap({ [KeyboardSurface(host: $0)] }) ?? [] if let fromValue = fromValue { fromValue.value.viewWillDisappear(false) fromValue.value.setIgnoreAppearanceMethodInvocations(true) @@ -368,7 +383,7 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate { fromValue.value.viewDidDisappear(false) } if let toValue = toValue { - self.applyLayout(layout: layout, to: toValue, transition: .immediate) + self.applyLayout(layout: layout, to: toValue, isMaster: true, transition: .immediate) toValue.value.displayNode.frame = CGRect(origin: CGPoint(), size: layout.size) toValue.value.viewWillAppear(false) toValue.value.setIgnoreAppearanceMethodInvocations(true) @@ -384,17 +399,34 @@ final class NavigationContainer: ASDisplayNode, UIGestureRecognizerDelegate { return Child(value: value, layout: layout) } - private func applyLayout(layout: ContainerViewLayout, to child: Child, transition: ContainedViewLayoutTransition) { - let childFrame = CGRect(origin: CGPoint(), size: layout.size) + private func applyLayout(layout: ContainerViewLayout, to child: Child, isMaster: Bool, transition: ContainedViewLayoutTransition) { + var childFrame = CGRect(origin: CGPoint(), size: layout.size) + var shouldSyncKeyboard = false + if let transition = self.state.transition { + childFrame.origin.x = child.value.displayNode.frame.origin.x + switch transition.type { + case .pop: + shouldSyncKeyboard = true + case .push: + break + } + } if child.value.displayNode.frame != childFrame { transition.updateFrame(node: child.value.displayNode, frame: childFrame) } + if shouldSyncKeyboard && isMaster { + self.syncKeyboard(leftEdge: childFrame.minX, transition: transition) + } if child.layout != layout { child.layout = layout child.value.containerLayoutUpdated(layout, transition: transition) } } + private func syncKeyboard(leftEdge: CGFloat, transition: ContainedViewLayoutTransition) { + self.keyboardViewManager?.update(leftEdge: leftEdge, transition: transition) + } + private func pendingChildIsReady(_ child: PendingChild) { if let pending = self.state.pending, pending === child { pending.isReady = true diff --git a/submodules/Display/Display/Navigation/NavigationController.swift b/submodules/Display/Display/Navigation/NavigationController.swift index a206e983e6..384179b408 100644 --- a/submodules/Display/Display/Navigation/NavigationController.swift +++ b/submodules/Display/Display/Navigation/NavigationController.swift @@ -151,7 +151,7 @@ open class NavigationController: UINavigationController, ContainableController, } var statusBarHost: StatusBarHost? - var keyboardManager: KeyboardManager? + var keyboardViewManager: KeyboardViewManager? public func updateMasterDetailsBlackout(_ blackout: MasterDetailLayoutBlackout?, transition: ContainedViewLayoutTransition) { self.masterDetailsBlackout = blackout @@ -341,12 +341,12 @@ open class NavigationController: UINavigationController, ContainableController, if previousModalContainer == nil { topModalDismissProgress = modalContainer.dismissProgress if case .compact = layout.metrics.widthClass { - modalContainer.container.keyboardManager = self.keyboardManager + modalContainer.container.keyboardViewManager = self.keyboardViewManager } else { - modalContainer.container.keyboardManager = nil + modalContainer.container.keyboardViewManager = nil } } else { - modalContainer.container.keyboardManager = nil + modalContainer.container.keyboardViewManager = nil } previousModalContainer = modalContainer } @@ -358,9 +358,9 @@ open class NavigationController: UINavigationController, ContainableController, switch rootContainer { case let .flat(flatContainer): if previousModalContainer == nil { - flatContainer.keyboardManager = self.keyboardManager + flatContainer.keyboardViewManager = self.keyboardViewManager } else { - flatContainer.keyboardManager = nil + flatContainer.keyboardViewManager = nil } transition.updateFrame(node: flatContainer, frame: CGRect(origin: CGPoint(), size: layout.size)) flatContainer.update(layout: layout, canBeClosed: false, controllers: controllers, transition: transition) diff --git a/submodules/Display/Display/Navigation/NavigationModalContainer.swift b/submodules/Display/Display/Navigation/NavigationModalContainer.swift index ad53c8c39e..0d01ad0723 100644 --- a/submodules/Display/Display/Navigation/NavigationModalContainer.swift +++ b/submodules/Display/Display/Navigation/NavigationModalContainer.swift @@ -53,6 +53,8 @@ final class NavigationModalContainer: ASDisplayNode, UIScrollViewDelegate { override func didLoad() { super.didLoad() + self.view.disablesInteractiveKeyboardGestureRecognizer = true + self.scrollNode.view.alwaysBounceVertical = false self.scrollNode.view.alwaysBounceHorizontal = false self.scrollNode.view.bounces = false diff --git a/submodules/Display/Display/NavigationTransitionCoordinator.swift b/submodules/Display/Display/NavigationTransitionCoordinator.swift index 7ebe56c0fa..5cbf82a864 100644 --- a/submodules/Display/Display/NavigationTransitionCoordinator.swift +++ b/submodules/Display/Display/NavigationTransitionCoordinator.swift @@ -20,49 +20,41 @@ class NavigationTransitionCoordinator { get { return self._progress } - set(value) { - self._progress = value - self.updateProgress(transition: .immediate) - } } - private let container: UIView + private let container: ASDisplayNode private let transition: NavigationTransition - let topView: UIView - private let viewSuperview: UIView? - let bottomView: UIView + let topNode: ASDisplayNode + let bottomNode: ASDisplayNode private let topNavigationBar: NavigationBar? private let bottomNavigationBar: NavigationBar? - private let dimView: UIView - private let shadowView: UIImageView + private let dimNode: ASDisplayNode + private let shadowNode: ASImageNode private let inlineNavigationBarTransition: Bool private(set) var animatingCompletion = false private var currentCompletion: (() -> Void)? - private var didUpdateProgress: ((CGFloat, ContainedViewLayoutTransition) -> Void)? + private var didUpdateProgress: ((CGFloat, ContainedViewLayoutTransition, CGRect, CGRect) -> Void)? - init(transition: NavigationTransition, container: UIView, topView: UIView, topNavigationBar: NavigationBar?, bottomView: UIView, bottomNavigationBar: NavigationBar?, didUpdateProgress: ((CGFloat, ContainedViewLayoutTransition) -> Void)? = nil) { + init(transition: NavigationTransition, container: ASDisplayNode, topNode: ASDisplayNode, topNavigationBar: NavigationBar?, bottomNode: ASDisplayNode, bottomNavigationBar: NavigationBar?, didUpdateProgress: ((CGFloat, ContainedViewLayoutTransition, CGRect, CGRect) -> Void)? = nil) { self.transition = transition self.container = container self.didUpdateProgress = didUpdateProgress - self.topView = topView - switch transition { - case .Push: - self.viewSuperview = bottomView.superview - case .Pop: - self.viewSuperview = topView.superview - } - self.bottomView = bottomView + self.topNode = topNode + self.bottomNode = bottomNode self.topNavigationBar = topNavigationBar self.bottomNavigationBar = bottomNavigationBar - self.dimView = UIView() - self.dimView.backgroundColor = UIColor.black - self.shadowView = UIImageView(image: shadowImage) + self.dimNode = ASDisplayNode() + self.dimNode.backgroundColor = UIColor.black + self.shadowNode = ASImageNode() + self.shadowNode.displaysAsynchronously = false + self.shadowNode.displayWithoutProcessing = true + self.shadowNode.image = shadowImage if let topNavigationBar = topNavigationBar, let bottomNavigationBar = bottomNavigationBar, !topNavigationBar.isHidden, !bottomNavigationBar.isHidden, topNavigationBar.canTransitionInline, bottomNavigationBar.canTransitionInline, topNavigationBar.item?.leftBarButtonItem == nil { - var topFrame = topNavigationBar.view.convert(topNavigationBar.bounds, to: container) - var bottomFrame = bottomNavigationBar.view.convert(bottomNavigationBar.bounds, to: container) + var topFrame = topNavigationBar.view.convert(topNavigationBar.bounds, to: container.view) + var bottomFrame = bottomNavigationBar.view.convert(bottomNavigationBar.bounds, to: container.view) topFrame.origin.x = 0.0 bottomFrame.origin.x = 0.0 self.inlineNavigationBarTransition = true// topFrame.equalTo(bottomFrame) @@ -72,23 +64,25 @@ class NavigationTransitionCoordinator { switch transition { case .Push: - self.viewSuperview?.insertSubview(topView, belowSubview: topView) + self.container.insertSubnode(topNode, belowSubnode: topNode) case .Pop: - self.viewSuperview?.insertSubview(bottomView, belowSubview: topView) + self.container.insertSubnode(bottomNode, belowSubnode: topNode) } - self.viewSuperview?.insertSubview(self.dimView, belowSubview: topView) - self.viewSuperview?.insertSubview(self.shadowView, belowSubview: dimView) + self.container.insertSubnode(self.dimNode, belowSubnode: topNode) + self.container.insertSubnode(self.shadowNode, belowSubnode: dimNode) self.maybeCreateNavigationBarTransition() - self.updateProgress(transition: .immediate) + self.updateProgress(0.0, transition: .immediate, completion: {}) } required init(coder aDecoder: NSCoder) { fatalError("init(coder:) has not been implemented") } - func updateProgress(transition: ContainedViewLayoutTransition) { + func updateProgress(_ progress: CGFloat, transition: ContainedViewLayoutTransition, completion: @escaping () -> Void) { + self._progress = progress + let position: CGFloat switch self.transition { case .Push: @@ -104,19 +98,25 @@ class NavigationTransitionCoordinator { let containerSize = self.container.bounds.size - self.topView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels(position * containerSize.width), y: 0.0), size: containerSize) - self.dimView.frame = CGRect(origin: CGPoint(x: 0.0, y: dimInset), size: CGSize(width: max(0.0, self.topView.frame.minX), height: self.container.bounds.size.height - dimInset)) - self.shadowView.frame = CGRect(origin: CGPoint(x: self.dimView.frame.maxX - shadowWidth, y: dimInset), size: CGSize(width: shadowWidth, height: containerSize.height - dimInset)) - self.dimView.alpha = (1.0 - position) * 0.15 - self.shadowView.alpha = (1.0 - position) * 0.9 - self.bottomView.frame = CGRect(origin: CGPoint(x: ((position - 1.0) * containerSize.width * 0.3), y: 0.0), size: containerSize) + let topFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(position * containerSize.width), y: 0.0), size: containerSize) + let bottomFrame = CGRect(origin: CGPoint(x: ((position - 1.0) * containerSize.width * 0.3), y: 0.0), size: containerSize) - self.updateNavigationBarTransition() + transition.updateFrame(node: self.topNode, frame: topFrame, completion: { _ in + completion() + }) + transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(x: 0.0, y: dimInset), size: CGSize(width: max(0.0, topFrame.minX), height: self.container.bounds.size.height - dimInset))) + transition.updateFrame(node: self.shadowNode, frame: CGRect(origin: CGPoint(x: self.dimNode.frame.maxX - shadowWidth, y: dimInset), size: CGSize(width: shadowWidth, height: containerSize.height - dimInset))) + transition.updateAlpha(node: self.dimNode, alpha: (1.0 - position) * 0.15) + transition.updateAlpha(node: self.shadowNode, alpha: (1.0 - position) * 0.9) - self.didUpdateProgress?(self.progress, transition) + transition.updateFrame(node: self.bottomNode, frame: bottomFrame) + + self.updateNavigationBarTransition(transition: transition) + + self.didUpdateProgress?(self.progress, transition, topFrame, bottomFrame) } - func updateNavigationBarTransition() { + func updateNavigationBarTransition(transition: ContainedViewLayoutTransition) { if let topNavigationBar = self.topNavigationBar, let bottomNavigationBar = self.bottomNavigationBar, self.inlineNavigationBarTransition { let position: CGFloat switch self.transition { @@ -126,8 +126,10 @@ class NavigationTransitionCoordinator { position = progress } - topNavigationBar.transitionState = NavigationBarTransitionState(navigationBar: bottomNavigationBar, transition: self.transition, role: .top, progress: position) - bottomNavigationBar.transitionState = NavigationBarTransitionState(navigationBar: topNavigationBar, transition: self.transition, role: .bottom, progress: position) + transition.animateView { + topNavigationBar.transitionState = NavigationBarTransitionState(navigationBar: bottomNavigationBar, transition: self.transition, role: .top, progress: position) + bottomNavigationBar.transitionState = NavigationBarTransitionState(navigationBar: topNavigationBar, transition: self.transition, role: .bottom, progress: position) + } } } @@ -156,45 +158,36 @@ class NavigationTransitionCoordinator { func animateCancel(_ completion: @escaping () -> ()) { self.currentCompletion = completion - UIView.animate(withDuration: 0.1, delay: 0.0, options: UIView.AnimationOptions(), animations: { () -> Void in - self.progress = 0.0 - }) { (completed) -> Void in - switch self.transition { - case .Push: - if let viewSuperview = self.viewSuperview { - viewSuperview.addSubview(self.bottomView) - } else { - self.bottomView.removeFromSuperview() - } - self.topView.removeFromSuperview() - case .Pop: - if let viewSuperview = self.viewSuperview { - viewSuperview.addSubview(self.topView) - } else { - self.topView.removeFromSuperview() - } - self.bottomView.removeFromSuperview() + self.updateProgress(0.0, transition: .animated(duration: 0.1, curve: .easeInOut), completion: { [weak self] in + guard let strongSelf = self else { + return + } + switch strongSelf.transition { + case .Push: + strongSelf.topNode.removeFromSupernode() + case .Pop: + strongSelf.bottomNode.removeFromSupernode() } - self.dimView.removeFromSuperview() - self.shadowView.removeFromSuperview() + strongSelf.dimNode.removeFromSupernode() + strongSelf.shadowNode.removeFromSupernode() - self.endNavigationBarTransition() + strongSelf.endNavigationBarTransition() - if let currentCompletion = self.currentCompletion { - self.currentCompletion = nil + if let currentCompletion = strongSelf.currentCompletion { + strongSelf.currentCompletion = nil currentCompletion() } - } + }) } func complete() { self.animatingCompletion = true - self.progress = 1.0 + self._progress = 1.0 - self.dimView.removeFromSuperview() - self.shadowView.removeFromSuperview() + self.dimNode.removeFromSupernode() + self.shadowNode.removeFromSupernode() self.endNavigationBarTransition() @@ -224,8 +217,8 @@ class NavigationTransitionCoordinator { } }*/ - self.dimView.removeFromSuperview() - self.shadowView.removeFromSuperview() + self.dimNode.removeFromSupernode() + self.shadowNode.removeFromSupernode() self.endNavigationBarTransition() @@ -236,17 +229,13 @@ class NavigationTransitionCoordinator { } if abs(velocity) < CGFloat.ulpOfOne && abs(self.progress) < CGFloat.ulpOfOne { - UIView.animate(withDuration: 0.5, delay: 0.0, options: UIView.AnimationOptions(rawValue: 7 << 16), animations: { - self.progress = 1.0 - }, completion: { _ in + self.updateProgress(1.0, transition: .animated(duration: 0.5, curve: .spring), completion: { f() }) } else { - UIView.animate(withDuration: Double(max(0.05, min(0.2, abs(distance / velocity)))), delay: 0.0, options:UIView.AnimationOptions(), animations: { () -> Void in - self.progress = 1.0 - }) { (completed) -> Void in + self.updateProgress(1.0, transition: .animated(duration: Double(max(0.05, min(0.2, abs(distance / velocity)))), curve: .easeInOut), completion: { f() - } + }) } } } diff --git a/submodules/Display/Display/Spring.swift b/submodules/Display/Display/Spring.swift index 169ed107db..ce4a55adf8 100644 --- a/submodules/Display/Display/Spring.swift +++ b/submodules/Display/Display/Spring.swift @@ -57,7 +57,7 @@ private func getTForX(_ x: CGFloat, _ x1: CGFloat, _ x2: CGFloat) -> CGFloat { return t } -func bezierPoint(_ x1: CGFloat, _ y1: CGFloat, _ x2: CGFloat, _ y2: CGFloat, _ x: CGFloat) -> CGFloat +public func bezierPoint(_ x1: CGFloat, _ y1: CGFloat, _ x2: CGFloat, _ y2: CGFloat, _ x: CGFloat) -> CGFloat { var value = calcBezier(getTForX(x, x1, x2), y1, y2) if value >= 0.997 { diff --git a/submodules/Display/Display/UIViewController+Navigation.h b/submodules/Display/Display/UIViewController+Navigation.h index 9c3f5dc007..ed78c4f164 100644 --- a/submodules/Display/Display/UIViewController+Navigation.h +++ b/submodules/Display/Display/UIViewController+Navigation.h @@ -21,6 +21,7 @@ typedef NS_OPTIONS(NSUInteger, UIResponderDisableAutomaticKeyboardHandling) { @interface UIView (Navigation) @property (nonatomic) bool disablesInteractiveTransitionGestureRecognizer; +@property (nonatomic) bool disablesInteractiveKeyboardGestureRecognizer; @property (nonatomic, copy) bool (^ disablesInteractiveTransitionGestureRecognizerNow)(); @property (nonatomic) UIResponderDisableAutomaticKeyboardHandling disableAutomaticKeyboardHandling; diff --git a/submodules/Display/Display/UIViewController+Navigation.m b/submodules/Display/Display/UIViewController+Navigation.m index d08b6a9780..09a327f821 100644 --- a/submodules/Display/Display/UIViewController+Navigation.m +++ b/submodules/Display/Display/UIViewController+Navigation.m @@ -35,6 +35,7 @@ static const void *UIViewControllerNavigationControllerKey = &UIViewControllerNa static const void *UIViewControllerPresentingControllerKey = &UIViewControllerPresentingControllerKey; static const void *UIViewControllerPresentingProxyControllerKey = &UIViewControllerPresentingProxyControllerKey; static const void *disablesInteractiveTransitionGestureRecognizerKey = &disablesInteractiveTransitionGestureRecognizerKey; +static const void *disablesInteractiveKeyboardGestureRecognizerKey = &disablesInteractiveKeyboardGestureRecognizerKey; static const void *disablesInteractiveTransitionGestureRecognizerNowKey = &disablesInteractiveTransitionGestureRecognizerNowKey; static const void *disableAutomaticKeyboardHandlingKey = &disableAutomaticKeyboardHandlingKey; static const void *setNeedsStatusBarAppearanceUpdateKey = &setNeedsStatusBarAppearanceUpdateKey; @@ -233,6 +234,14 @@ static bool notyfyingShiftState = false; [self setAssociatedObject:@(disablesInteractiveTransitionGestureRecognizer) forKey:disablesInteractiveTransitionGestureRecognizerKey]; } +- (bool)disablesInteractiveKeyboardGestureRecognizer { + return [[self associatedObjectForKey:disablesInteractiveKeyboardGestureRecognizerKey] boolValue]; +} + +- (void)setDisablesInteractiveKeyboardGestureRecognizer:(bool)disablesInteractiveKeyboardGestureRecognizer { + [self setAssociatedObject:@(disablesInteractiveKeyboardGestureRecognizer) forKey:disablesInteractiveKeyboardGestureRecognizerKey]; +} + - (bool (^)())disablesInteractiveTransitionGestureRecognizerNow { return [self associatedObjectForKey:disablesInteractiveTransitionGestureRecognizerNowKey]; } diff --git a/submodules/Display/Display/WindowContent.swift b/submodules/Display/Display/WindowContent.swift index b592479516..8a2562fd69 100644 --- a/submodules/Display/Display/WindowContent.swift +++ b/submodules/Display/Display/WindowContent.swift @@ -129,6 +129,9 @@ public func doesViewTreeDisableInteractiveTransitionGestureRecognizer(_ view: UI if view.disablesInteractiveTransitionGestureRecognizer { return true } + if view.disablesInteractiveKeyboardGestureRecognizer { + return true + } if let f = view.disablesInteractiveTransitionGestureRecognizerNow, f() { return true } @@ -279,6 +282,7 @@ public class Window1 { private let statusBarHost: StatusBarHost? private let statusBarManager: StatusBarManager? private let keyboardManager: KeyboardManager? + private let keyboardViewManager: KeyboardViewManager? private var statusBarChangeObserver: AnyObject? private var keyboardRotationChangeObserver: AnyObject? private var keyboardFrameChangeObserver: AnyObject? @@ -344,10 +348,12 @@ public class Window1 { statusBarHeight = statusBarHost.statusBarFrame.size.height self.statusBarManager = StatusBarManager(host: statusBarHost, volumeControlStatusBar: self.volumeControlStatusBar, volumeControlStatusBarNode: self.volumeControlStatusBarNode) self.keyboardManager = KeyboardManager(host: statusBarHost) + self.keyboardViewManager = KeyboardViewManager(host: statusBarHost) } else { statusBarHeight = self.deviceMetrics.statusBarHeight self.statusBarManager = nil self.keyboardManager = nil + self.keyboardViewManager = nil } let isLandscape = boundsSize.width > boundsSize.height @@ -692,7 +698,7 @@ public class Window1 { if let rootController = self._rootController { if let rootController = rootController as? NavigationController { rootController.statusBarHost = self.statusBarHost - rootController.keyboardManager = self.keyboardManager + rootController.keyboardViewManager = self.keyboardViewManager } if !self.windowLayout.size.width.isZero && !self.windowLayout.size.height.isZero { rootController.displayNode.frame = CGRect(origin: CGPoint(), size: self.windowLayout.size) diff --git a/submodules/MtProtoKit/TON/TON.h b/submodules/MtProtoKit/TON/TON.h index 18d6b5585a..79084ea377 100644 --- a/submodules/MtProtoKit/TON/TON.h +++ b/submodules/MtProtoKit/TON/TON.h @@ -78,16 +78,12 @@ NS_ASSUME_NONNULL_BEGIN - (instancetype)initWithKeystoreDirectory:(NSString *)keystoreDirectory config:(NSString *)config performExternalRequest:(void (^)(TONExternalRequest * _Nonnull))performExternalRequest; - (MTSignal *)createKeyWithLocalPassword:(NSData *)localPassword mnemonicPassword:(NSData *)mnemonicPassword; -- (MTSignal *)getTestWalletAccountAddressWithPublicKey:(NSString *)publicKey; -- (MTSignal *)getTestGiverAccountState; -- (MTSignal *)getTestGiverAddress; -- (MTSignal *)testGiverSendGramsWithAccountState:(TONAccountState *)accountState accountAddress:(NSString *)accountAddress amount:(int64_t)amount; +- (MTSignal *)getWalletAccountAddressWithPublicKey:(NSString *)publicKey; - (MTSignal *)getAccountStateWithAddress:(NSString *)accountAddress; - (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 *)importKeyWithLocalPassword:(NSData *)localPassword mnemonicPassword:(NSData *)mnemonicPassword wordList:(NSArray *)wordList; -- (MTSignal *)deleteKeyWithPublicKey:(NSString *)publicKey; -- (MTSignal *)makeWalletInitialized:(TONKey *)key localPassword:(NSData *)localPassword; +- (MTSignal *)deleteKey:(TONKey *)key; - (MTSignal *)getTransactionListWithAddress:(NSString * _Nonnull)address lt:(int64_t)lt hash:(NSData * _Nonnull)hash; @end diff --git a/submodules/MtProtoKit/TON/TON.mm b/submodules/MtProtoKit/TON/TON.mm index 35f14ea149..6c483c6c9e 100644 --- a/submodules/MtProtoKit/TON/TON.mm +++ b/submodules/MtProtoKit/TON/TON.mm @@ -353,11 +353,11 @@ typedef enum { }] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]]; } -- (MTSignal *)getTestWalletAccountAddressWithPublicKey:(NSString *)publicKey { +- (MTSignal *)getWalletAccountAddressWithPublicKey:(NSString *)publicKey { return [[[[MTSignal alloc] initWithGenerator:^id(MTSubscriber *subscriber) { NSData *publicKeyData = [publicKey dataUsingEncoding:NSUTF8StringEncoding]; if (publicKeyData == nil) { - [subscriber putError:[[TONError alloc] initWithText:@"Error encoding UTF8 string in getTestWalletAccountAddressWithPublicKey"]]; + [subscriber putError:[[TONError alloc] initWithText:@"Error encoding UTF8 string in getWalletAccountAddressWithPublicKey"]]; return [[MTBlockDisposable alloc] initWithBlock:^{}]; } @@ -377,8 +377,8 @@ typedef enum { } }]; - auto query = make_object( - make_object( + auto query = make_object( + make_object( makeString(publicKeyData) ) ); @@ -389,89 +389,6 @@ typedef enum { }] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]]; } -- (MTSignal *)getTestGiverAccountState { - return [[[[MTSignal alloc] initWithGenerator:^id(MTSubscriber *subscriber) { - uint64_t requestId = _nextRequestId; - _nextRequestId += 1; - - _requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr &object) { - if (object->get_id() == tonlib_api::error::ID) { - auto error = tonlib_api::move_object_as(object); - [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; - } else if (object->get_id() == tonlib_api::testGiver_accountState::ID) { - auto result = tonlib_api::move_object_as(object); - TONTransactionId *lastTransactionId = nil; - if (result->last_transaction_id_ != nullptr) { - lastTransactionId = [[TONTransactionId alloc] initWithLt:result->last_transaction_id_->lt_ transactionHash:makeData(result->last_transaction_id_->hash_)]; - } - [subscriber putNext:[[TONAccountState alloc] initWithIsInitialized:true balance:result->balance_ seqno:result->seqno_ lastTransactionId:lastTransactionId syncUtime:result->sync_utime_]]; - [subscriber putCompletion]; - } else { - assert(false); - } - }]; - - auto query = make_object(); - _client->send({ requestId, std::move(query) }); - - return [[MTBlockDisposable alloc] initWithBlock:^{ - }]; - }] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]]; -} - -- (MTSignal *)getTestGiverAddress { - return [[[[MTSignal alloc] initWithGenerator:^id(MTSubscriber *subscriber) { - uint64_t requestId = _nextRequestId; - _nextRequestId += 1; - - _requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr &object) { - if (object->get_id() == tonlib_api::error::ID) { - auto error = tonlib_api::move_object_as(object); - [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; - } else if (object->get_id() == tonlib_api::accountAddress::ID) { - auto result = tonlib_api::move_object_as(object); - [subscriber putNext:[[NSString alloc] initWithUTF8String:result->account_address_.c_str()]]; - [subscriber putCompletion]; - } else { - assert(false); - } - }]; - - auto query = make_object(); - _client->send({ requestId, std::move(query) }); - - return [[MTBlockDisposable alloc] initWithBlock:^{ - }]; - }] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]]; -} - -- (MTSignal *)testGiverSendGramsWithAccountState:(TONAccountState *)accountState accountAddress:(NSString *)accountAddress amount:(int64_t)amount { - return [[[[MTSignal alloc] initWithGenerator:^id(MTSubscriber *subscriber) { - uint64_t requestId = _nextRequestId; - _nextRequestId += 1; - - _requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr &object) { - if (object->get_id() == tonlib_api::error::ID) { - auto error = tonlib_api::move_object_as(object); - [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; - } else { - [subscriber putCompletion]; - } - }]; - - auto query = make_object(make_object( - accountAddress.UTF8String), - accountState.seqno, - amount, - std::string() - ); - _client->send({ requestId, std::move(query) }); - - return [[MTBlockDisposable alloc] initWithBlock:^{ - }]; - }] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]]; -} - - (MTSignal *)getAccountStateWithAddress:(NSString *)accountAddress { return [[[[MTSignal alloc] initWithGenerator:^id(MTSubscriber *subscriber) { uint64_t requestId = _nextRequestId; @@ -489,16 +406,8 @@ typedef enum { } [subscriber putNext:[[TONAccountState alloc] initWithIsInitialized:false balance:result->account_state_->balance_ seqno:-1 lastTransactionId:lastTransactionId syncUtime:result->account_state_->sync_utime_]]; [subscriber putCompletion]; - } else if (object->get_id() == tonlib_api::generic_accountStateTestWallet::ID) { - auto result = tonlib_api::move_object_as(object); - TONTransactionId *lastTransactionId = nil; - if (result->account_state_->last_transaction_id_ != nullptr) { - lastTransactionId = [[TONTransactionId alloc] initWithLt:result->account_state_->last_transaction_id_->lt_ transactionHash:makeData(result->account_state_->last_transaction_id_->hash_)]; - } - [subscriber putNext:[[TONAccountState alloc] initWithIsInitialized:true balance:result->account_state_->balance_ seqno:result->account_state_->seqno_ lastTransactionId:lastTransactionId syncUtime:result->account_state_->sync_utime_]]; - [subscriber putCompletion]; - } else if (object->get_id() == tonlib_api::generic_accountStateTestGiver::ID) { - auto result = tonlib_api::move_object_as(object); + } else if (object->get_id() == tonlib_api::generic_accountStateWallet::ID) { + auto result = tonlib_api::move_object_as(object); TONTransactionId *lastTransactionId = nil; if (result->account_state_->last_transaction_id_ != nullptr) { lastTransactionId = [[TONTransactionId alloc] initWithLt:result->account_state_->last_transaction_id_->lt_ transactionHash:makeData(result->account_state_->last_transaction_id_->hash_)]; @@ -611,42 +520,6 @@ typedef enum { }] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]]; } -- (MTSignal *)makeWalletInitialized:(TONKey *)key localPassword:(NSData *)localPassword { - return [[[[MTSignal alloc] initWithGenerator:^id(MTSubscriber *subscriber) { - NSData *publicKeyData = [key.publicKey dataUsingEncoding:NSUTF8StringEncoding]; - if (publicKeyData == nil) { - [subscriber putError:[[TONError alloc] initWithText:@"Error encoding UTF8 string in exportKey"]]; - return [[MTBlockDisposable alloc] initWithBlock:^{}]; - } - - uint64_t requestId = _nextRequestId; - _nextRequestId += 1; - - _requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr &object) { - if (object->get_id() == tonlib_api::error::ID) { - auto error = tonlib_api::move_object_as(object); - [subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]]; - } else { - [subscriber putCompletion]; - } - }]; - - auto query = make_object( - make_object( - make_object( - makeString(publicKeyData), - makeSecureString(key.secret) - ), - makeSecureString(localPassword) - ) - ); - _client->send({ requestId, std::move(query) }); - - return [[MTBlockDisposable alloc] initWithBlock:^{ - }]; - }] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]]; -} - - (MTSignal *)importKeyWithLocalPassword:(NSData *)localPassword mnemonicPassword:(NSData *)mnemonicPassword wordList:(NSArray *)wordList { return [[[[MTSignal alloc] initWithGenerator:^id(MTSubscriber *subscriber) { uint64_t requestId = _nextRequestId; @@ -688,11 +561,11 @@ typedef enum { }] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]]; } -- (MTSignal *)deleteKeyWithPublicKey:(NSString *)publicKey { +- (MTSignal *)deleteKey:(TONKey *)key { return [[[[MTSignal alloc] initWithGenerator:^id(MTSubscriber *subscriber) { - NSData *publicKeyData = [publicKey dataUsingEncoding:NSUTF8StringEncoding]; + NSData *publicKeyData = [key.publicKey dataUsingEncoding:NSUTF8StringEncoding]; if (publicKeyData == nil) { - [subscriber putError:[[TONError alloc] initWithText:@"Error encoding UTF8 string in deleteKeyWithPublicKey"]]; + [subscriber putError:[[TONError alloc] initWithText:@"Error encoding UTF8 string in deleteKey"]]; return [[MTBlockDisposable alloc] initWithBlock:^{}]; } @@ -709,7 +582,10 @@ typedef enum { }]; auto query = make_object( - makeString(publicKeyData) + make_object( + makeString(publicKeyData), + makeSecureString(key.secret) + ) ); _client->send({ requestId, std::move(query) }); diff --git a/submodules/SSignalKit/SwiftSignalKit/SwiftSignalKit.xcodeproj/xcshareddata/xcschemes/SwiftSignalKit.xcscheme b/submodules/SSignalKit/SwiftSignalKit/SwiftSignalKit.xcodeproj/xcshareddata/xcschemes/SwiftSignalKit.xcscheme index 843e7441be..c82fe8764f 100644 --- a/submodules/SSignalKit/SwiftSignalKit/SwiftSignalKit.xcodeproj/xcshareddata/xcschemes/SwiftSignalKit.xcscheme +++ b/submodules/SSignalKit/SwiftSignalKit/SwiftSignalKit.xcodeproj/xcshareddata/xcschemes/SwiftSignalKit.xcscheme @@ -29,6 +29,8 @@ shouldUseLaunchSchemeArgsEnv = "YES"> + + + + + + + + deliverOnMainQueue).start(next: { wallets in - if let tonContext = context.tonContext { - if !wallets.wallets.isEmpty { - let _ = (testGiverWalletAddress(tonInstance: tonContext.instance) - |> deliverOnMainQueue).start(next: { address in - arguments.pushController(WalletInfoScreen(context: context, tonContext: tonContext, walletInfo: wallets.wallets[0].info, address: address)) - }) - } - } - }) - }) - case let .getGrams(theme): - return ItemListActionItem(theme: theme, title: "Update Wallet", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { - guard let context = arguments.context else { - return - } - let _ = (availableWallets(postbox: context.account.postbox) - |> deliverOnMainQueue).start(next: { wallets in - if let tonContext = context.tonContext { - if !wallets.wallets.isEmpty { - let _ = (walletAddress(publicKey: wallets.wallets[0].info.publicKey, tonInstance: tonContext.instance) - |> deliverOnMainQueue).start(next: { address in - let _ = (getGramsFromTestGiver(address: address, amount: 1500000000, tonInstance: tonContext.instance) - |> deliverOnMainQueue).start(completed: { - let presentationData = arguments.sharedContext.currentPresentationData.with { $0 } - let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .success) - arguments.presentController(controller, nil) - }) - }) - } - } - }) - }) case let .optimizeDatabase(theme): return ItemListActionItem(theme: theme, title: "Optimize Database", kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { guard let context = arguments.context else { @@ -599,8 +552,6 @@ private func debugControllerEntries(presentationData: PresentationData, loggingS entries.append(.resetData(presentationData.theme)) entries.append(.resetDatabase(presentationData.theme)) entries.append(.resetHoles(presentationData.theme)) - entries.append(.openDebugWallet(presentationData.theme)) - entries.append(.getGrams(presentationData.theme)) entries.append(.optimizeDatabase(presentationData.theme)) entries.append(.photoPreview(presentationData.theme, experimentalSettings.chatListPhotos)) entries.append(.knockoutWallpaper(presentationData.theme, experimentalSettings.knockoutWallpaper)) diff --git a/submodules/TelegramCore/TelegramCore/Wallets.swift b/submodules/TelegramCore/TelegramCore/Wallets.swift index b216bb67e0..b198a21183 100644 --- a/submodules/TelegramCore/TelegramCore/Wallets.swift +++ b/submodules/TelegramCore/TelegramCore/Wallets.swift @@ -186,7 +186,7 @@ public final class TonInstance { self.impl.with { impl in impl.withInstance { ton in - let cancel = ton.getTestWalletAccountAddress(withPublicKey: publicKey.rawValue).start(next: { address in + let cancel = ton.getWalletAccountAddress(withPublicKey: publicKey.rawValue).start(next: { address in guard let address = address as? String else { return } @@ -205,32 +205,7 @@ public final class TonInstance { } } - fileprivate func testGiverWalletAddress() -> Signal { - return Signal { subscriber in - let disposable = MetaDisposable() - - self.impl.with { impl in - impl.withInstance { ton in - let cancel = ton.getTestGiverAddress().start(next: { address in - guard let address = address as? String else { - return - } - subscriber.putNext(address) - subscriber.putCompletion() - }, error: { _ in - }, completed: { - }) - disposable.set(ActionDisposable { - cancel?.dispose() - }) - } - } - - return disposable - } - } - - private func getWalletStateRaw(address: String) -> Signal { + private func getWalletStateRaw(address: String) -> Signal { return Signal { subscriber in let disposable = MetaDisposable() @@ -242,6 +217,7 @@ public final class TonInstance { } subscriber.putNext(state) }, error: { _ in + subscriber.putError(.generic) }, completed: { subscriber.putCompletion() }) @@ -255,7 +231,7 @@ public final class TonInstance { } } - fileprivate func getWalletState(address: String) -> Signal<(WalletState, Int64), NoError> { + fileprivate func getWalletState(address: String) -> Signal<(WalletState, Int64), GetWalletStateError> { return self.getWalletStateRaw(address: address) |> map { state in return (WalletState(balance: state.balance, lastTransactionId: state.lastTransactionId.flatMap(WalletTransactionId.init(tonTransactionId:))), state.syncUtime) @@ -314,40 +290,20 @@ public final class TonInstance { } } - fileprivate func getTestGiverAccountState() -> Signal { - return Signal { subscriber in - let disposable = MetaDisposable() - - self.impl.with { impl in - impl.withInstance { ton in - let cancel = ton.getTestGiverAccountState().start(next: { state in - subscriber.putNext(state as? TONAccountState) - subscriber.putCompletion() - }, error: { _ in - subscriber.putNext(nil) - subscriber.putCompletion() - }, completed: { - }) - } - } - - return disposable + fileprivate func sendGramsFromWallet(keychain: TonKeychain, serverSalt: Data, walletInfo: WalletInfo, fromAddress: String, toAddress: String, amount: Int64, textMessage: String) -> Signal { + return keychain.decrypt(walletInfo.encryptedSecret) + |> mapError { _ -> SendGramsFromWalletError in + return .secretDecryptionFailed } - } - - fileprivate func getGramsFromTestGiver(address: String, amount: Int64) -> Signal { - return self.getTestGiverAccountState() - |> castError(GetGramsFromTestGiverError.self) - |> mapToSignal { state in - guard let state = state else { - return .fail(.generic) - } + |> mapToSignal { decryptedSecret -> Signal in + let key = TONKey(publicKey: walletInfo.publicKey.rawValue, secret: decryptedSecret) 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.sendGrams(from: key, localPassword: serverSalt, fromAddress: fromAddress, toAddress: toAddress, amount: amount, textMessage: textMessage).start(next: { _ in + preconditionFailure() }, error: { _ in subscriber.putError(.generic) }, completed: { @@ -364,104 +320,6 @@ public final class TonInstance { } } - private enum MakeWalletInitializedError { - case generic - } - - private func makeWalletInitialized(key: TONKey, serverSalt: Data) -> Signal { - return Signal { subscriber in - let disposable = MetaDisposable() - - self.impl.with { impl in - impl.withInstance { ton in - let cancel = ton.makeWalletInitialized(key, localPassword: serverSalt).start(next: { _ in - preconditionFailure() - }, error: { _ in - subscriber.putError(.generic) - }, completed: { - subscriber.putCompletion() - }) - disposable.set(ActionDisposable { - cancel?.dispose() - }) - } - } - - return disposable - } - } - - private func ensureWalletInitialized(address: String, key: TONKey, serverSalt: Data) -> Signal { - return self.getWalletStateRaw(address: address) - |> castError(MakeWalletInitializedError.self) - |> mapToSignal { state -> Signal in - if state.isInitialized { - return .complete() - } else { - let poll: Signal = self.getWalletStateRaw(address: address) - |> castError(MakeWalletInitializedError.self) - |> mapToSignal { state -> Signal in - if state.isInitialized { - return .single(Void()) - } else { - return .complete() - } - } - - let pollUntilInitialized = ( - poll - |> then(.complete() - |> delay(2.0, queue: Queue.concurrentDefaultQueue())) - ) - |> restart - |> take(1) - - return self.makeWalletInitialized(key: key, serverSalt: serverSalt) - |> then( - pollUntilInitialized - |> ignoreValues - ) - } - } - } - - fileprivate func sendGramsFromWallet(keychain: TonKeychain, serverSalt: Data, walletInfo: WalletInfo, fromAddress: String, toAddress: String, amount: Int64, textMessage: String) -> Signal { - return keychain.decrypt(walletInfo.encryptedSecret) - |> mapError { _ -> SendGramsFromWalletError in - return .secretDecryptionFailed - } - |> mapToSignal { decryptedSecret -> Signal in - let key = TONKey(publicKey: walletInfo.publicKey.rawValue, secret: decryptedSecret) - - return self.ensureWalletInitialized(address: fromAddress, key: key, serverSalt: serverSalt) - |> mapError { _ -> SendGramsFromWalletError in - return .generic - } - |> then( - Signal { subscriber in - let disposable = MetaDisposable() - - self.impl.with { impl in - impl.withInstance { ton in - let cancel = ton.sendGrams(from: key, localPassword: serverSalt, fromAddress: fromAddress, toAddress: toAddress, amount: amount, textMessage: textMessage).start(next: { _ in - preconditionFailure() - }, error: { _ in - subscriber.putError(.generic) - }, completed: { - subscriber.putCompletion() - }) - disposable.set(ActionDisposable { - cancel?.dispose() - }) - } - } - - return disposable - } - ) - } - } - fileprivate func walletRestoreWords(walletInfo: WalletInfo, keychain: TonKeychain, serverSalt: Data) -> Signal<[String], WalletRestoreWordsError> { return keychain.decrypt(walletInfo.encryptedSecret) |> mapError { _ -> WalletRestoreWordsError in @@ -494,6 +352,35 @@ public final class TonInstance { } } } + + fileprivate func deleteLocalWalletData(walletInfo: WalletInfo, keychain: TonKeychain, serverSalt: Data) -> Signal { + return keychain.decrypt(walletInfo.encryptedSecret) + |> mapError { _ -> DeleteLocalWalletDataError in + return .secretDecryptionFailed + } + |> mapToSignal { decryptedSecret -> Signal in + return Signal { subscriber in + let disposable = MetaDisposable() + + self.impl.with { impl in + impl.withInstance { ton in + let cancel = ton.delete(TONKey(publicKey: walletInfo.publicKey.rawValue, secret: decryptedSecret)).start(next: { _ in + assertionFailure() + }, error: { _ in + subscriber.putError(.generic) + }, completed: { + subscriber.putCompletion() + }) + disposable.set(ActionDisposable { + cancel?.dispose() + }) + } + } + + return disposable + } + } + } } public struct WalletPublicKey: Codable, Hashable { @@ -663,15 +550,35 @@ public func importWallet(postbox: Postbox, network: Network, tonInstance: TonIns } } -public func debugDeleteWallets(postbox: Postbox) -> Signal { - return postbox.transaction { transaction -> Void in - transaction.updatePreferencesEntry(key: PreferencesKeys.walletCollection, { current in - var walletCollection = (current as? WalletCollection) ?? WalletCollection(wallets: []) - walletCollection.wallets = [] - return walletCollection - }) +public enum DeleteLocalWalletDataError { + case generic + case secretDecryptionFailed +} + +public func deleteLocalWalletData(postbox: Postbox, network: Network, tonInstance: TonInstance, keychain: TonKeychain, walletInfo: WalletInfo) -> Signal { + return getServerWalletSalt(network: network) + |> mapError { _ -> DeleteLocalWalletDataError in + return .generic + } + |> mapToSignal { serverSalt -> Signal in + return tonInstance.deleteLocalWalletData(walletInfo: walletInfo, keychain: keychain, serverSalt: serverSalt) + |> then( + postbox.transaction { transaction -> Void in + transaction.updatePreferencesEntry(key: PreferencesKeys.walletCollection, { current in + var walletCollection = (current as? WalletCollection) ?? WalletCollection(wallets: []) + for i in 0 ..< walletCollection.wallets.count { + if walletCollection.wallets[i].info.publicKey == walletInfo.publicKey { + walletCollection.wallets.remove(at: i) + break + } + } + return walletCollection + }) + } + |> castError(DeleteLocalWalletDataError.self) + |> ignoreValues + ) } - |> ignoreValues } public enum WalletRestoreWordsError { @@ -703,11 +610,11 @@ public func walletAddress(publicKey: WalletPublicKey, tonInstance: TonInstance) return tonInstance.walletAddress(publicKey: publicKey) } -public func testGiverWalletAddress(tonInstance: TonInstance) -> Signal { - return tonInstance.testGiverWalletAddress() +private enum GetWalletStateError { + case generic } -private func getWalletState(address: String, tonInstance: TonInstance) -> Signal<(WalletState, Int64), NoError> { +private func getWalletState(address: String, tonInstance: TonInstance) -> Signal<(WalletState, Int64), GetWalletStateError> { return tonInstance.getWalletState(address: address) } @@ -738,7 +645,9 @@ public func getCombinedWalletState(postbox: Postbox, walletInfo: WalletInfo, ton |> castError(GetCombinedWalletStateError.self) |> mapToSignal { address -> Signal in return getWalletState(address: address, tonInstance: tonInstance) - |> castError(GetCombinedWalletStateError.self) + |> mapError { _ -> GetCombinedWalletStateError in + return .generic + } |> mapToSignal { walletState, syncUtime -> Signal in let topTransactions: Signal<[WalletTransaction], GetCombinedWalletStateError> if walletState.lastTransactionId == cachedState?.walletState.lastTransactionId { @@ -772,14 +681,6 @@ public func getCombinedWalletState(postbox: Postbox, walletInfo: WalletInfo, ton } } -public enum GetGramsFromTestGiverError { - case generic -} - -public func getGramsFromTestGiver(address: String, amount: Int64, tonInstance: TonInstance) -> Signal { - return tonInstance.getGramsFromTestGiver(address: address, amount: amount) -} - public enum SendGramsFromWalletError { case generic case secretDecryptionFailed diff --git a/submodules/TelegramUI/Images.xcassets/Wallet/RefreshIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Wallet/RefreshIcon.imageset/Contents.json new file mode 100644 index 0000000000..f35b36343a --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Wallet/RefreshIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "idiom" : "universal", + "filename" : "􀊯 2.pdf" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Wallet/RefreshIcon.imageset/􀊯 2.pdf b/submodules/TelegramUI/Images.xcassets/Wallet/RefreshIcon.imageset/􀊯 2.pdf new file mode 100644 index 0000000000000000000000000000000000000000..d557394c0a8fa994116b98b73e859fc34a203773 GIT binary patch literal 171124 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%0AbD+ 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 znES�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
      6)0s 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 literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/TelegramUI/ChatControllerNode.swift b/submodules/TelegramUI/TelegramUI/ChatControllerNode.swift index 4ccaa6a4e9..a78fafa318 100644 --- a/submodules/TelegramUI/TelegramUI/ChatControllerNode.swift +++ b/submodules/TelegramUI/TelegramUI/ChatControllerNode.swift @@ -600,7 +600,7 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate { var effectiveInputNodeHeight: CGFloat? if let inputNodeHeightAndOverflow = inputNodeHeightAndOverflow { if let upperInputPositionBound = self.upperInputPositionBound { - effectiveInputNodeHeight = min(layout.size.height - max(0.0, upperInputPositionBound), inputNodeHeightAndOverflow.0) + effectiveInputNodeHeight = max(0.0, min(layout.size.height - max(0.0, upperInputPositionBound), inputNodeHeightAndOverflow.0)) } else { effectiveInputNodeHeight = inputNodeHeightAndOverflow.0 } diff --git a/submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/SendingGrams.tgs b/submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/SendingGrams.tgs new file mode 100644 index 0000000000000000000000000000000000000000..9876c9658e3fb856a90b928440ad551d7e8569db GIT binary patch literal 27574 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%0A@7#b5?kB)XD01 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 literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletEmpty.tgs b/submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletEmpty.tgs new file mode 100644 index 0000000000000000000000000000000000000000..b33b54599fdf7c865626699d19ce0188307bb2e7 GIT binary patch literal 20243 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 literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletWordCheck.tgs b/submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletWordCheck.tgs new file mode 100644 index 0000000000000000000000000000000000000000..9008fc51e24d13477501617ca6f4453c5721df11 GIT binary patch literal 12985 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 literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletWordList.tgs b/submodules/TelegramUI/TelegramUI/Resources/WalletAnimations/WalletWordList.tgs new file mode 100644 index 0000000000000000000000000000000000000000..278b1531b6778a50511b38fd3e20414179d2498e GIT binary patch literal 7178 zcmV+l9QETLiwFP!000021MOYwmfJRxew97?O>t4U!>{7wAKra z`lMJ6*YNY_-+z(^KmNIS=eo_qH#zzzpY+$}4}5%-)Beey-@DD%N+F@} zZ9aqWfBa(~W9l}4*1!1FKZakNbM+Vgi+_2PJE9quV(0jO!)<%y3*MW~=)vdV;Cnas z6q>kcd?onLr4Db44!a*lV-z=^NhS6D`(=WUANsV{IBZ|2eqn~^4GkDx|l--Nl z9pfGY#&&OK@>$$hsTVVzKVUp}Eyg=w8gqp2&bdd>dxV$VgPJ6J*`q$zQUrlkF*z+y?Wicf_I+YF|3q;|HP_mn*8MxU)k z9shgSs&4ezT3Ds#NZznaGR8!yH--&LqS6OOyfpaZ{z<;bIX?8>%Ne`YJ^cFd>nC-(@!-ub-`G3< z{HOr=U0WtUJp8(8O&D%0UH$&Os89L)AiX-F{2LTo`eJkEd~XvQeD6K=iBe~6@3X~O z|Jc0y`Qw*|PiWh(KedCdHj6t=PT70)Zht6;Uw8<5SZ{I7h;0L(q> z5nV9pW;5HbW}mZKyG+B!pu=}seXgYs|2#PD;p2q#{lgP2zSf`>*E_gEg}DcVR#NX- z0dJk_*@a(6?LJ41x!tjJCEsU=viHIDK3aacKDo3G+H!+-@hEO{Z!EFQ=8-A34qwOm zx(_CGEF!a=9px>gr|)oD7_J%*{noLhx2*I-WMzPxY@_RQ;?a+xcS=bNgvg^AGUFoD zb3*ROakFw`>&I{ZIs+iAUvswnfRa)vtVONSe{9Kel(Ki^N{abZ80CE!u>$<TQUfWhbz}){Chx#HetQ2Pano+(RL#tl&$0 z!6meU;$A^PJV+9};o+rW==Bi3K7M@`HT66_e{`Gi=j4_Hup%3Yz)di06MCHJ1E0a( zARCg=LjsZUeQ-`F$>JRL7X#ax+rX}eSg6FlNQ^}+R=Ek^Hu4pP{m4rV&5-!Eu{LoFriUx|E(c$zkjV3>vAvD4<1aSqcm7N45hlY|dRC0#7MY=dZx;tLn>IYI zTOQ`(_CRW~a>Ga=vcf}FH1`R?Jth!i6(Kx>>?2D$-zUJ4B0tJkJ1GI}7BWyNd^KD# zzL__27t2l=i_~r6tF40Avy9^oiJmnqRTvw?SHaaz=~*EE{ACu%Dmb05&=sjkl*8fS zQ6y_*@E-7L=~i&fePETxpFWIW#7BTe7~%7%oYXsP_ir)&+`HWlfL5<#$>@q$TpsF= zE%T6i9#xT%ECA&=pdF?3A;NOUvHb2Wi$WIBEJ{hh#y9l=+~?cFE`U~!@=x-SfF&P{ z$i=`)dV2T4^->kz`tHLfQbg;*NID1wBUk1ZOud5W;QjeRa2nYLu}rKZXP9^X3jFCF@Q8Gaz- z6ZOeu$<7(EHi&)v>_W#DtqtKVI{WizOlrUc?+u!iSn!3I*|2vocC!5F4Y7sugKC~d z@X0u{7M@rtJNJx*2-}QIG-2c!_LO7Cf+*AIMEFpA-j2ja44jV9NqO`X30j)!YaKVC zddt2ZKKuz?UUj$lg$lT%oOHrL?g zLk^v=*BsOA0bZ;MFIL5AU?me6gs@cD4YvF?TBFX0-hTShL!9oaTm~nya7c zPf~BJ*04+_^db0F=G<8395L)>eV(U`uIUt~rA?XzU~>=+iyGW#p%%PAy!tGW)`;us zf~+pcHC>RSa(zE+P^ToO;OvhSpqv$xr>~p z^Q_H0KUK2g$ZAH%^20OZnoG0dg;bLU=U{yoO;y@huySSp@GWG;Z-%G0J;!_=8&*Yj z4k>^G4h}`YQ*v1Cuo_v<8(C255IH+6%mPG+8MDXPlJHNNDYNFx5zA*b+q{A>k447037P(CGI1DDadzSme`}U*RudA`P8f&YucHhR@5gzwMP*%*-GWABRGalBQ zi|8>4i8JH#u&fcb`)l!sGpm!tgX$#VNExfSA~ShcSVjKcFTp}XDH#2`mgLK|Zq7mB zoQv*utOngBH)9zvzT3E57+U+`K1+CQ7pdX3NC~g)A~n1gDdDwUq=wfbCA_wa)bLuQ zgx7YF8eWT(@Y*g?!)uWeUfV@#c%53p3#JE_zD0|P2(wyb8?(%`B6tgh7OC$vQ#vyb zH72vvnA}xaq@gY)NnOfXwW?kJUcG*-_3Mk8QI_8(WSO{U2 z!?8u(pNPD0VM`vZvflPMBk@zM9EU!8wheRQ7e;qltPLp$gv4GP1dEYkM6vDaw2Lv- zI|_kJb3_BbE1XqJdr8?zWzMs36PCbD8sWysF%N*C;a+gVZ=6ULrWT_61rACDM1_v= zrBW#Hdt`QRLb@yH#1}%xmNw);f|K_G9k~2P+G(Z7-w1ZNpE0ol*oo4?prG(O#Eus~ z7gsstof$hN-4r}zuZtc&ijmPnQPo$pvUn!l96a1GENv%5C8?svBs3r^TwVb^C2}V+ z!J$57(mI2DNUkrsoHdE^r-YD&)eBta4hV>Gm7C(`rG^fe0*kiVo2NW*IhW`wjg5vBE`UxbPA0cU4YOth>|891 zV28~c7bM#vOB2sW4~l>gF35syJ_cM6_@%4qEW}h9J(lkl^_U;tS;I!XRM=tyV#i3n z7>Tl4kxw0}h!m|J;-!=l=3E9+o=h&vaSrEUXTE;@9B$GDxY1C#uai2@#0`>=(KM;x zF_n^JA^8Hh(W1*#&CBy~1LM=lyOFK9i7H)1AY27F)PUowtwRktwwgNBkYlT*Lk&5Z z6_-|!VZp@5NDBCCNu48`P8>r{>VromoZi~%%#!kC#f?M7ntj+1jbTG!fQ^x*8zbBp znWCk*A=IEe6E~t)F!@^U!2N2{7&f(NP=k%}6VzZD`4}}O<`HTz>x4$Av8bkYCThU? zWrP}LJ8$C%Hb@+^2eE@qP2TNb1A?<7*r2by9l?eP3FSxz+yL+;U0++-XY6AJcn(GS zSjUTp(>meI=!U7}D48^d55MdPKHiU{j**pEQpXS-2=D}>M<)0)!J{=2_(*D@WT}Y9 zSCl&Vuo=cO2PLle&?5yF2J|?jG0_5RtIRQ0ES;do%-X1IekOWMN+a|@QJ1l@V&*J3 zfsvrz6zcjhnhYFqQbKm>hwGviR$s;G{M;Y}bVpF|6iLo<-=|lwbgWx#8vrGi_w$UB*dD8xBxhA&0ej*xd=FG`f5#IU5cGGf3?~=m(RB_*Un$LY~f?on4ps) z2lCUceXL;B$0DhA3G^V?1AK&6LJx80DSD)ql}xTRiXO@}Yypa(C$R^wm45ju53sPm zm1QFE?a;GkKX2!u=Oo*8?XsUtkfkd_{u7ptK&RzDGxq!|_mKbeYyR_g$nhEZHYPCl zksc@5J0{fvA54facVaV@J!1{e@O%rkoo@!5HT`+p^e0UE0PwWHAPUG3&_lbo@&@Q> zfk7m#u*k%g zB_>4gWZbi?T_x=HV`aWhl8s$%l>rTF2K0s*P^!~)s+A0GK=C$aW<-BoJ4ihDh>fqiE^h~1IJ@)FNAeM3krf@>%K{^;+PkHAJ$mh za%bw6o6*A3VR<5Z@#G>KKw;rkD0ncIaF;BpbC+-Gn1?lWe#>yOQ_p-9PIl^>kHX1L zo%2yRnIBYh5jPGehc$J+WP@ zB@Ha^WDu9Q10O4CRL5x60-5uI2U=TRD|60LyTCa|5oxO{WeQ_j13@+V$rKC!JWCm{ zdX+C&|HxM7E%>sFIgG`U*cdfW_+;Cs_1V#WQNJ=7%lUV*0aDy6r zz>Q34!F?4sIcRPq28E3+6$lsrBtX+^P1iw{`;a8A6C{kon#=G*B-qpQAa#`}#8wf4)`46;?U~|pX;0%M?a6qr z&UTh$U?{rbmhQmC9quZ3dzS?=Eq5rUjTRM!Cg_GiFp~8Jd^NT#Vu27IJw5H22!^&- z?*hfYkpzvtJQ>R@1PP(v zSYzobWvmEfWa+G=JB{>RfCZ?@PO9}dQO`M|T&eEj&c?ECkPxI!DEEUALSUJRI~1g7 zRm4ba`Fy-QFJhjF8V9Kh&f}I+?MTTk@Vht844RsBu}x24%gxoQrA8(OFnX#-_$aw18#5m>BbwG4n|4|Z zEl;9+gBFG*DRK)4l+yuI&`?2uuL3(WdNqTYY8Z*fwun%1rscn(RtiA@H6ib#$eET# z=igu+dn6;;9!-QCIrd|*O*IgB$s^}wm&8uR%`-j}$=Xz5|2iC2hL>#3W@hbPA6u5H zV8VmubtOM7`(Bq&%MJ@td^1EzwrQ-44=EwjV*IJVLADP*nNG_!ve4Evd&I~bgH1=Tr(@2}-c|R83}V886u@((nOO0_rqJ)DY&CaFl$H_wX6gpErYS$8*fK_;VA`~@QnzK2W)E`!jnUT_1f12^ zZ1y@ZjtAD)Vnd^vSi5@gn7|OE_em8QVr|&^f`5tXFERzwOhAz>=s=))O4dg+1yf68 zpcQy5JqiU$dDorZV7Dm2>O30|oKi@^4T{A3U>vpb^j?ZnvPqNo>csP(zlzMz!no^- zx*s_$P_gjgoDDH_3k;u)?1*u2D22tjvHnLWEPig8?sQP5J5S1VPpiygGsz@Q7D8*- z6kx8-EXyFV|F;OdQE+!rX_h9XN#=KtePZj`r_WTj1G8XJ#dWAWX}A~Q&q?qV*jg~ zITV@W9Gl+fZarmn>nZz$G@9SERQ~-@NiZlZr7_3Pkj6q_%HEf4A$L$=_()*Wcl)2i+dmv%cz)n7c?AwG||9vsKO)eY42zS{e zpRyZ|6&W(Y*Vc)y6o7r22q`aa8T${imr<2}e-GUPDSn!6DbMScuO}GBZ^V2qW;H9% zH!ELI_Sb}dGQ0MuEG5J^Wv|Q{jnWXdarRmHdC-Makhj6!Mmyn|S*Vo`fumgeDvH}5 zL+?`>C@Bj?f`$;UMKU=VS+*u|i%*r58!2a3Np`WmBZl3DT}L$#n9l7w%WvYlAQ79^ zbvAmb_6dzby2~4SDque~RuHMKA;jJ-SoxT-WGS?+N&pr7ZRtJhN#avN(wGuRZaB+_ zx$WHU2&{2;v8kvKDlPDedmPSW6{mkv)AHIPaNJgQ!cgjWJ#ka&jQ;;8oPf# zp}D=y-(Jg=e+es2W}&^D6^SrUEmp>xIhLtbX<)>5JePU)3p3)bEj)a(YwrwY)IPap zu6-*b-VY--Win>Ow*S*xV#K9&VnleN7H}zkddN0=kO?w+ay^`5!sWDKW;Y@18p7UI zaQ@(UXqlcq*RqxU?nn=&89kWa9Q%2{F#FlD+Vj&xx32`>{0Wj(hZ&xK=_)PMn#c88X*@zR^*<|w1~ z$u)EBTbc03V#2pQ7vySb=*c3r<@BYb*kjlWmnsdbd**0E5OsIy@i^~-QD}V}rM*^R zr?%{8Nt1Ts!ZqHzuz)!f2<2{+^2y8;{qo$ji+NVYnI{c#(A|Z{L$wQrkq4F-HOiK{ zgyVoRX(cXO1Ga0x_BB}Z3W{!sg;q8^Bn;8f;hN*2+jIp5w`-8ky$$)0FlixQvxac5 ztJ?nFA>5N0(z)ds(oXbZo}st{MR`1&>;3X9xoeTdYC(TUa0g0q4eQ=HOWt)%>2#L7 zhBG*XOPfxdp|l9ZNyb;p!dF+(#$X=y*;M90#6PB)XIT= zmNW!acNZRy`_dIu<)~4Zn!Ml4Mwzq{m#xv?yXMStXuy{IqsD&EEiV9f9Ixm`6OfNA zzTGwD+Y^Ams+<#7;}$zzjGOAX*;GgF>$#~dSISu0wMSTs0G5Yr*gXEB_lhl5ip z`C(J&-QCGn`Omr=yMNl!7cBZc`61k*nP=<^>|up+cD*&WOSX40Jo}KxHu`8DDhwG} zs5}Pek=4}Q=(COG-sJsZJiF0n8{67|aqXar{%(ffPk)@0iK-tn&mvpx<7B$DdlY_& zbIlk}ia(EG?BR@kdS=GHtc1~ZL5}~(ud`x&;njq1ea-o6E%b9WE3e&Kq&H9x4F=k|ogrn9@~$`dI-_@*>x?s2l$~06X8aP-YLj(q zYoXMaAl*3;lvF9DQDlESpgNUyPSkVETdj`rw=Jr$3fxuTt^)T57r1MUko#VN`{Dcl M14@yr?F0V+0DgN3IRF3v literal 0 HcmV?d00001 diff --git a/submodules/WalletUI/Sources/WalletInfoEmptyNode.swift b/submodules/WalletUI/Sources/WalletInfoEmptyNode.swift index e4411116b0..e2b570db60 100644 --- a/submodules/WalletUI/Sources/WalletInfoEmptyNode.swift +++ b/submodules/WalletUI/Sources/WalletInfoEmptyNode.swift @@ -6,15 +6,18 @@ import TelegramPresentationData import TelegramCore import AnimationUI import SwiftSignalKit +import AppBundle class WalletInfoEmptyItem: ListViewItem { + let account: Account let theme: PresentationTheme let strings: PresentationStrings let address: String let selectable: Bool = false - init(theme: PresentationTheme, strings: PresentationStrings, address: String) { + init(account: Account, theme: PresentationTheme, strings: PresentationStrings, address: String) { + self.account = account self.theme = theme self.strings = strings self.address = address @@ -22,7 +25,7 @@ class WalletInfoEmptyItem: ListViewItem { func nodeConfiguredForParams(async: @escaping (@escaping () -> Void) -> Void, params: ListViewItemLayoutParams, synchronousLoads: Bool, previousItem: ListViewItem?, nextItem: ListViewItem?, completion: @escaping (ListViewItemNode, @escaping () -> (Signal?, (ListViewItemApply) -> Void)) -> Void) { async { - let node = WalletInfoEmptyItemNode() + let node = WalletInfoEmptyItemNode(account: self.account) node.insets = UIEdgeInsets() node.layoutForParams(params, item: self, previousItem: previousItem, nextItem: nextItem) Queue.mainQueue().async { @@ -54,21 +57,20 @@ class WalletInfoEmptyItem: ListViewItem { final class WalletInfoEmptyItemNode: ListViewItemNode { private let offsetContainer: ASDisplayNode - private let iconNode: ASImageNode private let animationNode: AnimatedStickerNode private let titleNode: TextNode private let textNode: TextNode private let addressNode: TextNode - init() { + init(account: Account) { self.offsetContainer = ASDisplayNode() - self.iconNode = ASImageNode() - self.iconNode.displayWithoutProcessing = true - self.iconNode.displaysAsynchronously = false - self.iconNode.image = UIImage(bundleImageName: "Wallet/DuckIcon") - self.animationNode = AnimatedStickerNode() + if let path = getAppBundle().path(forResource: "WalletEmpty", ofType: "tgs") { + self.animationNode.setup(account: account, resource: .localFile(path), width: 280, height: 280, mode: .direct) + self.animationNode.visibility = true + } + self.animationNode.visibility = true self.titleNode = TextNode() self.titleNode.displaysAsynchronously = false @@ -79,7 +81,6 @@ final class WalletInfoEmptyItemNode: ListViewItemNode { self.wantsTrailingItemSpaceUpdates = true - self.offsetContainer.addSubnode(self.iconNode) self.offsetContainer.addSubnode(self.animationNode) self.offsetContainer.addSubnode(self.titleNode) self.offsetContainer.addSubnode(self.textNode) @@ -101,8 +102,8 @@ final class WalletInfoEmptyItemNode: ListViewItemNode { let termsSpacing: CGFloat = 11.0 let buttonHeight: CGFloat = 50.0 - let iconSize: CGSize - iconSize = self.iconNode.image?.size ?? CGSize(width: 140.0, height: 140.0) + let iconSize = CGSize(width: 140.0, height: 140.0) + self.animationNode.updateLayout(size: iconSize) let makeTitleLayout = TextNode.asyncLayout(self.titleNode) let makeTextLayout = TextNode.asyncLayout(self.textNode) @@ -145,7 +146,7 @@ final class WalletInfoEmptyItemNode: ListViewItemNode { let transition: ContainedViewLayoutTransition = .immediate - transition.updateFrameAdditive(node: strongSelf.iconNode, frame: iconFrame) + transition.updateFrameAdditive(node: strongSelf.animationNode, frame: iconFrame) strongSelf.animationNode.updateLayout(size: iconFrame.size) transition.updateFrameAdditive(node: strongSelf.animationNode, frame: iconFrame) transition.updateFrameAdditive(node: strongSelf.titleNode, frame: titleFrame) diff --git a/submodules/WalletUI/Sources/WalletInfoScreen.swift b/submodules/WalletUI/Sources/WalletInfoScreen.swift index fd3a253f1c..0ec3c238c6 100644 --- a/submodules/WalletUI/Sources/WalletInfoScreen.swift +++ b/submodules/WalletUI/Sources/WalletInfoScreen.swift @@ -8,63 +8,10 @@ import Display import Postbox import TelegramCore import SolidRoundedButtonNode -import AnimationUI import SwiftSignalKit import MergeLists import TelegramStringFormatting -private func stringForRelativeUpdateTime(strings: PresentationStrings, day: RelativeTimestampFormatDay, dateTimeFormat: PresentationDateTimeFormat, hours: Int32, minutes: Int32) -> String { - let dayString: String - switch day { - case .today: - dayString = strings.Updated_TodayAt(stringForShortTimestamp(hours: hours, minutes: minutes, dateTimeFormat: dateTimeFormat)).0 - case .yesterday: - dayString = strings.Updated_YesterdayAt(stringForShortTimestamp(hours: hours, minutes: minutes, dateTimeFormat: dateTimeFormat)).0 - } - return dayString -} - -private func lastUpdateTimestampString(strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, statusTimestamp: Int32, relativeTo timestamp: Int32) -> String { - let difference = timestamp - statusTimestamp - let expanded = true - if difference < 60 { - return strings.Updated_JustNow - } else if difference < 60 * 60 && !expanded { - let minutes = difference / 60 - return strings.Updated_MinutesAgo(minutes) - } else { - var t: time_t = time_t(statusTimestamp) - var timeinfo: tm = tm() - localtime_r(&t, &timeinfo) - - var now: time_t = time_t(timestamp) - var timeinfoNow: tm = tm() - localtime_r(&now, &timeinfoNow) - - if timeinfo.tm_year != timeinfoNow.tm_year { - return strings.Updated_AtDate(stringForTimestamp(day: timeinfo.tm_mday, month: timeinfo.tm_mon + 1, year: timeinfo.tm_year, dateTimeFormat: dateTimeFormat)).0 - } - - let dayDifference = timeinfo.tm_yday - timeinfoNow.tm_yday - if dayDifference == 0 || dayDifference == -1 { - let day: RelativeTimestampFormatDay - if dayDifference == 0 { - if expanded { - day = .today - } else { - let minutes = difference / (60 * 60) - return strings.Updated_HoursAgo(minutes) - } - } else { - day = .yesterday - } - return stringForRelativeUpdateTime(strings: strings, day: day, dateTimeFormat: dateTimeFormat, hours: timeinfo.tm_hour, minutes: timeinfo.tm_min) - } else { - return strings.Updated_AtDate(stringForTimestamp(day: timeinfo.tm_mday, month: timeinfo.tm_mon + 1, year: timeinfo.tm_year, dateTimeFormat: dateTimeFormat)).0 - } - } -} - public final class WalletInfoScreen: ViewController { private let context: AccountContext private let tonContext: TonContext @@ -132,6 +79,11 @@ public final class WalletInfoScreen: ViewController { return } strongSelf.push(walletTransactionInfoController(context: strongSelf.context, tonContext: strongSelf.tonContext, walletInfo: strongSelf.walletInfo, walletTransaction: transaction)) + }, present: { [weak self] c in + guard let strongSelf = self else { + return + } + strongSelf.present(c, in: .window(.root)) }) self.displayNodeDidLoad() @@ -147,22 +99,39 @@ public final class WalletInfoScreen: ViewController { } private final class WalletInfoBalanceNode: ASDisplayNode { - let balanceTextNode: ImmediateTextNode + let balanceIntegralTextNode: ImmediateTextNode + let balanceFractionalTextNode: ImmediateTextNode let balanceIconNode: ASImageNode var balance: String = " " { didSet { - self.balanceTextNode.attributedText = NSAttributedString(string: self.balance, font: Font.bold(39.0), textColor: .white) + let integralString = NSMutableAttributedString() + let fractionalString = NSMutableAttributedString() + if let range = self.balance.range(of: ".") { + let integralPart = String(self.balance[.. CGFloat { + func update(width: CGFloat, scaleTransition: CGFloat, transition: ContainedViewLayoutTransition) -> CGFloat { let sideInset: CGFloat = 16.0 let balanceIconSpacing: CGFloat = 8.0 - let balanceTextSize = self.balanceTextNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: 200.0)) + let balanceIntegralTextSize = self.balanceIntegralTextNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: 200.0)) + let balanceFractionalTextSize = self.balanceFractionalTextNode.updateLayout(CGSize(width: width - sideInset * 2.0, height: 200.0)) let balanceIconSize = self.balanceIconNode.image?.size ?? CGSize(width: 38.0, height: 34.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 fractionalScale: CGFloat = scaleTransition * 0.5 + (1.0 - scaleTransition) * 1.0 + + let balanceOrigin = CGPoint(x: floor((width - balanceIntegralTextSize.width - balanceFractionalTextSize.width * fractionalScale - balanceIconSpacing - balanceIconSize.width / 2.0) / 2.0), y: 0.0) + + let balanceIntegralTextFrame = CGRect(origin: balanceOrigin, size: balanceIntegralTextSize) + var balanceFractionalTextFrame = CGRect(origin: CGPoint(x: balanceIntegralTextFrame.maxX, y: balanceIntegralTextFrame.maxY - balanceFractionalTextSize.height), size: balanceFractionalTextSize) + let apparentBalanceFractionalTextFrame = CGRect(origin: balanceFractionalTextFrame.origin, size: CGSize(width: balanceFractionalTextFrame.width * fractionalScale, height: balanceFractionalTextFrame.height * fractionalScale)) + balanceFractionalTextFrame.origin.x -= balanceFractionalTextFrame.width / 2.0 * (1.0 - fractionalScale) + balanceFractionalTextFrame.origin.y += balanceFractionalTextFrame.height / 4.0 * (1.0 - fractionalScale) + let balanceIconFrame: CGRect - 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) + balanceIconFrame = CGRect(origin: CGPoint(x: apparentBalanceFractionalTextFrame.maxX + balanceIconSpacing, y: balanceIntegralTextFrame.minY + floor((balanceIntegralTextFrame.height - balanceIconSize.height) / 2.0)), size: balanceIconSize) + + transition.updateFrameAdditive(node: self.balanceIntegralTextNode, frame: balanceIntegralTextFrame) + transition.updateFrameAsPositionAndBounds(node: self.balanceFractionalTextNode, frame: balanceFractionalTextFrame) + transition.updateTransformScale(node: self.balanceFractionalTextNode, scale: fractionalScale) transition.updateFrame(node: self.balanceIconNode, frame: balanceIconFrame) - return balanceTextSize.height + return balanceIntegralTextSize.height } } private final class WalletInfoHeaderNode: ASDisplayNode { var balance: Int64? var isRefreshing: Bool = false - - var timestampString: String = "" { - didSet { - self.balanceTimestampNode.attributedText = NSAttributedString(string: self.timestampString, font: Font.regular(13), textColor: UIColor(white: 1.0, alpha: 0.6)) - } - } + var timestamp: Int32? let balanceNode: WalletInfoBalanceNode - private let refreshNode: AnimatedStickerNode + private let refreshNode: WalletRefreshNode private let balanceSubtitleNode: ImmediateTextNode - private let balanceTimestampNode: ImmediateTextNode private let receiveButtonNode: SolidRoundedButtonNode private let sendButtonNode: SolidRoundedButtonNode private let headerBackgroundNode: ASImageNode - init(account: Account, theme: PresentationTheme, sendAction: @escaping () -> Void, receiveAction: @escaping () -> Void) { - self.balanceNode = WalletInfoBalanceNode(theme: theme) + init(account: Account, presentationData: PresentationData, sendAction: @escaping () -> Void, receiveAction: @escaping () -> Void) { + self.balanceNode = WalletInfoBalanceNode(theme: presentationData.theme) self.balanceSubtitleNode = ImmediateTextNode() self.balanceSubtitleNode.displaysAsynchronously = false self.balanceSubtitleNode.attributedText = NSAttributedString(string: "your balance", font: Font.regular(13), textColor: UIColor(white: 1.0, alpha: 0.6)) - self.balanceTimestampNode = ImmediateTextNode() - self.balanceTimestampNode.displaysAsynchronously = false - self.balanceTimestampNode.attributedText = NSAttributedString(string: "", font: Font.regular(13), textColor: UIColor(white: 1.0, alpha: 0.6)) - self.headerBackgroundNode = ASImageNode() self.headerBackgroundNode.displaysAsynchronously = false self.headerBackgroundNode.displayWithoutProcessing = true @@ -237,12 +209,7 @@ private final class WalletInfoHeaderNode: ASDisplayNode { self.receiveButtonNode = SolidRoundedButtonNode(title: "Receive", icon: UIImage(bundleImageName: "Wallet/ReceiveButtonIcon"), theme: SolidRoundedButtonTheme(backgroundColor: .white, foregroundColor: .black), height: 50.0, cornerRadius: 10.0, gloss: false) self.sendButtonNode = SolidRoundedButtonNode(title: "Send", icon: UIImage(bundleImageName: "Wallet/SendButtonIcon"), theme: SolidRoundedButtonTheme(backgroundColor: .white, foregroundColor: .black), height: 50.0, cornerRadius: 10.0, gloss: false) - self.refreshNode = AnimatedStickerNode() - self.refreshNode.playToCompletionOnStop = true - self.refreshNode.automaticallyLoadFirstFrame = true - if let path = getAppBundle().path(forResource: "celebrate", ofType: "tgs") { - self.refreshNode.setup(account: account, resource: .localFile(path), width: Int(32.0 * UIScreenScale), height: Int(32.0 * UIScreenScale), mode: .direct) - } + self.refreshNode = WalletRefreshNode(strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat) super.init() @@ -251,7 +218,6 @@ private final class WalletInfoHeaderNode: ASDisplayNode { self.addSubnode(self.sendButtonNode) self.addSubnode(self.balanceNode) self.addSubnode(self.balanceSubtitleNode) - self.addSubnode(self.balanceTimestampNode) self.addSubnode(self.refreshNode) self.receiveButtonNode.pressed = { @@ -284,9 +250,10 @@ private final class WalletInfoHeaderNode: ASDisplayNode { let buttonAlpha = buttonTransition * 1.0 let balanceSubtitleSize = self.balanceSubtitleNode.updateLayout(CGSize(width: size.width - sideInset * 2.0, height: 200.0)) - let balanceTimestampSize = self.balanceTimestampNode.updateLayout(CGSize(width: size.width - sideInset * 2.0, height: 200.0)) - let balanceHeight = self.balanceNode.update(width: size.width, transition: transition) + let headerScaleTransition: CGFloat = max(0.0, min(1.0, (effectiveOffset - minHeaderOffset) / (maxHeaderOffset - minHeaderOffset))) + + let balanceHeight = self.balanceNode.update(width: size.width, scaleTransition: headerScaleTransition, transition: transition) let balanceSize = CGSize(width: size.width, height: balanceHeight) let maxHeaderScale: CGFloat = min(1.0, (size.width - 40.0) / balanceSize.width) @@ -296,29 +263,21 @@ private final class WalletInfoHeaderNode: ASDisplayNode { let minHeaderY = navigationHeight - 44.0 + floor((44.0 - minHeaderHeight) / 2.0) let maxHeaderY = floor((size.height - balanceSize.height) / 2.0 - balanceSubtitleSize.height) - let headerScaleTransition: CGFloat = max(0.0, min(1.0, (effectiveOffset - minHeaderOffset) / (maxHeaderOffset - minHeaderOffset))) let headerPositionTransition: CGFloat = max(0.0, (effectiveOffset - minHeaderOffset) / (maxOffset - minHeaderOffset)) let headerY = headerPositionTransition * maxHeaderY + (1.0 - headerPositionTransition) * minHeaderY let headerScale = headerScaleTransition * maxHeaderScale + (1.0 - headerScaleTransition) * minHeaderScale - let refreshSize = CGSize(width: 32.0, height: 32.0) - self.refreshNode.updateLayout(size: refreshSize) + let refreshSize = CGSize(width: 0.0, height: 0.0) transition.updateFrame(node: self.refreshNode, frame: CGRect(origin: CGPoint(x: floor((size.width - refreshSize.width) / 2.0), y: navigationHeight - 44.0 + floor((44.0 - refreshSize.height) / 2.0)), size: refreshSize)) + transition.updateAlpha(node: self.refreshNode, alpha: headerScaleTransition) if self.balance == nil { - transition.updateAlpha(node: self.refreshNode, alpha: 0.0) - transition.updateSublayerTransformScale(node: self.refreshNode, scale: 0.1) - self.refreshNode.visibility = false + self.refreshNode.update(state: .pullToRefresh(self.timestamp ?? 0, 0.0)) } else if self.isRefreshing { - transition.updateAlpha(node: self.refreshNode, alpha: 1.0) - transition.updateSublayerTransformScale(node: self.refreshNode, scale: 1.0) - self.refreshNode.visibility = true + self.refreshNode.update(state: .refreshing) } else { let refreshOffset: CGFloat = 20.0 - let refreshScaleTransition: CGFloat = max(0.0, min(1.0, (offset - maxOffset) / refreshOffset)) - transition.updateAlpha(node: self.refreshNode, alpha: refreshScaleTransition) - let refreshScale: CGFloat = refreshScaleTransition * 1.0 + (1.0 - refreshScaleTransition) * 0.1 - transition.updateSublayerTransformScale(node: self.refreshNode, scale: refreshScale) - self.refreshNode.visibility = false + let refreshScaleTransition: CGFloat = max(0.0, (offset - maxOffset) / refreshOffset) + self.refreshNode.update(state: .pullToRefresh(self.timestamp ?? 0, refreshScaleTransition * 0.25)) } let balanceFrame = CGRect(origin: CGPoint(x: 0.0, y: headerY), size: balanceSize) @@ -328,11 +287,6 @@ private final class WalletInfoHeaderNode: ASDisplayNode { let balanceSubtitleFrame = CGRect(origin: CGPoint(x: floor((size.width - balanceSubtitleSize.width) / 2.0), y: balanceFrame.midY + (balanceFrame.height / 2.0 * headerScale) + balanceSubtitleSpacing), size: balanceSubtitleSize) transition.updateFrameAdditive(node: self.balanceSubtitleNode, frame: balanceSubtitleFrame) - let balanceTimestampFrame = CGRect(origin: CGPoint(x: floor((size.width - balanceTimestampSize.width) / 2.0), y: balanceSubtitleFrame.maxY + 2.0), size: balanceTimestampSize) - transition.updateFrameAdditive(node: self.balanceTimestampNode, frame: balanceTimestampFrame) - - transition.updateAlpha(node: self.balanceTimestampNode, alpha: headerScaleTransition) - let headerHeight: CGFloat = 1000.0 transition.updateFrame(node: self.headerBackgroundNode, frame: CGRect(origin: CGPoint(x: 0.0, y: effectiveOffset + 10.0 - headerHeight), size: CGSize(width: size.width, height: headerHeight))) @@ -360,11 +314,11 @@ private final class WalletInfoHeaderNode: ASDisplayNode { if self.balance == nil { self.balanceNode.isHidden = true self.balanceSubtitleNode.isHidden = true - self.balanceTimestampNode.isHidden = true + self.refreshNode.isHidden = true } else { self.balanceNode.isHidden = false self.balanceSubtitleNode.isHidden = false - self.balanceTimestampNode.isHidden = false + self.refreshNode.isHidden = false } transition.updateFrame(node: self.receiveButtonNode, frame: receiveButtonFrame) transition.updateAlpha(node: self.receiveButtonNode, alpha: buttonAlpha) @@ -390,14 +344,10 @@ private final class WalletInfoHeaderNode: ASDisplayNode { 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.balanceSubtitleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) - self.balanceTimestampNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + self.refreshNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) } self.balanceNode.isLoading = false } - - func animateBeganRefreshing() { - //self.refreshNode.layer.animate(from: 0.5 as NSNumber, to: 0.0 as NSNumber, keyPath: "transform.scale", timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, duration: 0.2, delay: 0.0, removeOnCompletion: true, additive: true) - } } private struct WalletInfoListTransaction { @@ -443,10 +393,10 @@ private enum WalletInfoListEntry: Equatable, Comparable, Identifiable { } } - func item(theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, action: @escaping (WalletTransaction) -> Void) -> ListViewItem { + func item(account: Account, theme: PresentationTheme, strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, action: @escaping (WalletTransaction) -> Void) -> ListViewItem { switch self { case let .empty(address): - return WalletInfoEmptyItem(theme: theme, strings: strings, address: address) + return WalletInfoEmptyItem(account: account, theme: theme, strings: strings, address: address) case let .transaction(_, transaction): return WalletInfoTransactionItem(theme: theme, strings: strings, dateTimeFormat: dateTimeFormat, walletTransaction: transaction, action: { action(transaction) @@ -455,12 +405,12 @@ private enum WalletInfoListEntry: Equatable, Comparable, Identifiable { } } -private func preparedTransition(from fromEntries: [WalletInfoListEntry], to toEntries: [WalletInfoListEntry], presentationData: PresentationData, action: @escaping (WalletTransaction) -> Void) -> WalletInfoListTransaction { +private func preparedTransition(from fromEntries: [WalletInfoListEntry], to toEntries: [WalletInfoListEntry], account: Account, presentationData: PresentationData, action: @escaping (WalletTransaction) -> Void) -> WalletInfoListTransaction { let (deleteIndices, indicesAndItems, updateIndices) = mergeListsStableWithUpdates(leftList: fromEntries, rightList: toEntries) let deletions = deleteIndices.map { ListViewDeleteItem(index: $0, directionHint: nil) } - let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, action: action), directionHint: nil) } - let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, action: action), directionHint: nil) } + let insertions = indicesAndItems.map { ListViewInsertItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, action: action), directionHint: nil) } + let updates = updateIndices.map { ListViewUpdateItem(index: $0.0, previousIndex: $0.2, item: $0.1.item(account: account, theme: presentationData.theme, strings: presentationData.strings, dateTimeFormat: presentationData.dateTimeFormat, action: action), directionHint: nil) } return WalletInfoListTransaction(deletions: deletions, insertions: insertions, updates: updates) } @@ -473,6 +423,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode { private let address: String private let openTransaction: (WalletTransaction) -> Void + private let present: (ViewController) -> Void private let hapticFeedback = HapticFeedback() @@ -502,15 +453,16 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode { private var updateTimestampTimer: SwiftSignalKit.Timer? - 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, present: @escaping (ViewController) -> Void) { self.account = account self.tonContext = tonContext self.presentationData = presentationData self.walletInfo = walletInfo self.address = address self.openTransaction = openTransaction + self.present = present - self.headerNode = WalletInfoHeaderNode(account: account, theme: presentationData.theme, sendAction: sendAction, receiveAction: receiveAction) + self.headerNode = WalletInfoHeaderNode(account: account, presentationData: presentationData, sendAction: sendAction, receiveAction: receiveAction) self.listNode = ListView() self.listNode.verticalScrollIndicatorColor = self.presentationData.theme.list.scrollIndicatorColor @@ -521,7 +473,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode { super.init() - self.backgroundColor = .white + self.backgroundColor = self.presentationData.theme.list.itemBlocksBackgroundColor self.addSubnode(self.listNode) self.addSubnode(self.headerNode) @@ -548,8 +500,6 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode { if !strongSelf.reloadingState && canBeginRefresh && isScrolling { if offset >= headerHeight + 100.0 { canBeginRefresh = false - strongSelf.headerNode.isRefreshing = true - strongSelf.headerNode.animateBeganRefreshing() strongSelf.hapticFeedback.impact() strongSelf.refreshTransactions() } @@ -596,13 +546,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode { guard let strongSelf = self, let combinedState = strongSelf.combinedState, !strongSelf.reloadingState else { return } - let string = lastUpdateTimestampString(strings: strongSelf.presentationData.strings, dateTimeFormat: strongSelf.presentationData.dateTimeFormat, statusTimestamp: Int32(clamping: combinedState.timestamp), relativeTo: Int32(Date().timeIntervalSince1970)) - if strongSelf.headerNode.timestampString != string { - strongSelf.headerNode.timestampString = string - if let (layout, navigationHeight) = strongSelf.validLayout { - strongSelf.headerNode.update(size: strongSelf.headerNode.bounds.size, navigationHeight: navigationHeight, offset: strongSelf.listOffset ?? 0.0, transition: .immediate) - } - } + strongSelf.headerNode.timestamp = Int32(clamping: combinedState.timestamp) }, queue: .mainQueue()) self.updateTimestampTimer?.start() } @@ -687,10 +631,9 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode { self.loadingMoreTransactions = true self.reloadingState = true - self.headerNode.timestampString = "updating" + self.headerNode.isRefreshing = true self.stateDisposable.set((getCombinedWalletState(postbox: self.account.postbox, walletInfo: self.walletInfo, tonInstance: self.tonContext.instance) - |> delay(self.combinedState == nil ? 0.0 : 2.0, queue: .mainQueue()) |> deliverOnMainQueue).start(next: { [weak self] value in guard let strongSelf = self else { return @@ -698,6 +641,9 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode { let combinedState: CombinedWalletState? switch value { case let .cached(state): + if strongSelf.combinedState != nil { + return + } if state == nil { strongSelf.loadingIndicator.startAnimating() } else { @@ -722,7 +668,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode { strongSelf.reloadingState = false - strongSelf.headerNode.timestampString = lastUpdateTimestampString(strings: strongSelf.presentationData.strings, dateTimeFormat: strongSelf.presentationData.dateTimeFormat, statusTimestamp: Int32(clamping: combinedState.timestamp), relativeTo: Int32(Date().timeIntervalSince1970)) + strongSelf.headerNode.timestamp = Int32(clamping: combinedState.timestamp) if strongSelf.isReady, let (layout, navigationHeight) = strongSelf.validLayout { strongSelf.headerNode.update(size: strongSelf.headerNode.bounds.size, navigationHeight: navigationHeight, offset: strongSelf.listOffset ?? 0.0, transition: .immediate) @@ -756,6 +702,35 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode { guard let strongSelf = self else { return } + + strongSelf.reloadingState = false + + if let combinedState = strongSelf.combinedState { + strongSelf.headerNode.timestamp = Int32(clamping: combinedState.timestamp) + } + + if strongSelf.isReady, let (layout, navigationHeight) = strongSelf.validLayout { + strongSelf.headerNode.update(size: strongSelf.headerNode.bounds.size, navigationHeight: navigationHeight, offset: strongSelf.listOffset ?? 0.0, transition: .immediate) + } + + strongSelf.loadingMoreTransactions = false + strongSelf.canLoadMoreTransactions = false + + strongSelf.headerNode.isRefreshing = false + + if strongSelf.isReady, let (layout, navigationHeight) = strongSelf.validLayout { + strongSelf.headerNode.update(size: strongSelf.headerNode.bounds.size, navigationHeight: navigationHeight, offset: strongSelf.listOffset ?? 0.0, transition: .animated(duration: 0.2, curve: .easeInOut)) + } + + if !strongSelf.didSetContentReady { + strongSelf.didSetContentReady = true + strongSelf.contentReady.set(.single(true)) + } + + strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: "An Error Occurred", text: "The wallet state can not be retrieved at this time. Please try again later.", actions: [ + TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: { + }) + ], actionLayout: .vertical)) })) } @@ -831,7 +806,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode { } } - let transaction = preparedTransition(from: self.currentEntries ?? [], to: updatedEntries, presentationData: self.presentationData, action: { [weak self] transaction in + let transaction = preparedTransition(from: self.currentEntries ?? [], to: updatedEntries, account: self.account, presentationData: self.presentationData, action: { [weak self] transaction in guard let strongSelf = self else { return } diff --git a/submodules/WalletUI/Sources/WalletInfoTransactionItem.swift b/submodules/WalletUI/Sources/WalletInfoTransactionItem.swift index fbddade9bc..89e138ea3d 100644 --- a/submodules/WalletUI/Sources/WalletInfoTransactionItem.swift +++ b/submodules/WalletUI/Sources/WalletInfoTransactionItem.swift @@ -112,7 +112,6 @@ class WalletInfoTransactionItemNode: ListViewItemNode { init() { self.backgroundNode = ASDisplayNode() self.backgroundNode.isLayerBacked = true - self.backgroundNode.backgroundColor = .white self.topStripeNode = ASDisplayNode() self.topStripeNode.isLayerBacked = true self.bottomStripeNode = ASDisplayNode() @@ -228,7 +227,17 @@ class WalletInfoTransactionItemNode: ListViewItemNode { let (directionLayout, directionApply) = makeDirectionLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: directionText, font: directionFont, textColor: item.theme.list.itemSecondaryTextColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - leftInset - 20.0, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) - let (titleLayout, titleApply) = makeTitleLayout(TextNodeLayoutArguments(attributedString: NSAttributedString(string: title, font: titleFont, textColor: titleColor), backgroundColor: nil, maximumNumberOfLines: 1, truncationType: .end, constrainedSize: CGSize(width: params.width - leftInset - leftInset - 20.0 - dateLayout.size.width - directionLayout.size.width - iconSize.width, height: CGFloat.greatestFiniteMagnitude), alignment: .natural, cutout: nil, insets: UIEdgeInsets())) + let titleString = NSMutableAttributedString() + if let range = title.range(of: ".") { + let integralPart = String(title[.. String { + let dayString: String + switch day { + case .today: + dayString = strings.Updated_TodayAt(stringForShortTimestamp(hours: hours, minutes: minutes, dateTimeFormat: dateTimeFormat)).0 + case .yesterday: + dayString = strings.Updated_YesterdayAt(stringForShortTimestamp(hours: hours, minutes: minutes, dateTimeFormat: dateTimeFormat)).0 + } + return dayString +} + +private func lastUpdateTimestampString(strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat, statusTimestamp: Int32, relativeTo timestamp: Int32) -> String { + let difference = timestamp - statusTimestamp + let expanded = true + if difference < 60 { + return strings.Updated_JustNow + } else if difference < 60 * 60 && !expanded { + let minutes = difference / 60 + return strings.Updated_MinutesAgo(minutes) + } else { + var t: time_t = time_t(statusTimestamp) + var timeinfo: tm = tm() + localtime_r(&t, &timeinfo) + + var now: time_t = time_t(timestamp) + var timeinfoNow: tm = tm() + localtime_r(&now, &timeinfoNow) + + if timeinfo.tm_year != timeinfoNow.tm_year { + return strings.Updated_AtDate(stringForTimestamp(day: timeinfo.tm_mday, month: timeinfo.tm_mon + 1, year: timeinfo.tm_year, dateTimeFormat: dateTimeFormat)).0 + } + + let dayDifference = timeinfo.tm_yday - timeinfoNow.tm_yday + if dayDifference == 0 || dayDifference == -1 { + let day: RelativeTimestampFormatDay + if dayDifference == 0 { + if expanded { + day = .today + } else { + let minutes = difference / (60 * 60) + return strings.Updated_HoursAgo(minutes) + } + } else { + day = .yesterday + } + return stringForRelativeUpdateTime(strings: strings, day: day, dateTimeFormat: dateTimeFormat, hours: timeinfo.tm_hour, minutes: timeinfo.tm_min) + } else { + return strings.Updated_AtDate(stringForTimestamp(day: timeinfo.tm_mday, month: timeinfo.tm_mon + 1, year: timeinfo.tm_year, dateTimeFormat: dateTimeFormat)).0 + } + } +} + +enum WalletRefreshState: Equatable { + case pullToRefresh(Int32, CGFloat) + case refreshing +} + +final class WalletRefreshNode: ASDisplayNode { + private let strings: PresentationStrings + private let dateTimeFormat: PresentationDateTimeFormat + private let iconContainer: ASDisplayNode + private let iconNode: ASImageNode + private let titleNode: ImmediateTextNode + + private var state: WalletRefreshState? + + private let animator: ConstantDisplayLinkAnimator + + init(strings: PresentationStrings, dateTimeFormat: PresentationDateTimeFormat) { + self.strings = strings + self.dateTimeFormat = dateTimeFormat + + self.iconContainer = ASDisplayNode() + + self.iconNode = ASImageNode() + self.iconNode.displaysAsynchronously = false + self.iconNode.displayWithoutProcessing = true + self.iconNode.image = UIImage(bundleImageName: "Wallet/RefreshIcon") + if let image = self.iconNode.image { + self.iconNode.frame = CGRect(origin: CGPoint(), size: image.size) + } + + self.titleNode = ImmediateTextNode() + self.titleNode.displaysAsynchronously = false + + var updateImpl: (() -> Void)? + self.animator = ConstantDisplayLinkAnimator(update: { + updateImpl?() + }) + + super.init() + + self.iconContainer.addSubnode(self.iconNode) + self.addSubnode(self.iconContainer) + self.addSubnode(self.titleNode) + + updateImpl = { [weak self] in + self?.updateAnimation() + } + } + + private var currentAngle: CGFloat = 0.0 + private var currentExtraSpeed: CGFloat = 0.0 + private var animateToZeroState: (Double, CGFloat)? + + private func updateAnimation() { + guard let state = self.state else { + return + } + + var speed: CGFloat = 0.0 + var baseValue: CGFloat = 0.0 + + switch state { + case .refreshing: + speed = 0.01 + self.animateToZeroState = nil + case let .pullToRefresh(_, value): + if self.currentExtraSpeed.isZero && self.animateToZeroState == nil && !self.currentAngle.isZero { + self.animateToZeroState = (CACurrentMediaTime(), self.currentAngle) + } + if self.animateToZeroState == nil { + baseValue = value + } + } + + if let (startTime, startValue) = self.animateToZeroState { + let endValue: CGFloat = floor(startValue) + 1.0 + let duration: Double = Double(endValue - startValue) * 1.0 + let timeDelta = (startTime + duration - CACurrentMediaTime()) + let t: CGFloat = 1.0 - CGFloat(max(0.0, min(1.0, timeDelta / duration))) + if t >= 1.0 - CGFloat.ulpOfOne { + self.animateToZeroState = nil + self.currentAngle = 0.0 + } else { + let bt = bezierPoint(0.23, 1.0, 0.32, 1.0, t) + self.currentAngle = startValue * (1.0 - bt) + endValue * bt + } + } else { + self.currentAngle += speed + self.currentExtraSpeed + } + self.currentExtraSpeed *= 0.97 + if abs(self.currentExtraSpeed) < 0.0001 { + self.currentExtraSpeed = 0.0 + } + + self.iconNode.layer.transform = CATransform3DMakeRotation((baseValue + self.currentAngle) * CGFloat.pi * 2.0, 0.0, 0.0, 1.0) + + if !self.currentExtraSpeed.isZero || !speed.isZero || self.animateToZeroState != nil { + self.animator.isPaused = false + } else { + self.animator.isPaused = true + } + } + + func update(state: WalletRefreshState) { + if self.state == state { + return + } + let previousState = self.state + self.state = state + + var pullProgress: CGFloat = 0.0 + + let title: String + switch state { + case let .pullToRefresh(ts, progress): + title = lastUpdateTimestampString(strings: self.strings, dateTimeFormat: dateTimeFormat, statusTimestamp: ts, relativeTo: Int32(Date().timeIntervalSince1970)) + pullProgress = progress + case .refreshing: + title = "updating" + } + + if let previousState = previousState { + switch state { + case .refreshing: + switch previousState { + case .refreshing: + break + default: + self.currentExtraSpeed = 0.05 + } + default: + self.currentExtraSpeed = 0.0 + } + } + + self.updateAnimation() + + self.titleNode.attributedText = NSAttributedString(string: title, font: Font.regular(13.0), textColor: UIColor(white: 1.0, alpha: 0.6)) + + let iconSize = self.iconNode.image?.size ?? CGSize(width: 20.0, height: 20.0) + let titleSize = self.titleNode.updateLayout(CGSize(width: 200.0, height: 100.0)) + let iconSpacing: CGFloat = 5.0 + + let contentWidth = iconSize.width + titleSize.width + iconSpacing + let contentOrigin = floor(-contentWidth / 2.0) + + self.iconContainer.frame = CGRect(origin: CGPoint(x: contentOrigin, y: floor(-iconSize.height / 2.0)), size: iconSize) + self.titleNode.frame = CGRect(origin: CGPoint(x: contentOrigin + iconSize.width + iconSpacing, y: floor(-titleSize.height / 2.0)), size: titleSize) + } +} diff --git a/submodules/WalletUI/Sources/WalletSettingsScreen.swift b/submodules/WalletUI/Sources/WalletSettingsScreen.swift index ee844ec86b..c691f15d90 100644 --- a/submodules/WalletUI/Sources/WalletSettingsScreen.swift +++ b/submodules/WalletUI/Sources/WalletSettingsScreen.swift @@ -104,11 +104,10 @@ public func walletSettingsController(context: AccountContext, tonContext: TonCon actionSheet.setItemGroups([ActionSheetItemGroup(items: [ ActionSheetButtonItem(title: "Delete Wallet", color: .destructive, action: { [weak actionSheet] in actionSheet?.dismissAnimated() - let _ = (debugDeleteWallets(postbox: context.account.postbox) - |> deliverOnMainQueue).start(completed: { - if let tonContext = context.tonContext { - replaceAllWalletControllersImpl?(WalletSplashScreen(context: context, tonContext: tonContext, mode: .intro)) - } + let _ = (deleteLocalWalletData(postbox: context.account.postbox, network: context.account.network, tonInstance: tonContext.instance, keychain: tonContext.keychain, walletInfo: walletInfo) + |> deliverOnMainQueue).start(error: { _ in + }, completed: { + replaceAllWalletControllersImpl?(WalletSplashScreen(context: context, tonContext: tonContext, mode: .intro)) }) }) ]), ActionSheetItemGroup(items: [ diff --git a/submodules/WalletUI/Sources/WalletSplashScreen.swift b/submodules/WalletUI/Sources/WalletSplashScreen.swift index be9679e21f..877316dbc7 100644 --- a/submodules/WalletUI/Sources/WalletSplashScreen.swift +++ b/submodules/WalletUI/Sources/WalletSplashScreen.swift @@ -49,6 +49,7 @@ public final class WalletSplashScreen: ViewController { super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: defaultNavigationPresentationData.strings)) + self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style self.navigationPresentation = .modalInLargeLayout self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) self.navigationBar?.intrinsicCanTransitionInline = false @@ -287,14 +288,22 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode { let body = MarkdownAttributeSet(font: Font.regular(13.0), textColor: self.presentationData.theme.list.itemSecondaryTextColor, additionalAttributes: [:]) let link = MarkdownAttributeSet(font: Font.regular(13.0), textColor: self.presentationData.theme.list.itemSecondaryTextColor, additionalAttributes: [NSAttributedString.Key.underlineStyle.rawValue: NSUnderlineStyle.single.rawValue as NSNumber]) termsText = parseMarkdownIntoAttributedString("By creating the wallet you accept\n[Terms of Conditions]().", attributes: MarkdownAttributes(body: body, bold: body, link: link, linkAttribute: { _ in nil }), textAlignment: .center) - self.iconNode.image = UIImage(bundleImageName: "Settings/Wallet/IntroIcon") + self.iconNode.image = nil + if let path = getAppBundle().path(forResource: "WalletIntroStatic", ofType: "tgs") { + self.animationNode.setup(account: account, resource: .localFile(path), width: 280, height: 280, mode: .direct) + self.animationNode.visibility = true + } secondaryActionText = "" case .created: title = "Congratulations" text = NSAttributedString(string: "Your Gram wallet has just been created. Only you control it.\n\nTo be able to always have access to it, please write down secret words and\nset up a secure passcode.", font: textFont, textColor: textColor) buttonText = "Proceed" termsText = NSAttributedString(string: "") - self.iconNode.image = UIImage(bundleImageName: "Settings/Wallet/CreatedIcon") + self.iconNode.image = nil + if let path = getAppBundle().path(forResource: "WalletCreated", ofType: "tgs") { + self.animationNode.setup(account: account, resource: .localFile(path), width: 280, height: 280, mode: .direct) + self.animationNode.visibility = true + } secondaryActionText = "" case .success: title = "Ready to go!" @@ -323,7 +332,11 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode { text = NSAttributedString(string: "Please wait a few seconds for your transaction to be processed...", font: textFont, textColor: textColor) buttonText = "" termsText = NSAttributedString(string: "") - self.iconNode.image = UIImage(bundleImageName: "Settings/Wallet/SendingIcon") + self.iconNode.image = nil + if let path = getAppBundle().path(forResource: "SendingGrams", ofType: "tgs") { + self.animationNode.setup(account: account, resource: .localFile(path), width: 280, height: 280, mode: .direct) + self.animationNode.visibility = true + } secondaryActionText = "" case let .sent(_, amount): title = "Done!" @@ -392,7 +405,7 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode { self.secondaryActionButtonNode = HighlightTrackingButtonNode() - self.buttonNode = SolidRoundedButtonNode(title: buttonText, theme: SolidRoundedButtonTheme(theme: self.presentationData.theme), height: 50.0, cornerRadius: 10.0, gloss: true) + self.buttonNode = SolidRoundedButtonNode(title: buttonText, theme: SolidRoundedButtonTheme(theme: self.presentationData.theme), height: 50.0, cornerRadius: 10.0, gloss: false) self.buttonNode.isHidden = buttonText.isEmpty super.init() diff --git a/submodules/WalletUI/Sources/WalletTransactionInfoScreen.swift b/submodules/WalletUI/Sources/WalletTransactionInfoScreen.swift index 2973664876..d02d745564 100644 --- a/submodules/WalletUI/Sources/WalletTransactionInfoScreen.swift +++ b/submodules/WalletUI/Sources/WalletTransactionInfoScreen.swift @@ -80,7 +80,7 @@ private enum WalletTransactionInfoEntry: ItemListNodeEntry { case let .infoHeader(theme, text): return ItemListSectionHeaderItem(theme: theme, text: text, sectionId: self.section) case let .infoAddress(theme, text): - return ItemListMultilineTextItem(theme: theme, text: text, enabledEntityTypes: [], sectionId: self.section, style: .blocks) + return ItemListMultilineTextItem(theme: theme, text: text, enabledEntityTypes: [], font: .monospace, sectionId: self.section, style: .blocks) case let .infoCopyAddress(theme, text): return ItemListActionItem(theme: theme, title: text, kind: .generic, alignment: .natural, sectionId: self.section, style: .blocks, action: { arguments.copyWalletAddress() @@ -346,20 +346,30 @@ private class WalletTransactionHeaderItemNode: ListViewItemNode { let leftInset: CGFloat = 15.0 + params.leftInset let verticalInset: CGFloat = 24.0 - let title: String + let balanceString: String let titleColor: UIColor let transferredValue = item.walletTransaction.transferredValue if transferredValue <= 0 { - title = "\(formatBalanceText(transferredValue, decimalSeparator: item.dateTimeFormat.decimalSeparator))" + balanceString = "\(formatBalanceText(transferredValue, decimalSeparator: item.dateTimeFormat.decimalSeparator))" titleColor = item.theme.list.itemPrimaryTextColor } else { - title = "+\(formatBalanceText(transferredValue, decimalSeparator: item.dateTimeFormat.decimalSeparator))" + balanceString = "+\(formatBalanceText(transferredValue, decimalSeparator: item.dateTimeFormat.decimalSeparator))" titleColor = item.theme.chatList.secretTitleColor } + let title = NSMutableAttributedString() + if let range = balanceString.range(of: ".") { + let integralPart = String(balanceString[.. Void, secondaryAction: @escaping () -> Void) { + init(account: Account, presentationData: PresentationData, mode: WalletWordCheckMode, possibleWordList: [String], action: @escaping () -> Void, secondaryAction: @escaping () -> Void) { self.presentationData = presentationData self.mode = mode self.action = action @@ -2576,9 +2578,7 @@ private final class WalletWordCheckScreenNode: ViewControllerTracingNode, UIScro self.scrollNode = ASScrollNode() self.scrollNode.canCancelAllTouchesInViews = true - self.iconNode = ASImageNode() - self.iconNode.displayWithoutProcessing = true - self.iconNode.displaysAsynchronously = false + self.animationNode = AnimatedStickerNode() let title: String let text: NSAttributedString @@ -2598,14 +2598,16 @@ private final class WalletWordCheckScreenNode: ViewControllerTracingNode, UIScro buttonText = "Continue" secondaryActionText = "" - self.iconNode.image = UIImage(bundleImageName: "Settings/Wallet/WordsCheckIcon") + if let path = getAppBundle().path(forResource: "WalletWordCheck", ofType: "tgs") { + self.animationNode.setup(account: account, resource: .localFile(path), width: 280, height: 280, mode: .direct) + self.animationNode.visibility = true + } case .import: title = "24 Secret Words" text = NSAttributedString(string: "Please restore access to your wallet by\nentering the 24 secret words you wrote down when creating the wallet.", font: Font.regular(16.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor) buttonText = "Continue" secondaryActionText = "I don't have those" wordIndices = Array(0 ..< 24) - self.iconNode.image = nil } self.navigationTitleNode = ImmediateTextNode() @@ -2660,7 +2662,7 @@ private final class WalletWordCheckScreenNode: ViewControllerTracingNode, UIScro self.addSubnode(self.scrollNode) - self.scrollNode.addSubnode(self.iconNode) + self.scrollNode.addSubnode(self.animationNode) self.scrollNode.addSubnode(self.titleNode) self.scrollNode.addSubnode(self.textNode) self.scrollNode.addSubnode(self.secondaryActionTitleNode) @@ -2773,7 +2775,14 @@ private final class WalletWordCheckScreenNode: ViewControllerTracingNode, UIScro transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: contentAreaSize)) - let iconSize = self.iconNode.image?.size ?? CGSize() + let iconSize: CGSize + switch self.mode { + case .import: + iconSize = CGSize() + case .verify: + iconSize = CGSize(width: 140.0, height: 140.0) + self.animationNode.updateLayout(size: iconSize) + } let titleSize = self.titleNode.updateLayout(CGSize(width: contentAreaSize.width - sideInset * 2.0, height: contentAreaSize.height)) let navigationTitleSize = self.navigationTitleNode.updateLayout(CGSize(width: contentAreaSize.width - sideInset * 2.0, height: contentAreaSize.height)) @@ -2787,7 +2796,7 @@ private final class WalletWordCheckScreenNode: ViewControllerTracingNode, UIScro let contentVerticalOrigin = navigationHeight + 10.0 let iconFrame = CGRect(origin: CGPoint(x: floor((contentAreaSize.width - iconSize.width) / 2.0), y: contentVerticalOrigin), size: iconSize) - transition.updateFrameAdditive(node: self.iconNode, frame: iconFrame) + transition.updateFrame(node: self.animationNode, frame: iconFrame) let titleFrame = CGRect(origin: CGPoint(x: floor((contentAreaSize.width - titleSize.width) / 2.0), y: iconFrame.maxY + iconSpacing), size: titleSize) transition.updateFrameAdditive(node: self.titleNode, frame: titleFrame) let textFrame = CGRect(origin: CGPoint(x: floor((contentAreaSize.width - textSize.width) / 2.0), y: titleFrame.maxY + titleSpacing), size: textSize) diff --git a/submodules/WalletUI/Sources/WalletWordDisplayScreen.swift b/submodules/WalletUI/Sources/WalletWordDisplayScreen.swift index 91ddd75057..09ebdbea8c 100644 --- a/submodules/WalletUI/Sources/WalletWordDisplayScreen.swift +++ b/submodules/WalletUI/Sources/WalletWordDisplayScreen.swift @@ -10,6 +10,7 @@ import TelegramCore import SolidRoundedButtonNode import UndoUI import AlertUI +import AnimationUI public final class WalletWordDisplayScreen: ViewController { private let context: AccountContext @@ -35,6 +36,7 @@ public final class WalletWordDisplayScreen: ViewController { super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: navigationBarTheme, strings: defaultNavigationPresentationData.strings)) + self.statusBar.statusBarStyle = self.presentationData.theme.rootController.statusBarStyle.style self.navigationPresentation = .modalInLargeLayout self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait) self.navigationBar?.intrinsicCanTransitionInline = false @@ -51,7 +53,7 @@ public final class WalletWordDisplayScreen: ViewController { } override public func loadDisplayNode() { - self.displayNode = WalletWordDisplayScreenNode(presentationData: self.presentationData, wordList: self.wordList, action: { [weak self] in + self.displayNode = WalletWordDisplayScreenNode(account: self.context.account, presentationData: self.presentationData, wordList: self.wordList, action: { [weak self] in guard let strongSelf = self else { return } @@ -103,7 +105,7 @@ private final class WalletWordDisplayScreenNode: ViewControllerTracingNode, UISc private let navigationSeparatorNode: ASDisplayNode private let navigationTitleNode: ImmediateTextNode private let scrollNode: ASScrollNode - private let iconNode: ASImageNode + private let animationNode: AnimatedStickerNode private let titleNode: ImmediateTextNode private let textNode: ImmediateTextNode private let wordNodes: [(ImmediateTextNode, ImmediateTextNode, ImmediateTextNode)] @@ -111,7 +113,7 @@ private final class WalletWordDisplayScreenNode: ViewControllerTracingNode, UISc private var navigationHeight: CGFloat? - init(presentationData: PresentationData, wordList: [String], action: @escaping () -> Void) { + init(account: Account, presentationData: PresentationData, wordList: [String], action: @escaping () -> Void) { self.presentationData = presentationData self.wordList = wordList self.action = action @@ -124,16 +126,16 @@ private final class WalletWordDisplayScreenNode: ViewControllerTracingNode, UISc self.scrollNode = ASScrollNode() - self.iconNode = ASImageNode() - self.iconNode.displayWithoutProcessing = true - self.iconNode.displaysAsynchronously = false + self.animationNode = AnimatedStickerNode() + if let path = getAppBundle().path(forResource: "WalletWordList", ofType: "tgs") { + self.animationNode.setup(account: account, resource: .localFile(path), width: 280, height: 280, mode: .direct) + self.animationNode.visibility = true + } let title: String = "24 Secret Words" let text: String = "Write down these 24 words in the correct order and store them in a secret place.\n\nUse these secret words to restore access to your wallet if you lose your passcode or Telegram account." let buttonText: String = "Done" - self.iconNode.image = UIImage(bundleImageName: "Settings/Wallet/WordsDisplayIcon") - self.titleNode = ImmediateTextNode() self.titleNode.displaysAsynchronously = false self.titleNode.attributedText = NSAttributedString(string: title, font: Font.bold(32.0), textColor: self.presentationData.theme.list.itemPrimaryTextColor) @@ -187,7 +189,7 @@ private final class WalletWordDisplayScreenNode: ViewControllerTracingNode, UISc self.addSubnode(self.scrollNode) - self.scrollNode.addSubnode(self.iconNode) + self.scrollNode.addSubnode(self.animationNode) self.scrollNode.addSubnode(self.titleNode) self.scrollNode.addSubnode(self.textNode) self.scrollNode.addSubnode(self.buttonNode) @@ -257,7 +259,8 @@ private final class WalletWordDisplayScreenNode: ViewControllerTracingNode, UISc transition.updateFrame(node: self.scrollNode, frame: CGRect(origin: CGPoint(), size: layout.size)) - let iconSize = self.iconNode.image?.size ?? CGSize(width: 50.0, height: 50.0) + let iconSize = CGSize(width: 140.0, height: 140.0) + self.animationNode.updateLayout(size: iconSize) let titleSize = self.titleNode.updateLayout(CGSize(width: layout.size.width - sideInset * 2.0, height: layout.size.height)) let navigationTitleSize = self.navigationTitleNode.updateLayout(CGSize(width: layout.size.width - sideInset * 2.0, height: layout.size.height)) @@ -268,7 +271,7 @@ private final class WalletWordDisplayScreenNode: ViewControllerTracingNode, UISc let contentVerticalOrigin = navigationHeight + 10.0 let iconFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - iconSize.width) / 2.0), y: contentVerticalOrigin), size: iconSize) - transition.updateFrameAdditive(node: self.iconNode, frame: iconFrame) + transition.updateFrameAdditive(node: self.animationNode, frame: iconFrame) let titleFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - titleSize.width) / 2.0), y: iconFrame.maxY + iconSpacing), size: titleSize) transition.updateFrameAdditive(node: self.titleNode, frame: titleFrame) let textFrame = CGRect(origin: CGPoint(x: floor((layout.size.width - textSize.width) / 2.0), y: titleFrame.maxY + titleSpacing), size: textSize) diff --git a/submodules/WalletUI/WalletUI.xcodeproj/project.pbxproj b/submodules/WalletUI/WalletUI.xcodeproj/project.pbxproj index 66750110b2..05ecee3e32 100644 --- a/submodules/WalletUI/WalletUI.xcodeproj/project.pbxproj +++ b/submodules/WalletUI/WalletUI.xcodeproj/project.pbxproj @@ -931,6 +931,17 @@ sourceTree SOURCE_ROOT + 1DD70E293117BD4900000000 + + isa + PBXFileReference + name + WalletRefreshNode.swift + path + Sources/WalletRefreshNode.swift + sourceTree + SOURCE_ROOT + 1DD70E2948FA33F200000000 isa @@ -1015,6 +1026,7 @@ 1DD70E296D49CFFF00000000 1DD70E29DE28A96800000000 1DD70E2979DDEBBB00000000 + 1DD70E293117BD4900000000 1DD70E2948FA33F200000000 1DD70E2986544B8D00000000 1DD70E2964068E1100000000 @@ -1109,6 +1121,13 @@ fileRef 1DD70E2979DDEBBB00000000 + E7A30F043117BD4900000000 + + isa + PBXBuildFile + fileRef + 1DD70E293117BD4900000000 + E7A30F0448FA33F200000000 isa @@ -1165,6 +1184,7 @@ E7A30F046D49CFFF00000000 E7A30F04DE28A96800000000 E7A30F0479DDEBBB00000000 + E7A30F043117BD4900000000 E7A30F0448FA33F200000000 E7A30F0486544B8D00000000 E7A30F0464068E1100000000 diff --git a/submodules/ton/tonlib-src/CMakeLists.txt b/submodules/ton/tonlib-src/CMakeLists.txt index 7f01f988b1..a2bb0676cb 100644 --- a/submodules/ton/tonlib-src/CMakeLists.txt +++ b/submodules/ton/tonlib-src/CMakeLists.txt @@ -375,6 +375,10 @@ target_link_libraries(test-tonlib tdutils tdactor adnllite tl_api ton_crypto ton add_executable(test-tonlib-offline test/test-td-main.cpp ${TONLIB_OFFLINE_TEST_SOURCE}) target_link_libraries(test-tonlib-offline tdutils tdactor adnllite tl_api ton_crypto ton_block fift-lib tl_tonlib_api tonlib) + +if (NOT CMAKE_CROSSCOMPILING) + add_dependencies(test-tonlib-offline gen_fif) +endif() #END tonlib diff --git a/submodules/ton/tonlib-src/crypto/block/check-proof.cpp b/submodules/ton/tonlib-src/crypto/block/check-proof.cpp index a856c876ee..c459b1b458 100644 --- a/submodules/ton/tonlib-src/crypto/block/check-proof.cpp +++ b/submodules/ton/tonlib-src/crypto/block/check-proof.cpp @@ -32,7 +32,7 @@ namespace block { using namespace std::literals::string_literals; td::Status check_block_header_proof(td::Ref root, ton::BlockIdExt blkid, ton::Bits256* store_shard_hash_to, - bool check_state_hash) { + bool check_state_hash, td::uint32* save_utime) { ton::RootHash vhash{root->get_hash().bits()}; if (vhash != blkid.root_hash) { return td::Status::Error(PSTRING() << " block header for block " << blkid.to_str() << " has incorrect root hash " @@ -47,6 +47,9 @@ td::Status check_block_header_proof(td::Ref root, ton::BlockIdExt blki if (!(tlb::unpack_cell(root, blk) && tlb::unpack_cell(blk.info, info))) { return td::Status::Error(std::string{"cannot unpack header for block "} + blkid.to_str()); } + if (save_utime) { + *save_utime = info.gen_utime; + } if (store_shard_hash_to) { vm::CellSlice upd_cs{vm::NoVmSpec(), blk.state_update}; if (!(upd_cs.is_special() && upd_cs.prefetch_long(8) == 4 // merkle update @@ -153,7 +156,8 @@ td::Status check_shard_proof(ton::BlockIdExt blk, ton::BlockIdExt shard_blk, td: } td::Status check_account_proof(td::Slice proof, ton::BlockIdExt shard_blk, const block::StdAddress& addr, - td::Ref root, ton::LogicalTime* last_trans_lt, ton::Bits256* last_trans_hash) { + td::Ref root, ton::LogicalTime* last_trans_lt, ton::Bits256* last_trans_hash, + td::uint32* save_utime) { TRY_RESULT_PREFIX(Q_roots, vm::std_boc_deserialize_multi(std::move(proof)), "cannot deserialize account proof"); if (Q_roots.size() != 2) { return td::Status::Error(PSLICE() << "account state proof must have exactly two roots"); @@ -169,9 +173,9 @@ td::Status check_account_proof(td::Slice proof, ton::BlockIdExt shard_blk, const return td::Status::Error("account state proof is invalid"); } ton::Bits256 state_hash = state_root->get_hash().bits(); - TRY_STATUS_PREFIX( - check_block_header_proof(vm::MerkleProof::virtualize(std::move(Q_roots[0]), 1), shard_blk, &state_hash, true), - "error in account shard block header proof : "); + TRY_STATUS_PREFIX(check_block_header_proof(vm::MerkleProof::virtualize(std::move(Q_roots[0]), 1), shard_blk, + &state_hash, true, save_utime), + "error in account shard block header proof : "); block::gen::ShardStateUnsplit::Record sstate; if (!(tlb::unpack_cell(std::move(state_root), sstate))) { return td::Status::Error("cannot unpack state header"); @@ -233,8 +237,8 @@ td::Result AccountState::validate(ton::BlockIdExt ref_blk, b TRY_STATUS(block::check_shard_proof(blk, shard_blk, shard_proof.as_slice())); Info res; - TRY_STATUS( - block::check_account_proof(proof.as_slice(), shard_blk, addr, root, &res.last_trans_lt, &res.last_trans_hash)); + TRY_STATUS(block::check_account_proof(proof.as_slice(), shard_blk, addr, root, &res.last_trans_lt, + &res.last_trans_hash, &res.gen_utime)); res.root = std::move(root); return res; diff --git a/submodules/ton/tonlib-src/crypto/block/check-proof.h b/submodules/ton/tonlib-src/crypto/block/check-proof.h index 664028155d..87d6b442b7 100644 --- a/submodules/ton/tonlib-src/crypto/block/check-proof.h +++ b/submodules/ton/tonlib-src/crypto/block/check-proof.h @@ -25,11 +25,12 @@ namespace block { using td::Ref; td::Status check_block_header_proof(td::Ref root, ton::BlockIdExt blkid, - ton::Bits256* store_shard_hash_to = nullptr, bool check_state_hash = false); + ton::Bits256* store_shard_hash_to = nullptr, bool check_state_hash = false, + td::uint32* save_utime = nullptr); td::Status check_shard_proof(ton::BlockIdExt blk, ton::BlockIdExt shard_blk, td::Slice shard_proof); td::Status check_account_proof(td::Slice proof, ton::BlockIdExt shard_blk, const block::StdAddress& addr, td::Ref root, ton::LogicalTime* last_trans_lt = nullptr, - ton::Bits256* last_trans_hash = nullptr); + ton::Bits256* last_trans_hash = nullptr, td::uint32* save_utime = nullptr); td::Result check_state_proof(ton::BlockIdExt blkid, td::Slice proof); td::Result> check_extract_state_proof(ton::BlockIdExt blkid, td::Slice proof, td::Slice data); @@ -47,6 +48,7 @@ struct AccountState { td::Ref root; ton::LogicalTime last_trans_lt = 0; ton::Bits256 last_trans_hash; + td::uint32 gen_utime{0}; }; td::Result validate(ton::BlockIdExt ref_blk, block::StdAddress addr) const; diff --git a/submodules/ton/tonlib-src/crypto/fift/IntCtx.h b/submodules/ton/tonlib-src/crypto/fift/IntCtx.h index 753d2cb1d4..1adb8f325a 100644 --- a/submodules/ton/tonlib-src/crypto/fift/IntCtx.h +++ b/submodules/ton/tonlib-src/crypto/fift/IntCtx.h @@ -77,6 +77,7 @@ struct IntCtx { vm::TonDb* ton_db{nullptr}; Dictionary* dictionary{nullptr}; SourceLookup* source_lookup{nullptr}; + int* now{nullptr}; private: std::string str; diff --git a/submodules/ton/tonlib-src/crypto/fift/SourceLookup.h b/submodules/ton/tonlib-src/crypto/fift/SourceLookup.h index 73d2cd918f..33ee71d4ef 100644 --- a/submodules/ton/tonlib-src/crypto/fift/SourceLookup.h +++ b/submodules/ton/tonlib-src/crypto/fift/SourceLookup.h @@ -20,6 +20,7 @@ #include #include "td/utils/Status.h" +#include "td/utils/Time.h" namespace fift { class FileLoader { @@ -43,10 +44,21 @@ class OsFileLoader : public FileLoader { bool is_file_exists(td::CSlice filename) override; }; +class OsTime { + public: + virtual ~OsTime() = default; + virtual td::uint32 now() = 0; +}; + +//TODO: rename SourceLookup class SourceLookup { public: SourceLookup() = default; - explicit SourceLookup(std::unique_ptr file_loader) : file_loader_(std::move(file_loader)) { + explicit SourceLookup(std::unique_ptr file_loader, std::unique_ptr os_time = {}) + : file_loader_(std::move(file_loader)), os_time_(std::move(os_time)) { + } + void set_os_time(std::unique_ptr os_time) { + os_time_ = std::move(os_time); } void add_include_path(td::string path); td::Result lookup_source(std::string filename, std::string current_dir); @@ -63,9 +75,16 @@ class SourceLookup { bool is_file_exists(td::CSlice filename) { return file_loader_->is_file_exists(filename); } + td::uint32 now() { + if (os_time_) { + return os_time_->now(); + } + return static_cast(td::Time::now()); + } protected: std::unique_ptr file_loader_; + std::unique_ptr os_time_; std::vector source_include_path_; }; } // namespace fift diff --git a/submodules/ton/tonlib-src/crypto/fift/utils.cpp b/submodules/ton/tonlib-src/crypto/fift/utils.cpp index e5f9a597e5..1c16b2cd4a 100644 --- a/submodules/ton/tonlib-src/crypto/fift/utils.cpp +++ b/submodules/ton/tonlib-src/crypto/fift/utils.cpp @@ -163,13 +163,15 @@ td::Result create_mem_source_lookup(std::string main, std::s return create_source_lookup(main, need_preamble, need_asm, need_ton_util, fift_dir); } -td::Result> compile_asm(td::Slice asm_code, std::string fift_dir) { +td::Result> compile_asm(td::Slice asm_code, std::string fift_dir, bool is_raw) { std::stringstream ss; TRY_RESULT(source_lookup, - create_source_lookup(PSTRING() << "\"Asm.fif\" include\n<{ " << asm_code << "\n}>c boc>B \"res\" B>file", + create_source_lookup(PSTRING() << "\"Asm.fif\" include\n " << (is_raw ? "<{" : "") << asm_code << "\n" + << (is_raw ? "}>c" : "") << " boc>B \"res\" B>file", true, true, true, fift_dir)); TRY_RESULT(res, run_fift(std::move(source_lookup), &ss)); TRY_RESULT(boc, res.read_file("res")); return vm::std_boc_deserialize(std::move(boc.data)); } + } // namespace fift diff --git a/submodules/ton/tonlib-src/crypto/fift/utils.h b/submodules/ton/tonlib-src/crypto/fift/utils.h index e6ed145287..e17eb8ae2c 100644 --- a/submodules/ton/tonlib-src/crypto/fift/utils.h +++ b/submodules/ton/tonlib-src/crypto/fift/utils.h @@ -31,5 +31,5 @@ td::Result create_mem_source_lookup(std::string main, std::s bool need_ton_util = true); td::Result mem_run_fift(std::string source, std::vector args = {}, std::string fift_dir = ""); td::Result mem_run_fift(SourceLookup source_lookup, std::vector args); -td::Result> compile_asm(td::Slice asm_code, std::string fift_dir = ""); +td::Result> compile_asm(td::Slice asm_code, std::string fift_dir = "", bool is_raw = true); } // namespace fift diff --git a/submodules/ton/tonlib-src/crypto/fift/words.cpp b/submodules/ton/tonlib-src/crypto/fift/words.cpp index 5c217fad44..01dcf802c8 100644 --- a/submodules/ton/tonlib-src/crypto/fift/words.cpp +++ b/submodules/ton/tonlib-src/crypto/fift/words.cpp @@ -1289,8 +1289,8 @@ void interpret_file_exists(IntCtx& ctx) { // custom and crypto -void interpret_now(vm::Stack& stack) { - stack.push_smallint(std::time(nullptr)); +void interpret_now(IntCtx& ctx) { + ctx.stack.push_smallint(ctx.source_lookup->now()); } void interpret_new_keypair(vm::Stack& stack) { @@ -2534,7 +2534,7 @@ void init_words_common(Dictionary& d) { d.def_ctx_word("B>file ", interpret_write_file); d.def_ctx_word("file-exists? ", interpret_file_exists); // custom & crypto - d.def_stack_word("now ", interpret_now); + d.def_ctx_word("now ", interpret_now); d.def_stack_word("newkeypair ", interpret_new_keypair); d.def_stack_word("priv>pub ", interpret_priv_key_to_pub); d.def_stack_word("ed25519_sign ", interpret_ed25519_sign); diff --git a/submodules/ton/tonlib-src/crypto/smartcont/new-wallet-v2.fif b/submodules/ton/tonlib-src/crypto/smartcont/new-wallet-v2.fif new file mode 100644 index 0000000000..eac43b168b --- /dev/null +++ b/submodules/ton/tonlib-src/crypto/smartcont/new-wallet-v2.fif @@ -0,0 +1,62 @@ +#!/usr/bin/env fift -s +"TonUtil.fif" include +"Asm.fif" include + +{ ."usage: " @' $0 type ." []" cr + ."Creates a new advanced wallet in specified workchain, with private key saved to or loaded from .pk" cr + ."('new-wallet.pk' by default)" cr 1 halt +} : usage +$# 1- -2 and ' usage if + +$1 parse-workchain-id =: wc // set workchain id from command line argument +def? $2 { @' $2 } { "new-wallet" } cond constant file-base + +."Creating new advanced wallet in workchain " wc . cr + +// Create new advanced wallet; code adapted from `wallet-code.fif` +<{ SETCP0 DUP IFNOTRET // return if recv_internal + DUP 85143 INT EQUAL IFJMP:<{ // "seqno" get-method + DROP c4 PUSHCTR CTOS 32 PLDU // cnt + }> + INC 32 THROWIF // fail unless recv_external + 9 PUSHPOW2 LDSLICEX DUP 32 LDU 32 LDU // signature in_msg msg_seqno valid_until cs + SWAP NOW LEQ 35 THROWIF // signature in_msg msg_seqno cs + c4 PUSH CTOS 32 LDU 256 LDU ENDS // signature in_msg msg_seqno cs stored_seqno public_key + s3 s1 XCPU // signature in_msg public_key cs stored_seqno msg_seqno stored_seqno + EQUAL 33 THROWIFNOT // signature in_msg public_key cs stored_seqno + s0 s3 XCHG HASHSU // signature stored_seqno public_key cs hash + s0 s4 s2 XC2PU CHKSIGNU 34 THROWIFNOT // cs stored_seqno public_key + ACCEPT + s0 s2 XCHG // public_key stored_seqno cs + WHILE:<{ + DUP SREFS // public_key stored_seqno cs _40 + }>DO<{ // public_key stored_seqno cs + // 3 INT 35 LSHIFT# 3 INT RAWRESERVE // reserve all but 103 Grams from the balance + 8 LDU LDREF s0 s2 XCHG // public_key stored_seqno cs _45 mode + SENDRAWMSG // public_key stored_seqno cs + }> + ENDS INC // public_key seqno' + NEWC 32 STU 256 STU ENDC c4 POP +}>c // >libref +// code + // data +null // no libraries + // create StateInit +dup ."StateInit: " +dup ."signing message: " +dup ."External message for initialization is " B dup Bx. cr +file-base +"-query.boc" tuck B>file +."(Saved wallet creating query to file " type .")" cr diff --git a/submodules/ton/tonlib-src/crypto/smartcont/testgiver.fif b/submodules/ton/tonlib-src/crypto/smartcont/testgiver.fif index bd9f8860c3..fe812487a5 100644 --- a/submodules/ton/tonlib-src/crypto/smartcont/testgiver.fif +++ b/submodules/ton/tonlib-src/crypto/smartcont/testgiver.fif @@ -24,7 +24,7 @@ dest_addr 2dup bounce 7 + .Addr ." = " .addr // create a message (NB: 01b00.., b = bounce) + amount Gram, 0 9 64 32 + + 1+ 1+ u, 0 32 u, "GIFT" $, b> dup ."enveloping message: " [-B ] []" cr + ."Creates a request to advanced wallet created by new-wallet-v2.fif, with private key loaded from file .pk " + ."and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" cr 1 halt +} : usage +def? $6 { @' $5 "-B" $= { @' $6 =: body-boc-file [forget] $6 def? $7 { @' $7 =: $5 [forget] $7 } { [forget] $5 } cond + @' $# 2- =: $# } if } if +$# dup 4 < swap 5 > or ' usage if + +true constant bounce + +$1 =: file-base +$2 bounce parse-load-address =: bounce 2=: dest_addr +$3 parse-int =: seqno +$4 $>GR =: amount +def? $5 { @' $5 } { "wallet-query" } cond constant savefile +3 constant send-mode // mode for SENDRAWMSG: +1 - sender pays fees, +2 - ignore errors +60 constant timeout // external message expires in 60 seconds + +file-base +".addr" load-address +2dup 2constant wallet_addr +."Source wallet address = " 2dup .addr cr 6 .Addr cr +file-base +".pk" load-keypair nip constant wallet_pk + +def? body-boc-file { @' body-boc-file file>B B>boc } { } cond +constant body-cell + +."Transferring " amount .GR ."to account " +dest_addr 2dup bounce 7 + .Addr ." = " .addr +."seqno=0x" seqno x. ."bounce=" bounce . cr +."Body of transfer message is " body-cell + +dup ."signing message: " +dup ."resulting external message: " B dup Bx. cr +savefile +".boc" tuck B>file +."Query expires in " timeout . ."seconds" cr +."(Saved to file " type .")" cr diff --git a/submodules/ton/tonlib-src/crypto/smartcont/wallet.fif b/submodules/ton/tonlib-src/crypto/smartcont/wallet.fif index 8742e3cc69..1159796399 100644 --- a/submodules/ton/tonlib-src/crypto/smartcont/wallet.fif +++ b/submodules/ton/tonlib-src/crypto/smartcont/wallet.fif @@ -5,9 +5,9 @@ ."Creates a request to simple wallet created by new-wallet.fif, with private key loaded from file .pk " ."and address from .addr, and saves it into .boc ('wallet-query.boc' by default)" cr 1 halt } : usage -$# dup 4 < swap 5 > or ' usage if def? $6 { @' $5 "-B" $= { @' $6 =: body-boc-file [forget] $6 def? $7 { @' $7 =: $5 [forget] $7 } { [forget] $5 } cond @' $# 2- =: $# } if } if +$# dup 4 < swap 5 > or ' usage if true constant bounce @@ -16,13 +16,14 @@ $2 bounce parse-load-address =: bounce 2=: dest_addr $3 parse-int =: seqno $4 $>GR =: amount def? $5 { @' $5 } { "wallet-query" } cond constant savefile +3 constant send-mode // mode for SENDRAWMSG: +1 - sender pays fees, +2 - ignore errors file-base +".addr" load-address 2dup 2constant wallet_addr ."Source wallet address = " 2dup .addr cr 6 .Addr cr file-base +".pk" load-keypair nip constant wallet_pk -def? body-boc-file { @' body-boc-file file>B B>boc } { } cond +def? body-boc-file { @' body-boc-file file>B B>boc } { } cond constant body-cell ."Transferring " amount .GR ."to account " @@ -34,7 +35,7 @@ dest_addr 2dup bounce 7 + .Addr ." = " .addr - + dup ."signing message: " "); + store_field_end(); + } + template void store_field(const char *name, const T &value) { store_field_begin(name); @@ -240,6 +247,12 @@ class TlStorerToString { store_field_end(); } + void store_bytes_field(const char *name, const SecureString &value) { + store_field_begin(name); + result.append(""); + store_field_end(); + } + template void store_bytes_field(const char *name, const BytesT &value) { static const char *hex = "0123456789ABCDEF"; diff --git a/submodules/ton/tonlib-src/tl/generate/auto/tl/ton_api.cpp b/submodules/ton/tonlib-src/tl/generate/auto/tl/ton_api.cpp index bfbdee4ed8..c6c6f32804 100644 --- a/submodules/ton/tonlib-src/tl/generate/auto/tl/ton_api.cpp +++ b/submodules/ton/tonlib-src/tl/generate/auto/tl/ton_api.cpp @@ -446,6 +446,8 @@ object_ptr Object::fetch(td::TlParser &p) { return tonNode_blockIdExt::fetch(p); case tonNode_blockSignature::ID: return tonNode_blockSignature::fetch(p); + case tonNode_blocksDescription::ID: + return tonNode_blocksDescription::fetch(p); case tonNode_blockBroadcast::ID: return tonNode_blockBroadcast::fetch(p); case tonNode_ihrMessageBroadcast::ID: @@ -454,8 +456,16 @@ object_ptr Object::fetch(td::TlParser &p) { return tonNode_externalMessageBroadcast::fetch(p); case tonNode_newShardBlockBroadcast::ID: return tonNode_newShardBlockBroadcast::fetch(p); + case tonNode_capabilities::ID: + return tonNode_capabilities::fetch(p); case tonNode_data::ID: return tonNode_data::fetch(p); + case tonNode_dataFull::ID: + return tonNode_dataFull::fetch(p); + case tonNode_dataFullEmpty::ID: + return tonNode_dataFullEmpty::fetch(p); + case tonNode_dataList::ID: + return tonNode_dataList::fetch(p); case tonNode_externalMessage::ID: return tonNode_externalMessage::fetch(p); case tonNode_ihrMessage::ID: @@ -482,6 +492,8 @@ object_ptr Object::fetch(td::TlParser &p) { return tonNode_sessionId::fetch(p); case tonNode_shardPublicOverlayId::ID: return tonNode_shardPublicOverlayId::fetch(p); + case tonNode_success::ID: + return tonNode_success::fetch(p); case tonNode_zeroStateIdExt::ID: return tonNode_zeroStateIdExt::fetch(p); case validator_group::ID: @@ -632,24 +644,44 @@ object_ptr Function::fetch(td::TlParser &p) { return tcp_ping::fetch(p); case tonNode_downloadBlock::ID: return tonNode_downloadBlock::fetch(p); + case tonNode_downloadBlockFull::ID: + return tonNode_downloadBlockFull::fetch(p); case tonNode_downloadBlockProof::ID: return tonNode_downloadBlockProof::fetch(p); case tonNode_downloadBlockProofLink::ID: return tonNode_downloadBlockProofLink::fetch(p); + case tonNode_downloadBlockProofLinks::ID: + return tonNode_downloadBlockProofLinks::fetch(p); + case tonNode_downloadBlockProofs::ID: + return tonNode_downloadBlockProofs::fetch(p); + case tonNode_downloadBlocks::ID: + return tonNode_downloadBlocks::fetch(p); + case tonNode_downloadNextBlockFull::ID: + return tonNode_downloadNextBlockFull::fetch(p); case tonNode_downloadPersistentState::ID: return tonNode_downloadPersistentState::fetch(p); case tonNode_downloadPersistentStateSlice::ID: return tonNode_downloadPersistentStateSlice::fetch(p); case tonNode_downloadZeroState::ID: return tonNode_downloadZeroState::fetch(p); + case tonNode_getCapabilities::ID: + return tonNode_getCapabilities::fetch(p); case tonNode_getNextBlockDescription::ID: return tonNode_getNextBlockDescription::fetch(p); + case tonNode_getNextBlocksDescription::ID: + return tonNode_getNextBlocksDescription::fetch(p); case tonNode_getNextKeyBlockIds::ID: return tonNode_getNextKeyBlockIds::fetch(p); + case tonNode_getPrevBlocksDescription::ID: + return tonNode_getPrevBlocksDescription::fetch(p); case tonNode_prepareBlock::ID: return tonNode_prepareBlock::fetch(p); case tonNode_prepareBlockProof::ID: return tonNode_prepareBlockProof::fetch(p); + case tonNode_prepareBlockProofs::ID: + return tonNode_prepareBlockProofs::fetch(p); + case tonNode_prepareBlocks::ID: + return tonNode_prepareBlocks::fetch(p); case tonNode_preparePersistentState::ID: return tonNode_preparePersistentState::fetch(p); case tonNode_prepareZeroState::ID: @@ -10634,6 +10666,50 @@ void tonNode_blockSignature::store(td::TlStorerToString &s, const char *field_na } } +tonNode_blocksDescription::tonNode_blocksDescription() + : ids_() + , incomplete_() +{} + +tonNode_blocksDescription::tonNode_blocksDescription(std::vector> &&ids_, bool incomplete_) + : ids_(std::move(ids_)) + , incomplete_(incomplete_) +{} + +const std::int32_t tonNode_blocksDescription::ID; + +object_ptr tonNode_blocksDescription::fetch(td::TlParser &p) { + return make_object(p); +} + +tonNode_blocksDescription::tonNode_blocksDescription(td::TlParser &p) +#define FAIL(error) p.set_error(error) + : ids_(TlFetchVector>::parse(p)) + , incomplete_(TlFetchBool::parse(p)) +#undef FAIL +{} + +void tonNode_blocksDescription::store(td::TlStorerCalcLength &s) const { + (void)sizeof(s); + TlStoreVector::store(ids_, s); + TlStoreBool::store(incomplete_, s); +} + +void tonNode_blocksDescription::store(td::TlStorerUnsafe &s) const { + (void)sizeof(s); + TlStoreVector::store(ids_, s); + TlStoreBool::store(incomplete_, s); +} + +void tonNode_blocksDescription::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "tonNode_blocksDescription"); + { const std::vector> &v = ids_; const std::uint32_t multiplicity = static_cast(v.size()); const auto vector_name = "vector[" + td::to_string(multiplicity)+ "]"; s.store_class_begin("ids", 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("incomplete", incomplete_); + s.store_class_end(); + } +} + object_ptr tonNode_Broadcast::fetch(td::TlParser &p) { #define FAIL(error) p.set_error(error); return nullptr; int constructor = p.fetch_int(); @@ -10834,6 +10910,50 @@ void tonNode_newShardBlockBroadcast::store(td::TlStorerToString &s, const char * } } +tonNode_capabilities::tonNode_capabilities() + : version_() + , capabilities_() +{} + +tonNode_capabilities::tonNode_capabilities(std::int32_t version_, std::int64_t capabilities_) + : version_(version_) + , capabilities_(capabilities_) +{} + +const std::int32_t tonNode_capabilities::ID; + +object_ptr tonNode_capabilities::fetch(td::TlParser &p) { + return make_object(p); +} + +tonNode_capabilities::tonNode_capabilities(td::TlParser &p) +#define FAIL(error) p.set_error(error) + : version_(TlFetchInt::parse(p)) + , capabilities_(TlFetchLong::parse(p)) +#undef FAIL +{} + +void tonNode_capabilities::store(td::TlStorerCalcLength &s) const { + (void)sizeof(s); + TlStoreBinary::store(version_, s); + TlStoreBinary::store(capabilities_, s); +} + +void tonNode_capabilities::store(td::TlStorerUnsafe &s) const { + (void)sizeof(s); + TlStoreBinary::store(version_, s); + TlStoreBinary::store(capabilities_, s); +} + +void tonNode_capabilities::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "tonNode_capabilities"); + s.store_field("version", version_); + s.store_field("capabilities", capabilities_); + s.store_class_end(); + } +} + tonNode_data::tonNode_data() : data_() {} @@ -10872,6 +10992,145 @@ void tonNode_data::store(td::TlStorerToString &s, const char *field_name) const } } +object_ptr tonNode_DataFull::fetch(td::TlParser &p) { +#define FAIL(error) p.set_error(error); return nullptr; + int constructor = p.fetch_int(); + switch (constructor) { + case tonNode_dataFull::ID: + return tonNode_dataFull::fetch(p); + case tonNode_dataFullEmpty::ID: + return tonNode_dataFullEmpty::fetch(p); + default: + FAIL(PSTRING() << "Unknown constructor found " << td::format::as_hex(constructor)); + } +#undef FAIL +} + +tonNode_dataFull::tonNode_dataFull() + : id_() + , proof_() + , block_() + , is_link_() +{} + +tonNode_dataFull::tonNode_dataFull(object_ptr &&id_, td::BufferSlice &&proof_, td::BufferSlice &&block_, bool is_link_) + : id_(std::move(id_)) + , proof_(std::move(proof_)) + , block_(std::move(block_)) + , is_link_(is_link_) +{} + +const std::int32_t tonNode_dataFull::ID; + +object_ptr tonNode_dataFull::fetch(td::TlParser &p) { + return make_object(p); +} + +tonNode_dataFull::tonNode_dataFull(td::TlParser &p) +#define FAIL(error) p.set_error(error) + : id_(TlFetchObject::parse(p)) + , proof_(TlFetchBytes::parse(p)) + , block_(TlFetchBytes::parse(p)) + , is_link_(TlFetchBool::parse(p)) +#undef FAIL +{} + +void tonNode_dataFull::store(td::TlStorerCalcLength &s) const { + (void)sizeof(s); + TlStoreObject::store(id_, s); + TlStoreString::store(proof_, s); + TlStoreString::store(block_, s); + TlStoreBool::store(is_link_, s); +} + +void tonNode_dataFull::store(td::TlStorerUnsafe &s) const { + (void)sizeof(s); + TlStoreObject::store(id_, s); + TlStoreString::store(proof_, s); + TlStoreString::store(block_, s); + TlStoreBool::store(is_link_, s); +} + +void tonNode_dataFull::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "tonNode_dataFull"); + if (id_ == nullptr) { s.store_field("id", "null"); } else { id_->store(s, "id"); } + s.store_bytes_field("proof", proof_); + s.store_bytes_field("block", block_); + s.store_field("is_link", is_link_); + s.store_class_end(); + } +} + +tonNode_dataFullEmpty::tonNode_dataFullEmpty() { +} + +const std::int32_t tonNode_dataFullEmpty::ID; + +object_ptr tonNode_dataFullEmpty::fetch(td::TlParser &p) { + return make_object(p); +} + +tonNode_dataFullEmpty::tonNode_dataFullEmpty(td::TlParser &p) +#define FAIL(error) p.set_error(error) +#undef FAIL +{ + (void)p; +} + +void tonNode_dataFullEmpty::store(td::TlStorerCalcLength &s) const { + (void)sizeof(s); +} + +void tonNode_dataFullEmpty::store(td::TlStorerUnsafe &s) const { + (void)sizeof(s); +} + +void tonNode_dataFullEmpty::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "tonNode_dataFullEmpty"); + s.store_class_end(); + } +} + +tonNode_dataList::tonNode_dataList() + : data_() +{} + +tonNode_dataList::tonNode_dataList(std::vector &&data_) + : data_(std::move(data_)) +{} + +const std::int32_t tonNode_dataList::ID; + +object_ptr tonNode_dataList::fetch(td::TlParser &p) { + return make_object(p); +} + +tonNode_dataList::tonNode_dataList(td::TlParser &p) +#define FAIL(error) p.set_error(error) + : data_(TlFetchVector>::parse(p)) +#undef FAIL +{} + +void tonNode_dataList::store(td::TlStorerCalcLength &s) const { + (void)sizeof(s); + TlStoreVector::store(data_, s); +} + +void tonNode_dataList::store(td::TlStorerUnsafe &s) const { + (void)sizeof(s); + TlStoreVector::store(data_, s); +} + +void tonNode_dataList::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "tonNode_dataList"); + { const std::vector &v = data_; const std::uint32_t multiplicity = static_cast(v.size()); const auto vector_name = "vector[" + td::to_string(multiplicity)+ "]"; s.store_class_begin("data", vector_name.c_str()); for (std::uint32_t i = 0; i < multiplicity; i++) { s.store_bytes_field("", v[i]); } s.store_class_end(); } + s.store_class_end(); + } +} + tonNode_externalMessage::tonNode_externalMessage() : data_() {} @@ -11415,6 +11674,37 @@ void tonNode_shardPublicOverlayId::store(td::TlStorerToString &s, const char *fi } } +tonNode_success::tonNode_success() { +} + +const std::int32_t tonNode_success::ID; + +object_ptr tonNode_success::fetch(td::TlParser &p) { + return make_object(p); +} + +tonNode_success::tonNode_success(td::TlParser &p) +#define FAIL(error) p.set_error(error) +#undef FAIL +{ + (void)p; +} + +void tonNode_success::store(td::TlStorerCalcLength &s) const { + (void)sizeof(s); +} + +void tonNode_success::store(td::TlStorerUnsafe &s) const { + (void)sizeof(s); +} + +void tonNode_success::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "tonNode_success"); + s.store_class_end(); + } +} + tonNode_zeroStateIdExt::tonNode_zeroStateIdExt() : workchain_() , root_hash_() @@ -14937,6 +15227,52 @@ tonNode_downloadBlock::ReturnType tonNode_downloadBlock::fetch_result(td::TlPars #undef FAIL } +tonNode_downloadBlockFull::tonNode_downloadBlockFull() + : block_() +{} + +tonNode_downloadBlockFull::tonNode_downloadBlockFull(object_ptr &&block_) + : block_(std::move(block_)) +{} + +const std::int32_t tonNode_downloadBlockFull::ID; + +object_ptr tonNode_downloadBlockFull::fetch(td::TlParser &p) { + return make_object(p); +} + +tonNode_downloadBlockFull::tonNode_downloadBlockFull(td::TlParser &p) +#define FAIL(error) p.set_error(error) + : block_(TlFetchObject::parse(p)) +#undef FAIL +{} + +void tonNode_downloadBlockFull::store(td::TlStorerCalcLength &s) const { + (void)sizeof(s); + s.store_binary(1780991133); + TlStoreObject::store(block_, s); +} + +void tonNode_downloadBlockFull::store(td::TlStorerUnsafe &s) const { + (void)sizeof(s); + s.store_binary(1780991133); + TlStoreObject::store(block_, s); +} + +void tonNode_downloadBlockFull::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "tonNode_downloadBlockFull"); + if (block_ == nullptr) { s.store_field("block", "null"); } else { block_->store(s, "block"); } + s.store_class_end(); + } +} + +tonNode_downloadBlockFull::ReturnType tonNode_downloadBlockFull::fetch_result(td::TlParser &p) { +#define FAIL(error) p.set_error(error); return ReturnType() + return TlFetchObject::parse(p); +#undef FAIL +} + tonNode_downloadBlockProof::tonNode_downloadBlockProof() : block_() {} @@ -15029,6 +15365,190 @@ tonNode_downloadBlockProofLink::ReturnType tonNode_downloadBlockProofLink::fetch #undef FAIL } +tonNode_downloadBlockProofLinks::tonNode_downloadBlockProofLinks() + : blocks_() +{} + +tonNode_downloadBlockProofLinks::tonNode_downloadBlockProofLinks(std::vector> &&blocks_) + : blocks_(std::move(blocks_)) +{} + +const std::int32_t tonNode_downloadBlockProofLinks::ID; + +object_ptr tonNode_downloadBlockProofLinks::fetch(td::TlParser &p) { + return make_object(p); +} + +tonNode_downloadBlockProofLinks::tonNode_downloadBlockProofLinks(td::TlParser &p) +#define FAIL(error) p.set_error(error) + : blocks_(TlFetchVector>::parse(p)) +#undef FAIL +{} + +void tonNode_downloadBlockProofLinks::store(td::TlStorerCalcLength &s) const { + (void)sizeof(s); + s.store_binary(684796771); + TlStoreVector::store(blocks_, s); +} + +void tonNode_downloadBlockProofLinks::store(td::TlStorerUnsafe &s) const { + (void)sizeof(s); + s.store_binary(684796771); + TlStoreVector::store(blocks_, s); +} + +void tonNode_downloadBlockProofLinks::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "tonNode_downloadBlockProofLinks"); + { const std::vector> &v = blocks_; const std::uint32_t multiplicity = static_cast(v.size()); const auto vector_name = "vector[" + td::to_string(multiplicity)+ "]"; s.store_class_begin("blocks", 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_class_end(); + } +} + +tonNode_downloadBlockProofLinks::ReturnType tonNode_downloadBlockProofLinks::fetch_result(td::TlParser &p) { +#define FAIL(error) p.set_error(error); return ReturnType() + return TlFetchBoxed, 351548179>::parse(p); +#undef FAIL +} + +tonNode_downloadBlockProofs::tonNode_downloadBlockProofs() + : blocks_() +{} + +tonNode_downloadBlockProofs::tonNode_downloadBlockProofs(std::vector> &&blocks_) + : blocks_(std::move(blocks_)) +{} + +const std::int32_t tonNode_downloadBlockProofs::ID; + +object_ptr tonNode_downloadBlockProofs::fetch(td::TlParser &p) { + return make_object(p); +} + +tonNode_downloadBlockProofs::tonNode_downloadBlockProofs(td::TlParser &p) +#define FAIL(error) p.set_error(error) + : blocks_(TlFetchVector>::parse(p)) +#undef FAIL +{} + +void tonNode_downloadBlockProofs::store(td::TlStorerCalcLength &s) const { + (void)sizeof(s); + s.store_binary(-1515170827); + TlStoreVector::store(blocks_, s); +} + +void tonNode_downloadBlockProofs::store(td::TlStorerUnsafe &s) const { + (void)sizeof(s); + s.store_binary(-1515170827); + TlStoreVector::store(blocks_, s); +} + +void tonNode_downloadBlockProofs::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "tonNode_downloadBlockProofs"); + { const std::vector> &v = blocks_; const std::uint32_t multiplicity = static_cast(v.size()); const auto vector_name = "vector[" + td::to_string(multiplicity)+ "]"; s.store_class_begin("blocks", 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_class_end(); + } +} + +tonNode_downloadBlockProofs::ReturnType tonNode_downloadBlockProofs::fetch_result(td::TlParser &p) { +#define FAIL(error) p.set_error(error); return ReturnType() + return TlFetchBoxed, 351548179>::parse(p); +#undef FAIL +} + +tonNode_downloadBlocks::tonNode_downloadBlocks() + : blocks_() +{} + +tonNode_downloadBlocks::tonNode_downloadBlocks(std::vector> &&blocks_) + : blocks_(std::move(blocks_)) +{} + +const std::int32_t tonNode_downloadBlocks::ID; + +object_ptr tonNode_downloadBlocks::fetch(td::TlParser &p) { + return make_object(p); +} + +tonNode_downloadBlocks::tonNode_downloadBlocks(td::TlParser &p) +#define FAIL(error) p.set_error(error) + : blocks_(TlFetchVector>::parse(p)) +#undef FAIL +{} + +void tonNode_downloadBlocks::store(td::TlStorerCalcLength &s) const { + (void)sizeof(s); + s.store_binary(1985594749); + TlStoreVector::store(blocks_, s); +} + +void tonNode_downloadBlocks::store(td::TlStorerUnsafe &s) const { + (void)sizeof(s); + s.store_binary(1985594749); + TlStoreVector::store(blocks_, s); +} + +void tonNode_downloadBlocks::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "tonNode_downloadBlocks"); + { const std::vector> &v = blocks_; const std::uint32_t multiplicity = static_cast(v.size()); const auto vector_name = "vector[" + td::to_string(multiplicity)+ "]"; s.store_class_begin("blocks", 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_class_end(); + } +} + +tonNode_downloadBlocks::ReturnType tonNode_downloadBlocks::fetch_result(td::TlParser &p) { +#define FAIL(error) p.set_error(error); return ReturnType() + return TlFetchBoxed, 351548179>::parse(p); +#undef FAIL +} + +tonNode_downloadNextBlockFull::tonNode_downloadNextBlockFull() + : prev_block_() +{} + +tonNode_downloadNextBlockFull::tonNode_downloadNextBlockFull(object_ptr &&prev_block_) + : prev_block_(std::move(prev_block_)) +{} + +const std::int32_t tonNode_downloadNextBlockFull::ID; + +object_ptr tonNode_downloadNextBlockFull::fetch(td::TlParser &p) { + return make_object(p); +} + +tonNode_downloadNextBlockFull::tonNode_downloadNextBlockFull(td::TlParser &p) +#define FAIL(error) p.set_error(error) + : prev_block_(TlFetchObject::parse(p)) +#undef FAIL +{} + +void tonNode_downloadNextBlockFull::store(td::TlStorerCalcLength &s) const { + (void)sizeof(s); + s.store_binary(1855993674); + TlStoreObject::store(prev_block_, s); +} + +void tonNode_downloadNextBlockFull::store(td::TlStorerUnsafe &s) const { + (void)sizeof(s); + s.store_binary(1855993674); + TlStoreObject::store(prev_block_, s); +} + +void tonNode_downloadNextBlockFull::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "tonNode_downloadNextBlockFull"); + if (prev_block_ == nullptr) { s.store_field("prev_block", "null"); } else { prev_block_->store(s, "prev_block"); } + s.store_class_end(); + } +} + +tonNode_downloadNextBlockFull::ReturnType tonNode_downloadNextBlockFull::fetch_result(td::TlParser &p) { +#define FAIL(error) p.set_error(error); return ReturnType() + return TlFetchObject::parse(p); +#undef FAIL +} + tonNode_downloadPersistentState::tonNode_downloadPersistentState() : block_() , masterchain_block_() @@ -15191,6 +15711,45 @@ tonNode_downloadZeroState::ReturnType tonNode_downloadZeroState::fetch_result(td #undef FAIL } +tonNode_getCapabilities::tonNode_getCapabilities() { +} + +const std::int32_t tonNode_getCapabilities::ID; + +object_ptr tonNode_getCapabilities::fetch(td::TlParser &p) { + return make_object(p); +} + +tonNode_getCapabilities::tonNode_getCapabilities(td::TlParser &p) +#define FAIL(error) p.set_error(error) +#undef FAIL +{ + (void)p; +} + +void tonNode_getCapabilities::store(td::TlStorerCalcLength &s) const { + (void)sizeof(s); + s.store_binary(-555345672); +} + +void tonNode_getCapabilities::store(td::TlStorerUnsafe &s) const { + (void)sizeof(s); + s.store_binary(-555345672); +} + +void tonNode_getCapabilities::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "tonNode_getCapabilities"); + s.store_class_end(); + } +} + +tonNode_getCapabilities::ReturnType tonNode_getCapabilities::fetch_result(td::TlParser &p) { +#define FAIL(error) p.set_error(error); return ReturnType() + return TlFetchBoxed, -172007232>::parse(p); +#undef FAIL +} + tonNode_getNextBlockDescription::tonNode_getNextBlockDescription() : prev_block_() {} @@ -15237,6 +15796,58 @@ tonNode_getNextBlockDescription::ReturnType tonNode_getNextBlockDescription::fet #undef FAIL } +tonNode_getNextBlocksDescription::tonNode_getNextBlocksDescription() + : prev_block_() + , limit_() +{} + +tonNode_getNextBlocksDescription::tonNode_getNextBlocksDescription(object_ptr &&prev_block_, std::int32_t limit_) + : prev_block_(std::move(prev_block_)) + , limit_(limit_) +{} + +const std::int32_t tonNode_getNextBlocksDescription::ID; + +object_ptr tonNode_getNextBlocksDescription::fetch(td::TlParser &p) { + return make_object(p); +} + +tonNode_getNextBlocksDescription::tonNode_getNextBlocksDescription(td::TlParser &p) +#define FAIL(error) p.set_error(error) + : prev_block_(TlFetchObject::parse(p)) + , limit_(TlFetchInt::parse(p)) +#undef FAIL +{} + +void tonNode_getNextBlocksDescription::store(td::TlStorerCalcLength &s) const { + (void)sizeof(s); + s.store_binary(1059590852); + TlStoreObject::store(prev_block_, s); + TlStoreBinary::store(limit_, s); +} + +void tonNode_getNextBlocksDescription::store(td::TlStorerUnsafe &s) const { + (void)sizeof(s); + s.store_binary(1059590852); + TlStoreObject::store(prev_block_, s); + TlStoreBinary::store(limit_, s); +} + +void tonNode_getNextBlocksDescription::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "tonNode_getNextBlocksDescription"); + if (prev_block_ == nullptr) { s.store_field("prev_block", "null"); } else { prev_block_->store(s, "prev_block"); } + s.store_field("limit", limit_); + s.store_class_end(); + } +} + +tonNode_getNextBlocksDescription::ReturnType tonNode_getNextBlocksDescription::fetch_result(td::TlParser &p) { +#define FAIL(error) p.set_error(error); return ReturnType() + return TlFetchBoxed, -701865684>::parse(p); +#undef FAIL +} + tonNode_getNextKeyBlockIds::tonNode_getNextKeyBlockIds() : block_() , max_size_() @@ -15289,6 +15900,64 @@ tonNode_getNextKeyBlockIds::ReturnType tonNode_getNextKeyBlockIds::fetch_result( #undef FAIL } +tonNode_getPrevBlocksDescription::tonNode_getPrevBlocksDescription() + : next_block_() + , limit_() + , cutoff_seqno_() +{} + +tonNode_getPrevBlocksDescription::tonNode_getPrevBlocksDescription(object_ptr &&next_block_, std::int32_t limit_, std::int32_t cutoff_seqno_) + : next_block_(std::move(next_block_)) + , limit_(limit_) + , cutoff_seqno_(cutoff_seqno_) +{} + +const std::int32_t tonNode_getPrevBlocksDescription::ID; + +object_ptr tonNode_getPrevBlocksDescription::fetch(td::TlParser &p) { + return make_object(p); +} + +tonNode_getPrevBlocksDescription::tonNode_getPrevBlocksDescription(td::TlParser &p) +#define FAIL(error) p.set_error(error) + : next_block_(TlFetchObject::parse(p)) + , limit_(TlFetchInt::parse(p)) + , cutoff_seqno_(TlFetchInt::parse(p)) +#undef FAIL +{} + +void tonNode_getPrevBlocksDescription::store(td::TlStorerCalcLength &s) const { + (void)sizeof(s); + s.store_binary(1550675145); + TlStoreObject::store(next_block_, s); + TlStoreBinary::store(limit_, s); + TlStoreBinary::store(cutoff_seqno_, s); +} + +void tonNode_getPrevBlocksDescription::store(td::TlStorerUnsafe &s) const { + (void)sizeof(s); + s.store_binary(1550675145); + TlStoreObject::store(next_block_, s); + TlStoreBinary::store(limit_, s); + TlStoreBinary::store(cutoff_seqno_, s); +} + +void tonNode_getPrevBlocksDescription::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "tonNode_getPrevBlocksDescription"); + if (next_block_ == nullptr) { s.store_field("next_block", "null"); } else { next_block_->store(s, "next_block"); } + s.store_field("limit", limit_); + s.store_field("cutoff_seqno", cutoff_seqno_); + s.store_class_end(); + } +} + +tonNode_getPrevBlocksDescription::ReturnType tonNode_getPrevBlocksDescription::fetch_result(td::TlParser &p) { +#define FAIL(error) p.set_error(error); return ReturnType() + return TlFetchBoxed, -701865684>::parse(p); +#undef FAIL +} + tonNode_prepareBlock::tonNode_prepareBlock() : block_() {} @@ -15387,6 +16056,104 @@ tonNode_prepareBlockProof::ReturnType tonNode_prepareBlockProof::fetch_result(td #undef FAIL } +tonNode_prepareBlockProofs::tonNode_prepareBlockProofs() + : blocks_() + , allow_partial_() +{} + +tonNode_prepareBlockProofs::tonNode_prepareBlockProofs(std::vector> &&blocks_, bool allow_partial_) + : blocks_(std::move(blocks_)) + , allow_partial_(allow_partial_) +{} + +const std::int32_t tonNode_prepareBlockProofs::ID; + +object_ptr tonNode_prepareBlockProofs::fetch(td::TlParser &p) { + return make_object(p); +} + +tonNode_prepareBlockProofs::tonNode_prepareBlockProofs(td::TlParser &p) +#define FAIL(error) p.set_error(error) + : blocks_(TlFetchVector>::parse(p)) + , allow_partial_(TlFetchBool::parse(p)) +#undef FAIL +{} + +void tonNode_prepareBlockProofs::store(td::TlStorerCalcLength &s) const { + (void)sizeof(s); + s.store_binary(-310791496); + TlStoreVector::store(blocks_, s); + TlStoreBool::store(allow_partial_, s); +} + +void tonNode_prepareBlockProofs::store(td::TlStorerUnsafe &s) const { + (void)sizeof(s); + s.store_binary(-310791496); + TlStoreVector::store(blocks_, s); + TlStoreBool::store(allow_partial_, s); +} + +void tonNode_prepareBlockProofs::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "tonNode_prepareBlockProofs"); + { const std::vector> &v = blocks_; const std::uint32_t multiplicity = static_cast(v.size()); const auto vector_name = "vector[" + td::to_string(multiplicity)+ "]"; s.store_class_begin("blocks", 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("allow_partial", allow_partial_); + s.store_class_end(); + } +} + +tonNode_prepareBlockProofs::ReturnType tonNode_prepareBlockProofs::fetch_result(td::TlParser &p) { +#define FAIL(error) p.set_error(error); return ReturnType() + return TlFetchObject::parse(p); +#undef FAIL +} + +tonNode_prepareBlocks::tonNode_prepareBlocks() + : blocks_() +{} + +tonNode_prepareBlocks::tonNode_prepareBlocks(std::vector> &&blocks_) + : blocks_(std::move(blocks_)) +{} + +const std::int32_t tonNode_prepareBlocks::ID; + +object_ptr tonNode_prepareBlocks::fetch(td::TlParser &p) { + return make_object(p); +} + +tonNode_prepareBlocks::tonNode_prepareBlocks(td::TlParser &p) +#define FAIL(error) p.set_error(error) + : blocks_(TlFetchVector>::parse(p)) +#undef FAIL +{} + +void tonNode_prepareBlocks::store(td::TlStorerCalcLength &s) const { + (void)sizeof(s); + s.store_binary(1795140604); + TlStoreVector::store(blocks_, s); +} + +void tonNode_prepareBlocks::store(td::TlStorerUnsafe &s) const { + (void)sizeof(s); + s.store_binary(1795140604); + TlStoreVector::store(blocks_, s); +} + +void tonNode_prepareBlocks::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "tonNode_prepareBlocks"); + { const std::vector> &v = blocks_; const std::uint32_t multiplicity = static_cast(v.size()); const auto vector_name = "vector[" + td::to_string(multiplicity)+ "]"; s.store_class_begin("blocks", 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_class_end(); + } +} + +tonNode_prepareBlocks::ReturnType tonNode_prepareBlocks::fetch_result(td::TlParser &p) { +#define FAIL(error) p.set_error(error); return ReturnType() + return TlFetchObject::parse(p); +#undef FAIL +} + tonNode_preparePersistentState::tonNode_preparePersistentState() : block_() , masterchain_block_() @@ -15546,13 +16313,13 @@ tonNode_slave_sendExtMessage::tonNode_slave_sendExtMessage(td::TlParser &p) void tonNode_slave_sendExtMessage::store(td::TlStorerCalcLength &s) const { (void)sizeof(s); - s.store_binary(2067425040); + s.store_binary(58127017); TlStoreObject::store(message_, s); } void tonNode_slave_sendExtMessage::store(td::TlStorerUnsafe &s) const { (void)sizeof(s); - s.store_binary(2067425040); + s.store_binary(58127017); TlStoreObject::store(message_, s); } @@ -15566,7 +16333,7 @@ void tonNode_slave_sendExtMessage::store(td::TlStorerToString &s, const char *fi tonNode_slave_sendExtMessage::ReturnType tonNode_slave_sendExtMessage::fetch_result(td::TlParser &p) { #define FAIL(error) p.set_error(error); return ReturnType() - return TlFetchBoxed::parse(p); + return TlFetchBoxed, -1063902129>::parse(p); #undef FAIL } diff --git a/submodules/ton/tonlib-src/tl/generate/auto/tl/ton_api.h b/submodules/ton/tonlib-src/tl/generate/auto/tl/ton_api.h index 41eec24536..537ece403d 100644 --- a/submodules/ton/tonlib-src/tl/generate/auto/tl/ton_api.h +++ b/submodules/ton/tonlib-src/tl/generate/auto/tl/ton_api.h @@ -310,10 +310,18 @@ class tonNode_blockIdExt; class tonNode_blockSignature; +class tonNode_blocksDescription; + class tonNode_Broadcast; +class tonNode_capabilities; + class tonNode_data; +class tonNode_DataFull; + +class tonNode_dataList; + class tonNode_externalMessage; class tonNode_ihrMessage; @@ -332,6 +340,8 @@ class tonNode_sessionId; class tonNode_shardPublicOverlayId; +class tonNode_success; + class tonNode_zeroStateIdExt; class validator_group; @@ -5806,6 +5816,31 @@ class tonNode_blockSignature final : public Object { void store(td::TlStorerToString &s, const char *field_name) const final; }; +class tonNode_blocksDescription final : public Object { + public: + std::vector> ids_; + bool incomplete_; + + tonNode_blocksDescription(); + + tonNode_blocksDescription(std::vector> &&ids_, bool incomplete_); + + static const std::int32_t ID = -701865684; + std::int32_t get_id() const final { + return ID; + } + + static object_ptr fetch(td::TlParser &p); + + explicit tonNode_blocksDescription(td::TlParser &p); + + void store(td::TlStorerCalcLength &s) const final; + + void store(td::TlStorerUnsafe &s) const final; + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + class tonNode_Broadcast: public Object { public: @@ -5913,6 +5948,31 @@ class tonNode_newShardBlockBroadcast final : public tonNode_Broadcast { void store(td::TlStorerToString &s, const char *field_name) const final; }; +class tonNode_capabilities final : public Object { + public: + std::int32_t version_; + std::int64_t capabilities_; + + tonNode_capabilities(); + + tonNode_capabilities(std::int32_t version_, std::int64_t capabilities_); + + static const std::int32_t ID = -172007232; + std::int32_t get_id() const final { + return ID; + } + + static object_ptr fetch(td::TlParser &p); + + explicit tonNode_capabilities(td::TlParser &p); + + void store(td::TlStorerCalcLength &s) const final; + + void store(td::TlStorerUnsafe &s) const final; + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + class tonNode_data final : public Object { public: td::BufferSlice data_; @@ -5937,6 +5997,84 @@ class tonNode_data final : public Object { void store(td::TlStorerToString &s, const char *field_name) const final; }; +class tonNode_DataFull: public Object { + public: + + static object_ptr fetch(td::TlParser &p); +}; + +class tonNode_dataFull final : public tonNode_DataFull { + public: + object_ptr id_; + td::BufferSlice proof_; + td::BufferSlice block_; + bool is_link_; + + tonNode_dataFull(); + + tonNode_dataFull(object_ptr &&id_, td::BufferSlice &&proof_, td::BufferSlice &&block_, bool is_link_); + + static const std::int32_t ID = -1101488237; + std::int32_t get_id() const final { + return ID; + } + + static object_ptr fetch(td::TlParser &p); + + explicit tonNode_dataFull(td::TlParser &p); + + void store(td::TlStorerCalcLength &s) const final; + + void store(td::TlStorerUnsafe &s) const final; + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + +class tonNode_dataFullEmpty final : public tonNode_DataFull { + public: + + tonNode_dataFullEmpty(); + + static const std::int32_t ID = 1466861002; + std::int32_t get_id() const final { + return ID; + } + + static object_ptr fetch(td::TlParser &p); + + explicit tonNode_dataFullEmpty(td::TlParser &p); + + void store(td::TlStorerCalcLength &s) const final; + + void store(td::TlStorerUnsafe &s) const final; + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + +class tonNode_dataList final : public Object { + public: + std::vector data_; + + tonNode_dataList(); + + explicit tonNode_dataList(std::vector &&data_); + + static const std::int32_t ID = 351548179; + std::int32_t get_id() const final { + return ID; + } + + static object_ptr fetch(td::TlParser &p); + + explicit tonNode_dataList(td::TlParser &p); + + void store(td::TlStorerCalcLength &s) const final; + + void store(td::TlStorerUnsafe &s) const final; + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + class tonNode_externalMessage final : public Object { public: td::BufferSlice data_; @@ -6255,6 +6393,27 @@ class tonNode_shardPublicOverlayId final : public Object { void store(td::TlStorerToString &s, const char *field_name) const final; }; +class tonNode_success final : public Object { + public: + + tonNode_success(); + + static const std::int32_t ID = -1063902129; + std::int32_t get_id() const final { + return ID; + } + + static object_ptr fetch(td::TlParser &p); + + explicit tonNode_success(td::TlParser &p); + + void store(td::TlStorerCalcLength &s) const final; + + void store(td::TlStorerUnsafe &s) const final; + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + class tonNode_zeroStateIdExt final : public Object { public: std::int32_t workchain_; @@ -8201,6 +8360,34 @@ class tonNode_downloadBlock final : public Function { static ReturnType fetch_result(td::TlParser &p); }; +class tonNode_downloadBlockFull final : public Function { + public: + object_ptr block_; + + tonNode_downloadBlockFull(); + + explicit tonNode_downloadBlockFull(object_ptr &&block_); + + static const std::int32_t ID = 1780991133; + std::int32_t get_id() const final { + return ID; + } + + using ReturnType = object_ptr; + + static object_ptr fetch(td::TlParser &p); + + explicit tonNode_downloadBlockFull(td::TlParser &p); + + void store(td::TlStorerCalcLength &s) const final; + + void store(td::TlStorerUnsafe &s) const final; + + void store(td::TlStorerToString &s, const char *field_name) const final; + + static ReturnType fetch_result(td::TlParser &p); +}; + class tonNode_downloadBlockProof final : public Function { public: object_ptr block_; @@ -8257,6 +8444,118 @@ class tonNode_downloadBlockProofLink final : public Function { static ReturnType fetch_result(td::TlParser &p); }; +class tonNode_downloadBlockProofLinks final : public Function { + public: + std::vector> blocks_; + + tonNode_downloadBlockProofLinks(); + + explicit tonNode_downloadBlockProofLinks(std::vector> &&blocks_); + + static const std::int32_t ID = 684796771; + std::int32_t get_id() const final { + return ID; + } + + using ReturnType = object_ptr; + + static object_ptr fetch(td::TlParser &p); + + explicit tonNode_downloadBlockProofLinks(td::TlParser &p); + + void store(td::TlStorerCalcLength &s) const final; + + void store(td::TlStorerUnsafe &s) const final; + + void store(td::TlStorerToString &s, const char *field_name) const final; + + static ReturnType fetch_result(td::TlParser &p); +}; + +class tonNode_downloadBlockProofs final : public Function { + public: + std::vector> blocks_; + + tonNode_downloadBlockProofs(); + + explicit tonNode_downloadBlockProofs(std::vector> &&blocks_); + + static const std::int32_t ID = -1515170827; + std::int32_t get_id() const final { + return ID; + } + + using ReturnType = object_ptr; + + static object_ptr fetch(td::TlParser &p); + + explicit tonNode_downloadBlockProofs(td::TlParser &p); + + void store(td::TlStorerCalcLength &s) const final; + + void store(td::TlStorerUnsafe &s) const final; + + void store(td::TlStorerToString &s, const char *field_name) const final; + + static ReturnType fetch_result(td::TlParser &p); +}; + +class tonNode_downloadBlocks final : public Function { + public: + std::vector> blocks_; + + tonNode_downloadBlocks(); + + explicit tonNode_downloadBlocks(std::vector> &&blocks_); + + static const std::int32_t ID = 1985594749; + std::int32_t get_id() const final { + return ID; + } + + using ReturnType = object_ptr; + + static object_ptr fetch(td::TlParser &p); + + explicit tonNode_downloadBlocks(td::TlParser &p); + + void store(td::TlStorerCalcLength &s) const final; + + void store(td::TlStorerUnsafe &s) const final; + + void store(td::TlStorerToString &s, const char *field_name) const final; + + static ReturnType fetch_result(td::TlParser &p); +}; + +class tonNode_downloadNextBlockFull final : public Function { + public: + object_ptr prev_block_; + + tonNode_downloadNextBlockFull(); + + explicit tonNode_downloadNextBlockFull(object_ptr &&prev_block_); + + static const std::int32_t ID = 1855993674; + std::int32_t get_id() const final { + return ID; + } + + using ReturnType = object_ptr; + + static object_ptr fetch(td::TlParser &p); + + explicit tonNode_downloadNextBlockFull(td::TlParser &p); + + void store(td::TlStorerCalcLength &s) const final; + + void store(td::TlStorerUnsafe &s) const final; + + void store(td::TlStorerToString &s, const char *field_name) const final; + + static ReturnType fetch_result(td::TlParser &p); +}; + class tonNode_downloadPersistentState final : public Function { public: object_ptr block_; @@ -8345,6 +8644,31 @@ class tonNode_downloadZeroState final : public Function { static ReturnType fetch_result(td::TlParser &p); }; +class tonNode_getCapabilities final : public Function { + public: + + tonNode_getCapabilities(); + + static const std::int32_t ID = -555345672; + std::int32_t get_id() const final { + return ID; + } + + using ReturnType = object_ptr; + + static object_ptr fetch(td::TlParser &p); + + explicit tonNode_getCapabilities(td::TlParser &p); + + void store(td::TlStorerCalcLength &s) const final; + + void store(td::TlStorerUnsafe &s) const final; + + void store(td::TlStorerToString &s, const char *field_name) const final; + + static ReturnType fetch_result(td::TlParser &p); +}; + class tonNode_getNextBlockDescription final : public Function { public: object_ptr prev_block_; @@ -8373,6 +8697,35 @@ class tonNode_getNextBlockDescription final : public Function { static ReturnType fetch_result(td::TlParser &p); }; +class tonNode_getNextBlocksDescription final : public Function { + public: + object_ptr prev_block_; + std::int32_t limit_; + + tonNode_getNextBlocksDescription(); + + tonNode_getNextBlocksDescription(object_ptr &&prev_block_, std::int32_t limit_); + + static const std::int32_t ID = 1059590852; + std::int32_t get_id() const final { + return ID; + } + + using ReturnType = object_ptr; + + static object_ptr fetch(td::TlParser &p); + + explicit tonNode_getNextBlocksDescription(td::TlParser &p); + + void store(td::TlStorerCalcLength &s) const final; + + void store(td::TlStorerUnsafe &s) const final; + + void store(td::TlStorerToString &s, const char *field_name) const final; + + static ReturnType fetch_result(td::TlParser &p); +}; + class tonNode_getNextKeyBlockIds final : public Function { public: object_ptr block_; @@ -8402,6 +8755,36 @@ class tonNode_getNextKeyBlockIds final : public Function { static ReturnType fetch_result(td::TlParser &p); }; +class tonNode_getPrevBlocksDescription final : public Function { + public: + object_ptr next_block_; + std::int32_t limit_; + std::int32_t cutoff_seqno_; + + tonNode_getPrevBlocksDescription(); + + tonNode_getPrevBlocksDescription(object_ptr &&next_block_, std::int32_t limit_, std::int32_t cutoff_seqno_); + + static const std::int32_t ID = 1550675145; + std::int32_t get_id() const final { + return ID; + } + + using ReturnType = object_ptr; + + static object_ptr fetch(td::TlParser &p); + + explicit tonNode_getPrevBlocksDescription(td::TlParser &p); + + void store(td::TlStorerCalcLength &s) const final; + + void store(td::TlStorerUnsafe &s) const final; + + void store(td::TlStorerToString &s, const char *field_name) const final; + + static ReturnType fetch_result(td::TlParser &p); +}; + class tonNode_prepareBlock final : public Function { public: object_ptr block_; @@ -8459,6 +8842,63 @@ class tonNode_prepareBlockProof final : public Function { static ReturnType fetch_result(td::TlParser &p); }; +class tonNode_prepareBlockProofs final : public Function { + public: + std::vector> blocks_; + bool allow_partial_; + + tonNode_prepareBlockProofs(); + + tonNode_prepareBlockProofs(std::vector> &&blocks_, bool allow_partial_); + + static const std::int32_t ID = -310791496; + std::int32_t get_id() const final { + return ID; + } + + using ReturnType = object_ptr; + + static object_ptr fetch(td::TlParser &p); + + explicit tonNode_prepareBlockProofs(td::TlParser &p); + + void store(td::TlStorerCalcLength &s) const final; + + void store(td::TlStorerUnsafe &s) const final; + + void store(td::TlStorerToString &s, const char *field_name) const final; + + static ReturnType fetch_result(td::TlParser &p); +}; + +class tonNode_prepareBlocks final : public Function { + public: + std::vector> blocks_; + + tonNode_prepareBlocks(); + + explicit tonNode_prepareBlocks(std::vector> &&blocks_); + + static const std::int32_t ID = 1795140604; + std::int32_t get_id() const final { + return ID; + } + + using ReturnType = object_ptr; + + static object_ptr fetch(td::TlParser &p); + + explicit tonNode_prepareBlocks(td::TlParser &p); + + void store(td::TlStorerCalcLength &s) const final; + + void store(td::TlStorerUnsafe &s) const final; + + void store(td::TlStorerToString &s, const char *field_name) const final; + + static ReturnType fetch_result(td::TlParser &p); +}; + class tonNode_preparePersistentState final : public Function { public: object_ptr block_; @@ -8549,12 +8989,12 @@ class tonNode_slave_sendExtMessage final : public Function { explicit tonNode_slave_sendExtMessage(object_ptr &&message_); - static const std::int32_t ID = 2067425040; + static const std::int32_t ID = 58127017; std::int32_t get_id() const final { return ID; } - using ReturnType = bool; + using ReturnType = object_ptr; static object_ptr fetch(td::TlParser &p); diff --git a/submodules/ton/tonlib-src/tl/generate/auto/tl/ton_api.hpp b/submodules/ton/tonlib-src/tl/generate/auto/tl/ton_api.hpp index 6471596805..1f033147db 100644 --- a/submodules/ton/tonlib-src/tl/generate/auto/tl/ton_api.hpp +++ b/submodules/ton/tonlib-src/tl/generate/auto/tl/ton_api.hpp @@ -647,6 +647,9 @@ bool downcast_call(Object &obj, const T &func) { case tonNode_blockSignature::ID: func(static_cast(obj)); return true; + case tonNode_blocksDescription::ID: + func(static_cast(obj)); + return true; case tonNode_blockBroadcast::ID: func(static_cast(obj)); return true; @@ -659,9 +662,21 @@ bool downcast_call(Object &obj, const T &func) { case tonNode_newShardBlockBroadcast::ID: func(static_cast(obj)); return true; + case tonNode_capabilities::ID: + func(static_cast(obj)); + return true; case tonNode_data::ID: func(static_cast(obj)); return true; + case tonNode_dataFull::ID: + func(static_cast(obj)); + return true; + case tonNode_dataFullEmpty::ID: + func(static_cast(obj)); + return true; + case tonNode_dataList::ID: + func(static_cast(obj)); + return true; case tonNode_externalMessage::ID: func(static_cast(obj)); return true; @@ -701,6 +716,9 @@ bool downcast_call(Object &obj, const T &func) { case tonNode_shardPublicOverlayId::ID: func(static_cast(obj)); return true; + case tonNode_success::ID: + func(static_cast(obj)); + return true; case tonNode_zeroStateIdExt::ID: func(static_cast(obj)); return true; @@ -925,12 +943,27 @@ bool downcast_call(Function &obj, const T &func) { case tonNode_downloadBlock::ID: func(static_cast(obj)); return true; + case tonNode_downloadBlockFull::ID: + func(static_cast(obj)); + return true; case tonNode_downloadBlockProof::ID: func(static_cast(obj)); return true; case tonNode_downloadBlockProofLink::ID: func(static_cast(obj)); return true; + case tonNode_downloadBlockProofLinks::ID: + func(static_cast(obj)); + return true; + case tonNode_downloadBlockProofs::ID: + func(static_cast(obj)); + return true; + case tonNode_downloadBlocks::ID: + func(static_cast(obj)); + return true; + case tonNode_downloadNextBlockFull::ID: + func(static_cast(obj)); + return true; case tonNode_downloadPersistentState::ID: func(static_cast(obj)); return true; @@ -940,18 +973,33 @@ bool downcast_call(Function &obj, const T &func) { case tonNode_downloadZeroState::ID: func(static_cast(obj)); return true; + case tonNode_getCapabilities::ID: + func(static_cast(obj)); + return true; case tonNode_getNextBlockDescription::ID: func(static_cast(obj)); return true; + case tonNode_getNextBlocksDescription::ID: + func(static_cast(obj)); + return true; case tonNode_getNextKeyBlockIds::ID: func(static_cast(obj)); return true; + case tonNode_getPrevBlocksDescription::ID: + func(static_cast(obj)); + return true; case tonNode_prepareBlock::ID: func(static_cast(obj)); return true; case tonNode_prepareBlockProof::ID: func(static_cast(obj)); return true; + case tonNode_prepareBlockProofs::ID: + func(static_cast(obj)); + return true; + case tonNode_prepareBlocks::ID: + func(static_cast(obj)); + return true; case tonNode_preparePersistentState::ID: func(static_cast(obj)); return true; @@ -1743,6 +1791,26 @@ bool downcast_call(tonNode_Broadcast &obj, const T &func) { } } +/** + * Calls specified function object with the specified object downcasted to the most-derived type. + * \param[in] obj Object to pass as an argument to the function object. + * \param[in] func Function object to which the object will be passed. + * \returns whether function object call has happened. Should always return true for correct parameters. + */ +template +bool downcast_call(tonNode_DataFull &obj, const T &func) { + switch (obj.get_id()) { + case tonNode_dataFull::ID: + func(static_cast(obj)); + return true; + case tonNode_dataFullEmpty::ID: + func(static_cast(obj)); + return true; + default: + return false; + } +} + /** * Calls specified function object with the specified object downcasted to the most-derived type. * \param[in] obj Object to pass as an argument to the function object. diff --git a/submodules/ton/tonlib-src/tl/generate/auto/tl/ton_api_json.cpp b/submodules/ton/tonlib-src/tl/generate/auto/tl/ton_api_json.cpp index d08d906c9d..66c7c9df17 100644 --- a/submodules/ton/tonlib-src/tl/generate/auto/tl/ton_api_json.cpp +++ b/submodules/ton/tonlib-src/tl/generate/auto/tl/ton_api_json.cpp @@ -400,6 +400,17 @@ Result tl_constructor_from_string(ton_api::tonNode_Broadcast *object, con } return it->second; } +Result tl_constructor_from_string(ton_api::tonNode_DataFull *object, const std::string &str) { + static const std::unordered_map m = { + {"tonNode.dataFull", -1101488237}, + {"tonNode.dataFullEmpty", 1466861002} + }; + auto it = m.find(str); + if (it == m.end()) { + return Status::Error(str + "Unknown class"); + } + return it->second; +} Result tl_constructor_from_string(ton_api::tonNode_Prepared *object, const std::string &str) { static const std::unordered_map m = { {"tonNode.prepared", -356205619}, @@ -686,11 +697,16 @@ Result tl_constructor_from_string(ton_api::Object *object, const std::str {"tonNode.blockId", -1211256473}, {"tonNode.blockIdExt", 1733487480}, {"tonNode.blockSignature", 1357921331}, + {"tonNode.blocksDescription", -701865684}, {"tonNode.blockBroadcast", -1372712699}, {"tonNode.ihrMessageBroadcast", 1381868723}, {"tonNode.externalMessageBroadcast", 1025185895}, {"tonNode.newShardBlockBroadcast", 183696060}, + {"tonNode.capabilities", -172007232}, {"tonNode.data", 1443505284}, + {"tonNode.dataFull", -1101488237}, + {"tonNode.dataFullEmpty", 1466861002}, + {"tonNode.dataList", 351548179}, {"tonNode.externalMessage", -596270583}, {"tonNode.ihrMessage", 1161085703}, {"tonNode.keyBlocks", 124144985}, @@ -704,6 +720,7 @@ Result tl_constructor_from_string(ton_api::Object *object, const std::str {"tonNode.notFoundState", 842598993}, {"tonNode.sessionId", 2056402618}, {"tonNode.shardPublicOverlayId", 1302254377}, + {"tonNode.success", -1063902129}, {"tonNode.zeroStateIdExt", 494024110}, {"validator.group", -120029535}, {"validator.config.global", -2038562966}, @@ -783,19 +800,29 @@ Result tl_constructor_from_string(ton_api::Function *object, const std::s {"overlay.query", -855800765}, {"tcp.ping", 1292381082}, {"tonNode.downloadBlock", -495814205}, + {"tonNode.downloadBlockFull", 1780991133}, {"tonNode.downloadBlockProof", 1272334218}, {"tonNode.downloadBlockProofLink", 632488134}, + {"tonNode.downloadBlockProofLinks", 684796771}, + {"tonNode.downloadBlockProofs", -1515170827}, + {"tonNode.downloadBlocks", 1985594749}, + {"tonNode.downloadNextBlockFull", 1855993674}, {"tonNode.downloadPersistentState", 2140791736}, {"tonNode.downloadPersistentStateSlice", -169220381}, {"tonNode.downloadZeroState", -1379131814}, + {"tonNode.getCapabilities", -555345672}, {"tonNode.getNextBlockDescription", 341160179}, + {"tonNode.getNextBlocksDescription", 1059590852}, {"tonNode.getNextKeyBlockIds", -219689029}, + {"tonNode.getPrevBlocksDescription", 1550675145}, {"tonNode.prepareBlock", 1973649230}, {"tonNode.prepareBlockProof", -2024000760}, + {"tonNode.prepareBlockProofs", -310791496}, + {"tonNode.prepareBlocks", 1795140604}, {"tonNode.preparePersistentState", -18209122}, {"tonNode.prepareZeroState", 1104021541}, {"tonNode.query", 1777542355}, - {"tonNode.slave.sendExtMessage", 2067425040}, + {"tonNode.slave.sendExtMessage", 58127017}, {"validatorSession.downloadCandidate", -520274443}, {"validatorSession.ping", 1745111469} }; @@ -4150,6 +4177,21 @@ Status from_json(ton_api::tonNode_blockSignature &to, JsonObject &from) { } return Status::OK(); } +Status from_json(ton_api::tonNode_blocksDescription &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "ids", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.ids_, value)); + } + } + { + TRY_RESULT(value, get_json_object_field(from, "incomplete", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.incomplete_, value)); + } + } + return Status::OK(); +} Status from_json(ton_api::tonNode_blockBroadcast &to, JsonObject &from) { { TRY_RESULT(value, get_json_object_field(from, "id", JsonValue::Type::Null, true)); @@ -4216,6 +4258,21 @@ Status from_json(ton_api::tonNode_newShardBlockBroadcast &to, JsonObject &from) } return Status::OK(); } +Status from_json(ton_api::tonNode_capabilities &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "version", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.version_, value)); + } + } + { + TRY_RESULT(value, get_json_object_field(from, "capabilities", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.capabilities_, value)); + } + } + return Status::OK(); +} Status from_json(ton_api::tonNode_data &to, JsonObject &from) { { TRY_RESULT(value, get_json_object_field(from, "data", JsonValue::Type::Null, true)); @@ -4225,6 +4282,45 @@ Status from_json(ton_api::tonNode_data &to, JsonObject &from) { } return Status::OK(); } +Status from_json(ton_api::tonNode_dataFull &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "id", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.id_, value)); + } + } + { + TRY_RESULT(value, get_json_object_field(from, "proof", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json_bytes(to.proof_, value)); + } + } + { + TRY_RESULT(value, get_json_object_field(from, "block", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json_bytes(to.block_, value)); + } + } + { + TRY_RESULT(value, get_json_object_field(from, "is_link", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.is_link_, value)); + } + } + return Status::OK(); +} +Status from_json(ton_api::tonNode_dataFullEmpty &to, JsonObject &from) { + return Status::OK(); +} +Status from_json(ton_api::tonNode_dataList &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "data", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json_vector_bytes(to.data_, value)); + } + } + return Status::OK(); +} Status from_json(ton_api::tonNode_externalMessage &to, JsonObject &from) { { TRY_RESULT(value, get_json_object_field(from, "data", JsonValue::Type::Null, true)); @@ -4354,6 +4450,9 @@ Status from_json(ton_api::tonNode_shardPublicOverlayId &to, JsonObject &from) { } return Status::OK(); } +Status from_json(ton_api::tonNode_success &to, JsonObject &from) { + return Status::OK(); +} Status from_json(ton_api::tonNode_zeroStateIdExt &to, JsonObject &from) { { TRY_RESULT(value, get_json_object_field(from, "workchain", JsonValue::Type::Null, true)); @@ -5416,6 +5515,15 @@ Status from_json(ton_api::tonNode_downloadBlock &to, JsonObject &from) { } return Status::OK(); } +Status from_json(ton_api::tonNode_downloadBlockFull &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "block", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.block_, value)); + } + } + return Status::OK(); +} Status from_json(ton_api::tonNode_downloadBlockProof &to, JsonObject &from) { { TRY_RESULT(value, get_json_object_field(from, "block", JsonValue::Type::Null, true)); @@ -5434,6 +5542,42 @@ Status from_json(ton_api::tonNode_downloadBlockProofLink &to, JsonObject &from) } return Status::OK(); } +Status from_json(ton_api::tonNode_downloadBlockProofLinks &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "blocks", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.blocks_, value)); + } + } + return Status::OK(); +} +Status from_json(ton_api::tonNode_downloadBlockProofs &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "blocks", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.blocks_, value)); + } + } + return Status::OK(); +} +Status from_json(ton_api::tonNode_downloadBlocks &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "blocks", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.blocks_, value)); + } + } + return Status::OK(); +} +Status from_json(ton_api::tonNode_downloadNextBlockFull &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "prev_block", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.prev_block_, value)); + } + } + return Status::OK(); +} Status from_json(ton_api::tonNode_downloadPersistentState &to, JsonObject &from) { { TRY_RESULT(value, get_json_object_field(from, "block", JsonValue::Type::Null, true)); @@ -5485,6 +5629,9 @@ Status from_json(ton_api::tonNode_downloadZeroState &to, JsonObject &from) { } return Status::OK(); } +Status from_json(ton_api::tonNode_getCapabilities &to, JsonObject &from) { + return Status::OK(); +} Status from_json(ton_api::tonNode_getNextBlockDescription &to, JsonObject &from) { { TRY_RESULT(value, get_json_object_field(from, "prev_block", JsonValue::Type::Null, true)); @@ -5494,6 +5641,21 @@ Status from_json(ton_api::tonNode_getNextBlockDescription &to, JsonObject &from) } return Status::OK(); } +Status from_json(ton_api::tonNode_getNextBlocksDescription &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "prev_block", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.prev_block_, value)); + } + } + { + TRY_RESULT(value, get_json_object_field(from, "limit", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.limit_, value)); + } + } + return Status::OK(); +} Status from_json(ton_api::tonNode_getNextKeyBlockIds &to, JsonObject &from) { { TRY_RESULT(value, get_json_object_field(from, "block", JsonValue::Type::Null, true)); @@ -5509,6 +5671,27 @@ Status from_json(ton_api::tonNode_getNextKeyBlockIds &to, JsonObject &from) { } return Status::OK(); } +Status from_json(ton_api::tonNode_getPrevBlocksDescription &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "next_block", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.next_block_, value)); + } + } + { + TRY_RESULT(value, get_json_object_field(from, "limit", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.limit_, value)); + } + } + { + TRY_RESULT(value, get_json_object_field(from, "cutoff_seqno", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.cutoff_seqno_, value)); + } + } + return Status::OK(); +} Status from_json(ton_api::tonNode_prepareBlock &to, JsonObject &from) { { TRY_RESULT(value, get_json_object_field(from, "block", JsonValue::Type::Null, true)); @@ -5533,6 +5716,30 @@ Status from_json(ton_api::tonNode_prepareBlockProof &to, JsonObject &from) { } return Status::OK(); } +Status from_json(ton_api::tonNode_prepareBlockProofs &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "blocks", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.blocks_, value)); + } + } + { + TRY_RESULT(value, get_json_object_field(from, "allow_partial", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.allow_partial_, value)); + } + } + return Status::OK(); +} +Status from_json(ton_api::tonNode_prepareBlocks &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "blocks", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.blocks_, value)); + } + } + return Status::OK(); +} Status from_json(ton_api::tonNode_preparePersistentState &to, JsonObject &from) { { TRY_RESULT(value, get_json_object_field(from, "block", JsonValue::Type::Null, true)); @@ -7164,6 +7371,12 @@ void to_json(JsonValueScope &jv, const ton_api::tonNode_blockSignature &object) jo << ctie("who", ToJson(object.who_)); jo << ctie("signature", ToJson(JsonBytes{object.signature_})); } +void to_json(JsonValueScope &jv, const ton_api::tonNode_blocksDescription &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "tonNode.blocksDescription"); + jo << ctie("ids", ToJson(object.ids_)); + jo << ctie("incomplete", ToJson(object.incomplete_)); +} void to_json(JsonValueScope &jv, const ton_api::tonNode_Broadcast &object) { ton_api::downcast_call(const_cast(object), [&jv](const auto &object) { to_json(jv, object); }); } @@ -7200,11 +7413,39 @@ void to_json(JsonValueScope &jv, const ton_api::tonNode_newShardBlockBroadcast & jo << ctie("block", ToJson(object.block_)); } } +void to_json(JsonValueScope &jv, const ton_api::tonNode_capabilities &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "tonNode.capabilities"); + jo << ctie("version", ToJson(object.version_)); + jo << ctie("capabilities", ToJson(JsonInt64{object.capabilities_})); +} void to_json(JsonValueScope &jv, const ton_api::tonNode_data &object) { auto jo = jv.enter_object(); jo << ctie("@type", "tonNode.data"); jo << ctie("data", ToJson(JsonBytes{object.data_})); } +void to_json(JsonValueScope &jv, const ton_api::tonNode_DataFull &object) { + ton_api::downcast_call(const_cast(object), [&jv](const auto &object) { to_json(jv, object); }); +} +void to_json(JsonValueScope &jv, const ton_api::tonNode_dataFull &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "tonNode.dataFull"); + if (object.id_) { + jo << ctie("id", ToJson(object.id_)); + } + jo << ctie("proof", ToJson(JsonBytes{object.proof_})); + jo << ctie("block", ToJson(JsonBytes{object.block_})); + jo << ctie("is_link", ToJson(object.is_link_)); +} +void to_json(JsonValueScope &jv, const ton_api::tonNode_dataFullEmpty &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "tonNode.dataFullEmpty"); +} +void to_json(JsonValueScope &jv, const ton_api::tonNode_dataList &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "tonNode.dataList"); + jo << ctie("data", ToJson(JsonVectorBytes(object.data_))); +} void to_json(JsonValueScope &jv, const ton_api::tonNode_externalMessage &object) { auto jo = jv.enter_object(); jo << ctie("@type", "tonNode.externalMessage"); @@ -7283,6 +7524,10 @@ void to_json(JsonValueScope &jv, const ton_api::tonNode_shardPublicOverlayId &ob jo << ctie("shard", ToJson(JsonInt64{object.shard_})); jo << ctie("zero_state_file_hash", ToJson(object.zero_state_file_hash_)); } +void to_json(JsonValueScope &jv, const ton_api::tonNode_success &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "tonNode.success"); +} void to_json(JsonValueScope &jv, const ton_api::tonNode_zeroStateIdExt &object) { auto jo = jv.enter_object(); jo << ctie("@type", "tonNode.zeroStateIdExt"); @@ -7736,6 +7981,13 @@ void to_json(JsonValueScope &jv, const ton_api::tonNode_downloadBlock &object) { jo << ctie("block", ToJson(object.block_)); } } +void to_json(JsonValueScope &jv, const ton_api::tonNode_downloadBlockFull &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "tonNode.downloadBlockFull"); + if (object.block_) { + jo << ctie("block", ToJson(object.block_)); + } +} void to_json(JsonValueScope &jv, const ton_api::tonNode_downloadBlockProof &object) { auto jo = jv.enter_object(); jo << ctie("@type", "tonNode.downloadBlockProof"); @@ -7750,6 +8002,28 @@ void to_json(JsonValueScope &jv, const ton_api::tonNode_downloadBlockProofLink & jo << ctie("block", ToJson(object.block_)); } } +void to_json(JsonValueScope &jv, const ton_api::tonNode_downloadBlockProofLinks &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "tonNode.downloadBlockProofLinks"); + jo << ctie("blocks", ToJson(object.blocks_)); +} +void to_json(JsonValueScope &jv, const ton_api::tonNode_downloadBlockProofs &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "tonNode.downloadBlockProofs"); + jo << ctie("blocks", ToJson(object.blocks_)); +} +void to_json(JsonValueScope &jv, const ton_api::tonNode_downloadBlocks &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "tonNode.downloadBlocks"); + jo << ctie("blocks", ToJson(object.blocks_)); +} +void to_json(JsonValueScope &jv, const ton_api::tonNode_downloadNextBlockFull &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "tonNode.downloadNextBlockFull"); + if (object.prev_block_) { + jo << ctie("prev_block", ToJson(object.prev_block_)); + } +} void to_json(JsonValueScope &jv, const ton_api::tonNode_downloadPersistentState &object) { auto jo = jv.enter_object(); jo << ctie("@type", "tonNode.downloadPersistentState"); @@ -7779,6 +8053,10 @@ void to_json(JsonValueScope &jv, const ton_api::tonNode_downloadZeroState &objec jo << ctie("block", ToJson(object.block_)); } } +void to_json(JsonValueScope &jv, const ton_api::tonNode_getCapabilities &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "tonNode.getCapabilities"); +} void to_json(JsonValueScope &jv, const ton_api::tonNode_getNextBlockDescription &object) { auto jo = jv.enter_object(); jo << ctie("@type", "tonNode.getNextBlockDescription"); @@ -7786,6 +8064,14 @@ void to_json(JsonValueScope &jv, const ton_api::tonNode_getNextBlockDescription jo << ctie("prev_block", ToJson(object.prev_block_)); } } +void to_json(JsonValueScope &jv, const ton_api::tonNode_getNextBlocksDescription &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "tonNode.getNextBlocksDescription"); + if (object.prev_block_) { + jo << ctie("prev_block", ToJson(object.prev_block_)); + } + jo << ctie("limit", ToJson(object.limit_)); +} void to_json(JsonValueScope &jv, const ton_api::tonNode_getNextKeyBlockIds &object) { auto jo = jv.enter_object(); jo << ctie("@type", "tonNode.getNextKeyBlockIds"); @@ -7794,6 +8080,15 @@ void to_json(JsonValueScope &jv, const ton_api::tonNode_getNextKeyBlockIds &obje } jo << ctie("max_size", ToJson(object.max_size_)); } +void to_json(JsonValueScope &jv, const ton_api::tonNode_getPrevBlocksDescription &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "tonNode.getPrevBlocksDescription"); + if (object.next_block_) { + jo << ctie("next_block", ToJson(object.next_block_)); + } + jo << ctie("limit", ToJson(object.limit_)); + jo << ctie("cutoff_seqno", ToJson(object.cutoff_seqno_)); +} void to_json(JsonValueScope &jv, const ton_api::tonNode_prepareBlock &object) { auto jo = jv.enter_object(); jo << ctie("@type", "tonNode.prepareBlock"); @@ -7809,6 +8104,17 @@ void to_json(JsonValueScope &jv, const ton_api::tonNode_prepareBlockProof &objec } jo << ctie("allow_partial", ToJson(object.allow_partial_)); } +void to_json(JsonValueScope &jv, const ton_api::tonNode_prepareBlockProofs &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "tonNode.prepareBlockProofs"); + jo << ctie("blocks", ToJson(object.blocks_)); + jo << ctie("allow_partial", ToJson(object.allow_partial_)); +} +void to_json(JsonValueScope &jv, const ton_api::tonNode_prepareBlocks &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "tonNode.prepareBlocks"); + jo << ctie("blocks", ToJson(object.blocks_)); +} void to_json(JsonValueScope &jv, const ton_api::tonNode_preparePersistentState &object) { auto jo = jv.enter_object(); jo << ctie("@type", "tonNode.preparePersistentState"); diff --git a/submodules/ton/tonlib-src/tl/generate/auto/tl/ton_api_json.h b/submodules/ton/tonlib-src/tl/generate/auto/tl/ton_api_json.h index a7e2b68ab8..cfa271e76f 100644 --- a/submodules/ton/tonlib-src/tl/generate/auto/tl/ton_api_json.h +++ b/submodules/ton/tonlib-src/tl/generate/auto/tl/ton_api_json.h @@ -41,6 +41,7 @@ Result tl_constructor_from_string(ton_api::tcp_Message *object, const std Result tl_constructor_from_string(ton_api::ton_BlockId *object, const std::string &str); Result tl_constructor_from_string(ton_api::tonNode_BlockDescription *object, const std::string &str); Result tl_constructor_from_string(ton_api::tonNode_Broadcast *object, const std::string &str); +Result tl_constructor_from_string(ton_api::tonNode_DataFull *object, const std::string &str); Result tl_constructor_from_string(ton_api::tonNode_Prepared *object, const std::string &str); Result tl_constructor_from_string(ton_api::tonNode_PreparedProof *object, const std::string &str); Result tl_constructor_from_string(ton_api::tonNode_PreparedState *object, const std::string &str); @@ -260,11 +261,16 @@ Status from_json(ton_api::tonNode_blockDescription &to, JsonObject &from); Status from_json(ton_api::tonNode_blockId &to, JsonObject &from); Status from_json(ton_api::tonNode_blockIdExt &to, JsonObject &from); Status from_json(ton_api::tonNode_blockSignature &to, JsonObject &from); +Status from_json(ton_api::tonNode_blocksDescription &to, JsonObject &from); Status from_json(ton_api::tonNode_blockBroadcast &to, JsonObject &from); Status from_json(ton_api::tonNode_ihrMessageBroadcast &to, JsonObject &from); Status from_json(ton_api::tonNode_externalMessageBroadcast &to, JsonObject &from); Status from_json(ton_api::tonNode_newShardBlockBroadcast &to, JsonObject &from); +Status from_json(ton_api::tonNode_capabilities &to, JsonObject &from); Status from_json(ton_api::tonNode_data &to, JsonObject &from); +Status from_json(ton_api::tonNode_dataFull &to, JsonObject &from); +Status from_json(ton_api::tonNode_dataFullEmpty &to, JsonObject &from); +Status from_json(ton_api::tonNode_dataList &to, JsonObject &from); Status from_json(ton_api::tonNode_externalMessage &to, JsonObject &from); Status from_json(ton_api::tonNode_ihrMessage &to, JsonObject &from); Status from_json(ton_api::tonNode_keyBlocks &to, JsonObject &from); @@ -278,6 +284,7 @@ Status from_json(ton_api::tonNode_preparedState &to, JsonObject &from); Status from_json(ton_api::tonNode_notFoundState &to, JsonObject &from); Status from_json(ton_api::tonNode_sessionId &to, JsonObject &from); Status from_json(ton_api::tonNode_shardPublicOverlayId &to, JsonObject &from); +Status from_json(ton_api::tonNode_success &to, JsonObject &from); Status from_json(ton_api::tonNode_zeroStateIdExt &to, JsonObject &from); Status from_json(ton_api::validator_group &to, JsonObject &from); Status from_json(ton_api::validator_config_global &to, JsonObject &from); @@ -348,15 +355,25 @@ Status from_json(ton_api::overlay_getRandomPeers &to, JsonObject &from); Status from_json(ton_api::overlay_query &to, JsonObject &from); Status from_json(ton_api::tcp_ping &to, JsonObject &from); Status from_json(ton_api::tonNode_downloadBlock &to, JsonObject &from); +Status from_json(ton_api::tonNode_downloadBlockFull &to, JsonObject &from); Status from_json(ton_api::tonNode_downloadBlockProof &to, JsonObject &from); Status from_json(ton_api::tonNode_downloadBlockProofLink &to, JsonObject &from); +Status from_json(ton_api::tonNode_downloadBlockProofLinks &to, JsonObject &from); +Status from_json(ton_api::tonNode_downloadBlockProofs &to, JsonObject &from); +Status from_json(ton_api::tonNode_downloadBlocks &to, JsonObject &from); +Status from_json(ton_api::tonNode_downloadNextBlockFull &to, JsonObject &from); Status from_json(ton_api::tonNode_downloadPersistentState &to, JsonObject &from); Status from_json(ton_api::tonNode_downloadPersistentStateSlice &to, JsonObject &from); Status from_json(ton_api::tonNode_downloadZeroState &to, JsonObject &from); +Status from_json(ton_api::tonNode_getCapabilities &to, JsonObject &from); Status from_json(ton_api::tonNode_getNextBlockDescription &to, JsonObject &from); +Status from_json(ton_api::tonNode_getNextBlocksDescription &to, JsonObject &from); Status from_json(ton_api::tonNode_getNextKeyBlockIds &to, JsonObject &from); +Status from_json(ton_api::tonNode_getPrevBlocksDescription &to, JsonObject &from); Status from_json(ton_api::tonNode_prepareBlock &to, JsonObject &from); Status from_json(ton_api::tonNode_prepareBlockProof &to, JsonObject &from); +Status from_json(ton_api::tonNode_prepareBlockProofs &to, JsonObject &from); +Status from_json(ton_api::tonNode_prepareBlocks &to, JsonObject &from); Status from_json(ton_api::tonNode_preparePersistentState &to, JsonObject &from); Status from_json(ton_api::tonNode_prepareZeroState &to, JsonObject &from); Status from_json(ton_api::tonNode_query &to, JsonObject &from); @@ -603,12 +620,18 @@ void to_json(JsonValueScope &jv, const ton_api::tonNode_blockDescription &object void to_json(JsonValueScope &jv, const ton_api::tonNode_blockId &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_blockIdExt &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_blockSignature &object); +void to_json(JsonValueScope &jv, const ton_api::tonNode_blocksDescription &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_Broadcast &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_blockBroadcast &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_ihrMessageBroadcast &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_externalMessageBroadcast &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_newShardBlockBroadcast &object); +void to_json(JsonValueScope &jv, const ton_api::tonNode_capabilities &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_data &object); +void to_json(JsonValueScope &jv, const ton_api::tonNode_DataFull &object); +void to_json(JsonValueScope &jv, const ton_api::tonNode_dataFull &object); +void to_json(JsonValueScope &jv, const ton_api::tonNode_dataFullEmpty &object); +void to_json(JsonValueScope &jv, const ton_api::tonNode_dataList &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_externalMessage &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_ihrMessage &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_keyBlocks &object); @@ -625,6 +648,7 @@ void to_json(JsonValueScope &jv, const ton_api::tonNode_preparedState &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_notFoundState &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_sessionId &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_shardPublicOverlayId &object); +void to_json(JsonValueScope &jv, const ton_api::tonNode_success &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_zeroStateIdExt &object); void to_json(JsonValueScope &jv, const ton_api::validator_group &object); void to_json(JsonValueScope &jv, const ton_api::validator_config_global &object); @@ -698,15 +722,25 @@ void to_json(JsonValueScope &jv, const ton_api::overlay_getRandomPeers &object); void to_json(JsonValueScope &jv, const ton_api::overlay_query &object); void to_json(JsonValueScope &jv, const ton_api::tcp_ping &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_downloadBlock &object); +void to_json(JsonValueScope &jv, const ton_api::tonNode_downloadBlockFull &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_downloadBlockProof &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_downloadBlockProofLink &object); +void to_json(JsonValueScope &jv, const ton_api::tonNode_downloadBlockProofLinks &object); +void to_json(JsonValueScope &jv, const ton_api::tonNode_downloadBlockProofs &object); +void to_json(JsonValueScope &jv, const ton_api::tonNode_downloadBlocks &object); +void to_json(JsonValueScope &jv, const ton_api::tonNode_downloadNextBlockFull &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_downloadPersistentState &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_downloadPersistentStateSlice &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_downloadZeroState &object); +void to_json(JsonValueScope &jv, const ton_api::tonNode_getCapabilities &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_getNextBlockDescription &object); +void to_json(JsonValueScope &jv, const ton_api::tonNode_getNextBlocksDescription &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_getNextKeyBlockIds &object); +void to_json(JsonValueScope &jv, const ton_api::tonNode_getPrevBlocksDescription &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_prepareBlock &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_prepareBlockProof &object); +void to_json(JsonValueScope &jv, const ton_api::tonNode_prepareBlockProofs &object); +void to_json(JsonValueScope &jv, const ton_api::tonNode_prepareBlocks &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_preparePersistentState &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_prepareZeroState &object); void to_json(JsonValueScope &jv, const ton_api::tonNode_query &object); diff --git a/submodules/ton/tonlib-src/tl/generate/auto/tl/tonlib_api.cpp b/submodules/ton/tonlib-src/tl/generate/auto/tl/tonlib_api.cpp index 0a6d3b95d8..42cb78eaac 100644 --- a/submodules/ton/tonlib-src/tl/generate/auto/tl/tonlib_api.cpp +++ b/submodules/ton/tonlib-src/tl/generate/auto/tl/tonlib_api.cpp @@ -172,6 +172,87 @@ void key::store(td::TlStorerToString &s, const char *field_name) const { } } +logStreamDefault::logStreamDefault() { +} + +const std::int32_t logStreamDefault::ID; + +void logStreamDefault::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "logStreamDefault"); + s.store_class_end(); + } +} + +logStreamFile::logStreamFile() + : path_() + , max_file_size_() +{} + +logStreamFile::logStreamFile(std::string const &path_, std::int64_t max_file_size_) + : path_(std::move(path_)) + , max_file_size_(max_file_size_) +{} + +const std::int32_t logStreamFile::ID; + +void logStreamFile::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "logStreamFile"); + s.store_field("path", path_); + s.store_field("max_file_size", max_file_size_); + s.store_class_end(); + } +} + +logStreamEmpty::logStreamEmpty() { +} + +const std::int32_t logStreamEmpty::ID; + +void logStreamEmpty::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "logStreamEmpty"); + s.store_class_end(); + } +} + +logTags::logTags() + : tags_() +{} + +logTags::logTags(std::vector &&tags_) + : tags_(std::move(tags_)) +{} + +const std::int32_t logTags::ID; + +void logTags::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "logTags"); + { const std::vector &v = tags_; const std::uint32_t multiplicity = static_cast(v.size()); const auto vector_name = "vector[" + td::to_string(multiplicity)+ "]"; s.store_class_begin("tags", vector_name.c_str()); for (std::uint32_t i = 0; i < multiplicity; i++) { s.store_field("", v[i]); } s.store_class_end(); } + s.store_class_end(); + } +} + +logVerbosityLevel::logVerbosityLevel() + : verbosity_level_() +{} + +logVerbosityLevel::logVerbosityLevel(std::int32_t verbosity_level_) + : verbosity_level_(verbosity_level_) +{} + +const std::int32_t logVerbosityLevel::ID; + +void logVerbosityLevel::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "logVerbosityLevel"); + s.store_field("verbosity_level", verbosity_level_); + s.store_class_end(); + } +} + ok::ok() { } @@ -265,6 +346,24 @@ void generic_accountStateTestWallet::store(td::TlStorerToString &s, const char * } } +generic_accountStateWallet::generic_accountStateWallet() + : account_state_() +{} + +generic_accountStateWallet::generic_accountStateWallet(object_ptr &&account_state_) + : account_state_(std::move(account_state_)) +{} + +const std::int32_t generic_accountStateWallet::ID; + +void generic_accountStateWallet::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "generic_accountStateWallet"); + if (account_state_ == nullptr) { s.store_field("account_state", "null"); } else { account_state_->store(s, "account_state"); } + s.store_class_end(); + } +} + generic_accountStateTestGiver::generic_accountStateTestGiver() : account_state_() {} @@ -301,42 +400,6 @@ void generic_accountStateUninited::store(td::TlStorerToString &s, const char *fi } } -generic_initialAccountStateRaw::generic_initialAccountStateRaw() - : initital_account_state_() -{} - -generic_initialAccountStateRaw::generic_initialAccountStateRaw(object_ptr &&initital_account_state_) - : initital_account_state_(std::move(initital_account_state_)) -{} - -const std::int32_t generic_initialAccountStateRaw::ID; - -void generic_initialAccountStateRaw::store(td::TlStorerToString &s, const char *field_name) const { - if (!LOG_IS_STRIPPED(ERROR)) { - s.store_class_begin(field_name, "generic_initialAccountStateRaw"); - if (initital_account_state_ == nullptr) { s.store_field("initital_account_state", "null"); } else { initital_account_state_->store(s, "initital_account_state"); } - s.store_class_end(); - } -} - -generic_initialAccountStateTestWallet::generic_initialAccountStateTestWallet() - : initital_account_state_() -{} - -generic_initialAccountStateTestWallet::generic_initialAccountStateTestWallet(object_ptr &&initital_account_state_) - : initital_account_state_(std::move(initital_account_state_)) -{} - -const std::int32_t generic_initialAccountStateTestWallet::ID; - -void generic_initialAccountStateTestWallet::store(td::TlStorerToString &s, const char *field_name) const { - if (!LOG_IS_STRIPPED(ERROR)) { - s.store_class_begin(field_name, "generic_initialAccountStateTestWallet"); - if (initital_account_state_ == nullptr) { s.store_field("initital_account_state", "null"); } else { initital_account_state_->store(s, "initital_account_state"); } - s.store_class_end(); - } -} - internal_transactionId::internal_transactionId() : lt_() , hash_() @@ -586,6 +649,72 @@ void uninited_accountState::store(td::TlStorerToString &s, const char *field_nam } } +wallet_accountState::wallet_accountState() + : balance_() + , seqno_() + , last_transaction_id_() + , sync_utime_() +{} + +wallet_accountState::wallet_accountState(std::int64_t balance_, std::int32_t seqno_, object_ptr &&last_transaction_id_, std::int64_t sync_utime_) + : balance_(balance_) + , seqno_(seqno_) + , last_transaction_id_(std::move(last_transaction_id_)) + , sync_utime_(sync_utime_) +{} + +const std::int32_t wallet_accountState::ID; + +void wallet_accountState::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "wallet_accountState"); + s.store_field("balance", balance_); + s.store_field("seqno", seqno_); + if (last_transaction_id_ == nullptr) { s.store_field("last_transaction_id", "null"); } else { last_transaction_id_->store(s, "last_transaction_id"); } + s.store_field("sync_utime", sync_utime_); + s.store_class_end(); + } +} + +wallet_initialAccountState::wallet_initialAccountState() + : public_key_() +{} + +wallet_initialAccountState::wallet_initialAccountState(std::string const &public_key_) + : public_key_(std::move(public_key_)) +{} + +const std::int32_t wallet_initialAccountState::ID; + +void wallet_initialAccountState::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "wallet_initialAccountState"); + s.store_field("public_key", public_key_); + s.store_class_end(); + } +} + +addLogMessage::addLogMessage() + : verbosity_level_() + , text_() +{} + +addLogMessage::addLogMessage(std::int32_t verbosity_level_, std::string const &text_) + : verbosity_level_(verbosity_level_) + , text_(std::move(text_)) +{} + +const std::int32_t addLogMessage::ID; + +void addLogMessage::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "addLogMessage"); + s.store_field("verbosity_level", verbosity_level_); + s.store_field("text", text_); + s.store_class_end(); + } +} + changeLocalPassword::changeLocalPassword() : input_key_() , new_local_password_() @@ -644,11 +773,11 @@ void createNewKey::store(td::TlStorerToString &s, const char *field_name) const } deleteKey::deleteKey() - : public_key_() + : key_() {} -deleteKey::deleteKey(std::string const &public_key_) - : public_key_(std::move(public_key_)) +deleteKey::deleteKey(object_ptr &&key_) + : key_(std::move(key_)) {} const std::int32_t deleteKey::ID; @@ -656,7 +785,7 @@ const std::int32_t deleteKey::ID; void deleteKey::store(td::TlStorerToString &s, const char *field_name) const { if (!LOG_IS_STRIPPED(ERROR)) { s.store_class_begin(field_name, "deleteKey"); - s.store_field("public_key", public_key_); + if (key_ == nullptr) { s.store_field("key", "null"); } else { key_->store(s, "key"); } s.store_class_end(); } } @@ -787,6 +916,60 @@ void getBip39Hints::store(td::TlStorerToString &s, const char *field_name) const } } +getLogStream::getLogStream() { +} + +const std::int32_t getLogStream::ID; + +void getLogStream::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "getLogStream"); + s.store_class_end(); + } +} + +getLogTagVerbosityLevel::getLogTagVerbosityLevel() + : tag_() +{} + +getLogTagVerbosityLevel::getLogTagVerbosityLevel(std::string const &tag_) + : tag_(std::move(tag_)) +{} + +const std::int32_t getLogTagVerbosityLevel::ID; + +void getLogTagVerbosityLevel::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "getLogTagVerbosityLevel"); + s.store_field("tag", tag_); + s.store_class_end(); + } +} + +getLogTags::getLogTags() { +} + +const std::int32_t getLogTags::ID; + +void getLogTags::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "getLogTags"); + s.store_class_end(); + } +} + +getLogVerbosityLevel::getLogVerbosityLevel() { +} + +const std::int32_t getLogVerbosityLevel::ID; + +void getLogVerbosityLevel::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "getLogVerbosityLevel"); + s.store_class_end(); + } +} + importEncryptedKey::importEncryptedKey() : local_password_() , key_password_() @@ -1036,6 +1219,63 @@ void runTests::store(td::TlStorerToString &s, const char *field_name) const { } } +setLogStream::setLogStream() + : log_stream_() +{} + +setLogStream::setLogStream(object_ptr &&log_stream_) + : log_stream_(std::move(log_stream_)) +{} + +const std::int32_t setLogStream::ID; + +void setLogStream::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "setLogStream"); + if (log_stream_ == nullptr) { s.store_field("log_stream", "null"); } else { log_stream_->store(s, "log_stream"); } + s.store_class_end(); + } +} + +setLogTagVerbosityLevel::setLogTagVerbosityLevel() + : tag_() + , new_verbosity_level_() +{} + +setLogTagVerbosityLevel::setLogTagVerbosityLevel(std::string const &tag_, std::int32_t new_verbosity_level_) + : tag_(std::move(tag_)) + , new_verbosity_level_(new_verbosity_level_) +{} + +const std::int32_t setLogTagVerbosityLevel::ID; + +void setLogTagVerbosityLevel::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "setLogTagVerbosityLevel"); + s.store_field("tag", tag_); + s.store_field("new_verbosity_level", new_verbosity_level_); + s.store_class_end(); + } +} + +setLogVerbosityLevel::setLogVerbosityLevel() + : new_verbosity_level_() +{} + +setLogVerbosityLevel::setLogVerbosityLevel(std::int32_t new_verbosity_level_) + : new_verbosity_level_(new_verbosity_level_) +{} + +const std::int32_t setLogVerbosityLevel::ID; + +void setLogVerbosityLevel::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "setLogVerbosityLevel"); + s.store_field("new_verbosity_level", new_verbosity_level_); + s.store_class_end(); + } +} + testGiver_getAccountAddress::testGiver_getAccountAddress() { } @@ -1170,5 +1410,92 @@ void testWallet_sendGrams::store(td::TlStorerToString &s, const char *field_name s.store_class_end(); } } + +wallet_getAccountAddress::wallet_getAccountAddress() + : initital_account_state_() +{} + +wallet_getAccountAddress::wallet_getAccountAddress(object_ptr &&initital_account_state_) + : initital_account_state_(std::move(initital_account_state_)) +{} + +const std::int32_t wallet_getAccountAddress::ID; + +void wallet_getAccountAddress::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "wallet_getAccountAddress"); + if (initital_account_state_ == nullptr) { s.store_field("initital_account_state", "null"); } else { initital_account_state_->store(s, "initital_account_state"); } + s.store_class_end(); + } +} + +wallet_getAccountState::wallet_getAccountState() + : account_address_() +{} + +wallet_getAccountState::wallet_getAccountState(object_ptr &&account_address_) + : account_address_(std::move(account_address_)) +{} + +const std::int32_t wallet_getAccountState::ID; + +void wallet_getAccountState::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "wallet_getAccountState"); + if (account_address_ == nullptr) { s.store_field("account_address", "null"); } else { account_address_->store(s, "account_address"); } + s.store_class_end(); + } +} + +wallet_init::wallet_init() + : private_key_() +{} + +wallet_init::wallet_init(object_ptr &&private_key_) + : private_key_(std::move(private_key_)) +{} + +const std::int32_t wallet_init::ID; + +void wallet_init::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "wallet_init"); + if (private_key_ == nullptr) { s.store_field("private_key", "null"); } else { private_key_->store(s, "private_key"); } + s.store_class_end(); + } +} + +wallet_sendGrams::wallet_sendGrams() + : private_key_() + , destination_() + , seqno_() + , valid_until_() + , amount_() + , message_() +{} + +wallet_sendGrams::wallet_sendGrams(object_ptr &&private_key_, object_ptr &&destination_, std::int32_t seqno_, std::int64_t valid_until_, std::int64_t amount_, std::string const &message_) + : private_key_(std::move(private_key_)) + , destination_(std::move(destination_)) + , seqno_(seqno_) + , valid_until_(valid_until_) + , amount_(amount_) + , message_(std::move(message_)) +{} + +const std::int32_t wallet_sendGrams::ID; + +void wallet_sendGrams::store(td::TlStorerToString &s, const char *field_name) const { + if (!LOG_IS_STRIPPED(ERROR)) { + s.store_class_begin(field_name, "wallet_sendGrams"); + if (private_key_ == nullptr) { s.store_field("private_key", "null"); } else { private_key_->store(s, "private_key"); } + if (destination_ == nullptr) { s.store_field("destination", "null"); } else { destination_->store(s, "destination"); } + s.store_field("seqno", seqno_); + s.store_field("valid_until", valid_until_); + s.store_field("amount", amount_); + s.store_bytes_field("message", message_); + s.store_class_end(); + } +} } // namespace tonlib_api } // namespace ton diff --git a/submodules/ton/tonlib-src/tl/generate/auto/tl/tonlib_api.h b/submodules/ton/tonlib-src/tl/generate/auto/tl/tonlib_api.h index 36277cd3a0..b1fabda282 100644 --- a/submodules/ton/tonlib-src/tl/generate/auto/tl/tonlib_api.h +++ b/submodules/ton/tonlib-src/tl/generate/auto/tl/tonlib_api.h @@ -60,6 +60,12 @@ class inputKey; class key; +class LogStream; + +class logTags; + +class logVerbosityLevel; + class ok; class options; @@ -68,8 +74,6 @@ class updateSendLiteServerQuery; class generic_AccountState; -class generic_InitialAccountState; - class internal_transactionId; class raw_accountState; @@ -90,6 +94,10 @@ class testWallet_initialAccountState; class uninited_accountState; +class wallet_accountState; + +class wallet_initialAccountState; + class Object; class Object: public TlObject { @@ -231,6 +239,85 @@ class key final : public Object { void store(td::TlStorerToString &s, const char *field_name) const final; }; +class LogStream: public Object { + public: +}; + +class logStreamDefault final : public LogStream { + public: + + logStreamDefault(); + + static const std::int32_t ID = 1390581436; + std::int32_t get_id() const final { + return ID; + } + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + +class logStreamFile final : public LogStream { + public: + std::string path_; + std::int64_t max_file_size_; + + logStreamFile(); + + logStreamFile(std::string const &path_, std::int64_t max_file_size_); + + static const std::int32_t ID = -1880085930; + std::int32_t get_id() const final { + return ID; + } + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + +class logStreamEmpty final : public LogStream { + public: + + logStreamEmpty(); + + static const std::int32_t ID = -499912244; + std::int32_t get_id() const final { + return ID; + } + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + +class logTags final : public Object { + public: + std::vector tags_; + + logTags(); + + explicit logTags(std::vector &&tags_); + + static const std::int32_t ID = -1604930601; + std::int32_t get_id() const final { + return ID; + } + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + +class logVerbosityLevel final : public Object { + public: + std::int32_t verbosity_level_; + + logVerbosityLevel(); + + explicit logVerbosityLevel(std::int32_t verbosity_level_); + + static const std::int32_t ID = 1734624234; + std::int32_t get_id() const final { + return ID; + } + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + class ok final : public Object { public: @@ -315,6 +402,22 @@ class generic_accountStateTestWallet final : public generic_AccountState { void store(td::TlStorerToString &s, const char *field_name) const final; }; +class generic_accountStateWallet final : public generic_AccountState { + public: + object_ptr account_state_; + + generic_accountStateWallet(); + + explicit generic_accountStateWallet(object_ptr &&account_state_); + + static const std::int32_t ID = 942582925; + std::int32_t get_id() const final { + return ID; + } + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + class generic_accountStateTestGiver final : public generic_AccountState { public: object_ptr account_state_; @@ -347,42 +450,6 @@ class generic_accountStateUninited final : public generic_AccountState { void store(td::TlStorerToString &s, const char *field_name) const final; }; -class generic_InitialAccountState: public Object { - public: -}; - -class generic_initialAccountStateRaw final : public generic_InitialAccountState { - public: - object_ptr initital_account_state_; - - generic_initialAccountStateRaw(); - - explicit generic_initialAccountStateRaw(object_ptr &&initital_account_state_); - - static const std::int32_t ID = -1178429153; - std::int32_t get_id() const final { - return ID; - } - - void store(td::TlStorerToString &s, const char *field_name) const final; -}; - -class generic_initialAccountStateTestWallet final : public generic_InitialAccountState { - public: - object_ptr initital_account_state_; - - generic_initialAccountStateTestWallet(); - - explicit generic_initialAccountStateTestWallet(object_ptr &&initital_account_state_); - - static const std::int32_t ID = 710924204; - std::int32_t get_id() const final { - return ID; - } - - void store(td::TlStorerToString &s, const char *field_name) const final; -}; - class internal_transactionId final : public Object { public: std::int64_t lt_; @@ -566,6 +633,60 @@ class uninited_accountState final : public Object { void store(td::TlStorerToString &s, const char *field_name) const final; }; +class wallet_accountState final : public Object { + public: + std::int64_t balance_; + std::int32_t seqno_; + object_ptr last_transaction_id_; + std::int64_t sync_utime_; + + wallet_accountState(); + + wallet_accountState(std::int64_t balance_, std::int32_t seqno_, object_ptr &&last_transaction_id_, std::int64_t sync_utime_); + + static const std::int32_t ID = -1919815977; + std::int32_t get_id() const final { + return ID; + } + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + +class wallet_initialAccountState final : public Object { + public: + std::string public_key_; + + wallet_initialAccountState(); + + explicit wallet_initialAccountState(std::string const &public_key_); + + static const std::int32_t ID = -1079249978; + std::int32_t get_id() const final { + return ID; + } + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + +class addLogMessage final : public Function { + public: + std::int32_t verbosity_level_; + std::string text_; + + addLogMessage(); + + addLogMessage(std::int32_t verbosity_level_, std::string const &text_); + + static const std::int32_t ID = 1597427692; + std::int32_t get_id() const final { + return ID; + } + + using ReturnType = object_ptr; + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + class changeLocalPassword final : public Function { public: object_ptr input_key_; @@ -622,13 +743,13 @@ class createNewKey final : public Function { class deleteKey final : public Function { public: - std::string public_key_; + object_ptr key_; deleteKey(); - explicit deleteKey(std::string const &public_key_); + explicit deleteKey(object_ptr &&key_); - static const std::int32_t ID = 917647652; + static const std::int32_t ID = -1579595571; std::int32_t get_id() const final { return ID; } @@ -752,6 +873,69 @@ class getBip39Hints final : public Function { void store(td::TlStorerToString &s, const char *field_name) const final; }; +class getLogStream final : public Function { + public: + + getLogStream(); + + static const std::int32_t ID = 1167608667; + std::int32_t get_id() const final { + return ID; + } + + using ReturnType = object_ptr; + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + +class getLogTagVerbosityLevel final : public Function { + public: + std::string tag_; + + getLogTagVerbosityLevel(); + + explicit getLogTagVerbosityLevel(std::string const &tag_); + + static const std::int32_t ID = 951004547; + std::int32_t get_id() const final { + return ID; + } + + using ReturnType = object_ptr; + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + +class getLogTags final : public Function { + public: + + getLogTags(); + + static const std::int32_t ID = -254449190; + std::int32_t get_id() const final { + return ID; + } + + using ReturnType = object_ptr; + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + +class getLogVerbosityLevel final : public Function { + public: + + getLogVerbosityLevel(); + + static const std::int32_t ID = 594057956; + std::int32_t get_id() const final { + return ID; + } + + using ReturnType = object_ptr; + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + class importEncryptedKey final : public Function { public: td::SecureString local_password_; @@ -979,6 +1163,61 @@ class runTests final : public Function { void store(td::TlStorerToString &s, const char *field_name) const final; }; +class setLogStream final : public Function { + public: + object_ptr log_stream_; + + setLogStream(); + + explicit setLogStream(object_ptr &&log_stream_); + + static const std::int32_t ID = -1364199535; + std::int32_t get_id() const final { + return ID; + } + + using ReturnType = object_ptr; + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + +class setLogTagVerbosityLevel final : public Function { + public: + std::string tag_; + std::int32_t new_verbosity_level_; + + setLogTagVerbosityLevel(); + + setLogTagVerbosityLevel(std::string const &tag_, std::int32_t new_verbosity_level_); + + static const std::int32_t ID = -2095589738; + std::int32_t get_id() const final { + return ID; + } + + using ReturnType = object_ptr; + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + +class setLogVerbosityLevel final : public Function { + public: + std::int32_t new_verbosity_level_; + + setLogVerbosityLevel(); + + explicit setLogVerbosityLevel(std::int32_t new_verbosity_level_); + + static const std::int32_t ID = -303429678; + std::int32_t get_id() const final { + return ID; + } + + using ReturnType = object_ptr; + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + class testGiver_getAccountAddress final : public Function { public: @@ -1106,5 +1345,82 @@ class testWallet_sendGrams final : public Function { void store(td::TlStorerToString &s, const char *field_name) const final; }; +class wallet_getAccountAddress final : public Function { + public: + object_ptr initital_account_state_; + + wallet_getAccountAddress(); + + explicit wallet_getAccountAddress(object_ptr &&initital_account_state_); + + static const std::int32_t ID = -1004103180; + std::int32_t get_id() const final { + return ID; + } + + using ReturnType = object_ptr; + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + +class wallet_getAccountState final : public Function { + public: + object_ptr account_address_; + + wallet_getAccountState(); + + explicit wallet_getAccountState(object_ptr &&account_address_); + + static const std::int32_t ID = 462294850; + std::int32_t get_id() const final { + return ID; + } + + using ReturnType = object_ptr; + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + +class wallet_init final : public Function { + public: + object_ptr private_key_; + + wallet_init(); + + explicit wallet_init(object_ptr &&private_key_); + + static const std::int32_t ID = 1528056782; + std::int32_t get_id() const final { + return ID; + } + + using ReturnType = object_ptr; + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + +class wallet_sendGrams final : public Function { + public: + object_ptr private_key_; + object_ptr destination_; + std::int32_t seqno_; + std::int64_t valid_until_; + std::int64_t amount_; + std::string message_; + + wallet_sendGrams(); + + wallet_sendGrams(object_ptr &&private_key_, object_ptr &&destination_, std::int32_t seqno_, std::int64_t valid_until_, std::int64_t amount_, std::string const &message_); + + static const std::int32_t ID = 789731197; + std::int32_t get_id() const final { + return ID; + } + + using ReturnType = object_ptr; + + void store(td::TlStorerToString &s, const char *field_name) const final; +}; + } // namespace tonlib_api } // namespace ton diff --git a/submodules/ton/tonlib-src/tl/generate/auto/tl/tonlib_api.hpp b/submodules/ton/tonlib-src/tl/generate/auto/tl/tonlib_api.hpp index 6631b6d8f0..bbc4c26f3f 100644 --- a/submodules/ton/tonlib-src/tl/generate/auto/tl/tonlib_api.hpp +++ b/submodules/ton/tonlib-src/tl/generate/auto/tl/tonlib_api.hpp @@ -38,6 +38,21 @@ bool downcast_call(Object &obj, const T &func) { case key::ID: func(static_cast(obj)); return true; + case logStreamDefault::ID: + func(static_cast(obj)); + return true; + case logStreamFile::ID: + func(static_cast(obj)); + return true; + case logStreamEmpty::ID: + func(static_cast(obj)); + return true; + case logTags::ID: + func(static_cast(obj)); + return true; + case logVerbosityLevel::ID: + func(static_cast(obj)); + return true; case ok::ID: func(static_cast(obj)); return true; @@ -53,18 +68,15 @@ bool downcast_call(Object &obj, const T &func) { case generic_accountStateTestWallet::ID: func(static_cast(obj)); return true; + case generic_accountStateWallet::ID: + func(static_cast(obj)); + return true; case generic_accountStateTestGiver::ID: func(static_cast(obj)); return true; case generic_accountStateUninited::ID: func(static_cast(obj)); return true; - case generic_initialAccountStateRaw::ID: - func(static_cast(obj)); - return true; - case generic_initialAccountStateTestWallet::ID: - func(static_cast(obj)); - return true; case internal_transactionId::ID: func(static_cast(obj)); return true; @@ -95,6 +107,12 @@ bool downcast_call(Object &obj, const T &func) { case uninited_accountState::ID: func(static_cast(obj)); return true; + case wallet_accountState::ID: + func(static_cast(obj)); + return true; + case wallet_initialAccountState::ID: + func(static_cast(obj)); + return true; default: return false; } @@ -109,6 +127,9 @@ bool downcast_call(Object &obj, const T &func) { template bool downcast_call(Function &obj, const T &func) { switch (obj.get_id()) { + case addLogMessage::ID: + func(static_cast(obj)); + return true; case changeLocalPassword::ID: func(static_cast(obj)); return true; @@ -139,6 +160,18 @@ bool downcast_call(Function &obj, const T &func) { case getBip39Hints::ID: func(static_cast(obj)); return true; + case getLogStream::ID: + func(static_cast(obj)); + return true; + case getLogTagVerbosityLevel::ID: + func(static_cast(obj)); + return true; + case getLogTags::ID: + func(static_cast(obj)); + return true; + case getLogVerbosityLevel::ID: + func(static_cast(obj)); + return true; case importEncryptedKey::ID: func(static_cast(obj)); return true; @@ -175,6 +208,15 @@ bool downcast_call(Function &obj, const T &func) { case runTests::ID: func(static_cast(obj)); return true; + case setLogStream::ID: + func(static_cast(obj)); + return true; + case setLogTagVerbosityLevel::ID: + func(static_cast(obj)); + return true; + case setLogVerbosityLevel::ID: + func(static_cast(obj)); + return true; case testGiver_getAccountAddress::ID: func(static_cast(obj)); return true; @@ -196,6 +238,41 @@ bool downcast_call(Function &obj, const T &func) { case testWallet_sendGrams::ID: func(static_cast(obj)); return true; + case wallet_getAccountAddress::ID: + func(static_cast(obj)); + return true; + case wallet_getAccountState::ID: + func(static_cast(obj)); + return true; + case wallet_init::ID: + func(static_cast(obj)); + return true; + case wallet_sendGrams::ID: + func(static_cast(obj)); + return true; + default: + return false; + } +} + +/** + * Calls specified function object with the specified object downcasted to the most-derived type. + * \param[in] obj Object to pass as an argument to the function object. + * \param[in] func Function object to which the object will be passed. + * \returns whether function object call has happened. Should always return true for correct parameters. + */ +template +bool downcast_call(LogStream &obj, const T &func) { + switch (obj.get_id()) { + case logStreamDefault::ID: + func(static_cast(obj)); + return true; + case logStreamFile::ID: + func(static_cast(obj)); + return true; + case logStreamEmpty::ID: + func(static_cast(obj)); + return true; default: return false; } @@ -216,6 +293,9 @@ bool downcast_call(generic_AccountState &obj, const T &func) { case generic_accountStateTestWallet::ID: func(static_cast(obj)); return true; + case generic_accountStateWallet::ID: + func(static_cast(obj)); + return true; case generic_accountStateTestGiver::ID: func(static_cast(obj)); return true; @@ -227,25 +307,5 @@ bool downcast_call(generic_AccountState &obj, const T &func) { } } -/** - * Calls specified function object with the specified object downcasted to the most-derived type. - * \param[in] obj Object to pass as an argument to the function object. - * \param[in] func Function object to which the object will be passed. - * \returns whether function object call has happened. Should always return true for correct parameters. - */ -template -bool downcast_call(generic_InitialAccountState &obj, const T &func) { - switch (obj.get_id()) { - case generic_initialAccountStateRaw::ID: - func(static_cast(obj)); - return true; - case generic_initialAccountStateTestWallet::ID: - func(static_cast(obj)); - return true; - default: - return false; - } -} - } // namespace tonlib_api } // namespace ton diff --git a/submodules/ton/tonlib-src/tl/generate/auto/tl/tonlib_api_json.cpp b/submodules/ton/tonlib-src/tl/generate/auto/tl/tonlib_api_json.cpp index 3c1402d996..eaef24cd46 100644 --- a/submodules/ton/tonlib-src/tl/generate/auto/tl/tonlib_api_json.cpp +++ b/submodules/ton/tonlib-src/tl/generate/auto/tl/tonlib_api_json.cpp @@ -14,12 +14,11 @@ namespace ton { namespace tonlib_api{ using namespace td; -Result tl_constructor_from_string(tonlib_api::generic_AccountState *object, const std::string &str) { +Result tl_constructor_from_string(tonlib_api::LogStream *object, const std::string &str) { static const std::unordered_map m = { - {"generic.accountStateRaw", -1387096685}, - {"generic.accountStateTestWallet", -1041955397}, - {"generic.accountStateTestGiver", 1134654598}, - {"generic.accountStateUninited", -908702008} + {"logStreamDefault", 1390581436}, + {"logStreamFile", -1880085930}, + {"logStreamEmpty", -499912244} }; auto it = m.find(str); if (it == m.end()) { @@ -27,10 +26,13 @@ Result tl_constructor_from_string(tonlib_api::generic_AccountState *objec } return it->second; } -Result tl_constructor_from_string(tonlib_api::generic_InitialAccountState *object, const std::string &str) { +Result tl_constructor_from_string(tonlib_api::generic_AccountState *object, const std::string &str) { static const std::unordered_map m = { - {"generic.initialAccountStateRaw", -1178429153}, - {"generic.initialAccountStateTestWallet", 710924204} + {"generic.accountStateRaw", -1387096685}, + {"generic.accountStateTestWallet", -1041955397}, + {"generic.accountStateWallet", 942582925}, + {"generic.accountStateTestGiver", 1134654598}, + {"generic.accountStateUninited", -908702008} }; auto it = m.find(str); if (it == m.end()) { @@ -48,15 +50,19 @@ Result tl_constructor_from_string(tonlib_api::Object *object, const std:: {"exportedPemKey", 1425473725}, {"inputKey", 869287093}, {"key", -1978362923}, + {"logStreamDefault", 1390581436}, + {"logStreamFile", -1880085930}, + {"logStreamEmpty", -499912244}, + {"logTags", -1604930601}, + {"logVerbosityLevel", 1734624234}, {"ok", -722616727}, {"options", -952483001}, {"updateSendLiteServerQuery", -1555130916}, {"generic.accountStateRaw", -1387096685}, {"generic.accountStateTestWallet", -1041955397}, + {"generic.accountStateWallet", 942582925}, {"generic.accountStateTestGiver", 1134654598}, {"generic.accountStateUninited", -908702008}, - {"generic.initialAccountStateRaw", -1178429153}, - {"generic.initialAccountStateTestWallet", 710924204}, {"internal.transactionId", -989527262}, {"raw.accountState", 461615898}, {"raw.initialAccountState", 777456197}, @@ -66,7 +72,9 @@ Result tl_constructor_from_string(tonlib_api::Object *object, const std:: {"testGiver.accountState", 860930426}, {"testWallet.accountState", 305698744}, {"testWallet.initialAccountState", -1231516227}, - {"uninited.accountState", 1768941188} + {"uninited.accountState", 1768941188}, + {"wallet.accountState", -1919815977}, + {"wallet.initialAccountState", -1079249978} }; auto it = m.find(str); if (it == m.end()) { @@ -76,16 +84,21 @@ Result tl_constructor_from_string(tonlib_api::Object *object, const std:: } Result tl_constructor_from_string(tonlib_api::Function *object, const std::string &str) { static const std::unordered_map m = { + {"addLogMessage", 1597427692}, {"changeLocalPassword", -1685491421}, {"close", -1187782273}, {"createNewKey", -1861385712}, - {"deleteKey", 917647652}, + {"deleteKey", -1579595571}, {"exportEncryptedKey", 155352861}, {"exportKey", 399723440}, {"exportPemKey", -2047752448}, {"generic.getAccountState", -657000446}, {"generic.sendGrams", 1523427648}, {"getBip39Hints", -1889640982}, + {"getLogStream", 1167608667}, + {"getLogTagVerbosityLevel", 951004547}, + {"getLogTags", -254449190}, + {"getLogVerbosityLevel", 594057956}, {"importEncryptedKey", 656724958}, {"importKey", -1607900903}, {"importPemKey", 76385617}, @@ -98,13 +111,20 @@ Result tl_constructor_from_string(tonlib_api::Function *object, const std {"raw.getTransactions", 935377269}, {"raw.sendMessage", 473889461}, {"runTests", -2039925427}, + {"setLogStream", -1364199535}, + {"setLogTagVerbosityLevel", -2095589738}, + {"setLogVerbosityLevel", -303429678}, {"testGiver.getAccountAddress", -540100768}, {"testGiver.getAccountState", 267738275}, {"testGiver.sendGrams", -1361914347}, {"testWallet.getAccountAddress", -1557748223}, {"testWallet.getAccountState", 654082364}, {"testWallet.init", 419055225}, - {"testWallet.sendGrams", 43200674} + {"testWallet.sendGrams", 43200674}, + {"wallet.getAccountAddress", -1004103180}, + {"wallet.getAccountState", 462294850}, + {"wallet.init", 1528056782}, + {"wallet.sendGrams", 789731197} }; auto it = m.find(str); if (it == m.end()) { @@ -202,6 +222,45 @@ Status from_json(tonlib_api::key &to, JsonObject &from) { } return Status::OK(); } +Status from_json(tonlib_api::logStreamDefault &to, JsonObject &from) { + return Status::OK(); +} +Status from_json(tonlib_api::logStreamFile &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "path", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.path_, value)); + } + } + { + TRY_RESULT(value, get_json_object_field(from, "max_file_size", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.max_file_size_, value)); + } + } + return Status::OK(); +} +Status from_json(tonlib_api::logStreamEmpty &to, JsonObject &from) { + return Status::OK(); +} +Status from_json(tonlib_api::logTags &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "tags", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.tags_, value)); + } + } + return Status::OK(); +} +Status from_json(tonlib_api::logVerbosityLevel &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "verbosity_level", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.verbosity_level_, value)); + } + } + return Status::OK(); +} Status from_json(tonlib_api::ok &to, JsonObject &from) { return Status::OK(); } @@ -259,6 +318,15 @@ Status from_json(tonlib_api::generic_accountStateTestWallet &to, JsonObject &fro } return Status::OK(); } +Status from_json(tonlib_api::generic_accountStateWallet &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "account_state", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.account_state_, value)); + } + } + return Status::OK(); +} Status from_json(tonlib_api::generic_accountStateTestGiver &to, JsonObject &from) { { TRY_RESULT(value, get_json_object_field(from, "account_state", JsonValue::Type::Null, true)); @@ -277,24 +345,6 @@ Status from_json(tonlib_api::generic_accountStateUninited &to, JsonObject &from) } return Status::OK(); } -Status from_json(tonlib_api::generic_initialAccountStateRaw &to, JsonObject &from) { - { - TRY_RESULT(value, get_json_object_field(from, "initital_account_state", JsonValue::Type::Null, true)); - if (value.type() != JsonValue::Type::Null) { - TRY_STATUS(from_json(to.initital_account_state_, value)); - } - } - return Status::OK(); -} -Status from_json(tonlib_api::generic_initialAccountStateTestWallet &to, JsonObject &from) { - { - TRY_RESULT(value, get_json_object_field(from, "initital_account_state", JsonValue::Type::Null, true)); - if (value.type() != JsonValue::Type::Null) { - TRY_STATUS(from_json(to.initital_account_state_, value)); - } - } - return Status::OK(); -} Status from_json(tonlib_api::internal_transactionId &to, JsonObject &from) { { TRY_RESULT(value, get_json_object_field(from, "lt", JsonValue::Type::Null, true)); @@ -523,6 +573,57 @@ Status from_json(tonlib_api::uninited_accountState &to, JsonObject &from) { } return Status::OK(); } +Status from_json(tonlib_api::wallet_accountState &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "balance", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.balance_, value)); + } + } + { + TRY_RESULT(value, get_json_object_field(from, "seqno", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.seqno_, value)); + } + } + { + TRY_RESULT(value, get_json_object_field(from, "last_transaction_id", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.last_transaction_id_, value)); + } + } + { + TRY_RESULT(value, get_json_object_field(from, "sync_utime", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.sync_utime_, value)); + } + } + return Status::OK(); +} +Status from_json(tonlib_api::wallet_initialAccountState &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "public_key", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.public_key_, value)); + } + } + return Status::OK(); +} +Status from_json(tonlib_api::addLogMessage &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "verbosity_level", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.verbosity_level_, value)); + } + } + { + TRY_RESULT(value, get_json_object_field(from, "text", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.text_, value)); + } + } + return Status::OK(); +} Status from_json(tonlib_api::changeLocalPassword &to, JsonObject &from) { { TRY_RESULT(value, get_json_object_field(from, "input_key", JsonValue::Type::Null, true)); @@ -564,9 +665,9 @@ Status from_json(tonlib_api::createNewKey &to, JsonObject &from) { } Status from_json(tonlib_api::deleteKey &to, JsonObject &from) { { - TRY_RESULT(value, get_json_object_field(from, "public_key", JsonValue::Type::Null, true)); + TRY_RESULT(value, get_json_object_field(from, "key", JsonValue::Type::Null, true)); if (value.type() != JsonValue::Type::Null) { - TRY_STATUS(from_json(to.public_key_, value)); + TRY_STATUS(from_json(to.key_, value)); } } return Status::OK(); @@ -661,6 +762,24 @@ Status from_json(tonlib_api::getBip39Hints &to, JsonObject &from) { } return Status::OK(); } +Status from_json(tonlib_api::getLogStream &to, JsonObject &from) { + return Status::OK(); +} +Status from_json(tonlib_api::getLogTagVerbosityLevel &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "tag", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.tag_, value)); + } + } + return Status::OK(); +} +Status from_json(tonlib_api::getLogTags &to, JsonObject &from) { + return Status::OK(); +} +Status from_json(tonlib_api::getLogVerbosityLevel &to, JsonObject &from) { + return Status::OK(); +} Status from_json(tonlib_api::importEncryptedKey &to, JsonObject &from) { { TRY_RESULT(value, get_json_object_field(from, "local_password", JsonValue::Type::Null, true)); @@ -835,6 +954,39 @@ Status from_json(tonlib_api::runTests &to, JsonObject &from) { } return Status::OK(); } +Status from_json(tonlib_api::setLogStream &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "log_stream", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.log_stream_, value)); + } + } + return Status::OK(); +} +Status from_json(tonlib_api::setLogTagVerbosityLevel &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "tag", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.tag_, value)); + } + } + { + TRY_RESULT(value, get_json_object_field(from, "new_verbosity_level", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.new_verbosity_level_, value)); + } + } + return Status::OK(); +} +Status from_json(tonlib_api::setLogVerbosityLevel &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "new_verbosity_level", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.new_verbosity_level_, value)); + } + } + return Status::OK(); +} Status from_json(tonlib_api::testGiver_getAccountAddress &to, JsonObject &from) { return Status::OK(); } @@ -928,6 +1080,72 @@ Status from_json(tonlib_api::testWallet_sendGrams &to, JsonObject &from) { } return Status::OK(); } +Status from_json(tonlib_api::wallet_getAccountAddress &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "initital_account_state", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.initital_account_state_, value)); + } + } + return Status::OK(); +} +Status from_json(tonlib_api::wallet_getAccountState &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "account_address", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.account_address_, value)); + } + } + return Status::OK(); +} +Status from_json(tonlib_api::wallet_init &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "private_key", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.private_key_, value)); + } + } + return Status::OK(); +} +Status from_json(tonlib_api::wallet_sendGrams &to, JsonObject &from) { + { + TRY_RESULT(value, get_json_object_field(from, "private_key", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.private_key_, value)); + } + } + { + TRY_RESULT(value, get_json_object_field(from, "destination", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.destination_, value)); + } + } + { + TRY_RESULT(value, get_json_object_field(from, "seqno", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.seqno_, value)); + } + } + { + TRY_RESULT(value, get_json_object_field(from, "valid_until", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + TRY_STATUS(from_json(to.valid_until_, value)); + } + } + { + TRY_RESULT(value, get_json_object_field(from, "amount", JsonValue::Type::Null, true)); + if (value.type() != JsonValue::Type::Null) { + 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(); +} void to_json(JsonValueScope &jv, const tonlib_api::accountAddress &object) { auto jo = jv.enter_object(); jo << ctie("@type", "accountAddress"); @@ -973,6 +1191,33 @@ void to_json(JsonValueScope &jv, const tonlib_api::key &object) { jo << ctie("public_key", ToJson(object.public_key_)); jo << ctie("secret", ToJson(JsonBytes{object.secret_})); } +void to_json(JsonValueScope &jv, const tonlib_api::LogStream &object) { + tonlib_api::downcast_call(const_cast(object), [&jv](const auto &object) { to_json(jv, object); }); +} +void to_json(JsonValueScope &jv, const tonlib_api::logStreamDefault &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "logStreamDefault"); +} +void to_json(JsonValueScope &jv, const tonlib_api::logStreamFile &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "logStreamFile"); + jo << ctie("path", ToJson(object.path_)); + jo << ctie("max_file_size", ToJson(object.max_file_size_)); +} +void to_json(JsonValueScope &jv, const tonlib_api::logStreamEmpty &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "logStreamEmpty"); +} +void to_json(JsonValueScope &jv, const tonlib_api::logTags &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "logTags"); + jo << ctie("tags", ToJson(object.tags_)); +} +void to_json(JsonValueScope &jv, const tonlib_api::logVerbosityLevel &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "logVerbosityLevel"); + jo << ctie("verbosity_level", ToJson(object.verbosity_level_)); +} void to_json(JsonValueScope &jv, const tonlib_api::ok &object) { auto jo = jv.enter_object(); jo << ctie("@type", "ok"); @@ -1007,6 +1252,13 @@ void to_json(JsonValueScope &jv, const tonlib_api::generic_accountStateTestWalle jo << ctie("account_state", ToJson(object.account_state_)); } } +void to_json(JsonValueScope &jv, const tonlib_api::generic_accountStateWallet &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "generic.accountStateWallet"); + if (object.account_state_) { + jo << ctie("account_state", ToJson(object.account_state_)); + } +} void to_json(JsonValueScope &jv, const tonlib_api::generic_accountStateTestGiver &object) { auto jo = jv.enter_object(); jo << ctie("@type", "generic.accountStateTestGiver"); @@ -1021,23 +1273,6 @@ void to_json(JsonValueScope &jv, const tonlib_api::generic_accountStateUninited jo << ctie("account_state", ToJson(object.account_state_)); } } -void to_json(JsonValueScope &jv, const tonlib_api::generic_InitialAccountState &object) { - tonlib_api::downcast_call(const_cast(object), [&jv](const auto &object) { to_json(jv, object); }); -} -void to_json(JsonValueScope &jv, const tonlib_api::generic_initialAccountStateRaw &object) { - auto jo = jv.enter_object(); - jo << ctie("@type", "generic.initialAccountStateRaw"); - if (object.initital_account_state_) { - jo << ctie("initital_account_state", ToJson(object.initital_account_state_)); - } -} -void to_json(JsonValueScope &jv, const tonlib_api::generic_initialAccountStateTestWallet &object) { - auto jo = jv.enter_object(); - jo << ctie("@type", "generic.initialAccountStateTestWallet"); - if (object.initital_account_state_) { - jo << ctie("initital_account_state", ToJson(object.initital_account_state_)); - } -} void to_json(JsonValueScope &jv, const tonlib_api::internal_transactionId &object) { auto jo = jv.enter_object(); jo << ctie("@type", "internal.transactionId"); @@ -1125,6 +1360,27 @@ void to_json(JsonValueScope &jv, const tonlib_api::uninited_accountState &object } jo << ctie("sync_utime", ToJson(object.sync_utime_)); } +void to_json(JsonValueScope &jv, const tonlib_api::wallet_accountState &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "wallet.accountState"); + jo << ctie("balance", ToJson(JsonInt64{object.balance_})); + jo << ctie("seqno", ToJson(object.seqno_)); + if (object.last_transaction_id_) { + jo << ctie("last_transaction_id", ToJson(object.last_transaction_id_)); + } + jo << ctie("sync_utime", ToJson(object.sync_utime_)); +} +void to_json(JsonValueScope &jv, const tonlib_api::wallet_initialAccountState &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "wallet.initialAccountState"); + jo << ctie("public_key", ToJson(object.public_key_)); +} +void to_json(JsonValueScope &jv, const tonlib_api::addLogMessage &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "addLogMessage"); + jo << ctie("verbosity_level", ToJson(object.verbosity_level_)); + jo << ctie("text", ToJson(object.text_)); +} void to_json(JsonValueScope &jv, const tonlib_api::changeLocalPassword &object) { auto jo = jv.enter_object(); jo << ctie("@type", "changeLocalPassword"); @@ -1147,7 +1403,9 @@ void to_json(JsonValueScope &jv, const tonlib_api::createNewKey &object) { void to_json(JsonValueScope &jv, const tonlib_api::deleteKey &object) { auto jo = jv.enter_object(); jo << ctie("@type", "deleteKey"); - jo << ctie("public_key", ToJson(object.public_key_)); + if (object.key_) { + jo << ctie("key", ToJson(object.key_)); + } } void to_json(JsonValueScope &jv, const tonlib_api::exportEncryptedKey &object) { auto jo = jv.enter_object(); @@ -1199,6 +1457,23 @@ void to_json(JsonValueScope &jv, const tonlib_api::getBip39Hints &object) { jo << ctie("@type", "getBip39Hints"); jo << ctie("prefix", ToJson(object.prefix_)); } +void to_json(JsonValueScope &jv, const tonlib_api::getLogStream &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "getLogStream"); +} +void to_json(JsonValueScope &jv, const tonlib_api::getLogTagVerbosityLevel &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "getLogTagVerbosityLevel"); + jo << ctie("tag", ToJson(object.tag_)); +} +void to_json(JsonValueScope &jv, const tonlib_api::getLogTags &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "getLogTags"); +} +void to_json(JsonValueScope &jv, const tonlib_api::getLogVerbosityLevel &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "getLogVerbosityLevel"); +} void to_json(JsonValueScope &jv, const tonlib_api::importEncryptedKey &object) { auto jo = jv.enter_object(); jo << ctie("@type", "importEncryptedKey"); @@ -1290,6 +1565,24 @@ void to_json(JsonValueScope &jv, const tonlib_api::runTests &object) { jo << ctie("@type", "runTests"); jo << ctie("dir", ToJson(object.dir_)); } +void to_json(JsonValueScope &jv, const tonlib_api::setLogStream &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "setLogStream"); + if (object.log_stream_) { + jo << ctie("log_stream", ToJson(object.log_stream_)); + } +} +void to_json(JsonValueScope &jv, const tonlib_api::setLogTagVerbosityLevel &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "setLogTagVerbosityLevel"); + jo << ctie("tag", ToJson(object.tag_)); + jo << ctie("new_verbosity_level", ToJson(object.new_verbosity_level_)); +} +void to_json(JsonValueScope &jv, const tonlib_api::setLogVerbosityLevel &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "setLogVerbosityLevel"); + jo << ctie("new_verbosity_level", ToJson(object.new_verbosity_level_)); +} void to_json(JsonValueScope &jv, const tonlib_api::testGiver_getAccountAddress &object) { auto jo = jv.enter_object(); jo << ctie("@type", "testGiver.getAccountAddress"); @@ -1342,5 +1635,40 @@ void to_json(JsonValueScope &jv, const tonlib_api::testWallet_sendGrams &object) jo << ctie("amount", ToJson(JsonInt64{object.amount_})); jo << ctie("message", ToJson(JsonBytes{object.message_})); } +void to_json(JsonValueScope &jv, const tonlib_api::wallet_getAccountAddress &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "wallet.getAccountAddress"); + if (object.initital_account_state_) { + jo << ctie("initital_account_state", ToJson(object.initital_account_state_)); + } +} +void to_json(JsonValueScope &jv, const tonlib_api::wallet_getAccountState &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "wallet.getAccountState"); + if (object.account_address_) { + jo << ctie("account_address", ToJson(object.account_address_)); + } +} +void to_json(JsonValueScope &jv, const tonlib_api::wallet_init &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "wallet.init"); + if (object.private_key_) { + jo << ctie("private_key", ToJson(object.private_key_)); + } +} +void to_json(JsonValueScope &jv, const tonlib_api::wallet_sendGrams &object) { + auto jo = jv.enter_object(); + jo << ctie("@type", "wallet.sendGrams"); + if (object.private_key_) { + jo << ctie("private_key", ToJson(object.private_key_)); + } + if (object.destination_) { + jo << ctie("destination", ToJson(object.destination_)); + } + jo << ctie("seqno", ToJson(object.seqno_)); + jo << ctie("valid_until", ToJson(object.valid_until_)); + jo << ctie("amount", ToJson(JsonInt64{object.amount_})); + jo << ctie("message", ToJson(JsonBytes{object.message_})); +} } // namespace tonlib_api } // namespace ton diff --git a/submodules/ton/tonlib-src/tl/generate/auto/tl/tonlib_api_json.h b/submodules/ton/tonlib-src/tl/generate/auto/tl/tonlib_api_json.h index 3021c65ae1..af09060e74 100644 --- a/submodules/ton/tonlib-src/tl/generate/auto/tl/tonlib_api_json.h +++ b/submodules/ton/tonlib-src/tl/generate/auto/tl/tonlib_api_json.h @@ -11,8 +11,8 @@ namespace ton { namespace tonlib_api{ using namespace td; +Result tl_constructor_from_string(tonlib_api::LogStream *object, const std::string &str); Result tl_constructor_from_string(tonlib_api::generic_AccountState *object, const std::string &str); -Result tl_constructor_from_string(tonlib_api::generic_InitialAccountState *object, const std::string &str); Result tl_constructor_from_string(tonlib_api::Object *object, const std::string &str); Result tl_constructor_from_string(tonlib_api::Function *object, const std::string &str); Status from_json(tonlib_api::accountAddress &to, JsonObject &from); @@ -23,15 +23,19 @@ Status from_json(tonlib_api::exportedKey &to, JsonObject &from); Status from_json(tonlib_api::exportedPemKey &to, JsonObject &from); Status from_json(tonlib_api::inputKey &to, JsonObject &from); Status from_json(tonlib_api::key &to, JsonObject &from); +Status from_json(tonlib_api::logStreamDefault &to, JsonObject &from); +Status from_json(tonlib_api::logStreamFile &to, JsonObject &from); +Status from_json(tonlib_api::logStreamEmpty &to, JsonObject &from); +Status from_json(tonlib_api::logTags &to, JsonObject &from); +Status from_json(tonlib_api::logVerbosityLevel &to, JsonObject &from); Status from_json(tonlib_api::ok &to, JsonObject &from); Status from_json(tonlib_api::options &to, JsonObject &from); Status from_json(tonlib_api::updateSendLiteServerQuery &to, JsonObject &from); Status from_json(tonlib_api::generic_accountStateRaw &to, JsonObject &from); Status from_json(tonlib_api::generic_accountStateTestWallet &to, JsonObject &from); +Status from_json(tonlib_api::generic_accountStateWallet &to, JsonObject &from); Status from_json(tonlib_api::generic_accountStateTestGiver &to, JsonObject &from); Status from_json(tonlib_api::generic_accountStateUninited &to, JsonObject &from); -Status from_json(tonlib_api::generic_initialAccountStateRaw &to, JsonObject &from); -Status from_json(tonlib_api::generic_initialAccountStateTestWallet &to, JsonObject &from); Status from_json(tonlib_api::internal_transactionId &to, JsonObject &from); Status from_json(tonlib_api::raw_accountState &to, JsonObject &from); Status from_json(tonlib_api::raw_initialAccountState &to, JsonObject &from); @@ -42,6 +46,9 @@ Status from_json(tonlib_api::testGiver_accountState &to, JsonObject &from); Status from_json(tonlib_api::testWallet_accountState &to, JsonObject &from); Status from_json(tonlib_api::testWallet_initialAccountState &to, JsonObject &from); Status from_json(tonlib_api::uninited_accountState &to, JsonObject &from); +Status from_json(tonlib_api::wallet_accountState &to, JsonObject &from); +Status from_json(tonlib_api::wallet_initialAccountState &to, JsonObject &from); +Status from_json(tonlib_api::addLogMessage &to, JsonObject &from); Status from_json(tonlib_api::changeLocalPassword &to, JsonObject &from); Status from_json(tonlib_api::close &to, JsonObject &from); Status from_json(tonlib_api::createNewKey &to, JsonObject &from); @@ -52,6 +59,10 @@ Status from_json(tonlib_api::exportPemKey &to, JsonObject &from); Status from_json(tonlib_api::generic_getAccountState &to, JsonObject &from); Status from_json(tonlib_api::generic_sendGrams &to, JsonObject &from); Status from_json(tonlib_api::getBip39Hints &to, JsonObject &from); +Status from_json(tonlib_api::getLogStream &to, JsonObject &from); +Status from_json(tonlib_api::getLogTagVerbosityLevel &to, JsonObject &from); +Status from_json(tonlib_api::getLogTags &to, JsonObject &from); +Status from_json(tonlib_api::getLogVerbosityLevel &to, JsonObject &from); Status from_json(tonlib_api::importEncryptedKey &to, JsonObject &from); Status from_json(tonlib_api::importKey &to, JsonObject &from); Status from_json(tonlib_api::importPemKey &to, JsonObject &from); @@ -64,6 +75,9 @@ Status from_json(tonlib_api::raw_getAccountState &to, JsonObject &from); Status from_json(tonlib_api::raw_getTransactions &to, JsonObject &from); Status from_json(tonlib_api::raw_sendMessage &to, JsonObject &from); Status from_json(tonlib_api::runTests &to, JsonObject &from); +Status from_json(tonlib_api::setLogStream &to, JsonObject &from); +Status from_json(tonlib_api::setLogTagVerbosityLevel &to, JsonObject &from); +Status from_json(tonlib_api::setLogVerbosityLevel &to, JsonObject &from); Status from_json(tonlib_api::testGiver_getAccountAddress &to, JsonObject &from); Status from_json(tonlib_api::testGiver_getAccountState &to, JsonObject &from); Status from_json(tonlib_api::testGiver_sendGrams &to, JsonObject &from); @@ -71,6 +85,10 @@ Status from_json(tonlib_api::testWallet_getAccountAddress &to, JsonObject &from) Status from_json(tonlib_api::testWallet_getAccountState &to, JsonObject &from); Status from_json(tonlib_api::testWallet_init &to, JsonObject &from); Status from_json(tonlib_api::testWallet_sendGrams &to, JsonObject &from); +Status from_json(tonlib_api::wallet_getAccountAddress &to, JsonObject &from); +Status from_json(tonlib_api::wallet_getAccountState &to, JsonObject &from); +Status from_json(tonlib_api::wallet_init &to, JsonObject &from); +Status from_json(tonlib_api::wallet_sendGrams &to, JsonObject &from); void to_json(JsonValueScope &jv, const tonlib_api::accountAddress &object); void to_json(JsonValueScope &jv, const tonlib_api::bip39Hints &object); void to_json(JsonValueScope &jv, const tonlib_api::error &object); @@ -79,17 +97,21 @@ void to_json(JsonValueScope &jv, const tonlib_api::exportedKey &object); void to_json(JsonValueScope &jv, const tonlib_api::exportedPemKey &object); void to_json(JsonValueScope &jv, const tonlib_api::inputKey &object); void to_json(JsonValueScope &jv, const tonlib_api::key &object); +void to_json(JsonValueScope &jv, const tonlib_api::LogStream &object); +void to_json(JsonValueScope &jv, const tonlib_api::logStreamDefault &object); +void to_json(JsonValueScope &jv, const tonlib_api::logStreamFile &object); +void to_json(JsonValueScope &jv, const tonlib_api::logStreamEmpty &object); +void to_json(JsonValueScope &jv, const tonlib_api::logTags &object); +void to_json(JsonValueScope &jv, const tonlib_api::logVerbosityLevel &object); void to_json(JsonValueScope &jv, const tonlib_api::ok &object); void to_json(JsonValueScope &jv, const tonlib_api::options &object); void to_json(JsonValueScope &jv, const tonlib_api::updateSendLiteServerQuery &object); void to_json(JsonValueScope &jv, const tonlib_api::generic_AccountState &object); void to_json(JsonValueScope &jv, const tonlib_api::generic_accountStateRaw &object); void to_json(JsonValueScope &jv, const tonlib_api::generic_accountStateTestWallet &object); +void to_json(JsonValueScope &jv, const tonlib_api::generic_accountStateWallet &object); void to_json(JsonValueScope &jv, const tonlib_api::generic_accountStateTestGiver &object); void to_json(JsonValueScope &jv, const tonlib_api::generic_accountStateUninited &object); -void to_json(JsonValueScope &jv, const tonlib_api::generic_InitialAccountState &object); -void to_json(JsonValueScope &jv, const tonlib_api::generic_initialAccountStateRaw &object); -void to_json(JsonValueScope &jv, const tonlib_api::generic_initialAccountStateTestWallet &object); void to_json(JsonValueScope &jv, const tonlib_api::internal_transactionId &object); void to_json(JsonValueScope &jv, const tonlib_api::raw_accountState &object); void to_json(JsonValueScope &jv, const tonlib_api::raw_initialAccountState &object); @@ -100,6 +122,9 @@ void to_json(JsonValueScope &jv, const tonlib_api::testGiver_accountState &objec void to_json(JsonValueScope &jv, const tonlib_api::testWallet_accountState &object); void to_json(JsonValueScope &jv, const tonlib_api::testWallet_initialAccountState &object); void to_json(JsonValueScope &jv, const tonlib_api::uninited_accountState &object); +void to_json(JsonValueScope &jv, const tonlib_api::wallet_accountState &object); +void to_json(JsonValueScope &jv, const tonlib_api::wallet_initialAccountState &object); +void to_json(JsonValueScope &jv, const tonlib_api::addLogMessage &object); void to_json(JsonValueScope &jv, const tonlib_api::changeLocalPassword &object); void to_json(JsonValueScope &jv, const tonlib_api::close &object); void to_json(JsonValueScope &jv, const tonlib_api::createNewKey &object); @@ -110,6 +135,10 @@ void to_json(JsonValueScope &jv, const tonlib_api::exportPemKey &object); void to_json(JsonValueScope &jv, const tonlib_api::generic_getAccountState &object); void to_json(JsonValueScope &jv, const tonlib_api::generic_sendGrams &object); void to_json(JsonValueScope &jv, const tonlib_api::getBip39Hints &object); +void to_json(JsonValueScope &jv, const tonlib_api::getLogStream &object); +void to_json(JsonValueScope &jv, const tonlib_api::getLogTagVerbosityLevel &object); +void to_json(JsonValueScope &jv, const tonlib_api::getLogTags &object); +void to_json(JsonValueScope &jv, const tonlib_api::getLogVerbosityLevel &object); void to_json(JsonValueScope &jv, const tonlib_api::importEncryptedKey &object); void to_json(JsonValueScope &jv, const tonlib_api::importKey &object); void to_json(JsonValueScope &jv, const tonlib_api::importPemKey &object); @@ -122,6 +151,9 @@ void to_json(JsonValueScope &jv, const tonlib_api::raw_getAccountState &object); void to_json(JsonValueScope &jv, const tonlib_api::raw_getTransactions &object); void to_json(JsonValueScope &jv, const tonlib_api::raw_sendMessage &object); void to_json(JsonValueScope &jv, const tonlib_api::runTests &object); +void to_json(JsonValueScope &jv, const tonlib_api::setLogStream &object); +void to_json(JsonValueScope &jv, const tonlib_api::setLogTagVerbosityLevel &object); +void to_json(JsonValueScope &jv, const tonlib_api::setLogVerbosityLevel &object); void to_json(JsonValueScope &jv, const tonlib_api::testGiver_getAccountAddress &object); void to_json(JsonValueScope &jv, const tonlib_api::testGiver_getAccountState &object); void to_json(JsonValueScope &jv, const tonlib_api::testGiver_sendGrams &object); @@ -129,6 +161,10 @@ void to_json(JsonValueScope &jv, const tonlib_api::testWallet_getAccountAddress void to_json(JsonValueScope &jv, const tonlib_api::testWallet_getAccountState &object); void to_json(JsonValueScope &jv, const tonlib_api::testWallet_init &object); void to_json(JsonValueScope &jv, const tonlib_api::testWallet_sendGrams &object); +void to_json(JsonValueScope &jv, const tonlib_api::wallet_getAccountAddress &object); +void to_json(JsonValueScope &jv, const tonlib_api::wallet_getAccountState &object); +void to_json(JsonValueScope &jv, const tonlib_api::wallet_init &object); +void to_json(JsonValueScope &jv, const tonlib_api::wallet_sendGrams &object); inline void to_json(JsonValueScope &jv, const ton::tonlib_api::Object &object) { ton::tonlib_api::downcast_call(const_cast(object),[&jv](const auto &object) { to_json(jv, object); }); } diff --git a/submodules/ton/tonlib-src/tl/generate/scheme/ton_api.tl b/submodules/ton/tonlib-src/tl/generate/scheme/ton_api.tl index 860ffd5d37..89827f7f5c 100644 --- a/submodules/ton/tonlib-src/tl/generate/scheme/ton_api.tl +++ b/submodules/ton/tonlib-src/tl/generate/scheme/ton_api.tl @@ -322,6 +322,7 @@ tonNode.zeroStateIdExt workchain:int root_hash:int256 file_hash:int256 = tonNode tonNode.blockDescriptionEmpty = tonNode.BlockDescription; tonNode.blockDescription id:tonNode.blockIdExt = tonNode.BlockDescription; +tonNode.blocksDescription ids:(vector tonNode.blockIdExt) incomplete:Bool = tonNode.BlocksDescription; tonNode.preparedProofEmpty = tonNode.PreparedProof; tonNode.preparedProof = tonNode.PreparedProof; tonNode.preparedProofLink = tonNode.PreparedProof; @@ -352,22 +353,42 @@ tonNode.keyBlocks blocks:(vector tonNode.blockIdExt) incomplete:Bool error:Bool ton.blockId root_cell_hash:int256 file_hash:int256 = ton.BlockId; ton.blockIdApprove root_cell_hash:int256 file_hash:int256 = ton.BlockId; +tonNode.dataList data:(vector bytes) = tonNode.DataList; + +tonNode.dataFull id:tonNode.blockIdExt proof:bytes block:bytes is_link:Bool = tonNode.DataFull; +tonNode.dataFullEmpty = tonNode.DataFull; + +tonNode.capabilities version:int capabilities:long = tonNode.Capabilities; + +tonNode.success = tonNode.Success; + ---functions--- tonNode.getNextBlockDescription prev_block:tonNode.blockIdExt = tonNode.BlockDescription; +tonNode.getNextBlocksDescription prev_block:tonNode.blockIdExt limit:int = tonNode.BlocksDescription; +tonNode.getPrevBlocksDescription next_block:tonNode.blockIdExt limit:int cutoff_seqno:int = tonNode.BlocksDescription; tonNode.prepareBlockProof block:tonNode.blockIdExt allow_partial:Bool = tonNode.PreparedProof; +tonNode.prepareBlockProofs blocks:(vector tonNode.blockIdExt) allow_partial:Bool = tonNode.PreparedProof; tonNode.prepareBlock block:tonNode.blockIdExt = tonNode.Prepared; +tonNode.prepareBlocks blocks:(vector tonNode.blockIdExt) = tonNode.Prepared; tonNode.preparePersistentState block:tonNode.blockIdExt masterchain_block:tonNode.blockIdExt = tonNode.PreparedState; tonNode.prepareZeroState block:tonNode.blockIdExt = tonNode.PreparedState; tonNode.getNextKeyBlockIds block:tonNode.blockIdExt max_size:int = tonNode.KeyBlocks; +tonNode.downloadNextBlockFull prev_block:tonNode.blockIdExt = tonNode.DataFull; +tonNode.downloadBlockFull block:tonNode.blockIdExt = tonNode.DataFull; tonNode.downloadBlock block:tonNode.blockIdExt = tonNode.Data; +tonNode.downloadBlocks blocks:(vector tonNode.blockIdExt) = tonNode.DataList; tonNode.downloadPersistentState block:tonNode.blockIdExt masterchain_block:tonNode.blockIdExt = tonNode.Data; tonNode.downloadPersistentStateSlice block:tonNode.blockIdExt masterchain_block:tonNode.blockIdExt offset:long max_size:long = tonNode.Data; tonNode.downloadZeroState block:tonNode.blockIdExt = tonNode.Data; tonNode.downloadBlockProof block:tonNode.blockIdExt = tonNode.Data; +tonNode.downloadBlockProofs blocks:(vector tonNode.blockIdExt) = tonNode.DataList; tonNode.downloadBlockProofLink block:tonNode.blockIdExt = tonNode.Data; +tonNode.downloadBlockProofLinks blocks:(vector tonNode.blockIdExt) = tonNode.DataList; -tonNode.slave.sendExtMessage message:tonNode.externalMessage = True; +tonNode.getCapabilities = tonNode.Capabilities; + +tonNode.slave.sendExtMessage message:tonNode.externalMessage = tonNode.Success; tonNode.query = Object; diff --git a/submodules/ton/tonlib-src/tl/generate/scheme/ton_api.tlo b/submodules/ton/tonlib-src/tl/generate/scheme/ton_api.tlo index 7ad04d37e5870c32a76bf471285bb183653b8505..6959d9a3c980cb5ecd17266f18386e40d8a183c9 100644 GIT binary patch delta 1152 zcmaDdnfb#KX5L4$^{p77VD(1c!;-=}iCWhrOY-yl@>5dvoO1G$vx_J1=2D$}LDE3- zK*Ij7A}~4U#Dc`6%$&@U%+%t^f-KULq;y0hrOaLmz!bP7mL$5B=HyI1C@U>0Z2Uz8 zMaCzyxMZ`S)N@7=f0bzm_+grYOOumRi;FiukiNsj$hUc~+~g7y6elHto#c{QoLrPy zP?DLS$G`xRzj;i^hY9Er>1X#fnKM(0Cx393nJh5fU~-1Brth}l>i5OW^sH@W?uGWpXsvMKt<3HXSgcI6?3XJFI*JneVAdcxdkPa5CcI@ z0;w-@X4B!YZ953E1X({y$b$5PLKdh1BtCh;cA3dC?iws0&gMT;Q&@FSLc16e+8{A> zYd~U~LuLo?GkR`by2@TcW$qF6EO^35$uG~#$xloHClF91Lp@(`*nkBb3O5ejU`^J( zs3AYu_plsmG1xHzjt0Oe;hF5<%K=Ug$iDa*ykV&{P^}cES_6<~WVN+NBg@3#A%^Ud zVu-JiEV4k?>}9?nPZnLXUus1Ox>q(FH355N^OD0)7)^diJi8|j)0UoEf}Hwc2?yjx zSS*3lO3l#ULN2l1H z%*l;GHY}hhwG5I+u^Yw=bP*^=aOD9#IQigFX(WRgo27Yql1oeS)6(LLQw#I*CkvWF zttvS7gLTKI%C}OJA0CtBE@s%1$yp4^8BeEKPu4kZ!}@3S|18nTjmJdTiy0WQ+t@iK nBZc|oGsgrrXB;^ggiNypD@UU2dTjA7l9@l3QX>~_yho&`Zrww diff --git a/submodules/ton/tonlib-src/tl/generate/scheme/tonlib_api.tl b/submodules/ton/tonlib-src/tl/generate/scheme/tonlib_api.tl index 356f263c2d..db12f3f1cb 100644 --- a/submodules/ton/tonlib-src/tl/generate/scheme/tonlib_api.tl +++ b/submodules/ton/tonlib-src/tl/generate/scheme/tonlib_api.tl @@ -39,20 +39,45 @@ raw.transactions transactions:vector previous_transaction_id:in testWallet.initialAccountState public_key:string = testWallet.InitialAccountState; testWallet.accountState balance:int64 seqno:int32 last_transaction_id:internal.transactionId sync_utime:int53 = testWallet.AccountState; +wallet.initialAccountState public_key:string = wallet.InitialAccountState; +wallet.accountState balance:int64 seqno:int32 last_transaction_id:internal.transactionId sync_utime:int53 = wallet.AccountState; + testGiver.accountState balance:int64 seqno:int32 last_transaction_id:internal.transactionId sync_utime:int53= testGiver.AccountState; uninited.accountState balance:int64 last_transaction_id:internal.transactionId sync_utime:int53 = uninited.AccountState; -generic.initialAccountStateRaw initital_account_state:raw.initialAccountState = generic.InitialAccountState; -generic.initialAccountStateTestWallet initital_account_state:testWallet.initialAccountState = generic.InitialAccountState; +//generic.initialAccountStateRaw initital_account_state:raw.initialAccountState = generic.InitialAccountState; +//generic.initialAccountStateTestWallet initital_account_state:testWallet.initialAccountState = generic.InitialAccountState; +//generic.initialAccountStateWallet initital_account_state:wallet.initialAccountState = generic.InitialAccountState; generic.accountStateRaw account_state:raw.accountState = generic.AccountState; generic.accountStateTestWallet account_state:testWallet.accountState = generic.AccountState; +generic.accountStateWallet account_state:wallet.accountState = generic.AccountState; generic.accountStateTestGiver account_state:testGiver.accountState = generic.AccountState; generic.accountStateUninited account_state:uninited.accountState = generic.AccountState; +//generic.sendGramsResult sent_until:int53 = generic.SendGramsResult; + updateSendLiteServerQuery id:int64 data:bytes = Update; +//@class LogStream @description Describes a stream to which TDLib internal log is written + +//@description The log is written to stderr or an OS specific log +logStreamDefault = LogStream; + +//@description The log is written to a file @path Path to the file to where the internal TDLib log will be written @max_file_size Maximum size of the file to where the internal TDLib log is written before the file will be auto-rotated +logStreamFile path:string max_file_size:int53 = LogStream; + +//@description The log is written nowhere +logStreamEmpty = LogStream; + + +//@description Contains a TDLib internal log verbosity level @verbosity_level Log verbosity level +logVerbosityLevel verbosity_level:int32 = LogVerbosityLevel; + +//@description Contains a list of available TDLib internal log tags @tags List of log tags +logTags tags:vector = LogTags; + ---functions--- init options:options = Ok; @@ -61,7 +86,7 @@ close = Ok; options.setConfig config:string = Ok; createNewKey local_password:secureBytes mnemonic_password:secureBytes random_extra_seed:secureBytes = Key; -deleteKey public_key:string = Ok; +deleteKey key:key = Ok; exportKey input_key:inputKey = ExportedKey; exportPemKey input_key:inputKey key_password:secureBytes = ExportedPemKey; exportEncryptedKey input_key:inputKey key_password:secureBytes = ExportedEncryptedKey; @@ -83,6 +108,11 @@ testWallet.getAccountAddress initital_account_state:testWallet.initialAccountSta testWallet.getAccountState account_address:accountAddress = testWallet.AccountState; testWallet.sendGrams private_key:inputKey destination:accountAddress seqno:int32 amount:int64 message:bytes = Ok; +wallet.init private_key:inputKey = Ok; +wallet.getAccountAddress initital_account_state:wallet.initialAccountState = AccountAddress; +wallet.getAccountState account_address:accountAddress = wallet.AccountState; +wallet.sendGrams private_key:inputKey destination:accountAddress seqno:int32 valid_until:int53 amount:int64 message:bytes = Ok; + testGiver.getAccountState = testGiver.AccountState; testGiver.getAccountAddress = AccountAddress; testGiver.sendGrams destination:accountAddress seqno:int32 amount:int64 message:bytes = Ok; @@ -95,3 +125,30 @@ onLiteServerQueryResult id:int64 bytes:bytes = Ok; onLiteServerQueryError id:int64 error:error = Ok; runTests dir:string = Ok; + +//@description Sets new log stream for internal logging of TDLib. This is an offline method. Can be called before authorization. Can be called synchronously @log_stream New log stream +setLogStream log_stream:LogStream = Ok; + +//@description Returns information about currently used log stream for internal logging of TDLib. This is an offline method. Can be called before authorization. Can be called synchronously +getLogStream = LogStream; + +//@description Sets the verbosity level of the internal logging of TDLib. This is an offline method. Can be called before authorization. Can be called synchronously +//@new_verbosity_level New value of the verbosity level for logging. Value 0 corresponds to fatal errors, value 1 corresponds to errors, value 2 corresponds to warnings and debug warnings, value 3 corresponds to informational, value 4 corresponds to debug, value 5 corresponds to verbose debug, value greater than 5 and up to 1023 can be used to enable even more logging +setLogVerbosityLevel new_verbosity_level:int32 = Ok; + +//@description Returns current verbosity level of the internal logging of TDLib. This is an offline method. Can be called before authorization. Can be called synchronously +getLogVerbosityLevel = LogVerbosityLevel; + +//@description Returns list of available TDLib internal log tags, for example, ["actor", "binlog", "connections", "notifications", "proxy"]. This is an offline method. Can be called before authorization. Can be called synchronously +getLogTags = LogTags; + +//@description Sets the verbosity level for a specified TDLib internal log tag. This is an offline method. Can be called before authorization. Can be called synchronously +//@tag Logging tag to change verbosity level @new_verbosity_level New verbosity level; 1-1024 +setLogTagVerbosityLevel tag:string new_verbosity_level:int32 = Ok; + +//@description Returns current verbosity level for a specified TDLib internal log tag. This is an offline method. Can be called before authorization. Can be called synchronously @tag Logging tag to change verbosity level +getLogTagVerbosityLevel tag:string = LogVerbosityLevel; + +//@description Adds a message to TDLib internal log. This is an offline method. Can be called before authorization. Can be called synchronously +//@verbosity_level Minimum verbosity level needed for the message to be logged, 0-1023 @text Text of a message to log +addLogMessage verbosity_level:int32 text:string = Ok; diff --git a/submodules/ton/tonlib-src/tl/generate/scheme/tonlib_api.tlo b/submodules/ton/tonlib-src/tl/generate/scheme/tonlib_api.tlo index 40a4ccdce44ce004914d6b93a3f7b4c6155e02a7..0a810f234f4208ceb583c1e49916cce6390ac466 100644 GIT binary patch delta 1482 zcma)6Z%7ki7{6`jrgP`~bGoV3He8lrDkU)@(K2)BgiujPVO;L@HaMN^&P}68Q2J&$ zUXaQ!_PHqIz63=Pi1fiSLlgT@AQ53|MnYfu(EHxqt8*H7pPqZ}eSXjH`8~h)W*fd= zVOA!}&r;fR>?u>P&nj+mRj2vFe2WwTTn|MV;Jm?RS(4i(O(@&S@nXEsZdJ}sz0uye;7AD!y$uRmm<#_O!{~QZCTogkLJWUK>$)2?4;es zncojz6~tgPrA${UFQ-h<{EP9G=1hU~z6iRxenCR)FsJ32Y#$~XsWq0SR^57ya~g4K z@rb#z?ZoCCX1n%MNB}!^dUUYx+m{l$kCTEF1%I0JtxOL$;O|Dq{9@=Dpx_*Pm`}!> z{dIaJV;i%sr%xI@5En%)REO2VG3Kr#93&1_=eXdAk@ezli8CmwqPiqiDV$HluttgO zegW$a$4sn+2!jvHLGyxMT|y1xQG@GDg&IsTo=Uu*#LlCS$~*-;=mn8bR~gsU74Gkq zS|m;a?aIO^WuG;S+m5%0N5K#2sC--_@XidaVP}Tca7@d{(^jX+iIs@}3768b&pb-r2K8l)x%{3N3D$ zw?RaTI1!&At4ns^*ZaXDR;}6H71fZ&iS-FR<>!S*jaBhKPaa#u-4he($rUgoi~B|G z|ANrAuQTKvj*9V*wqcu!uxECHM3$(`zG=3zIlUm}-{)4~?bLiQt*)~w;+~-hBH{Kw zAo-1J!~mTR+PHPEc-kI=r-~YA#e`0y(|N&qyD(4tw;}b;CtM&(@iekUUb3g(HL@M) zF_`CWcHL;tF3;5JMbI0lk8nLAQiiZI!clkFk{;!RP{5C_W=MeVSr$%Z)9|nR@;@p` B22=n5 delta 362 zcmaD6b;gVL(QJJy1}IS8$m`Cu`2v#$BTL$<1J5S^VU}QJ0m=zXR1{X->^FC^bb4xD zYEfpgo@ZWWNoHb>V{&qSX{RM9n0{>LxI3e}_Ozyi{|d4Zq;JEQF8QmJZ25f#HtW}GRh vIjJS7-l>%g44M1hUYUGB++?$X%miqV%~64PYLlD? get_wallet_source() { + std::string code = R"ABCD( +SETCP0 DUP IFNOTRET // return if recv_internal + DUP 85143 INT EQUAL IFJMP:<{ // "seqno" get-method + DROP c4 PUSHCTR CTOS 32 PLDU // cnt + }> + INC 32 THROWIF // fail unless recv_external + 9 PUSHPOW2 LDSLICEX DUP 32 LDU 32 LDU // signature in_msg msg_seqno valid_until cs + SWAP NOW LEQ 35 THROWIF // signature in_msg msg_seqno cs + c4 PUSH CTOS 32 LDU 256 LDU ENDS // signature in_msg msg_seqno cs stored_seqno public_key + s3 s1 XCPU // signature in_msg public_key cs stored_seqno msg_seqno stored_seqno + EQUAL 33 THROWIFNOT // signature in_msg public_key cs stored_seqno + s0 s3 XCHG HASHSU // signature stored_seqno public_key cs hash + s0 s4 s2 XC2PU CHKSIGNU 34 THROWIFNOT // cs stored_seqno public_key + ACCEPT + s0 s2 XCHG // public_key stored_seqno cs + WHILE:<{ + DUP SREFS // public_key stored_seqno cs _40 + }>DO<{ // public_key stored_seqno cs + // 3 INT 35 LSHIFT# 3 INT RAWRESERVE // reserve all but 103 Grams from the balance + 8 LDU LDREF s0 s2 XCHG // public_key stored_seqno cs _45 mode + SENDRAWMSG // public_key stored_seqno cs + }> + ENDS INC // public_key seqno' + NEWC 32 STU 256 STU ENDC c4 POP +)ABCD"; + return fift::compile_asm(code).move_as_ok(); +} + TEST(Tonlib, TestWallet) { LOG(ERROR) << td::base64_encode(std_boc_serialize(get_test_wallet_source()).move_as_ok()); CHECK(get_test_wallet_source()->get_hash() == TestWallet::get_init_code()->get_hash()); @@ -114,6 +144,74 @@ TEST(Tonlib, TestWallet) { LOG(ERROR) << "-------"; vm::load_cell_slice(vm::std_boc_deserialize(new_wallet_query).move_as_ok()).print_rec(std::cerr); CHECK(vm::std_boc_deserialize(new_wallet_query).move_as_ok()->get_hash() == res->get_hash()); + + fift_output.source_lookup.write_file("/main.fif", load_source("smartcont/wallet.fif")).ensure(); + auto dest = block::StdAddress::parse("Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX").move_as_ok(); + fift_output = + fift::mem_run_fift(std::move(fift_output.source_lookup), + {"aba", "new-wallet", "Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX", "123", "321"}) + .move_as_ok(); + auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data; + auto gift_message = GenericAccount::create_ext_message( + address, {}, TestWallet::make_a_gift_message(priv_key, 123, 321000000000ll, "TEST", dest)); + LOG(ERROR) << "-------"; + vm::load_cell_slice(gift_message).print_rec(std::cerr); + LOG(ERROR) << "-------"; + vm::load_cell_slice(vm::std_boc_deserialize(wallet_query).move_as_ok()).print_rec(std::cerr); + CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == gift_message->get_hash()); +} + +td::Ref get_wallet_source_fc() { + return fift::compile_asm(load_source("smartcont/wallet-code.fif"), "", false).move_as_ok(); +} + +TEST(Tonlib, Wallet) { + LOG(ERROR) << td::base64_encode(std_boc_serialize(get_wallet_source()).move_as_ok()); + CHECK(get_wallet_source()->get_hash() == Wallet::get_init_code()->get_hash()); + + auto fift_output = fift::mem_run_fift(load_source("smartcont/new-wallet-v2.fif"), {"aba", "0"}).move_as_ok(); + + auto new_wallet_pk = fift_output.source_lookup.read_file("new-wallet.pk").move_as_ok().data; + auto new_wallet_query = fift_output.source_lookup.read_file("new-wallet-query.boc").move_as_ok().data; + auto new_wallet_addr = fift_output.source_lookup.read_file("new-wallet.addr").move_as_ok().data; + + td::Ed25519::PrivateKey priv_key{td::SecureString{new_wallet_pk}}; + auto pub_key = priv_key.get_public_key().move_as_ok(); + auto init_state = Wallet::get_init_state(pub_key); + auto init_message = Wallet::get_init_message(priv_key); + auto address = GenericAccount::get_address(0, init_state); + + CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32)); + + td::Ref res = GenericAccount::create_ext_message(address, init_state, init_message); + + LOG(ERROR) << "-------"; + vm::load_cell_slice(res).print_rec(std::cerr); + LOG(ERROR) << "-------"; + vm::load_cell_slice(vm::std_boc_deserialize(new_wallet_query).move_as_ok()).print_rec(std::cerr); + CHECK(vm::std_boc_deserialize(new_wallet_query).move_as_ok()->get_hash() == res->get_hash()); + + fift_output.source_lookup.write_file("/main.fif", load_source("smartcont/wallet-v2.fif")).ensure(); + class ZeroOsTime : public fift::OsTime { + public: + td::uint32 now() override { + return 0; + } + }; + fift_output.source_lookup.set_os_time(std::make_unique()); + auto dest = block::StdAddress::parse("Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX").move_as_ok(); + fift_output = + fift::mem_run_fift(std::move(fift_output.source_lookup), + {"aba", "new-wallet", "Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX", "123", "321"}) + .move_as_ok(); + auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data; + auto gift_message = GenericAccount::create_ext_message( + address, {}, Wallet::make_a_gift_message(priv_key, 123, 60, 321000000000ll, "TESTv2", dest)); + LOG(ERROR) << "-------"; + vm::load_cell_slice(gift_message).print_rec(std::cerr); + LOG(ERROR) << "-------"; + vm::load_cell_slice(vm::std_boc_deserialize(wallet_query).move_as_ok()).print_rec(std::cerr); + CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == gift_message->get_hash()); } TEST(Tonlib, TestGiver) { @@ -346,9 +444,9 @@ TEST(Tonlib, KeysApi) { //importKey local_password:bytes mnemonic_password:bytes exported_key:exportedKey = Key; auto new_local_password = td::SecureString("new_local_password"); // import already existed key - sync_send(client, make_object(new_local_password.copy(), mnemonic_password.copy(), - make_object(copy_word_list()))) - .ensure_error(); + //sync_send(client, make_object(new_local_password.copy(), mnemonic_password.copy(), + //make_object(copy_word_list()))) + //.ensure_error(); { auto export_password = td::SecureString("export password"); @@ -361,7 +459,9 @@ TEST(Tonlib, KeysApi) { export_password.copy())) .move_as_ok(); - sync_send(client, make_object(key->public_key_)).move_as_ok(); + sync_send(client, + make_object(make_object(key->public_key_, key->secret_.copy()))) + .move_as_ok(); sync_send(client, make_object( new_local_password.copy(), wrong_export_password.copy(), @@ -374,10 +474,13 @@ TEST(Tonlib, KeysApi) { make_object(exported_encrypted_key->data_.copy()))) .move_as_ok(); CHECK(imported_encrypted_key->public_key_ == key->public_key_); + key = std::move(imported_encrypted_key); } //deleteKey public_key:bytes = Ok; - sync_send(client, make_object(key->public_key_)).move_as_ok(); + sync_send(client, + make_object(make_object(key->public_key_, key->secret_.copy()))) + .move_as_ok(); auto err1 = sync_send(client, make_object( new_local_password.copy(), td::SecureString("wrong password"), @@ -410,11 +513,13 @@ TEST(Tonlib, KeysApi) { LOG(ERROR) << to_string(exported_pem_key); //importPemKey exported_key:exportedPemKey key_password:bytes = Key; - sync_send(client, make_object( - new_local_password.copy(), pem_password.copy(), - make_object(exported_pem_key->pem_.copy()))) - .ensure_error(); - sync_send(client, make_object(key->public_key_)).move_as_ok(); + //sync_send(client, make_object( + //new_local_password.copy(), pem_password.copy(), + //make_object(exported_pem_key->pem_.copy()))) + //.ensure_error(); + sync_send(client, make_object( + make_object(imported_key->public_key_, imported_key->secret_.copy()))) + .move_as_ok(); sync_send(client, make_object( new_local_password.copy(), td::SecureString("wrong pem password"), make_object(exported_pem_key->pem_.copy()))) diff --git a/submodules/ton/tonlib-src/tonlib/tonlib/GenericAccount.cpp b/submodules/ton/tonlib-src/tonlib/tonlib/GenericAccount.cpp index 330393c0e7..08ea33bdc9 100644 --- a/submodules/ton/tonlib-src/tonlib/tonlib/GenericAccount.cpp +++ b/submodules/ton/tonlib-src/tonlib/tonlib/GenericAccount.cpp @@ -28,7 +28,7 @@ td::Ref GenericAccount::get_init_state(td::Ref code, td::Ref .finalize(); } block::StdAddress GenericAccount::get_address(ton::WorkchainId workchain_id, const td::Ref& init_state) { - return block::StdAddress(workchain_id, init_state->get_hash().bits(), false); + return block::StdAddress(workchain_id, init_state->get_hash().bits(), true /*bounce*/); } td::Ref GenericAccount::create_ext_message(const block::StdAddress& address, td::Ref new_state, td::Ref body) { diff --git a/submodules/ton/tonlib-src/tonlib/tonlib/KeyStorage.cpp b/submodules/ton/tonlib-src/tonlib/tonlib/KeyStorage.cpp index 1c25764328..6137c0e347 100644 --- a/submodules/ton/tonlib-src/tonlib/tonlib/KeyStorage.cpp +++ b/submodules/ton/tonlib-src/tonlib/tonlib/KeyStorage.cpp @@ -24,14 +24,23 @@ #include "td/utils/filesystem.h" #include "td/utils/port/path.h" +#include "td/utils/crypto.h" namespace tonlib { -std::string to_file_name(td::Slice public_key) { - return td::buffer_to_hex(public_key); +std::string to_file_name_old(const KeyStorage::Key &key) { + return td::buffer_to_hex(key.public_key); } -std::string KeyStorage::to_file_path(td::Slice public_key) { - return directory_ + TD_DIR_SLASH + to_file_name(public_key); +std::string KeyStorage::to_file_path_old(const Key &key) { + return directory_ + TD_DIR_SLASH + to_file_name_old(key); +} + +std::string to_file_name(const KeyStorage::Key &key) { + return td::buffer_to_hex(td::sha512(key.secret.as_slice()).substr(0, 32)); +} + +std::string KeyStorage::to_file_path(const Key &key) { + return directory_ + TD_DIR_SLASH + to_file_name(key); } td::Status KeyStorage::set_directory(std::string directory) { TRY_RESULT(path, td::realpath(directory)); @@ -52,8 +61,8 @@ td::Result KeyStorage::save_key(const DecryptedKey &decrypted_k auto size = encrypted_key.encrypted_data.size(); - LOG(ERROR) << "SAVE " << to_file_name(res.public_key); - TRY_RESULT(to_file, td::FileFd::open(to_file_path(res.public_key), td::FileFd::CreateNew | td::FileFd::Write)); + LOG(ERROR) << "SAVE " << to_file_name(res); + TRY_RESULT(to_file, td::FileFd::open(to_file_path(res), td::FileFd::CreateNew | td::FileFd::Write)); TRY_RESULT(written, to_file.write(encrypted_key.encrypted_data)); if (written != static_cast(size)) { return td::Status::Error(PSLICE() << "Failed to write file: written " << written << " bytes instead of " << size); @@ -74,7 +83,16 @@ td::Result KeyStorage::create_new_key(td::Slice local_password, } td::Result KeyStorage::export_decrypted_key(InputKey input_key) { - TRY_RESULT(encrypted_data, td::read_file_secure(to_file_path(input_key.key.public_key))); + auto r_encrypted_data = td::read_file_secure(to_file_path(input_key.key)); + if (r_encrypted_data.is_error()) { + r_encrypted_data = td::read_file_secure(to_file_path_old(input_key.key)); + if (r_encrypted_data.is_ok()) { + LOG(WARNING) << "Restore private from deprecated location " << to_file_path_old(input_key.key) << " --> " + << to_file_path(input_key.key); + td::rename(to_file_path_old(input_key.key), to_file_path(input_key.key)).ignore(); + } + } + TRY_RESULT(encrypted_data, std::move(r_encrypted_data)); EncryptedKey encrypted_key{std::move(encrypted_data), td::Ed25519::PublicKey(std::move(input_key.key.public_key)), std::move(input_key.key.secret)}; return encrypted_key.decrypt(std::move(input_key.local_password)); @@ -94,8 +112,8 @@ td::Result KeyStorage::load_private_key(InputKey input_k return std::move(private_key); } -td::Status KeyStorage::delete_key(td::Slice public_key) { - return td::unlink(to_file_path(public_key)); +td::Status KeyStorage::delete_key(const Key &key) { + return td::unlink(to_file_path(key)); } td::Result KeyStorage::import_key(td::Slice local_password, td::Slice mnemonic_password, @@ -122,6 +140,7 @@ td::Result KeyStorage::change_local_password(InputKey input_key Key res; res.public_key = std::move(input_key.key.public_key); res.secret = std::move(new_secret); + TRY_STATUS(td::copy_file(to_file_path(input_key.key), to_file_path(res))); return std::move(res); } diff --git a/submodules/ton/tonlib-src/tonlib/tonlib/KeyStorage.h b/submodules/ton/tonlib-src/tonlib/tonlib/KeyStorage.h index 305872be23..f7982805ef 100644 --- a/submodules/ton/tonlib-src/tonlib/tonlib/KeyStorage.h +++ b/submodules/ton/tonlib-src/tonlib/tonlib/KeyStorage.h @@ -57,7 +57,7 @@ class KeyStorage { td::Result export_encrypted_key(InputKey input_key, td::Slice key_password); td::Result change_local_password(InputKey input_key, td::Slice new_local_password); - td::Status delete_key(td::Slice public_key); + td::Status delete_key(const Key& key); td::Result import_key(td::Slice local_password, td::Slice mnemonic_password, ExportedKey exported_key); td::Result import_pem_key(td::Slice local_password, td::Slice key_password, ExportedPemKey exported_key); @@ -72,6 +72,7 @@ class KeyStorage { td::Result save_key(const DecryptedKey& mnemonic, td::Slice local_password); td::Result export_decrypted_key(InputKey input_key); - std::string to_file_path(td::Slice public_key); + std::string to_file_path(const Key& key); + std::string to_file_path_old(const Key& key); }; } // namespace tonlib diff --git a/submodules/ton/tonlib-src/tonlib/tonlib/LastBlockStorage.cpp b/submodules/ton/tonlib-src/tonlib/tonlib/LastBlockStorage.cpp index fa979a3ca3..684b2c056c 100644 --- a/submodules/ton/tonlib-src/tonlib/tonlib/LastBlockStorage.cpp +++ b/submodules/ton/tonlib-src/tonlib/tonlib/LastBlockStorage.cpp @@ -1,3 +1,21 @@ +/* + This file is part of TON Blockchain Library. + + TON Blockchain Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + TON Blockchain Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with TON Blockchain Library. If not, see . + + Copyright 2017-2019 Telegram Systems LLP +*/ #include "LastBlockStorage.h" #include "td/utils/as.h" diff --git a/submodules/ton/tonlib-src/tonlib/tonlib/LastBlockStorage.h b/submodules/ton/tonlib-src/tonlib/tonlib/LastBlockStorage.h index fa920ef6ed..d8cbd3fcd1 100644 --- a/submodules/ton/tonlib-src/tonlib/tonlib/LastBlockStorage.h +++ b/submodules/ton/tonlib-src/tonlib/tonlib/LastBlockStorage.h @@ -1,3 +1,21 @@ +/* + This file is part of TON Blockchain Library. + + TON Blockchain Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + TON Blockchain Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with TON Blockchain Library. If not, see . + + Copyright 2017-2019 Telegram Systems LLP +*/ #pragma once #include "tonlib/LastBlock.h" diff --git a/submodules/ton/tonlib-src/tonlib/tonlib/Logging.cpp b/submodules/ton/tonlib-src/tonlib/tonlib/Logging.cpp new file mode 100644 index 0000000000..043fe0190d --- /dev/null +++ b/submodules/ton/tonlib-src/tonlib/tonlib/Logging.cpp @@ -0,0 +1,137 @@ +/* + This file is part of TON Blockchain Library. + + TON Blockchain Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + TON Blockchain Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with TON Blockchain Library. If not, see . + + Copyright 2017-2019 Telegram Systems LLP +*/ +#include "Logging.h" + +#include "auto/tl/tonlib_api.h" + +#include "td/utils/FileLog.h" +#include "td/utils/logging.h" +#include "td/utils/misc.h" +#include "td/utils/misc.h" + +#include +#include +#include + +namespace tonlib { + +static std::mutex logging_mutex; +static td::FileLog file_log; +static td::TsLog ts_log(&file_log); +static td::NullLog null_log; + +td::int32 VERBOSITY_NAME(abc) = VERBOSITY_NAME(DEBUG); +td::int32 VERBOSITY_NAME(bcd) = VERBOSITY_NAME(DEBUG); +#define ADD_TAG(tag) \ + { #tag, &VERBOSITY_NAME(tag) } +static const std::map log_tags{ADD_TAG(abc), ADD_TAG(bcd)}; +#undef ADD_TAG + +td::Status Logging::set_current_stream(tonlib_api::object_ptr stream) { + if (stream == nullptr) { + return td::Status::Error("Log stream must not be empty"); + } + + std::lock_guard lock(logging_mutex); + switch (stream->get_id()) { + case tonlib_api::logStreamDefault::ID: + td::log_interface = td::default_log_interface; + return td::Status::OK(); + case tonlib_api::logStreamFile::ID: { + auto file_stream = tonlib_api::move_object_as(stream); + auto max_log_file_size = file_stream->max_file_size_; + if (max_log_file_size <= 0) { + return td::Status::Error("Max log file size should be positive"); + } + + TRY_STATUS(file_log.init(file_stream->path_, max_log_file_size)); + std::atomic_thread_fence(std::memory_order_release); // better than nothing + td::log_interface = &ts_log; + return td::Status::OK(); + } + case tonlib_api::logStreamEmpty::ID: + td::log_interface = &null_log; + return td::Status::OK(); + default: + UNREACHABLE(); + return td::Status::OK(); + } +} + +td::Result> Logging::get_current_stream() { + std::lock_guard lock(logging_mutex); + if (td::log_interface == td::default_log_interface) { + return tonlib_api::make_object(); + } + if (td::log_interface == &null_log) { + return tonlib_api::make_object(); + } + if (td::log_interface == &ts_log) { + return tonlib_api::make_object(file_log.get_path().str(), + file_log.get_rotate_threshold()); + } + return td::Status::Error("Log stream is unrecognized"); +} + +td::Status Logging::set_verbosity_level(int new_verbosity_level) { + std::lock_guard lock(logging_mutex); + if (0 <= new_verbosity_level && new_verbosity_level <= VERBOSITY_NAME(NEVER)) { + SET_VERBOSITY_LEVEL(VERBOSITY_NAME(FATAL) + new_verbosity_level); + return td::Status::OK(); + } + + return td::Status::Error("Wrong new verbosity level specified"); +} + +int Logging::get_verbosity_level() { + std::lock_guard lock(logging_mutex); + return GET_VERBOSITY_LEVEL(); +} + +td::vector Logging::get_tags() { + return transform(log_tags, [](auto &tag) { return tag.first.str(); }); +} + +td::Status Logging::set_tag_verbosity_level(td::Slice tag, int new_verbosity_level) { + auto it = log_tags.find(tag); + if (it == log_tags.end()) { + return td::Status::Error("Log tag is not found"); + } + + std::lock_guard lock(logging_mutex); + *it->second = td::clamp(new_verbosity_level, 1, VERBOSITY_NAME(NEVER)); + return td::Status::OK(); +} + +td::Result Logging::get_tag_verbosity_level(td::Slice tag) { + auto it = log_tags.find(tag); + if (it == log_tags.end()) { + return td::Status::Error("Log tag is not found"); + } + + std::lock_guard lock(logging_mutex); + return *it->second; +} + +void Logging::add_message(int log_verbosity_level, td::Slice message) { + int VERBOSITY_NAME(client) = td::clamp(log_verbosity_level, 0, VERBOSITY_NAME(NEVER)); + VLOG(client) << message; +} + +} // namespace tonlib diff --git a/submodules/ton/tonlib-src/tonlib/tonlib/Logging.h b/submodules/ton/tonlib-src/tonlib/tonlib/Logging.h new file mode 100644 index 0000000000..9dbda9d367 --- /dev/null +++ b/submodules/ton/tonlib-src/tonlib/tonlib/Logging.h @@ -0,0 +1,49 @@ +/* + This file is part of TON Blockchain Library. + + TON Blockchain Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + TON Blockchain Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with TON Blockchain Library. If not, see . + + Copyright 2017-2019 Telegram Systems LLP +*/ +#pragma once + +#include "auto/tl/tonlib_api.h" + +#include "td/utils/common.h" +#include "td/utils/Slice.h" +#include "td/utils/Status.h" + +namespace tonlib { +namespace tonlib_api = ton::tonlib_api; + +class Logging { + public: + static td::Status set_current_stream(tonlib_api::object_ptr stream); + + static td::Result> get_current_stream(); + + static td::Status set_verbosity_level(int new_verbosity_level); + + static int get_verbosity_level(); + + static std::vector get_tags(); + + static td::Status set_tag_verbosity_level(td::Slice tag, int new_verbosity_level); + + static td::Result get_tag_verbosity_level(td::Slice tag); + + static void add_message(int log_verbosity_level, td::Slice message); +}; + +} // namespace tonlib diff --git a/submodules/ton/tonlib-src/tonlib/tonlib/TestGiver.cpp b/submodules/ton/tonlib-src/tonlib/tonlib/TestGiver.cpp index 9ea58e54e2..50c68571ce 100644 --- a/submodules/ton/tonlib-src/tonlib/tonlib/TestGiver.cpp +++ b/submodules/ton/tonlib-src/tonlib/tonlib/TestGiver.cpp @@ -34,7 +34,7 @@ vm::CellHash TestGiver::get_init_code_hash() { td::Ref TestGiver::make_a_gift_message(td::uint32 seqno, td::uint64 gramms, td::Slice message, const block::StdAddress& dest_address) { - CHECK(message.size() <= 128); + CHECK(message.size() <= 124); td::BigInt256 dest_addr; dest_addr.import_bits(dest_address.addr.as_bitslice()); vm::CellBuilder cb; @@ -45,7 +45,7 @@ td::Ref TestGiver::make_a_gift_message(td::uint32 seqno, td::uint64 gr .store_int256(dest_addr, 256); block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms)); - auto message_inner = cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes(message).finalize(); + auto message_inner = cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4).store_bytes(message).finalize(); return vm::CellBuilder().store_long(seqno, 32).store_long(1, 8).store_ref(message_inner).finalize(); } } // namespace tonlib diff --git a/submodules/ton/tonlib-src/tonlib/tonlib/TestWallet.cpp b/submodules/ton/tonlib-src/tonlib/tonlib/TestWallet.cpp index c092bac29c..022eaca9db 100644 --- a/submodules/ton/tonlib-src/tonlib/tonlib/TestWallet.cpp +++ b/submodules/ton/tonlib-src/tonlib/tonlib/TestWallet.cpp @@ -40,17 +40,20 @@ td::Ref TestWallet::get_init_message(const td::Ed25519::PrivateKey& pr td::Ref TestWallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, td::int64 gramms, td::Slice message, const block::StdAddress& dest_address) { - CHECK(message.size() <= 128); + CHECK(message.size() <= 124); td::BigInt256 dest_addr; dest_addr.import_bits(dest_address.addr.as_bitslice()); vm::CellBuilder cb; - cb.append_cellslice(binary_bitstring_to_cellslice("b{010000100}").move_as_ok()) + cb.append_cellslice(binary_bitstring_to_cellslice("b{01}").move_as_ok()) + .store_long(dest_address.bounceable, 1) + .append_cellslice(binary_bitstring_to_cellslice("b{000100}").move_as_ok()) .store_long(dest_address.workchain, 8) .store_int256(dest_addr, 256); block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms)); - auto message_inner = cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes(message).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); + auto message_inner = cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4).store_bytes(message).finalize(); + td::int8 send_mode = 3; + auto message_outer = + vm::CellBuilder().store_long(seqno, 32).store_long(send_mode, 8).store_ref(message_inner).finalize(); 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_outer)).finalize(); } diff --git a/submodules/ton/tonlib-src/tonlib/tonlib/TonlibClient.cpp b/submodules/ton/tonlib-src/tonlib/tonlib/TonlibClient.cpp index ea0c6c81e9..babf6ed0a8 100644 --- a/submodules/ton/tonlib-src/tonlib/tonlib/TonlibClient.cpp +++ b/submodules/ton/tonlib-src/tonlib/tonlib/TonlibClient.cpp @@ -22,7 +22,9 @@ #include "tonlib/ExtClientOutbound.h" #include "tonlib/GenericAccount.h" #include "tonlib/LastBlock.h" +#include "tonlib/Logging.h" #include "tonlib/TestWallet.h" +#include "tonlib/Wallet.h" #include "tonlib/TestGiver.h" #include "tonlib/utils.h" #include "tonlib/keys/Mnemonic.h" @@ -72,7 +74,6 @@ struct RawAccountState { td::Ref code; td::Ref data; block::AccountState::Info info; - td::int64 sync_utime = 0; }; td::Result to_balance_or_throw(td::Ref balance_ref) { @@ -189,7 +190,7 @@ class GetRawAccountState : public td::actor::Actor { auto serialized_state = account_state.state.clone(); RawAccountState res; res.info = std::move(info); - res.sync_utime = last_block_.utime; + LOG_IF(ERROR, res.info.gen_utime > last_block_.utime) << res.info.gen_utime << " " << last_block_.utime; auto cell = res.info.root; if (cell.is_null()) { return res; @@ -412,8 +413,17 @@ bool TonlibClient::is_static_request(td::int32 id) { case tonlib_api::runTests::ID: case tonlib_api::raw_getAccountAddress::ID: case tonlib_api::testWallet_getAccountAddress::ID: + case tonlib_api::wallet_getAccountAddress::ID: case tonlib_api::testGiver_getAccountAddress::ID: case tonlib_api::getBip39Hints::ID: + case tonlib_api::setLogStream::ID: + case tonlib_api::getLogStream::ID: + case tonlib_api::setLogVerbosityLevel::ID: + case tonlib_api::getLogVerbosityLevel::ID: + case tonlib_api::getLogTags::ID: + case tonlib_api::setLogTagVerbosityLevel::ID: + case tonlib_api::getLogTagVerbosityLevel::ID: + case tonlib_api::addLogMessage::ID: return true; default: return false; @@ -449,6 +459,12 @@ td::Result get_account_address(const tonlib_api::testWallet_i return GenericAccount::get_address(0 /*zerochain*/, TestWallet::get_init_state(key)); } +td::Result get_account_address(const tonlib_api::wallet_initialAccountState& test_wallet_state) { + TRY_RESULT(key_bytes, block::PublicKey::parse(test_wallet_state.public_key_)); + auto key = td::Ed25519::PublicKey(td::SecureString(key_bytes.key)); + return GenericAccount::get_address(0 /*zerochain*/, Wallet::get_init_state(key)); +} + tonlib_api::object_ptr TonlibClient::do_static_request( const tonlib_api::raw_getAccountAddress& request) { auto r_account_address = get_account_address(*request.initital_account_state_); @@ -465,6 +481,14 @@ tonlib_api::object_ptr TonlibClient::do_static_request( } return tonlib_api::make_object(r_account_address.ok().rserialize()); } +tonlib_api::object_ptr TonlibClient::do_static_request( + const tonlib_api::wallet_getAccountAddress& request) { + auto r_account_address = get_account_address(*request.initital_account_state_); + if (r_account_address.is_error()) { + return status_to_tonlib_api(r_account_address.error()); + } + return tonlib_api::make_object(r_account_address.ok().rserialize()); +} tonlib_api::object_ptr TonlibClient::do_static_request( const tonlib_api::testGiver_getAccountAddress& request) { return tonlib_api::make_object(TestGiver::address().rserialize()); @@ -547,8 +571,8 @@ td::Result> to_raw_accountS .as_slice() .str(); } - return tonlib_api::make_object(raw_state.balance, std::move(code), std::move(data), - to_transaction_id(raw_state.info), raw_state.sync_utime); + return tonlib_api::make_object( + raw_state.balance, std::move(code), std::move(data), to_transaction_id(raw_state.info), raw_state.info.gen_utime); } td::Result to_std_address_or_throw(td::Ref cs) { @@ -601,6 +625,10 @@ td::Result> to_raw_message_or_th if (body->size() % 8 == 0) { body_message = std::string(body->size() / 8, 0); body->prefetch_bytes(td::MutableSlice(body_message).ubegin(), body->size() / 8); + if (body_message.size() >= 4 && body_message[0] == 0 && body_message[1] == 0 && body_message[2] == 0 && + body_message[3] == 0) { + body_message = body_message.substr(4); + } } return tonlib_api::make_object(std::move(src), std::move(dest), balance, @@ -711,7 +739,22 @@ td::Result> to_testW return td::Status::Error("Failed to parse seq_no"); } return tonlib_api::make_object( - raw_state.balance, static_cast(seqno), to_transaction_id(raw_state.info), raw_state.sync_utime); + raw_state.balance, static_cast(seqno), to_transaction_id(raw_state.info), raw_state.info.gen_utime); +} + +td::Result> to_wallet_accountState( + RawAccountState&& raw_state) { + if (raw_state.data.is_null()) { + return td::Status::Error(400, "Not a Wallet"); + } + auto ref = raw_state.data->prefetch_ref(); + auto cs = vm::load_cell_slice(std::move(ref)); + auto seqno = cs.fetch_ulong(32); + if (seqno == cs.fetch_ulong_eof) { + return td::Status::Error("Failed to parse seq_no"); + } + return tonlib_api::make_object( + raw_state.balance, static_cast(seqno), to_transaction_id(raw_state.info), raw_state.info.gen_utime); } td::Result> to_testGiver_accountState( @@ -726,7 +769,7 @@ td::Result> to_testGi return td::Status::Error("Failed to parse seq_no"); } return tonlib_api::make_object( - raw_state.balance, static_cast(seqno), to_transaction_id(raw_state.info), raw_state.sync_utime); + raw_state.balance, static_cast(seqno), to_transaction_id(raw_state.info), raw_state.info.gen_utime); } td::Result> to_generic_accountState( @@ -734,7 +777,7 @@ td::Result> to_generic_ if (raw_state.code.is_null()) { return tonlib_api::make_object( tonlib_api::make_object(raw_state.balance, to_transaction_id(raw_state.info), - raw_state.sync_utime)); + raw_state.info.gen_utime)); } auto code_hash = raw_state.code->prefetch_ref()->get_hash(); @@ -742,6 +785,10 @@ td::Result> to_generic_ TRY_RESULT(test_wallet, to_testWallet_accountState(std::move(raw_state))); return tonlib_api::make_object(std::move(test_wallet)); } + if (code_hash == Wallet::get_init_code_hash()) { + TRY_RESULT(wallet, to_wallet_accountState(std::move(raw_state))); + return tonlib_api::make_object(std::move(wallet)); + } if (code_hash == TestGiver::get_init_code_hash()) { TRY_RESULT(test_wallet, to_testGiver_accountState(std::move(raw_state))); return tonlib_api::make_object(std::move(test_wallet)); @@ -858,7 +905,7 @@ td::Status TonlibClient::do_request(const tonlib_api::testWallet_sendGrams& requ if (!request.private_key_) { return td::Status::Error(400, "Field private_key must not be empty"); } - if (request.message_.size() > 128) { + if (request.message_.size() > 124) { return td::Status::Error(400, "Message is too long"); } TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_)); @@ -866,16 +913,22 @@ td::Status TonlibClient::do_request(const tonlib_api::testWallet_sendGrams& requ TRY_RESULT(input_key, from_tonlib(*request.private_key_)); auto address = GenericAccount::get_address( 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))); - return do_request(tonlib_api::raw_sendMessage( - tonlib_api::make_object(address.rserialize()), "", - vm::std_boc_serialize(TestWallet::make_a_gift_message( - td::Ed25519::PrivateKey(std::move(private_key.private_key)), - request.seqno_, request.amount_, request.message_, account_address)) - .move_as_ok() - .as_slice() - .str()), - std::move(promise)); + TRY_RESULT(private_key_str, key_storage_.load_private_key(std::move(input_key))); + auto private_key = td::Ed25519::PrivateKey(std::move(private_key_str.private_key)); + std::string init_state; + if (request.seqno_ == 0) { + TRY_RESULT(public_key, private_key.get_public_key()); + init_state = vm::std_boc_serialize(TestWallet::get_init_state(public_key)).move_as_ok().as_slice().str(); + } + return do_request( + tonlib_api::raw_sendMessage( + tonlib_api::make_object(address.rserialize()), std::move(init_state), + vm::std_boc_serialize(TestWallet::make_a_gift_message(private_key, request.seqno_, request.amount_, + request.message_, account_address)) + .move_as_ok() + .as_slice() + .str()), + std::move(promise)); } td::Status TonlibClient::do_request(tonlib_api::testWallet_getAccountState& request, @@ -896,13 +949,84 @@ td::Status TonlibClient::do_request(tonlib_api::testWallet_getAccountState& requ return td::Status::OK(); } +// Wallet +td::Status TonlibClient::do_request(const tonlib_api::wallet_init& request, + td::Promise>&& promise) { + if (!request.private_key_) { + return td::Status::Error(400, "Field private_key must not be empty"); + } + TRY_RESULT(input_key, from_tonlib(*request.private_key_)); + auto init_state = Wallet::get_init_state(td::Ed25519::PublicKey(input_key.key.public_key.copy())); + auto address = GenericAccount::get_address(0 /*zerochain*/, init_state); + TRY_RESULT(private_key, key_storage_.load_private_key(std::move(input_key))); + auto init_message = Wallet::get_init_message(td::Ed25519::PrivateKey(std::move(private_key.private_key))); + return do_request( + tonlib_api::raw_sendMessage(tonlib_api::make_object(address.rserialize()), + vm::std_boc_serialize(init_state).move_as_ok().as_slice().str(), + vm::std_boc_serialize(init_message).move_as_ok().as_slice().str()), + std::move(promise)); +} + +td::Status TonlibClient::do_request(const tonlib_api::wallet_sendGrams& request, + td::Promise>&& promise) { + if (!request.destination_) { + return td::Status::Error(400, "Field destination must not be empty"); + } + if (!request.private_key_) { + return td::Status::Error(400, "Field private_key must not be empty"); + } + if (request.message_.size() > 124) { + return td::Status::Error(400, "Message is too long"); + } + TRY_RESULT(valid_until, td::narrow_cast_safe(request.valid_until_)); + TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_)); + account_address.bounceable = false; + TRY_RESULT(input_key, from_tonlib(*request.private_key_)); + auto address = GenericAccount::get_address( + 0 /*zerochain*/, Wallet::get_init_state(td::Ed25519::PublicKey(input_key.key.public_key.copy()))); + TRY_RESULT(private_key_str, key_storage_.load_private_key(std::move(input_key))); + auto private_key = td::Ed25519::PrivateKey(std::move(private_key_str.private_key)); + std::string init_state; + if (request.seqno_ == 0) { + TRY_RESULT(public_key, private_key.get_public_key()); + init_state = vm::std_boc_serialize(Wallet::get_init_state(public_key)).move_as_ok().as_slice().str(); + } + return do_request( + tonlib_api::raw_sendMessage( + tonlib_api::make_object(address.rserialize()), std::move(init_state), + vm::std_boc_serialize(Wallet::make_a_gift_message(private_key, request.seqno_, valid_until, request.amount_, + request.message_, account_address)) + .move_as_ok() + .as_slice() + .str()), + std::move(promise)); +} + +td::Status TonlibClient::do_request(tonlib_api::wallet_getAccountState& request, + td::Promise>&& promise) { + if (!request.account_address_) { + return td::Status::Error(400, "Field account_address must not be empty"); + } + TRY_RESULT(account_address, block::StdAddress::parse(request.account_address_->account_address_)); + td::actor::create_actor( + "GetAccountState", client_.get_client(), std::move(account_address), + [promise = std::move(promise)](td::Result r_state) mutable { + if (r_state.is_error()) { + return promise.set_error(r_state.move_as_error()); + } + promise.set_result(to_wallet_accountState(r_state.move_as_ok())); + }) + .release(); + return td::Status::OK(); +} + // TestGiver td::Status TonlibClient::do_request(const tonlib_api::testGiver_sendGrams& request, td::Promise>&& promise) { if (!request.destination_) { return td::Status::Error(400, "Field destination must not be empty"); } - if (request.message_.size() > 128) { + if (request.message_.size() > 124) { return td::Status::Error(400, "Message is too long"); } TRY_RESULT(account_address, block::StdAddress::parse(request.destination_->account_address_)); @@ -949,61 +1073,182 @@ td::Status TonlibClient::do_request(const tonlib_api::generic_getAccountState& r return td::Status::OK(); } +class TonlibQueryActor : public td::actor::Actor { + public: + TonlibQueryActor(td::actor::ActorShared client) : client_(std::move(client)) { + } + template + void send_query(QueryT query, td::Promise promise) { + td::actor::send_lambda(client_, + [self = client_.get(), query = std::move(query), promise = std::move(promise)]() mutable { + auto status = self.get_actor_unsafe().do_request(query, std::move(promise)); + if (status.is_error()) { + promise.set_error(std::move(status)); + } + }); + } + + private: + td::actor::ActorShared client_; +}; + +class GenericSendGrams : public TonlibQueryActor { + public: + GenericSendGrams(td::actor::ActorShared client, tonlib_api::generic_sendGrams send_grams, + td::Promise>&& promise) + : TonlibQueryActor(std::move(client)), send_grams_(std::move(send_grams)), promise_(std::move(promise)) { + } + + private: + tonlib_api::generic_sendGrams send_grams_; + td::Promise> promise_; + + tonlib_api::object_ptr source_state_; + block::StdAddress source_address_; + + tonlib_api::object_ptr destination_state_; + bool is_destination_bounce_{false}; + + void check(td::Status status) { + if (status.is_error()) { + LOG(ERROR) << status; + promise_.set_error(std::move(status)); + return stop(); + } + } + + void start_up() override { + check(do_start_up()); + } + + td::Status do_start_up() { + alarm_timestamp() = td::Timestamp::in(15); + if (!send_grams_.destination_) { + return td::Status::Error(400, "Field destination must not be empty"); + } + TRY_RESULT(destination_address, block::StdAddress::parse(send_grams_.destination_->account_address_)); + is_destination_bounce_ = destination_address.bounceable; + + if (!send_grams_.source_) { + return td::Status::Error(400, "Field source must not be empty"); + } + TRY_RESULT(source_address, block::StdAddress::parse(send_grams_.source_->account_address_)); + source_address_ = std::move(source_address); + + send_query(tonlib_api::generic_getAccountState( + tonlib_api::make_object(send_grams_.source_->account_address_)), + [actor_id = actor_id(this)](auto r_res) { + send_closure(actor_id, &GenericSendGrams::on_source_state, std::move(r_res)); + }); + send_query(tonlib_api::generic_getAccountState( + tonlib_api::make_object(send_grams_.destination_->account_address_)), + [actor_id = actor_id(this)](auto r_res) { + send_closure(actor_id, &GenericSendGrams::on_destination_state, std::move(r_res)); + }); + return do_loop(); + } + + static tonlib_api::object_ptr clone(const tonlib_api::object_ptr& ptr) { + if (!ptr) { + return nullptr; + } + return tonlib_api::make_object(ptr->public_key_, ptr->secret_.copy()); + } + + static tonlib_api::object_ptr clone(const tonlib_api::object_ptr& ptr) { + if (!ptr) { + return nullptr; + } + return tonlib_api::make_object(clone(ptr->key_), ptr->local_password_.copy()); + } + + void on_source_state(td::Result> r_state) { + check(do_on_source_state(std::move(r_state))); + } + + td::Status do_on_source_state(td::Result> r_state) { + TRY_RESULT(state, std::move(r_state)); + source_state_ = std::move(state); + if (source_state_->get_id() == tonlib_api::generic_accountStateUninited::ID && send_grams_.private_key_ && + send_grams_.private_key_->key_) { + TRY_RESULT(key_bytes, block::PublicKey::parse(send_grams_.private_key_->key_->public_key_)); + auto key = td::Ed25519::PublicKey(td::SecureString(key_bytes.key)); + + if (GenericAccount::get_address(0 /*zerochain*/, TestWallet::get_init_state(key)).addr == source_address_.addr) { + source_state_ = tonlib_api::make_object( + tonlib_api::make_object(-1, 0, nullptr, 0)); + } else if (GenericAccount::get_address(0 /*zerochain*/, Wallet::get_init_state(key)).addr == + source_address_.addr) { + source_state_ = tonlib_api::make_object( + tonlib_api::make_object(-1, 0, nullptr, 0)); + } + } + return do_loop(); + } + + void on_destination_state(td::Result> r_state) { + check(do_on_destination_state(std::move(r_state))); + } + + td::Status do_on_destination_state(td::Result> r_state) { + TRY_RESULT(state, std::move(r_state)); + destination_state_ = std::move(state); + if (destination_state_->get_id() == tonlib_api::generic_accountStateUninited::ID) { + //FIXME + //return td::Status::Error("Transfer to uninited wallet"); + } + return do_loop(); + } + + void alarm() override { + check(td::Status::Error("Timeout")); + } + td::Status do_loop() { + if (!source_state_ || !destination_state_) { + return td::Status::OK(); + } + downcast_call(*source_state_, + td::overloaded( + [&](tonlib_api::generic_accountStateTestGiver& test_giver_state) { + send_query(tonlib_api::testGiver_sendGrams( + std::move(send_grams_.destination_), test_giver_state.account_state_->seqno_, + send_grams_.amount_, std::move(send_grams_.message_)), + std::move(promise_)); + stop(); + }, + [&](tonlib_api::generic_accountStateTestWallet& test_wallet_state) { + send_query(tonlib_api::testWallet_sendGrams( + std::move(send_grams_.private_key_), std::move(send_grams_.destination_), + test_wallet_state.account_state_->seqno_, send_grams_.amount_, + std::move(send_grams_.message_)), + std::move(promise_)); + stop(); + }, + [&](tonlib_api::generic_accountStateWallet& test_wallet_state) { + send_query(tonlib_api::wallet_sendGrams( + std::move(send_grams_.private_key_), std::move(send_grams_.destination_), + test_wallet_state.account_state_->seqno_, std::numeric_limits::max(), + send_grams_.amount_, std::move(send_grams_.message_)), + std::move(promise_)); + stop(); + }, + [&](tonlib_api::generic_accountStateUninited&) { + promise_.set_error(td::Status::Error(400, "Account is not inited")); + stop(); + }, + [&](tonlib_api::generic_accountStateRaw&) { + promise_.set_error(td::Status::Error(400, "Unknown account type")); + stop(); + })); + return td::Status::OK(); + } +}; + td::Status TonlibClient::do_request(tonlib_api::generic_sendGrams& request, td::Promise>&& promise) { - TRY_RESULT(account_address, block::StdAddress::parse(request.source_->account_address_)); - LOG(INFO) << "Send " << request.amount_ << " nanograms from " << account_address.rserialize(); - td::actor::create_actor( - "GetAccountState", client_.get_client(), std::move(account_address), - [promise = std::move(promise), self = this, actor_id = td::actor::actor_id(), - private_key = std::move(request.private_key_), destination = std::move(request.destination_), - amount = request.amount_, message = std::move(request.message_)](td::Result r_state) mutable { - if (r_state.is_error()) { - return promise.set_error(r_state.move_as_error()); - } - auto rr_state = to_generic_accountState(r_state.move_as_ok()); - if (rr_state.is_error()) { - return promise.set_error(rr_state.move_as_error()); - } - auto state = rr_state.move_as_ok(); - - downcast_call(*state, td::overloaded( - [&](tonlib_api::generic_accountStateTestGiver& test_giver_state) { - send_lambda(actor_id, - [promise = std::move(promise), self, - query = tonlib_api::testGiver_sendGrams( - std::move(destination), test_giver_state.account_state_->seqno_, - amount, std::move(message))]() mutable { - LOG(INFO) << "Send " << to_string(query); - auto status = self->do_request(query, std::move(promise)); - if (status.is_error()) { - CHECK(promise); - promise.set_error(std::move(status)); - } - }); - return; - }, - [&](tonlib_api::generic_accountStateTestWallet& test_wallet_state) { - send_lambda(actor_id, [promise = std::move(promise), self, - query = tonlib_api::testWallet_sendGrams( - std::move(private_key), std::move(destination), - test_wallet_state.account_state_->seqno_, amount, - std::move(message))]() mutable { - auto status = self->do_request(query, std::move(promise)); - if (status.is_error()) { - 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_accountStateRaw&) { - promise.set_error(td::Status::Error(400, "Unknown account type")); - })); - }) - .release(); + auto id = actor_id_++; + actors_[id] = td::actor::create_actor("GenericSendGrams", actor_shared(this, id), + std::move(request), std::move(promise)); return td::Status::OK(); } @@ -1029,8 +1274,14 @@ td::Status TonlibClient::do_request(const tonlib_api::exportKey& request, td::Status TonlibClient::do_request(const tonlib_api::deleteKey& request, td::Promise>&& promise) { - TRY_RESULT(key_bytes, block::PublicKey::parse(request.public_key_)); - TRY_STATUS(key_storage_.delete_key(key_bytes.key)); + if (!request.key_) { + return td::Status::Error(400, "Field key must not be empty"); + } + TRY_RESULT(key_bytes, block::PublicKey::parse(request.key_->public_key_)); + KeyStorage::Key key; + key.public_key = td::SecureString(key_bytes.key); + key.secret = std::move(request.key_->secret_); + TRY_STATUS(key_storage_.delete_key(key)); promise.set_value(tonlib_api::make_object()); return td::Status::OK(); } @@ -1141,4 +1392,59 @@ td::Status TonlibClient::do_request(const tonlib_api::onLiteServerQueryError& re return td::Status::OK(); } +tonlib_api::object_ptr TonlibClient::do_static_request(tonlib_api::setLogStream& request) { + auto result = Logging::set_current_stream(std::move(request.log_stream_)); + if (result.is_ok()) { + return tonlib_api::make_object(); + } else { + return tonlib_api::make_object(400, result.message().str()); + } +} +tonlib_api::object_ptr TonlibClient::do_static_request(const tonlib_api::getLogStream& request) { + auto result = Logging::get_current_stream(); + if (result.is_ok()) { + return result.move_as_ok(); + } else { + return tonlib_api::make_object(400, result.error().message().str()); + } +} +tonlib_api::object_ptr TonlibClient::do_static_request( + const tonlib_api::setLogVerbosityLevel& request) { + auto result = Logging::set_verbosity_level(static_cast(request.new_verbosity_level_)); + if (result.is_ok()) { + return tonlib_api::make_object(); + } else { + return tonlib_api::make_object(400, result.message().str()); + } +} +tonlib_api::object_ptr TonlibClient::do_static_request( + const tonlib_api::setLogTagVerbosityLevel& request) { + auto result = Logging::set_tag_verbosity_level(request.tag_, static_cast(request.new_verbosity_level_)); + if (result.is_ok()) { + return tonlib_api::make_object(); + } else { + return tonlib_api::make_object(400, result.message().str()); + } +} +tonlib_api::object_ptr TonlibClient::do_static_request( + const tonlib_api::getLogVerbosityLevel& request) { + return tonlib_api::make_object(Logging::get_verbosity_level()); +} +tonlib_api::object_ptr TonlibClient::do_static_request( + const tonlib_api::getLogTagVerbosityLevel& request) { + auto result = Logging::get_tag_verbosity_level(request.tag_); + if (result.is_ok()) { + return tonlib_api::make_object(result.ok()); + } else { + return tonlib_api::make_object(400, result.error().message().str()); + } +} +tonlib_api::object_ptr TonlibClient::do_static_request(const tonlib_api::getLogTags& request) { + return tonlib_api::make_object(Logging::get_tags()); +} +tonlib_api::object_ptr TonlibClient::do_static_request(const tonlib_api::addLogMessage& request) { + Logging::add_message(request.verbosity_level_, request.text_); + return tonlib_api::make_object(); +} + } // namespace tonlib diff --git a/submodules/ton/tonlib-src/tonlib/tonlib/TonlibClient.h b/submodules/ton/tonlib-src/tonlib/tonlib/TonlibClient.h index a45fd6fdfc..5ac81c2ca2 100644 --- a/submodules/ton/tonlib-src/tonlib/tonlib/TonlibClient.h +++ b/submodules/ton/tonlib-src/tonlib/tonlib/TonlibClient.h @@ -28,6 +28,8 @@ #include "td/actor/actor.h" +#include + namespace tonlib { class TonlibClient : public td::actor::Actor { public: @@ -58,6 +60,9 @@ class TonlibClient : public td::actor::Actor { td::actor::ActorOwn raw_last_block_; ExtClient client_; + std::map> actors_; + td::int64 actor_id_{1}; + ExtClientRef get_client_ref(); void init_ext_client(); void init_last_block(); @@ -65,12 +70,17 @@ class TonlibClient : public td::actor::Actor { bool is_closing_{false}; td::uint32 ref_cnt_{1}; void hangup_shared() override { - ref_cnt_--; + auto it = actors_.find(get_link_token()); + if (it != actors_.end()) { + actors_.erase(it); + } else { + ref_cnt_--; + } try_stop(); } void hangup() override; void try_stop() { - if (is_closing_ && ref_cnt_ == 0) { + if (is_closing_ && ref_cnt_ == 0 && actors_.empty()) { stop(); } } @@ -86,8 +96,19 @@ class TonlibClient : public td::actor::Actor { static object_ptr do_static_request(const tonlib_api::runTests& request); static object_ptr do_static_request(const tonlib_api::raw_getAccountAddress& request); static object_ptr do_static_request(const tonlib_api::testWallet_getAccountAddress& request); + static object_ptr do_static_request(const tonlib_api::wallet_getAccountAddress& request); static object_ptr do_static_request(const tonlib_api::testGiver_getAccountAddress& request); static object_ptr do_static_request(tonlib_api::getBip39Hints& request); + + static object_ptr do_static_request(tonlib_api::setLogStream& request); + static object_ptr do_static_request(const tonlib_api::getLogStream& request); + static object_ptr do_static_request(const tonlib_api::setLogVerbosityLevel& request); + static object_ptr do_static_request(const tonlib_api::setLogTagVerbosityLevel& request); + static object_ptr do_static_request(const tonlib_api::getLogVerbosityLevel& request); + static object_ptr do_static_request(const tonlib_api::getLogTagVerbosityLevel& request); + static object_ptr do_static_request(const tonlib_api::getLogTags& request); + static object_ptr do_static_request(const tonlib_api::addLogMessage& request); + template td::Status do_request(const T& request, P&& promise) { return td::Status::Error(400, "Function is unsupported"); @@ -112,6 +133,11 @@ class TonlibClient : public td::actor::Actor { td::Status do_request(tonlib_api::testWallet_getAccountState& request, td::Promise>&& promise); + td::Status do_request(const tonlib_api::wallet_init& request, td::Promise>&& promise); + td::Status do_request(const tonlib_api::wallet_sendGrams& request, td::Promise>&& promise); + td::Status do_request(tonlib_api::wallet_getAccountState& request, + td::Promise>&& promise); + td::Status do_request(const tonlib_api::testGiver_getAccountState& request, td::Promise>&& promise); td::Status do_request(const tonlib_api::testGiver_sendGrams& request, @@ -145,5 +171,7 @@ class TonlibClient : public td::actor::Actor { td::Promise>&& promise); void proxy_request(td::int64 query_id, std::string data); + + friend class TonlibQueryActor; }; } // namespace tonlib diff --git a/submodules/ton/tonlib-src/tonlib/tonlib/Wallet.cpp b/submodules/ton/tonlib-src/tonlib/tonlib/Wallet.cpp new file mode 100644 index 0000000000..7546e8fd22 --- /dev/null +++ b/submodules/ton/tonlib-src/tonlib/tonlib/Wallet.cpp @@ -0,0 +1,89 @@ +/* + This file is part of TON Blockchain Library. + + TON Blockchain Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + TON Blockchain Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with TON Blockchain Library. If not, see . + + Copyright 2017-2019 Telegram Systems LLP +*/ +#include "tonlib/Wallet.h" +#include "tonlib/GenericAccount.h" +#include "tonlib/utils.h" + +#include "vm/boc.h" +#include "td/utils/base64.h" + +#include + +namespace tonlib { +td::Ref Wallet::get_init_state(const td::Ed25519::PublicKey& public_key) { + auto code = get_init_code(); + auto data = get_init_data(public_key); + return GenericAccount::get_init_state(std::move(code), std::move(data)); +} + +td::Ref Wallet::get_init_message(const td::Ed25519::PrivateKey& private_key) { + td::uint32 seqno = 0; + td::uint32 valid_until = std::numeric_limits::max(); + auto signature = + private_key + .sign(vm::CellBuilder().store_long(seqno, 32).store_long(valid_until, 32).finalize()->get_hash().as_slice()) + .move_as_ok(); + return vm::CellBuilder().store_bytes(signature).store_long(seqno, 32).store_long(valid_until, 32).finalize(); +} + +td::Ref Wallet::make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, + td::uint32 valid_until, td::int64 gramms, td::Slice message, + const block::StdAddress& dest_address) { + CHECK(message.size() <= 124); + td::BigInt256 dest_addr; + dest_addr.import_bits(dest_address.addr.as_bitslice()); + vm::CellBuilder cb; + cb.append_cellslice(binary_bitstring_to_cellslice("b{01}").move_as_ok()) + .store_long(dest_address.bounceable, 1) + .append_cellslice(binary_bitstring_to_cellslice("b{000100}").move_as_ok()) + .store_long(dest_address.workchain, 8) + .store_int256(dest_addr, 256); + block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms)); + auto message_inner = cb.store_zeroes(9 + 64 + 32 + 1 + 1).store_bytes("\0\0\0\0", 4).store_bytes(message).finalize(); + td::int8 send_mode = 3; + auto message_outer = vm::CellBuilder() + .store_long(seqno, 32) + .store_long(valid_until, 32) + .store_long(send_mode, 8) + .store_ref(message_inner) + .finalize(); + std::string seq_no(4, 0); + 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_outer)).finalize(); +} + +td::Ref Wallet::get_init_code() { + static auto res = [] { + auto serialized_code = td::base64_decode( + "te6ccgEEAQEAAAAAVwAAqv8AIN0gggFMl7qXMO1E0NcLH+Ck8mCDCNcYINMf0x8B+CO78mPtRNDTH9P/" + "0VExuvKhA/kBVBBC+RDyovgAApMg10qW0wfUAvsA6NGkyMsfy//J7VQ=") + .move_as_ok(); + return vm::std_boc_deserialize(serialized_code).move_as_ok(); + }(); + return res; +} + +vm::CellHash Wallet::get_init_code_hash() { + return get_init_code()->get_hash(); +} + +td::Ref Wallet::get_init_data(const td::Ed25519::PublicKey& public_key) { + return vm::CellBuilder().store_long(0, 32).store_bytes(public_key.as_octet_string()).finalize(); +} +} // namespace tonlib diff --git a/submodules/ton/tonlib-src/tonlib/tonlib/Wallet.h b/submodules/ton/tonlib-src/tonlib/tonlib/Wallet.h new file mode 100644 index 0000000000..2f2d8a8de1 --- /dev/null +++ b/submodules/ton/tonlib-src/tonlib/tonlib/Wallet.h @@ -0,0 +1,38 @@ +/* + This file is part of TON Blockchain Library. + + TON Blockchain Library is free software: you can redistribute it and/or modify + it under the terms of the GNU Lesser General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + TON Blockchain Library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public License + along with TON Blockchain Library. If not, see . + + Copyright 2017-2019 Telegram Systems LLP +*/ +#pragma once + +#include "vm/cells.h" +#include "Ed25519.h" +#include "block/block.h" + +namespace tonlib { +class Wallet { + public: + static td::Ref get_init_state(const td::Ed25519::PublicKey& public_key); + static td::Ref get_init_message(const td::Ed25519::PrivateKey& private_key); + static td::Ref make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 seqno, + td::uint32 valid_until, td::int64 gramms, td::Slice message, + const block::StdAddress& dest_address); + + static td::Ref get_init_code(); + static vm::CellHash get_init_code_hash(); + static td::Ref get_init_data(const td::Ed25519::PublicKey& public_key); +}; +} // namespace tonlib diff --git a/submodules/ton/tonlib-src/tonlib/tonlib/tonlib-cli.cpp b/submodules/ton/tonlib-src/tonlib/tonlib/tonlib-cli.cpp index f6ce8883a9..191adba647 100644 --- a/submodules/ton/tonlib-src/tonlib/tonlib/tonlib-cli.cpp +++ b/submodules/ton/tonlib-src/tonlib/tonlib/tonlib-cli.cpp @@ -27,6 +27,7 @@ class TonlibCli : public td::actor::Actor { std::string config; std::string key_dir{"."}; bool use_callbacks_for_network{false}; + bool use_simple_wallet{false}; }; TonlibCli(Options options) : options_(std::move(options)) { } @@ -281,7 +282,7 @@ class TonlibCli : public td::actor::Actor { info.public_key = key->public_key_; info.secret = std::move(key->secret_); keys_.push_back(std::move(info)); - export_key(info.public_key, keys_.size() - 1, std::move(password)); + //export_key(key->public_key_, keys_.size() - 1, std::move(password)); store_keys(); }); } @@ -388,8 +389,11 @@ class TonlibCli : public td::actor::Actor { auto r_key_i = to_key_i(key); using tonlib_api::make_object; if (r_key_i.is_ok()) { - auto obj = tonlib::TonlibClient::static_request(make_object( - make_object(keys_[r_key_i.ok()].public_key))); + auto obj = options_.use_simple_wallet + ? tonlib::TonlibClient::static_request(make_object( + make_object(keys_[r_key_i.ok()].public_key))) + : tonlib::TonlibClient::static_request(make_object( + make_object(keys_[r_key_i.ok()].public_key))); if (obj->get_id() != tonlib_api::error::ID) { Address res; res.address = ton::move_tl_object_as(obj); @@ -476,12 +480,19 @@ class TonlibCli : public td::actor::Actor { using tonlib_api::make_object; send_query(make_object(td::SecureString(password), td::SecureString(), make_object(std::move(words))), - [](auto r_res) { + [this, password = td::SecureString(password)](auto r_res) { if (r_res.is_error()) { td::TerminalIO::out() << "Can't import key " << r_res.error() << "\n"; return; } - td::TerminalIO::out() << to_string(r_res.ok()); + auto key = r_res.move_as_ok(); + LOG(ERROR) << to_string(key); + KeyInfo info; + info.public_key = key->public_key_; + info.secret = std::move(key->secret_); + keys_.push_back(std::move(info)); + export_key(key->public_key_, keys_.size() - 1, std::move(password)); + store_keys(); }); } @@ -618,7 +629,7 @@ class TonlibCli : public td::actor::Actor { std::move(to.address), grams, message.str()), [](auto r_res) { if (r_res.is_error()) { - td::TerminalIO::out() << "Can't get state: " << r_res.error() << "\n"; + td::TerminalIO::out() << "Can't transfer: " << r_res.error() << "\n"; return; } td::TerminalIO::out() << to_string(r_res.ok()); @@ -648,16 +659,29 @@ class TonlibCli : public td::actor::Actor { void init_simple_wallet(std::string key, size_t key_i, td::Slice password) { using tonlib_api::make_object; - send_query(make_object(make_object( - make_object(keys_[key_i].public_key, keys_[key_i].secret.copy()), - td::SecureString(password))), - [key = std::move(key)](auto r_res) { - if (r_res.is_error()) { - td::TerminalIO::out() << "Can't init wallet with key: [" << key << "] " << r_res.error() << "\n"; - return; - } - td::TerminalIO::out() << to_string(r_res.ok()); - }); + if (options_.use_simple_wallet) { + send_query(make_object(make_object( + make_object(keys_[key_i].public_key, keys_[key_i].secret.copy()), + td::SecureString(password))), + [key = std::move(key)](auto r_res) { + if (r_res.is_error()) { + td::TerminalIO::out() << "Can't init wallet with key: [" << key << "] " << r_res.error() << "\n"; + return; + } + td::TerminalIO::out() << to_string(r_res.ok()); + }); + } else { + send_query(make_object(make_object( + make_object(keys_[key_i].public_key, keys_[key_i].secret.copy()), + td::SecureString(password))), + [key = std::move(key)](auto r_res) { + if (r_res.is_error()) { + td::TerminalIO::out() << "Can't init wallet with key: [" << key << "] " << r_res.error() << "\n"; + return; + } + td::TerminalIO::out() << to_string(r_res.ok()); + }); + } } void get_hints(td::Slice prefix) { @@ -705,10 +729,14 @@ int main(int argc, char* argv[]) { options.config = std::move(data); return td::Status::OK(); }); - p.add_option('c', "use_callbacks_for_network (for debug)", "do not use this", [&]() { + p.add_option('c', "use-callbacks-for-network", "do not use this", [&]() { options.use_callbacks_for_network = true; return td::Status::OK(); }); + p.add_option('S', "use-simple-wallet", "do not use this", [&]() { + options.use_simple_wallet = true; + return td::Status::OK(); + }); auto S = p.run(argc, argv); if (S.is_error()) {