Wallet errors

This commit is contained in:
Peter 2019-10-03 18:49:38 +04:00
parent d27fb1fdce
commit 2328a24d3f
117 changed files with 5759 additions and 3278 deletions

View File

@ -4780,6 +4780,7 @@ Any member of this group will be able to see messages in the channel.";
"Wallet.Info.Send" = "Send";
"Wallet.Info.RefreshErrorTitle" = "An Error Occurred";
"Wallet.Info.RefreshErrorText" = "The wallet state can not be retrieved at this time. Please try again later.";
"Wallet.Info.RefreshErrorNetworkText" = "The wallet state can not be retrieved at this time. Please try again later.";
"Wallet.Info.UnknownTransaction" = "Empty Transaction";
"Wallet.Info.TransactionTo" = "to";
"Wallet.Info.TransactionFrom" = "from";
@ -4881,8 +4882,12 @@ Any member of this group will be able to see messages in the channel.";
"Wallet.Words.NotDoneOk" = "OK, Sorry";
"Wallet.Words.NotDoneResponse" = "Apologies Accepted";
"Wallet.Send.NetworkError" = "Network Error";
"Wallet.Send.ErrorNotEnoughFunds" = "Not Enough Funds";
"Wallet.Send.ErrorInvalidAddress" = "Invalid wallet address. Please correct and try again.";
"Wallet.Send.ErrorDecryptionFailed" = "Please make sure that your device has a passcode set in iOS Settings and try again.";
"Wallet.Send.UninitializedTitle" = "Warning";
"Wallet.Send.UninitializedText" = "This address belongs to an empty wallet. Are you sure you want to transfer grams to it?";
"Wallet.Send.SendAnyway" = "Send Anyway";
"Wallet.Receive.CreateInvoice" = "Create Invoice";

View File

@ -47,32 +47,47 @@ private func fetchRawData(prefix: String) -> Signal<Data, FetchError> {
}
@available(iOS 10.0, *)
public func cloudDataAdditionalAddressSource(phoneNumber: Signal<String?, NoError>) -> Signal<MTBackupDatacenterData, NoError> {
return phoneNumber
|> take(1)
|> mapToSignal { phoneNumber -> Signal<MTBackupDatacenterData, NoError> in
var prefix = ""
if let phoneNumber = phoneNumber, phoneNumber.count >= 1 {
prefix = String(phoneNumber[phoneNumber.startIndex ..< phoneNumber.index(after: phoneNumber.startIndex)])
}
return fetchRawData(prefix: prefix)
|> map { data -> MTBackupDatacenterData? in
if let datacenterData = MTIPDataDecode(data, phoneNumber ?? "") {
return datacenterData
} else {
return nil
private final class CloudDataPrefixContext {
private let prefix: String
private let value = Promise<Data?>()
private var lastRequestTimestamp: Double?
init(prefix: String) {
self.prefix = prefix
}
private func fetch() {
let fetchSignal = (
fetchRawData(prefix: self.prefix)
|> map(Optional.init)
|> `catch` { error -> Signal<Data?, NoError> in
switch error {
case .networkUnavailable:
return .complete()
default:
return .single(nil)
}
}
|> restart
)
|> take(1)
self.value.set(fetchSignal)
}
func get() -> Signal<Data?, NoError> {
var shouldFetch = false
let timestamp = CFAbsoluteTimeGetCurrent()
if let lastRequestTimestamp = self.lastRequestTimestamp {
shouldFetch = timestamp >= lastRequestTimestamp + 1.0 * 60.0
} else {
shouldFetch = true
}
|> `catch` { error -> Signal<MTBackupDatacenterData?, NoError> in
return .complete()
}
|> mapToSignal { data -> Signal<MTBackupDatacenterData, NoError> in
if let data = data {
return .single(data)
} else {
return .complete()
}
if shouldFetch {
self.lastRequestTimestamp = timestamp
self.fetch()
}
return self.value.get()
}
}
@ -80,32 +95,25 @@ public func cloudDataAdditionalAddressSource(phoneNumber: Signal<String?, NoErro
private final class CloudDataContextObject {
private let queue: Queue
private var prefixContexts: [String: CloudDataPrefixContext] = [:]
init(queue: Queue) {
self.queue = queue
let container = CKContainer.default()
let publicDatabase = container.database(with: .public)
/*let changesOperation = CKFetchDatabaseChangesOperation(previousServerChangeToken: nil)
changesOperation.fetchAllChanges = true
changesOperation.recordZoneWithIDChangedBlock = { _ in
print("recordZoneWithIDChangedBlock")
}
func get(prefix: String) -> Signal<Data?, NoError> {
let context: CloudDataPrefixContext
if let current = self.prefixContexts[prefix] {
context = current
} else {
context = CloudDataPrefixContext(prefix: prefix)
}
changesOperation.recordZoneWithIDWasDeletedBlock = { _ in
}
changesOperation.changeTokenUpdatedBlock = { _ in
print("changeTokenUpdatedBlock")
}
changesOperation.fetchDatabaseChangesCompletionBlock = { serverChangeToken, isMoreComing, error in
print("done")
}
publicDatabase.add(changesOperation)*/
return context.get()
}
}
public protocol CloudDataContext {
func get(phoneNumber: Signal<String?, NoError>) -> Signal<MTBackupDatacenterData, NoError>
}
@available(iOS 10.0, *)
@ -119,4 +127,29 @@ public final class CloudDataContextImpl: CloudDataContext {
return CloudDataContextObject(queue: queue)
})
}
public func get(phoneNumber: Signal<String?, NoError>) -> Signal<MTBackupDatacenterData, NoError> {
return phoneNumber
|> take(1)
|> mapToSignal { phoneNumber -> Signal<MTBackupDatacenterData, NoError> in
var prefix = ""
if let phoneNumber = phoneNumber, phoneNumber.count >= 1 {
prefix = String(phoneNumber[phoneNumber.startIndex ..< phoneNumber.index(after: phoneNumber.startIndex)])
}
return Signal { subscriber in
let disposable = MetaDisposable()
self.impl.with { impl in
disposable.set(impl.get(prefix: prefix).start(next: { data in
if let data = data, let datacenterData = MTIPDataDecode(data, phoneNumber ?? "") {
subscriber.putNext(datacenterData)
subscriber.putCompletion()
} else {
subscriber.putCompletion()
}
}))
}
return disposable
}
}
}
}

View File

@ -392,7 +392,7 @@ public func createPollController(context: AccountContext, peerId: PeerId, comple
controller?.present(c, in: .window(.root), with: a)
}
dismissImpl = { [weak controller] in
controller?.view.endEditing(true)
//controller?.view.endEditing(true)
controller?.dismiss()
}
ensureTextVisibleImpl = { [weak controller] in

View File

@ -401,8 +401,8 @@ open class NavigationController: UINavigationController, ContainableController,
}
if self.currentTopVisibleOverlayContainerStatusBar !== topVisibleOverlayContainerWithStatusBar {
self.currentTopVisibleOverlayContainerStatusBar = topVisibleOverlayContainerWithStatusBar
animateStatusBarStyleTransition = true
self.currentTopVisibleOverlayContainerStatusBar = topVisibleOverlayContainerWithStatusBar
}
var previousModalContainer: NavigationModalContainer?

View File

@ -375,9 +375,9 @@ open class GalleryControllerNode: ASDisplayNode, UIScrollViewDelegate, UIGesture
if !self.areControlsHidden {
if transition < 0.5 {
self.statusBar?.updateAlpha(0.0, transition: .animated(duration: 0.3, curve: .easeInOut))
self.statusBar?.statusBarStyle = .Ignore
} else {
self.statusBar?.updateAlpha(1.0, transition: .animated(duration: 0.3, curve: .easeInOut))
self.statusBar?.statusBarStyle = .White
}
self.navigationBar?.alpha = transition
self.footerNode.alpha = transition

View File

@ -307,7 +307,6 @@ static NSString *makeRandomPadding() {
if (![datacenterData isKindOfClass:[MTBackupDatacenterData class]]) {
return [MTSignal complete];
}
MTBackupDatacenterData *datacenterData = MTIPDataDecode(finalData, phoneNumber);
if (datacenterData != nil && [self checkIpData:datacenterData timestamp:(int32_t)[currentContext globalTime] source:@"resolveExternal"]) {
return [MTSignal single:datacenterData];
} else {

View File

@ -964,6 +964,9 @@ static NSData *decrypt_TL_data(unsigned char buffer[256]) {
@end
MTBackupDatacenterData *MTIPDataDecode(NSData *data, NSString *phoneNumber) {
if (data.length < 256) {
return nil;
}
unsigned char buffer[256];
memcpy(buffer, data.bytes, 256);
NSData *result = decrypt_TL_data(buffer);

View File

@ -92,6 +92,7 @@ NS_ASSUME_NONNULL_BEGIN
- (MTSignal *)exportKey:(TONKey *)key localPassword:(NSData *)localPassword;
- (MTSignal *)importKeyWithLocalPassword:(NSData *)localPassword mnemonicPassword:(NSData *)mnemonicPassword wordList:(NSArray<NSString *> *)wordList;
- (MTSignal *)deleteKey:(TONKey *)key;
- (MTSignal *)deleteAllKeys;
- (MTSignal *)getTransactionListWithAddress:(NSString * _Nonnull)address lt:(int64_t)lt hash:(NSData * _Nonnull)hash;
@end

View File

@ -328,7 +328,9 @@ typedef enum {
false,
false
),
keystoreDirectory.UTF8String
make_object<tonlib_api::keyStoreTypeDirectory>(
keystoreDirectory.UTF8String
)
));
_client->send({ requestId, std::move(query) });
@ -636,6 +638,28 @@ typedef enum {
}] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]];
}
- (MTSignal *)deleteAllKeys {
return [[[[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
uint64_t requestId = _nextRequestId;
_nextRequestId += 1;
_requestHandlers[@(requestId)] = [[TONRequestHandler alloc] initWithCompletion:^(tonlib_api::object_ptr<tonlib_api::Object> &object) {
if (object->get_id() == tonlib_api::error::ID) {
auto error = tonlib_api::move_object_as<tonlib_api::error>(object);
[subscriber putError:[[TONError alloc] initWithText:[[NSString alloc] initWithUTF8String:error->message_.c_str()]]];
} else {
[subscriber putCompletion];
}
}];
auto query = make_object<tonlib_api::deleteAllKeys>();
_client->send({ requestId, std::move(query) });
return [[MTBlockDisposable alloc] initWithBlock:^{
}];
}] startOn:[MTQueue mainQueue]] deliverOn:[MTQueue mainQueue]];
}
- (MTSignal *)getTransactionListWithAddress:(NSString * _Nonnull)address lt:(int64_t)lt hash:(NSData * _Nonnull)hash {
return [[[[MTSignal alloc] initWithGenerator:^id<MTDisposable>(MTSubscriber *subscriber) {
NSData *addressData = [address dataUsingEncoding:NSUTF8StringEncoding];

View File

@ -135,6 +135,55 @@ public func retry<T, E>(_ delayIncrement: Double, maxDelay: Double, onQueue queu
}
}
public func retry<T, E>(retryOnError: @escaping (E) -> Bool, delayIncrement: Double, maxDelay: Double, maxRetries: Int, onQueue queue: Queue) -> (_ signal: Signal<T, E>) -> Signal<T, E> {
return { signal in
return Signal { subscriber in
let shouldRetry = Atomic(value: true)
let currentDelay = Atomic<(Double, Int)>(value: (0.0, 0))
let currentDisposable = MetaDisposable()
let start = recursiveFunction { recurse in
let currentShouldRetry = shouldRetry.with { value in
return value
}
if currentShouldRetry {
let disposable = signal.start(next: { next in
subscriber.putNext(next)
}, error: { error in
if !retryOnError(error) {
subscriber.putError(error)
} else {
let (delay, count) = currentDelay.modify { value, count in
return (min(maxDelay, value + delayIncrement), count + 1)
}
if count >= maxRetries {
subscriber.putError(error)
} else {
let time: DispatchTime = DispatchTime.now() + Double(delay)
queue.queue.asyncAfter(deadline: time, execute: {
recurse()
})
}
}
}, completed: {
let _ = shouldRetry.swap(false)
subscriber.putCompletion()
})
currentDisposable.set(disposable)
}
}
start()
return ActionDisposable {
currentDisposable.dispose()
let _ = shouldRetry.swap(false)
}
}
}
}
public func restartIfError<T, E>(_ signal: Signal<T, E>) -> Signal<T, NoError> {
return Signal<T, NoError> { subscriber in
let shouldRetry = Atomic(value: true)

View File

@ -417,6 +417,8 @@ public struct NetworkInitializationArguments {
}
}
private let cloudDataContext = makeCloudDataContext()
func initializedNetwork(arguments: NetworkInitializationArguments, supplementary: Bool, datacenterId: Int, keychain: Keychain, basePath: String, testingEnvironment: Bool, languageCode: String?, proxySettings: ProxySettings?, networkSettings: NetworkSettings?, phoneNumber: String?) -> Signal<Network, NoError> {
return Signal { subscriber in
let queue = Queue()
@ -484,17 +486,18 @@ func initializedNetwork(arguments: NetworkInitializationArguments, supplementary
var wrappedAdditionalSource: MTSignal?
if #available(iOS 10.0, *) {
let additionalSource = cloudDataAdditionalAddressSource(phoneNumber: .single(phoneNumber ?? ""))
wrappedAdditionalSource = MTSignal(generator: { subscriber in
let disposable = additionalSource.start(next: { value in
subscriber?.putNext(value)
}, completed: {
subscriber?.putCompletion()
if let cloudDataContext = cloudDataContext {
wrappedAdditionalSource = MTSignal(generator: { subscriber in
let disposable = cloudDataContext.get(phoneNumber: .single(phoneNumber)).start(next: { value in
subscriber?.putNext(value)
}, completed: {
subscriber?.putCompletion()
})
return MTBlockDisposable(block: {
disposable.dispose()
})
})
return MTBlockDisposable(block: {
disposable.dispose()
})
})
}
}
context.setDiscoverBackupAddressListSignal(MTBackupAddressSignals.fetchBackupIps(testingEnvironment, currentContext: context, additionalSource: wrappedAdditionalSource, phoneNumber: phoneNumber))
@ -1010,7 +1013,8 @@ public final class Network: NSObject, MTRequestMessageServiceDelegate {
}
public func retryRequest<T>(signal: Signal<T, MTRpcError>) -> Signal<T, NoError> {
return signal |> retry(0.2, maxDelay: 5.0, onQueue: Queue.concurrentDefaultQueue())
return signal
|> retry(0.2, maxDelay: 5.0, onQueue: Queue.concurrentDefaultQueue())
}
class Keychain: NSObject, MTKeychain {
@ -1045,7 +1049,7 @@ class Keychain: NSObject, MTKeychain {
}
}
public func makeCloudDataContext() -> Any? {
func makeCloudDataContext() -> CloudDataContext? {
if #available(iOS 10.0, *) {
return CloudDataContextImpl()
} else {

View File

@ -219,8 +219,16 @@ public final class TonInstance {
return
}
subscriber.putNext(state)
}, error: { _ in
subscriber.putError(.generic)
}, error: { error in
if let error = error as? TONError {
if error.text.hasPrefix("LITE_SERVER_") {
subscriber.putError(.network)
} else {
subscriber.putError(.generic)
}
} else {
subscriber.putError(.generic)
}
}, completed: {
subscriber.putCompletion()
})
@ -241,7 +249,7 @@ public final class TonInstance {
}
}
fileprivate func walletLastTransactionId(address: String) -> Signal<WalletTransactionId?, NoError> {
fileprivate func walletLastTransactionId(address: String) -> Signal<WalletTransactionId?, WalletLastTransactionIdError> {
return Signal { subscriber in
let disposable = MetaDisposable()
@ -253,7 +261,16 @@ public final class TonInstance {
return
}
subscriber.putNext(state.lastTransactionId.flatMap(WalletTransactionId.init(tonTransactionId:)))
}, error: { _ in
}, error: { error in
if let error = error as? TONError {
if error.text.hasPrefix("ДITE_SERVER_") {
subscriber.putError(.network)
} else {
subscriber.putError(.generic)
}
} else {
subscriber.putError(.generic)
}
}, completed: {
subscriber.putCompletion()
})
@ -279,7 +296,16 @@ public final class TonInstance {
return
}
subscriber.putNext(transactions.map(WalletTransaction.init(tonTransaction:)))
}, error: { _ in
}, error: { error in
if let error = error as? TONError {
if error.text.hasPrefix("LITE_SERVER_") {
subscriber.putError(.network)
} else {
subscriber.putError(.generic)
}
} else {
subscriber.putError(.generic)
}
}, completed: {
subscriber.putCompletion()
})
@ -308,10 +334,16 @@ public final class TonInstance {
subscriber.putCompletion()
}, error: { error in
if let error = error as? TONError {
if error.text == "Failed to parse account address" {
if error.text.hasPrefix("INVALID_ACCOUNT_ADDRESS") {
subscriber.putError(.invalidAddress)
} else if error.text.hasPrefix("DANGEROUS_TRANSACTION") {
subscriber.putError(.destinationIsNotInitialized)
} else if error.text.hasPrefix("MESSAGE_TOO_LONG") {
subscriber.putError(.messageTooLong)
} else if error.text.hasPrefix("NOT_ENOUGH_FUNDS") {
subscriber.putError(.notEnoughFunds)
} else if error.text.hasPrefix("LITE_SERVER_") {
subscriber.putError(.network)
} else {
subscriber.putError(.generic)
}
@ -364,6 +396,29 @@ public final class TonInstance {
}
}
fileprivate func deleteAllLocalWalletsData() -> Signal<Never, DeleteAllLocalWalletsDataError> {
return Signal { subscriber in
let disposable = MetaDisposable()
self.impl.with { impl in
impl.withInstance { ton in
let cancel = ton.deleteAllKeys().start(next: { _ in
assertionFailure()
}, error: { _ in
subscriber.putError(.generic)
}, completed: {
subscriber.putCompletion()
})
disposable.set(ActionDisposable {
cancel?.dispose()
})
}
}
return disposable
}
}
fileprivate func deleteLocalWalletData(walletInfo: WalletInfo, keychain: TonKeychain, serverSalt: Data) -> Signal<Never, DeleteLocalWalletDataError> {
return keychain.decrypt(walletInfo.encryptedSecret)
|> mapError { error -> DeleteLocalWalletDataError in
@ -580,6 +635,24 @@ public func importWallet(postbox: Postbox, network: Network, tonInstance: TonIns
}
}
public enum DeleteAllLocalWalletsDataError {
case generic
}
public func deleteAllLocalWalletsData(postbox: Postbox, network: Network, tonInstance: TonInstance) -> Signal<Never, DeleteAllLocalWalletsDataError> {
return tonInstance.deleteAllLocalWalletsData()
|> then(
postbox.transaction { transaction -> Void in
transaction.updatePreferencesEntry(key: PreferencesKeys.walletCollection, { current in
let walletCollection = (current as? WalletCollection) ?? WalletCollection(wallets: [])
return walletCollection
})
}
|> castError(DeleteAllLocalWalletsDataError.self)
|> ignoreValues
)
}
public enum DeleteLocalWalletDataError {
case generic
case secretDecryptionFailed(TonKeychainDecryptDataError)
@ -642,6 +715,7 @@ public func walletAddress(publicKey: WalletPublicKey, tonInstance: TonInstance)
private enum GetWalletStateError {
case generic
case network
}
private func getWalletState(address: String, tonInstance: TonInstance) -> Signal<(WalletState, Int64), GetWalletStateError> {
@ -650,6 +724,7 @@ private func getWalletState(address: String, tonInstance: TonInstance) -> Signal
public enum GetCombinedWalletStateError {
case generic
case network
}
public enum CombinedWalletStateResult {
@ -682,8 +757,19 @@ public func getCombinedWalletState(postbox: Postbox, subject: CombinedWalletStat
|> castError(GetCombinedWalletStateError.self)
|> mapToSignal { address -> Signal<CombinedWalletStateResult, GetCombinedWalletStateError> in
return getWalletState(address: address, tonInstance: tonInstance)
|> mapError { _ -> GetCombinedWalletStateError in
return .generic
|> retryTonRequest(isNetworkError: { error in
if case .network = error {
return true
} else {
return false
}
})
|> mapError { error -> GetCombinedWalletStateError in
if case .network = error {
return .network
} else {
return .generic
}
}
|> mapToSignal { walletState, syncUtime -> Signal<CombinedWalletStateResult, GetCombinedWalletStateError> in
let topTransactions: Signal<[WalletTransaction], GetCombinedWalletStateError>
@ -691,8 +777,12 @@ public func getCombinedWalletState(postbox: Postbox, subject: CombinedWalletStat
topTransactions = .single(cachedState?.topTransactions ?? [])
} else {
topTransactions = getWalletTransactions(address: address, previousId: nil, tonInstance: tonInstance)
|> mapError { _ -> GetCombinedWalletStateError in
return .generic
|> mapError { error -> GetCombinedWalletStateError in
if case .network = error {
return .network
} else {
return .generic
}
}
}
return topTransactions
@ -744,6 +834,9 @@ public enum SendGramsFromWalletError {
case secretDecryptionFailed
case invalidAddress
case destinationIsNotInitialized
case messageTooLong
case notEnoughFunds
case network
}
public func sendGramsFromWallet(network: Network, tonInstance: TonInstance, walletInfo: WalletInfo, decryptedSecret: Data, serverSalt: Data, toAddress: String, amount: Int64, textMessage: String, forceIfDestinationNotInitialized: Bool, timeout: Int32, randomId: Int64) -> Signal<Never, SendGramsFromWalletError> {
@ -862,6 +955,7 @@ private extension WalletTransaction {
public enum GetWalletTransactionsError {
case generic
case network
}
public func getWalletTransactions(address: String, previousId: WalletTransactionId?, tonInstance: TonInstance) -> Signal<[WalletTransaction], GetWalletTransactionsError> {
@ -885,18 +979,50 @@ public func getWalletTransactions(address: String, previousId: WalletTransaction
}
}
private func retryTonRequest<T, E>(isNetworkError: @escaping (E) -> Bool) -> (Signal<T, E>) -> Signal<T, E> {
return { signal in
return signal
|> retry(retryOnError: isNetworkError, delayIncrement: 0.2, maxDelay: 5.0, maxRetries: 3, onQueue: Queue.concurrentDefaultQueue())
}
}
private enum WalletLastTransactionIdError {
case generic
case network
}
private func getWalletTransactionsOnce(address: String, previousId: WalletTransactionId?, tonInstance: TonInstance) -> Signal<[WalletTransaction], GetWalletTransactionsError> {
let previousIdValue: Signal<WalletTransactionId?, GetWalletTransactionsError>
if let previousId = previousId {
previousIdValue = .single(previousId)
} else {
previousIdValue = tonInstance.walletLastTransactionId(address: address)
|> castError(GetWalletTransactionsError.self)
|> retryTonRequest(isNetworkError: { error in
if case .network = error {
return true
} else {
return false
}
})
|> mapError { error -> GetWalletTransactionsError in
if case .network = error {
return .network
} else {
return .generic
}
}
}
return previousIdValue
|> mapToSignal { previousId in
if let previousId = previousId {
return tonInstance.getWalletTransactions(address: address, previousId: previousId)
|> retryTonRequest(isNetworkError: { error in
if case .network = error {
return true
} else {
return false
}
})
} else {
return .single([])
}

View File

@ -244,8 +244,6 @@ final class SharedApplicationContext {
private let deviceToken = Promise<Data?>(nil)
private var cloudDataContext: Any?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
precondition(!testIsLaunched)
testIsLaunched = true
@ -262,8 +260,6 @@ final class SharedApplicationContext {
self.window = window
self.nativeWindow = window
self.cloudDataContext = makeCloudDataContext()
let clearNotificationsManager = ClearNotificationsManager(getNotificationIds: { completion in
if #available(iOS 10.0, *) {
UNUserNotificationCenter.current().getDeliveredNotifications(completionHandler: { notifications in

View File

@ -751,7 +751,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
strongSelf.didSetContentReady = true
strongSelf.contentReady.set(.single(true))
}
}, error: { [weak self] _ in
}, error: { [weak self] error in
guard let strongSelf = self else {
return
}
@ -780,7 +780,14 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
strongSelf.contentReady.set(.single(true))
}
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.Wallet_Info_RefreshErrorTitle, text: strongSelf.presentationData.strings.Wallet_Info_RefreshErrorText, actions: [
let text: String
switch error {
case .generic:
text = strongSelf.presentationData.strings.Wallet_Info_RefreshErrorText
case .network:
text = strongSelf.presentationData.strings.Wallet_Info_RefreshErrorNetworkText
}
strongSelf.present(standardTextAlertController(theme: AlertControllerTheme(presentationTheme: strongSelf.presentationData.theme), title: strongSelf.presentationData.strings.Wallet_Info_RefreshErrorTitle, text: text, actions: [
TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {
})
], actionLayout: .vertical), nil)

View File

@ -120,7 +120,7 @@ public func walletSettingsController(context: AccountContext, tonContext: TonCon
actionSheet?.dismissAnimated()
let controller = OverlayStatusController(theme: presentationData.theme, strings: presentationData.strings, type: .loading(cancelled: nil))
presentControllerImpl?(controller, nil)
let _ = (deleteLocalWalletData(postbox: context.account.postbox, network: context.account.network, tonInstance: tonContext.instance, keychain: tonContext.keychain, walletInfo: walletInfo)
let _ = (deleteAllLocalWalletsData(postbox: context.account.postbox, network: context.account.network, tonInstance: tonContext.instance)
|> deliverOnMainQueue).start(error: { [weak controller] _ in
controller?.dismiss()
}, completed: { [weak controller] in

View File

@ -142,14 +142,20 @@ public final class WalletSplashScreen: ViewController {
switch error {
case .generic:
text = strongSelf.presentationData.strings.Login_UnknownError
case .network:
text = strongSelf.presentationData.strings.Wallet_Send_NetworkError
case .notEnoughFunds:
text = strongSelf.presentationData.strings.Wallet_Send_ErrorNotEnoughFunds
case .messageTooLong:
text = strongSelf.presentationData.strings.Login_UnknownError
case .invalidAddress:
text = strongSelf.presentationData.strings.Wallet_Send_ErrorInvalidAddress
case .secretDecryptionFailed:
text = strongSelf.presentationData.strings.Wallet_Send_ErrorDecryptionFailed
case .destinationIsNotInitialized:
if !forceIfDestinationNotInitialized {
text = "This address belongs to an empty wallet. Are you sure you want to transfer grams to it?"
let controller = textAlertController(context: strongSelf.context, title: "Warning", text: text, actions: [
text = strongSelf.presentationData.strings.Wallet_Send_UninitializedText
let controller = textAlertController(context: strongSelf.context, title: strongSelf.presentationData.strings.Wallet_Send_UninitializedTitle, text: text, actions: [
TextAlertAction(type: .genericAction, title: strongSelf.presentationData.strings.Common_Cancel, action: {
if let navigationController = strongSelf.navigationController as? NavigationController {
navigationController.popViewController(animated: true)

View File

@ -3,7 +3,7 @@ import TelegramStringFormatting
import UrlEscaping
let walletAddressLength: Int = 48
let walletTextLimit: Int = 124
let walletTextLimit: Int = 1024
func formatAddress(_ address: String) -> String {
var address = address

View File

@ -315,6 +315,8 @@ if (NOT CMAKE_CROSSCOMPILING)
GenFif(DEST smartcont/config-code.fif SOURCE smartcont/config-code.fc)
GenFif(DEST smartcont/wallet-code.fif SOURCE smartcont/wallet-code.fc)
GenFif(DEST smartcont/simple-wallet-code.fif SOURCE smartcont/simple-wallet-code.fc)
GenFif(DEST smartcont/highload-wallet-code.fif SOURCE smartcont/highload-wallet-code.fc)
GenFif(DEST smartcont/highload-wallet-v2-code.fif SOURCE smartcont/highload-wallet-v2-code.fc)
GenFif(DEST smartcont/elector-code.fif SOURCE smartcont/elector-code.fc)
endif()
@ -333,3 +335,6 @@ target_link_libraries(dump-block PUBLIC ton_crypto fift-lib ton_block)
if (WINGETOPT_FOUND)
target_link_libraries_system(dump-block wingetopt)
endif()
install(TARGETS fift func RUNTIME DESTINATION bin)
install(DIRECTORY fift/lib/ DESTINATION lib/fift)

View File

@ -8891,6 +8891,7 @@ bool TransactionDescr::skip(vm::CellSlice& cs) const {
&& cs.advance(2);
case trans_split_prepare:
return cs.advance(528)
&& t_Maybe_TrStoragePhase.skip(cs)
&& t_TrComputePhase.skip(cs)
&& t_Maybe_Ref_TrActionPhase.skip(cs)
&& cs.advance(2);
@ -8902,6 +8903,7 @@ bool TransactionDescr::skip(vm::CellSlice& cs) const {
&& cs.advance(1);
case trans_merge_install:
return cs.advance_ext(0x10210)
&& t_Maybe_TrStoragePhase.skip(cs)
&& t_Maybe_TrCreditPhase.skip(cs)
&& t_TrComputePhase.skip(cs)
&& t_Maybe_Ref_TrActionPhase.skip(cs)
@ -8932,6 +8934,7 @@ bool TransactionDescr::validate_skip(vm::CellSlice& cs, bool weak) const {
&& cs.advance(2);
case trans_split_prepare:
return cs.advance(528)
&& t_Maybe_TrStoragePhase.validate_skip(cs, weak)
&& t_TrComputePhase.validate_skip(cs, weak)
&& t_Maybe_Ref_TrActionPhase.validate_skip(cs, weak)
&& cs.advance(2);
@ -8947,6 +8950,7 @@ bool TransactionDescr::validate_skip(vm::CellSlice& cs, bool weak) const {
return cs.fetch_ulong(4) == 7
&& cs.advance(524)
&& t_Transaction.validate_skip_ref(cs, weak)
&& t_Maybe_TrStoragePhase.validate_skip(cs, weak)
&& t_Maybe_TrCreditPhase.validate_skip(cs, weak)
&& t_TrComputePhase.validate_skip(cs, weak)
&& t_Maybe_Ref_TrActionPhase.validate_skip(cs, weak)
@ -8998,7 +9002,7 @@ bool TransactionDescr::cell_unpack_trans_storage(Ref<vm::Cell> cell_ref, Ref<Cel
bool TransactionDescr::unpack(vm::CellSlice& cs, TransactionDescr::Record_trans_tick_tock& data) const {
return cs.fetch_ulong(3) == 1
&& cs.fetch_bool_to(data.is_tock)
&& t_TrStoragePhase.fetch_to(cs, data.storage)
&& t_TrStoragePhase.fetch_to(cs, data.storage_ph)
&& t_TrComputePhase.fetch_to(cs, data.compute_ph)
&& t_Maybe_Ref_TrActionPhase.fetch_to(cs, data.action)
&& cs.fetch_bool_to(data.aborted)
@ -9014,6 +9018,7 @@ bool TransactionDescr::cell_unpack(Ref<vm::Cell> cell_ref, TransactionDescr::Rec
bool TransactionDescr::unpack(vm::CellSlice& cs, TransactionDescr::Record_trans_split_prepare& data) const {
return cs.fetch_ulong(4) == 4
&& cs.fetch_subslice_to(524, data.split_info)
&& t_Maybe_TrStoragePhase.fetch_to(cs, data.storage_ph)
&& t_TrComputePhase.fetch_to(cs, data.compute_ph)
&& t_Maybe_Ref_TrActionPhase.fetch_to(cs, data.action)
&& cs.fetch_bool_to(data.aborted)
@ -9082,6 +9087,7 @@ bool TransactionDescr::unpack(vm::CellSlice& cs, TransactionDescr::Record_trans_
return cs.fetch_ulong(4) == 7
&& cs.fetch_subslice_to(524, data.split_info)
&& cs.fetch_ref_to(data.prepare_transaction)
&& t_Maybe_TrStoragePhase.fetch_to(cs, data.storage_ph)
&& t_Maybe_TrCreditPhase.fetch_to(cs, data.credit_ph)
&& t_TrComputePhase.fetch_to(cs, data.compute_ph)
&& t_Maybe_Ref_TrActionPhase.fetch_to(cs, data.action)
@ -9135,7 +9141,7 @@ bool TransactionDescr::cell_pack_trans_storage(Ref<vm::Cell>& cell_ref, Ref<Cell
bool TransactionDescr::pack(vm::CellBuilder& cb, const TransactionDescr::Record_trans_tick_tock& data) const {
return cb.store_long_bool(1, 3)
&& cb.store_ulong_rchk_bool(data.is_tock, 1)
&& t_TrStoragePhase.store_from(cb, data.storage)
&& t_TrStoragePhase.store_from(cb, data.storage_ph)
&& t_TrComputePhase.store_from(cb, data.compute_ph)
&& t_Maybe_Ref_TrActionPhase.store_from(cb, data.action)
&& cb.store_ulong_rchk_bool(data.aborted, 1)
@ -9150,6 +9156,7 @@ bool TransactionDescr::cell_pack(Ref<vm::Cell>& cell_ref, const TransactionDescr
bool TransactionDescr::pack(vm::CellBuilder& cb, const TransactionDescr::Record_trans_split_prepare& data) const {
return cb.store_long_bool(4, 4)
&& cb.append_cellslice_chk(data.split_info, 524)
&& t_Maybe_TrStoragePhase.store_from(cb, data.storage_ph)
&& t_TrComputePhase.store_from(cb, data.compute_ph)
&& t_Maybe_Ref_TrActionPhase.store_from(cb, data.action)
&& cb.store_ulong_rchk_bool(data.aborted, 1)
@ -9213,6 +9220,7 @@ bool TransactionDescr::pack(vm::CellBuilder& cb, const TransactionDescr::Record_
return cb.store_long_bool(7, 4)
&& cb.append_cellslice_chk(data.split_info, 524)
&& cb.store_ref_bool(data.prepare_transaction)
&& t_Maybe_TrStoragePhase.store_from(cb, data.storage_ph)
&& t_Maybe_TrCreditPhase.store_from(cb, data.credit_ph)
&& t_TrComputePhase.store_from(cb, data.compute_ph)
&& t_Maybe_Ref_TrActionPhase.store_from(cb, data.action)
@ -9254,7 +9262,7 @@ bool TransactionDescr::print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const {
return cs.advance(3)
&& pp.open("trans_tick_tock")
&& pp.fetch_uint_field(cs, 1, "is_tock")
&& pp.field("storage")
&& pp.field("storage_ph")
&& t_TrStoragePhase.print_skip(pp, cs)
&& pp.field("compute_ph")
&& t_TrComputePhase.print_skip(pp, cs)
@ -9268,6 +9276,8 @@ bool TransactionDescr::print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const {
&& pp.open("trans_split_prepare")
&& pp.field("split_info")
&& t_SplitMergeInfo.print_skip(pp, cs)
&& pp.field("storage_ph")
&& t_Maybe_TrStoragePhase.print_skip(pp, cs)
&& pp.field("compute_ph")
&& t_TrComputePhase.print_skip(pp, cs)
&& pp.field("action")
@ -9300,6 +9310,8 @@ bool TransactionDescr::print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const {
&& t_SplitMergeInfo.print_skip(pp, cs)
&& pp.field("prepare_transaction")
&& t_Transaction.print_ref(pp, cs.fetch_ref())
&& pp.field("storage_ph")
&& t_Maybe_TrStoragePhase.print_skip(pp, cs)
&& pp.field("credit_ph")
&& t_Maybe_TrCreditPhase.print_skip(pp, cs)
&& pp.field("compute_ph")
@ -14089,11 +14101,13 @@ const StoragePrices t_StoragePrices;
//
// code for type `GasLimitsPrices`
//
constexpr unsigned char GasLimitsPrices::cons_tag[2];
constexpr unsigned char GasLimitsPrices::cons_tag[3];
int GasLimitsPrices::get_tag(const vm::CellSlice& cs) const {
switch (cs.bselect(6, 0x180000000000000ULL)) {
switch (cs.bselect(6, 0x1b0000000000000ULL)) {
case 0:
return gas_flat_pfx;
case 2:
return cs.bit_at(6) ? gas_prices_ext : gas_prices;
default:
return -1;
@ -14106,6 +14120,8 @@ int GasLimitsPrices::check_tag(const vm::CellSlice& cs) const {
return cs.prefetch_ulong(8) == 0xdd ? gas_prices : -1;
case gas_prices_ext:
return cs.prefetch_ulong(8) == 0xde ? gas_prices_ext : -1;
case gas_flat_pfx:
return cs.prefetch_ulong(8) == 0xd1 ? gas_flat_pfx : -1;
}
return -1;
}
@ -14116,6 +14132,9 @@ bool GasLimitsPrices::skip(vm::CellSlice& cs) const {
return cs.advance(392);
case gas_prices_ext:
return cs.advance(456);
case gas_flat_pfx:
return cs.advance(136)
&& skip(cs);
}
return false;
}
@ -14128,6 +14147,10 @@ bool GasLimitsPrices::validate_skip(vm::CellSlice& cs, bool weak) const {
case gas_prices_ext:
return cs.fetch_ulong(8) == 0xde
&& cs.advance(448);
case gas_flat_pfx:
return cs.fetch_ulong(8) == 0xd1
&& cs.advance(128)
&& validate_skip(cs, weak);
}
return false;
}
@ -14165,6 +14188,32 @@ bool GasLimitsPrices::cell_unpack(Ref<vm::Cell> cell_ref, GasLimitsPrices::Recor
return unpack(cs, data) && cs.empty_ext();
}
bool GasLimitsPrices::unpack(vm::CellSlice& cs, GasLimitsPrices::Record_gas_flat_pfx& data) const {
return cs.fetch_ulong(8) == 0xd1
&& cs.fetch_uint_to(64, data.flat_gas_limit)
&& cs.fetch_uint_to(64, data.flat_gas_price)
&& fetch_to(cs, data.other);
}
bool GasLimitsPrices::unpack_gas_flat_pfx(vm::CellSlice& cs, unsigned long long& flat_gas_limit, unsigned long long& flat_gas_price, Ref<CellSlice>& other) const {
return cs.fetch_ulong(8) == 0xd1
&& cs.fetch_uint_to(64, flat_gas_limit)
&& cs.fetch_uint_to(64, flat_gas_price)
&& fetch_to(cs, other);
}
bool GasLimitsPrices::cell_unpack(Ref<vm::Cell> cell_ref, GasLimitsPrices::Record_gas_flat_pfx& data) const {
if (cell_ref.is_null()) { return false; }
auto cs = load_cell_slice(std::move(cell_ref));
return unpack(cs, data) && cs.empty_ext();
}
bool GasLimitsPrices::cell_unpack_gas_flat_pfx(Ref<vm::Cell> cell_ref, unsigned long long& flat_gas_limit, unsigned long long& flat_gas_price, Ref<CellSlice>& other) const {
if (cell_ref.is_null()) { return false; }
auto cs = load_cell_slice(std::move(cell_ref));
return unpack_gas_flat_pfx(cs, flat_gas_limit, flat_gas_price, other) && cs.empty_ext();
}
bool GasLimitsPrices::pack(vm::CellBuilder& cb, const GasLimitsPrices::Record_gas_prices& data) const {
return cb.store_long_bool(0xdd, 8)
&& cb.store_ulong_rchk_bool(data.gas_price, 64)
@ -14196,6 +14245,30 @@ bool GasLimitsPrices::cell_pack(Ref<vm::Cell>& cell_ref, const GasLimitsPrices::
return pack(cb, data) && std::move(cb).finalize_to(cell_ref);
}
bool GasLimitsPrices::pack(vm::CellBuilder& cb, const GasLimitsPrices::Record_gas_flat_pfx& data) const {
return cb.store_long_bool(0xd1, 8)
&& cb.store_ulong_rchk_bool(data.flat_gas_limit, 64)
&& cb.store_ulong_rchk_bool(data.flat_gas_price, 64)
&& store_from(cb, data.other);
}
bool GasLimitsPrices::pack_gas_flat_pfx(vm::CellBuilder& cb, unsigned long long flat_gas_limit, unsigned long long flat_gas_price, Ref<CellSlice> other) const {
return cb.store_long_bool(0xd1, 8)
&& cb.store_ulong_rchk_bool(flat_gas_limit, 64)
&& cb.store_ulong_rchk_bool(flat_gas_price, 64)
&& store_from(cb, other);
}
bool GasLimitsPrices::cell_pack(Ref<vm::Cell>& cell_ref, const GasLimitsPrices::Record_gas_flat_pfx& data) const {
vm::CellBuilder cb;
return pack(cb, data) && std::move(cb).finalize_to(cell_ref);
}
bool GasLimitsPrices::cell_pack_gas_flat_pfx(Ref<vm::Cell>& cell_ref, unsigned long long flat_gas_limit, unsigned long long flat_gas_price, Ref<CellSlice> other) const {
vm::CellBuilder cb;
return pack_gas_flat_pfx(cb, flat_gas_limit, flat_gas_price, std::move(other)) && std::move(cb).finalize_to(cell_ref);
}
bool GasLimitsPrices::print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const {
switch (get_tag(cs)) {
case gas_prices:
@ -14219,6 +14292,14 @@ bool GasLimitsPrices::print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const {
&& pp.fetch_uint_field(cs, 64, "freeze_due_limit")
&& pp.fetch_uint_field(cs, 64, "delete_due_limit")
&& pp.close();
case gas_flat_pfx:
return cs.fetch_ulong(8) == 0xd1
&& pp.open("gas_flat_pfx")
&& pp.fetch_uint_field(cs, 64, "flat_gas_limit")
&& pp.fetch_uint_field(cs, 64, "flat_gas_price")
&& pp.field("other")
&& print_skip(pp, cs)
&& pp.close();
}
return pp.fail("unknown constructor for GasLimitsPrices");
}

View File

@ -3831,24 +3831,25 @@ struct TransactionDescr::Record_trans_ord {
struct TransactionDescr::Record_trans_tick_tock {
typedef TransactionDescr type_class;
bool is_tock; // is_tock : Bool
Ref<CellSlice> storage; // storage : TrStoragePhase
Ref<CellSlice> storage_ph; // storage_ph : TrStoragePhase
Ref<CellSlice> compute_ph; // compute_ph : TrComputePhase
Ref<CellSlice> action; // action : Maybe ^TrActionPhase
bool aborted; // aborted : Bool
bool destroyed; // destroyed : Bool
Record_trans_tick_tock() = default;
Record_trans_tick_tock(bool _is_tock, Ref<CellSlice> _storage, Ref<CellSlice> _compute_ph, Ref<CellSlice> _action, bool _aborted, bool _destroyed) : is_tock(_is_tock), storage(std::move(_storage)), compute_ph(std::move(_compute_ph)), action(std::move(_action)), aborted(_aborted), destroyed(_destroyed) {}
Record_trans_tick_tock(bool _is_tock, Ref<CellSlice> _storage_ph, Ref<CellSlice> _compute_ph, Ref<CellSlice> _action, bool _aborted, bool _destroyed) : is_tock(_is_tock), storage_ph(std::move(_storage_ph)), compute_ph(std::move(_compute_ph)), action(std::move(_action)), aborted(_aborted), destroyed(_destroyed) {}
};
struct TransactionDescr::Record_trans_split_prepare {
typedef TransactionDescr type_class;
Ref<CellSlice> split_info; // split_info : SplitMergeInfo
Ref<CellSlice> storage_ph; // storage_ph : Maybe TrStoragePhase
Ref<CellSlice> compute_ph; // compute_ph : TrComputePhase
Ref<CellSlice> action; // action : Maybe ^TrActionPhase
bool aborted; // aborted : Bool
bool destroyed; // destroyed : Bool
Record_trans_split_prepare() = default;
Record_trans_split_prepare(Ref<CellSlice> _split_info, Ref<CellSlice> _compute_ph, Ref<CellSlice> _action, bool _aborted, bool _destroyed) : split_info(std::move(_split_info)), compute_ph(std::move(_compute_ph)), action(std::move(_action)), aborted(_aborted), destroyed(_destroyed) {}
Record_trans_split_prepare(Ref<CellSlice> _split_info, Ref<CellSlice> _storage_ph, Ref<CellSlice> _compute_ph, Ref<CellSlice> _action, bool _aborted, bool _destroyed) : split_info(std::move(_split_info)), storage_ph(std::move(_storage_ph)), compute_ph(std::move(_compute_ph)), action(std::move(_action)), aborted(_aborted), destroyed(_destroyed) {}
};
struct TransactionDescr::Record_trans_split_install {
@ -3873,13 +3874,14 @@ struct TransactionDescr::Record_trans_merge_install {
typedef TransactionDescr type_class;
Ref<CellSlice> split_info; // split_info : SplitMergeInfo
Ref<Cell> prepare_transaction; // prepare_transaction : ^Transaction
Ref<CellSlice> storage_ph; // storage_ph : Maybe TrStoragePhase
Ref<CellSlice> credit_ph; // credit_ph : Maybe TrCreditPhase
Ref<CellSlice> compute_ph; // compute_ph : TrComputePhase
Ref<CellSlice> action; // action : Maybe ^TrActionPhase
bool aborted; // aborted : Bool
bool destroyed; // destroyed : Bool
Record_trans_merge_install() = default;
Record_trans_merge_install(Ref<CellSlice> _split_info, Ref<Cell> _prepare_transaction, Ref<CellSlice> _credit_ph, Ref<CellSlice> _compute_ph, Ref<CellSlice> _action, bool _aborted, bool _destroyed) : split_info(std::move(_split_info)), prepare_transaction(std::move(_prepare_transaction)), credit_ph(std::move(_credit_ph)), compute_ph(std::move(_compute_ph)), action(std::move(_action)), aborted(_aborted), destroyed(_destroyed) {}
Record_trans_merge_install(Ref<CellSlice> _split_info, Ref<Cell> _prepare_transaction, Ref<CellSlice> _storage_ph, Ref<CellSlice> _credit_ph, Ref<CellSlice> _compute_ph, Ref<CellSlice> _action, bool _aborted, bool _destroyed) : split_info(std::move(_split_info)), prepare_transaction(std::move(_prepare_transaction)), storage_ph(std::move(_storage_ph)), credit_ph(std::move(_credit_ph)), compute_ph(std::move(_compute_ph)), action(std::move(_action)), aborted(_aborted), destroyed(_destroyed) {}
};
extern const TransactionDescr t_TransactionDescr;
@ -6166,11 +6168,12 @@ extern const StoragePrices t_StoragePrices;
//
struct GasLimitsPrices final : TLB_Complex {
enum { gas_prices, gas_prices_ext };
enum { gas_flat_pfx, gas_prices, gas_prices_ext };
static constexpr int cons_len_exact = 8;
static constexpr unsigned char cons_tag[2] = { 0xdd, 0xde };
static constexpr unsigned char cons_tag[3] = { 0xd1, 0xdd, 0xde };
struct Record_gas_prices;
struct Record_gas_prices_ext;
struct Record_gas_flat_pfx;
bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override;
bool unpack(vm::CellSlice& cs, Record_gas_prices& data) const;
@ -6181,6 +6184,14 @@ struct GasLimitsPrices final : TLB_Complex {
bool cell_unpack(Ref<vm::Cell> cell_ref, Record_gas_prices_ext& data) const;
bool pack(vm::CellBuilder& cb, const Record_gas_prices_ext& data) const;
bool cell_pack(Ref<vm::Cell>& cell_ref, const Record_gas_prices_ext& data) const;
bool unpack(vm::CellSlice& cs, Record_gas_flat_pfx& data) const;
bool unpack_gas_flat_pfx(vm::CellSlice& cs, unsigned long long& flat_gas_limit, unsigned long long& flat_gas_price, Ref<CellSlice>& other) const;
bool cell_unpack(Ref<vm::Cell> cell_ref, Record_gas_flat_pfx& data) const;
bool cell_unpack_gas_flat_pfx(Ref<vm::Cell> cell_ref, unsigned long long& flat_gas_limit, unsigned long long& flat_gas_price, Ref<CellSlice>& other) const;
bool pack(vm::CellBuilder& cb, const Record_gas_flat_pfx& data) const;
bool pack_gas_flat_pfx(vm::CellBuilder& cb, unsigned long long flat_gas_limit, unsigned long long flat_gas_price, Ref<CellSlice> other) const;
bool cell_pack(Ref<vm::Cell>& cell_ref, const Record_gas_flat_pfx& data) const;
bool cell_pack_gas_flat_pfx(Ref<vm::Cell>& cell_ref, unsigned long long flat_gas_limit, unsigned long long flat_gas_price, Ref<CellSlice> other) const;
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override;
std::ostream& print_type(std::ostream& os) const override {
return os << "GasLimitsPrices";
@ -6214,6 +6225,15 @@ struct GasLimitsPrices::Record_gas_prices_ext {
Record_gas_prices_ext(unsigned long long _gas_price, unsigned long long _gas_limit, unsigned long long _special_gas_limit, unsigned long long _gas_credit, unsigned long long _block_gas_limit, unsigned long long _freeze_due_limit, unsigned long long _delete_due_limit) : gas_price(_gas_price), gas_limit(_gas_limit), special_gas_limit(_special_gas_limit), gas_credit(_gas_credit), block_gas_limit(_block_gas_limit), freeze_due_limit(_freeze_due_limit), delete_due_limit(_delete_due_limit) {}
};
struct GasLimitsPrices::Record_gas_flat_pfx {
typedef GasLimitsPrices type_class;
unsigned long long flat_gas_limit; // flat_gas_limit : uint64
unsigned long long flat_gas_price; // flat_gas_price : uint64
Ref<CellSlice> other; // other : GasLimitsPrices
Record_gas_flat_pfx() = default;
Record_gas_flat_pfx(unsigned long long _flat_gas_limit, unsigned long long _flat_gas_price, Ref<CellSlice> _other) : flat_gas_limit(_flat_gas_limit), flat_gas_price(_flat_gas_price), other(std::move(_other)) {}
};
extern const GasLimitsPrices t_GasLimitsPrices;
//
@ -6522,7 +6542,7 @@ extern const ValidatorSignedTempKey t_ValidatorSignedTempKey;
//
struct ConfigParam final : TLB_Complex {
enum { cons32, cons33, cons34, cons35, cons36, cons37, config_mc_block_limits, config_block_limits, cons14, cons0, cons1, cons2, cons3, cons4, cons6, cons7, cons9, cons12, cons15, cons16, cons17, cons18, cons31, cons39, cons28, cons8, cons29, config_mc_gas_prices, config_gas_prices, config_mc_fwd_prices, config_fwd_prices };
enum { cons32, cons33, cons34, cons35, cons36, cons37, config_mc_block_limits, config_block_limits, cons14, cons0, cons1, cons2, cons3, cons4, cons6, cons7, cons9, cons12, cons15, cons16, cons17, cons18, cons31, cons39, cons28, cons8, config_mc_gas_prices, config_gas_prices, cons29, config_mc_fwd_prices, config_fwd_prices };
static constexpr int cons_len_exact = 0;
int m_;
ConfigParam(int m) : m_(m) {}

View File

@ -1143,6 +1143,20 @@ bool TrStoragePhase::validate_skip(vm::CellSlice& cs, bool weak) const {
&& t_AccStatusChange.validate_skip(cs, weak); // status_change:AccStatusChange
}
bool TrStoragePhase::get_storage_fees(vm::CellSlice& cs, td::RefInt256& storage_fees) const {
return t_Grams.as_integer_skip_to(cs, storage_fees); // storage_fees_collected:Grams
}
bool TrStoragePhase::maybe_get_storage_fees(vm::CellSlice& cs, td::RefInt256& storage_fees) const {
auto z = cs.fetch_ulong(1);
if (!z) {
storage_fees = td::make_refint(0);
return true;
} else {
return z == 1 && get_storage_fees(cs, storage_fees);
}
}
const TrStoragePhase t_TrStoragePhase;
bool TrCreditPhase::skip(vm::CellSlice& cs) const {
@ -1322,13 +1336,14 @@ bool TransactionDescr::skip(vm::CellSlice& cs) const {
&& t_TrStoragePhase.skip(cs); // storage_ph:TrStoragePhase
case trans_tick_tock:
return cs.advance(4) // trans_tick_tock$001 is_tock:Bool
&& t_TrStoragePhase.skip(cs) // storage:TrStoragePhase
&& t_TrStoragePhase.skip(cs) // storage_ph:TrStoragePhase
&& t_TrComputePhase.skip(cs) // compute_ph:TrComputePhase
&& Maybe<RefTo<TrActionPhase>>{}.skip(cs) // action:(Maybe ^TrActionPhase)
&& cs.advance(2); // aborted:Bool destroyed:Bool
case trans_split_prepare:
return cs.advance(4) // trans_split_prepare$0100
&& t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo
&& Maybe<TrStoragePhase>{}.skip(cs) // storage_ph:(Maybe TrStoragePhase)
&& t_TrComputePhase.skip(cs) // compute_ph:TrComputePhase
&& Maybe<RefTo<TrActionPhase>>{}.skip(cs) // action:(Maybe ^TrActionPhase)
&& cs.advance(2); // aborted:Bool destroyed:Bool
@ -1346,6 +1361,7 @@ bool TransactionDescr::skip(vm::CellSlice& cs) const {
return cs.advance(4) // trans_merge_install$0111
&& t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo
&& t_Ref_Transaction.skip(cs) // prepare_transaction:^Transaction
&& Maybe<TrStoragePhase>{}.skip(cs) // storage_ph:(Maybe TrStoragePhase)
&& Maybe<TrCreditPhase>{}.skip(cs) // credit_ph:(Maybe TrCreditPhase)
&& Maybe<TrComputePhase>{}.skip(cs) // compute_ph:TrComputePhase
&& Maybe<RefTo<TrActionPhase>>{}.skip(cs) // action:(Maybe ^TrActionPhase)
@ -1370,13 +1386,14 @@ bool TransactionDescr::validate_skip(vm::CellSlice& cs, bool weak) const {
&& t_TrStoragePhase.validate_skip(cs, weak); // storage_ph:TrStoragePhase
case trans_tick_tock:
return cs.advance(4) // trans_tick_tock$001 is_tock:Bool
&& t_TrStoragePhase.validate_skip(cs, weak) // storage:TrStoragePhase
&& t_TrStoragePhase.validate_skip(cs, weak) // storage_ph:TrStoragePhase
&& t_TrComputePhase.validate_skip(cs, weak) // compute_ph:TrComputePhase
&& Maybe<RefTo<TrActionPhase>>{}.validate_skip(cs, weak) // action:(Maybe ^TrActionPhase)
&& cs.advance(2); // aborted:Bool destroyed:Bool
case trans_split_prepare:
return cs.advance(4) // trans_split_prepare$0100
&& t_SplitMergeInfo.validate_skip(cs, weak) // split_info:SplitMergeInfo
&& Maybe<TrStoragePhase>{}.validate_skip(cs, weak) // storage_ph:(Maybe TrStoragePhase)
&& t_TrComputePhase.validate_skip(cs, weak) // compute_ph:TrComputePhase
&& Maybe<RefTo<TrActionPhase>>{}.validate_skip(cs, weak) // action:(Maybe ^TrActionPhase)
&& cs.advance(2); // aborted:Bool destroyed:Bool
@ -1394,6 +1411,7 @@ bool TransactionDescr::validate_skip(vm::CellSlice& cs, bool weak) const {
return cs.advance(4) // trans_merge_install$0111
&& t_SplitMergeInfo.validate_skip(cs, weak) // split_info:SplitMergeInfo
&& t_Ref_Transaction.validate_skip(cs, weak) // prepare_transaction:^Transaction
&& Maybe<TrStoragePhase>{}.validate_skip(cs, weak) // storage_ph:(Maybe TrStoragePhase)
&& Maybe<TrCreditPhase>{}.validate_skip(cs, weak) // credit_ph:(Maybe TrCreditPhase)
&& Maybe<TrComputePhase>{}.validate_skip(cs, weak) // compute_ph:TrComputePhase
&& Maybe<RefTo<TrActionPhase>>{}.validate_skip(cs, weak) // action:(Maybe ^TrActionPhase)
@ -1407,6 +1425,53 @@ int TransactionDescr::get_tag(const vm::CellSlice& cs) const {
return (t >= 0 && t <= 7) ? (t == 3 ? 2 : t) : -1;
}
bool TransactionDescr::skip_to_storage_phase(vm::CellSlice& cs, bool& found) const {
found = false;
switch (get_tag(cs)) {
case trans_ord:
return cs.advance(4 + 1) // trans_ord$0000 storage_first:Bool
&& cs.fetch_bool_to(found); // storage_ph:(Maybe TrStoragePhase)
case trans_storage:
return cs.advance(4) // trans_storage$0001
&& (found = true); // storage_ph:TrStoragePhase
case trans_tick_tock:
return cs.advance(4) // trans_tick_tock$001 is_tock:Bool
&& (found = true); // storage_ph:TrStoragePhase
case trans_split_prepare:
return cs.advance(4) // trans_split_prepare$0100
&& t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo
&& cs.fetch_bool_to(found); // storage_ph:(Maybe TrStoragePhase)
case trans_split_install:
return true;
case trans_merge_prepare:
return cs.advance(4) // trans_merge_prepare$0110
&& t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo
&& (found = true); // storage_ph:TrStoragePhase
case trans_merge_install:
return cs.advance(4) // trans_merge_install$0111
&& t_SplitMergeInfo.skip(cs) // split_info:SplitMergeInfo
&& t_Ref_Transaction.skip(cs) // prepare_transaction:^Transaction
&& cs.fetch_bool_to(found); // storage_ph:(Maybe TrStoragePhase)
}
return false;
}
bool TransactionDescr::get_storage_fees(Ref<vm::Cell> cell, td::RefInt256& storage_fees) const {
if (cell.is_null()) {
return false;
}
auto cs = vm::load_cell_slice(std::move(cell));
bool found;
if (!skip_to_storage_phase(cs, found)) {
return false;
} else if (found) {
return t_TrStoragePhase.get_storage_fees(cs, storage_fees);
} else {
storage_fees = td::make_refint(0);
return true;
}
}
const TransactionDescr t_TransactionDescr;
bool Transaction_aux::skip(vm::CellSlice& cs) const {
@ -1447,6 +1512,32 @@ bool Transaction::validate_skip(vm::CellSlice& cs, bool weak) const {
&& RefTo<TransactionDescr>{}.validate_skip(cs, weak); // description:^TransactionDescr
}
bool Transaction::get_storage_fees(Ref<vm::Cell> cell, td::RefInt256& storage_fees) const {
Ref<vm::Cell> tdescr;
return get_descr(std::move(cell), tdescr) && t_TransactionDescr.get_storage_fees(std::move(tdescr), storage_fees);
}
bool Transaction::get_descr(Ref<vm::Cell> cell, Ref<vm::Cell>& tdescr) const {
if (cell.is_null()) {
return false;
} else {
auto cs = vm::load_cell_slice(std::move(cell));
return cs.is_valid() && get_descr(cs, tdescr) && cs.empty_ext();
}
}
bool Transaction::get_descr(vm::CellSlice& cs, Ref<vm::Cell>& tdescr) const {
return cs.advance(
4 + 256 + 64 + 256 + 64 + 32 +
15) // transaction$0111 account_addr:uint256 lt:uint64 prev_trans_hash:bits256 prev_trans_lt:uint64 now:uint32 outmsg_cnt:uint15
&& t_AccountStatus.skip(cs) // orig_status:AccountStatus
&& t_AccountStatus.skip(cs) // end_status:AccountStatus
&& cs.advance_refs(1) // ^[ in_msg:(Maybe ^Message) out_msgs:(HashmapE 15 ^Message) ]
&& t_CurrencyCollection.skip(cs) // total_fees:CurrencyCollection
&& cs.advance_refs(1) // state_update:^(MERKLE_UPDATE Account)
&& cs.fetch_ref_to(tdescr); // description:^TransactionDescr
}
bool Transaction::get_total_fees(vm::CellSlice&& cs, block::CurrencyCollection& total_fees) const {
return cs.is_valid() && cs.fetch_ulong(4) == 7 // transaction$0111
&&

View File

@ -614,6 +614,8 @@ extern const AccStatusChange t_AccStatusChange;
struct TrStoragePhase final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override;
bool get_storage_fees(vm::CellSlice& cs, td::RefInt256& storage_fees) const;
bool maybe_get_storage_fees(vm::CellSlice& cs, td::RefInt256& storage_fees) const;
};
extern const TrStoragePhase t_TrStoragePhase;
@ -693,6 +695,8 @@ struct TransactionDescr final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override;
int get_tag(const vm::CellSlice& cs) const override;
bool skip_to_storage_phase(vm::CellSlice& cs, bool& found) const;
bool get_storage_fees(Ref<vm::Cell> cell, td::RefInt256& storage_fees) const;
};
extern const TransactionDescr t_TransactionDescr;
@ -708,6 +712,9 @@ struct Transaction final : TLB_Complex {
bool skip(vm::CellSlice& cs) const override;
bool validate_skip(vm::CellSlice& cs, bool weak = false) const override;
bool get_total_fees(vm::CellSlice&& cs, block::CurrencyCollection& total_fees) const;
bool get_descr(Ref<vm::Cell> cell, Ref<vm::Cell>& tdescr) const;
bool get_descr(vm::CellSlice& cs, Ref<vm::Cell>& tdescr) const;
bool get_storage_fees(Ref<vm::Cell> cell, td::RefInt256& storage_fees) const;
};
extern const Transaction t_Transaction;

View File

@ -368,14 +368,14 @@ std::unique_ptr<MsgProcessedUptoCollection> MsgProcessedUptoCollection::unpack(t
return v && v->valid ? std::move(v) : std::unique_ptr<MsgProcessedUptoCollection>{};
}
bool MsgProcessedUpto::contains(const MsgProcessedUpto& other) const& {
bool MsgProcessedUpto::contains(const MsgProcessedUpto& other) const & {
return ton::shard_is_ancestor(shard, other.shard) && mc_seqno >= other.mc_seqno &&
(last_inmsg_lt > other.last_inmsg_lt ||
(last_inmsg_lt == other.last_inmsg_lt && !(last_inmsg_hash < other.last_inmsg_hash)));
}
bool MsgProcessedUpto::contains(ton::ShardId other_shard, ton::LogicalTime other_lt, td::ConstBitPtr other_hash,
ton::BlockSeqno other_mc_seqno) const& {
ton::BlockSeqno other_mc_seqno) const & {
return ton::shard_is_ancestor(shard, other_shard) && mc_seqno >= other_mc_seqno &&
(last_inmsg_lt > other_lt || (last_inmsg_lt == other_lt && !(last_inmsg_hash < other_hash)));
}
@ -552,7 +552,9 @@ bool MsgProcessedUpto::already_processed(const EnqueuedMsgDescr& msg) const {
if (msg.lt_ == last_inmsg_lt && last_inmsg_hash < msg.hash_) {
return false;
}
if (ton::shard_contains(shard, msg.cur_prefix_.account_id_prefix)) {
if (msg.same_workchain() && ton::shard_contains(shard, msg.cur_prefix_.account_id_prefix)) {
// this branch is needed only for messages generated in the same shard
// (such messages could have been processed without a reference from the masterchain)
// ? enable this branch only if an extra boolean parameter is set ?
return true;
}

View File

@ -145,6 +145,9 @@ struct EnqueuedMsgDescr {
return false;
}
bool unpack(vm::CellSlice& cs);
bool same_workchain() const {
return cur_prefix_.workchain == next_prefix_.workchain;
}
};
using compute_shard_end_lt_func_t = std::function<ton::LogicalTime(ton::AccountIdPrefixFull)>;

View File

@ -325,7 +325,7 @@ trans_ord$0000 credit_first:Bool
trans_storage$0001 storage_ph:TrStoragePhase
= TransactionDescr;
trans_tick_tock$001 is_tock:Bool storage:TrStoragePhase
trans_tick_tock$001 is_tock:Bool storage_ph:TrStoragePhase
compute_ph:TrComputePhase action:(Maybe ^TrActionPhase)
aborted:Bool destroyed:Bool = TransactionDescr;
//
@ -333,6 +333,7 @@ split_merge_info$_ cur_shard_pfx_len:(## 6)
acc_split_depth:(## 6) this_addr:bits256 sibling_addr:bits256
= SplitMergeInfo;
trans_split_prepare$0100 split_info:SplitMergeInfo
storage_ph:(Maybe TrStoragePhase)
compute_ph:TrComputePhase action:(Maybe ^TrActionPhase)
aborted:Bool destroyed:Bool
= TransactionDescr;
@ -345,6 +346,7 @@ trans_merge_prepare$0110 split_info:SplitMergeInfo
= TransactionDescr;
trans_merge_install$0111 split_info:SplitMergeInfo
prepare_transaction:^Transaction
storage_ph:(Maybe TrStoragePhase)
credit_ph:(Maybe TrCreditPhase)
compute_ph:TrComputePhase action:(Maybe ^TrActionPhase)
aborted:Bool destroyed:Bool
@ -609,6 +611,9 @@ gas_prices_ext#de gas_price:uint64 gas_limit:uint64 special_gas_limit:uint64 gas
block_gas_limit:uint64 freeze_due_limit:uint64 delete_due_limit:uint64
= GasLimitsPrices;
gas_flat_pfx#d1 flat_gas_limit:uint64 flat_gas_price:uint64 other:GasLimitsPrices
= GasLimitsPrices;
config_mc_gas_prices#_ GasLimitsPrices = ConfigParam 20;
config_gas_prices#_ GasLimitsPrices = ConfigParam 21;

View File

@ -672,9 +672,56 @@ bool Transaction::prepare_credit_phase() {
return true;
}
bool ComputePhaseConfig::parse_GasLimitsPrices(Ref<vm::Cell> cell, td::RefInt256& freeze_due_limit,
td::RefInt256& delete_due_limit) {
return cell.not_null() &&
parse_GasLimitsPrices(vm::load_cell_slice_ref(std::move(cell)), freeze_due_limit, delete_due_limit);
}
bool ComputePhaseConfig::parse_GasLimitsPrices(Ref<vm::CellSlice> cs, td::RefInt256& freeze_due_limit,
td::RefInt256& delete_due_limit) {
if (cs.is_null()) {
return false;
}
block::gen::GasLimitsPrices::Record_gas_flat_pfx flat;
if (tlb::csr_unpack(cs, flat)) {
bool ok = parse_GasLimitsPrices(std::move(flat.other), freeze_due_limit, delete_due_limit);
flat_gas_limit = flat.flat_gas_limit;
flat_gas_price = flat.flat_gas_price;
return ok;
}
flat_gas_limit = flat_gas_price = 0;
auto f = [&](const auto& r, td::uint64 spec_limit) {
gas_limit = r.gas_limit;
special_gas_limit = spec_limit;
gas_credit = r.gas_credit;
gas_price = r.gas_price;
freeze_due_limit = td::RefInt256{true, r.freeze_due_limit};
delete_due_limit = td::RefInt256{true, r.delete_due_limit};
};
block::gen::GasLimitsPrices::Record_gas_prices_ext rec;
if (tlb::csr_unpack(cs, rec)) {
f(rec, rec.special_gas_limit);
} else {
block::gen::GasLimitsPrices::Record_gas_prices rec0;
if (tlb::csr_unpack(std::move(cs), rec0)) {
f(rec0, rec0.gas_limit);
} else {
return false;
}
}
compute_threshold();
return true;
}
void ComputePhaseConfig::compute_threshold() {
gas_price256 = td::RefInt256{true, gas_price};
max_gas_threshold = td::rshift(gas_price256 * gas_limit, 16, 1);
if (gas_limit > flat_gas_limit) {
max_gas_threshold =
td::rshift(gas_price256 * (gas_limit - flat_gas_limit), 16, 1) + td::make_refint(flat_gas_price);
} else {
max_gas_threshold = td::make_refint(flat_gas_price);
}
}
td::uint64 ComputePhaseConfig::gas_bought_for(td::RefInt256 nanograms) const {
@ -684,8 +731,11 @@ td::uint64 ComputePhaseConfig::gas_bought_for(td::RefInt256 nanograms) const {
if (nanograms >= max_gas_threshold) {
return gas_limit;
}
auto res = td::div(std::move(nanograms) << 16, gas_price256);
return res->to_long();
if (nanograms < flat_gas_price) {
return 0;
}
auto res = td::div((std::move(nanograms) - flat_gas_price) << 16, gas_price256);
return res->to_long() + flat_gas_limit;
}
td::RefInt256 ComputePhaseConfig::compute_gas_price(td::uint64 gas_used) const {
@ -855,6 +905,16 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
cp.skip_reason = ComputePhase::sk_no_gas;
return true;
}
// Compute gas limits
if (!compute_gas_limits(cp, cfg)) {
compute_phase.reset();
return false;
}
if (!cp.gas_limit && !cp.gas_credit) {
// no gas
cp.skip_reason = ComputePhase::sk_no_gas;
return true;
}
if (in_msg_state.not_null()) {
LOG(DEBUG) << "HASH(in_msg_state) = " << in_msg_state->get_hash().bits().to_hex(256)
<< ", account_state_hash = " << account.state_hash.to_hex();
@ -883,11 +943,6 @@ bool Transaction::prepare_compute_phase(const ComputePhaseConfig& cfg) {
} else if (in_msg_state.not_null()) {
unpack_msg_state(true); // use only libraries
}
// Compute gas limits
if (!compute_gas_limits(cp, cfg)) {
compute_phase.reset();
return false;
}
// initialize VM
Ref<vm::Stack> stack = prepare_vm_stack(cp);
if (stack.is_null()) {

View File

@ -98,6 +98,8 @@ struct ComputePhaseConfig {
td::uint64 gas_limit;
td::uint64 special_gas_limit;
td::uint64 gas_credit;
td::uint64 flat_gas_limit = 0;
td::uint64 flat_gas_price = 0;
static constexpr td::uint64 gas_infty = (1ULL << 63) - 1;
td::RefInt256 gas_price256;
td::RefInt256 max_gas_threshold;
@ -126,6 +128,8 @@ struct ComputePhaseConfig {
Ref<vm::Cell> get_lib_root() const {
return libraries ? libraries->get_root_cell() : Ref<vm::Cell>{};
}
bool parse_GasLimitsPrices(Ref<vm::CellSlice> cs, td::RefInt256& freeze_due_limit, td::RefInt256& delete_due_limit);
bool parse_GasLimitsPrices(Ref<vm::Cell> cell, td::RefInt256& freeze_due_limit, td::RefInt256& delete_due_limit);
};
// msg_fwd_fees = (lump_price + ceil((bit_price * msg.bits + cell_price * msg.cells)/2^16)) nanograms

View File

@ -79,7 +79,7 @@ class SourceLookup {
if (os_time_) {
return os_time_->now();
}
return static_cast<td::uint32>(td::Time::now());
return static_cast<td::uint32>(td::Clocks::system());
}
protected:

View File

@ -25,7 +25,7 @@
{ bl word 1 { -rot 2 'nop does swap 0 (create) }
} :: 2=:
{ <b swap s, b> } : s>c
{ s>c hash } : shash
{ s>c hashB } : shash
// to be more efficiently re-implemented in C++ in the future
{ dup 0< ' negate if } : abs
{ 2dup > ' swap if } : minmax

View File

@ -764,13 +764,17 @@ void interpret_string_to_bytes(vm::Stack& stack) {
stack.push_bytes(stack.pop_string());
}
void interpret_bytes_hash(vm::Stack& stack) {
void interpret_bytes_hash(vm::Stack& stack, bool as_uint) {
std::string str = stack.pop_bytes();
unsigned char buffer[32];
digest::hash_str<digest::SHA256>(buffer, str.c_str(), str.size());
td::RefInt256 x{true};
x.write().import_bytes(buffer, 32, false);
stack.push_int(std::move(x));
if (as_uint) {
td::RefInt256 x{true};
x.write().import_bytes(buffer, 32, false);
stack.push_int(std::move(x));
} else {
stack.push_bytes(std::string{(char*)buffer, 32});
}
}
void interpret_empty(vm::Stack& stack) {
@ -892,11 +896,15 @@ void interpret_builder_remaining_bitrefs(vm::Stack& stack, int mode) {
}
}
void interpret_cell_hash(vm::Stack& stack) {
void interpret_cell_hash(vm::Stack& stack, bool as_uint) {
auto cell = stack.pop_cell();
td::RefInt256 hash{true};
hash.write().import_bytes(cell->get_hash().as_slice().ubegin(), 32, false);
stack.push_int(std::move(hash));
if (as_uint) {
td::RefInt256 hash{true};
hash.write().import_bytes(cell->get_hash().as_slice().ubegin(), 32, false);
stack.push_int(std::move(hash));
} else {
stack.push_bytes(cell->get_hash().as_slice().str());
}
}
void interpret_store_ref(vm::Stack& stack) {
@ -934,7 +942,9 @@ void interpret_fetch(vm::Stack& stack, int mode) {
auto n = stack.pop_smallint_range(256 + (mode & 1));
auto cs = stack.pop_cellslice();
if (!cs->have(n)) {
stack.push(std::move(cs));
if (mode & 2) {
stack.push(std::move(cs));
}
stack.push_bool(false);
if (!(mode & 4)) {
throw IntError{"end of data while reading integer from cell"};
@ -959,7 +969,9 @@ void interpret_fetch_bytes(vm::Stack& stack, int mode) {
unsigned n = stack.pop_smallint_range(127);
auto cs = stack.pop_cellslice();
if (!cs->have(n * 8)) {
stack.push(std::move(cs));
if (mode & 2) {
stack.push(std::move(cs));
}
stack.push_bool(false);
if (!(mode & 4)) {
throw IntError{"end of data while reading byte string from cell"};
@ -970,7 +982,7 @@ void interpret_fetch_bytes(vm::Stack& stack, int mode) {
if (mode & 2) {
cs.write().fetch_bytes(tmp, n);
} else {
cs.write().prefetch_bytes(tmp, n);
cs->prefetch_bytes(tmp, n);
}
std::string s{tmp, tmp + n};
if (mode & 1) {
@ -978,7 +990,9 @@ void interpret_fetch_bytes(vm::Stack& stack, int mode) {
} else {
stack.push_string(std::move(s));
}
stack.push(std::move(cs));
if (mode & 2) {
stack.push(std::move(cs));
}
if (mode & 4) {
stack.push_bool(true);
}
@ -1009,13 +1023,15 @@ void interpret_cell_remaining(vm::Stack& stack) {
void interpret_fetch_ref(vm::Stack& stack, int mode) {
auto cs = stack.pop_cellslice();
if (!cs->have_refs(1)) {
stack.push(std::move(cs));
if (mode & 2) {
stack.push(std::move(cs));
}
stack.push_bool(false);
if (!(mode & 4)) {
throw IntError{"end of data while reading reference from cell"};
}
} else {
auto cell = (mode & 2) ? cs.write().fetch_ref() : cs.write().prefetch_ref();
auto cell = (mode & 2) ? cs.write().fetch_ref() : cs->prefetch_ref();
if (mode & 2) {
stack.push(std::move(cs));
}
@ -2474,7 +2490,9 @@ void init_words_common(Dictionary& d) {
d.def_stack_word("B>Lu@+ ", std::bind(interpret_bytes_fetch_int, _1, 0x12));
d.def_stack_word("B>Li@+ ", std::bind(interpret_bytes_fetch_int, _1, 0x13));
d.def_stack_word("$>B ", interpret_string_to_bytes);
d.def_stack_word("Bhash ", interpret_bytes_hash);
d.def_stack_word("Bhash ", std::bind(interpret_bytes_hash, _1, true));
d.def_stack_word("Bhashu ", std::bind(interpret_bytes_hash, _1, true));
d.def_stack_word("BhashB ", std::bind(interpret_bytes_hash, _1, false));
// cell manipulation (create, write and modify cells)
d.def_stack_word("<b ", interpret_empty);
d.def_stack_word("i, ", std::bind(interpret_store, _1, true));
@ -2496,7 +2514,9 @@ void init_words_common(Dictionary& d) {
d.def_stack_word("brembits ", std::bind(interpret_builder_remaining_bitrefs, _1, 1));
d.def_stack_word("bremrefs ", std::bind(interpret_builder_remaining_bitrefs, _1, 2));
d.def_stack_word("brembitrefs ", std::bind(interpret_builder_remaining_bitrefs, _1, 3));
d.def_stack_word("hash ", interpret_cell_hash);
d.def_stack_word("hash ", std::bind(interpret_cell_hash, _1, true));
d.def_stack_word("hashu ", std::bind(interpret_cell_hash, _1, true));
d.def_stack_word("hashB ", std::bind(interpret_cell_hash, _1, false));
// cellslice manipulation (read from cells)
d.def_stack_word("<s ", interpret_from_cell);
d.def_stack_word("i@ ", std::bind(interpret_fetch, _1, 1));

View File

@ -628,7 +628,7 @@ bool Op::generate_code_step(Stack& stack) {
stack.opt_show();
StackLayout layout1 = stack.vars();
bool next_empty = next->is_empty();
stack.o << (next_empty ? "WHILEEND:<{" : "WHILE:<{");
stack.o << "WHILE:<{";
stack.o.indent();
stack.forget_const();
block0->generate_code_all(stack);
@ -638,7 +638,7 @@ bool Op::generate_code_step(Stack& stack) {
stack.modified();
stack.o.undent();
Stack stack_copy{stack};
stack.o << (next_empty ? "}>" : "}>DO<{");
stack.o << (next_empty ? "}>DO:" : "}>DO<{");
if (!next_empty) {
stack.o.indent();
}

View File

@ -169,6 +169,11 @@ bool SourceReader::load_line() {
error("line too long");
return false;
}
if (len && cur_line.back() == '\r') {
// CP/M line breaks support
cur_line.pop_back();
--len;
}
loc.text = cur_line;
cur_line_len = (int)len;
loc.line_pos = 0;

View File

@ -15,10 +15,10 @@ cr ."initial basechain state is:" cr dup <s csr. cr
dup dup 31 boc+>B dup Bx. cr
dup "basestate0" +suffix +".boc" tuck B>file
."(Initial basechain state saved to file " type .")" cr
Bhash dup =: basestate0_fhash
Bhashu dup =: basestate0_fhash
."file hash=" dup x. space 256 u>B dup B>base64url type cr
"basestate0" +suffix +".fhash" B>file
hash dup =: basestate0_rhash
hashu dup =: basestate0_rhash
."root hash=" dup x. space 256 u>B dup B>base64url type cr
"basestate0" +suffix +".rhash" B>file
@ -227,10 +227,10 @@ cr cr ."new state is:" cr dup <s csr. cr
dup 31 boc+>B dup Bx. cr
dup "zerostate" +suffix +".boc" tuck B>file
."(Initial masterchain state saved to file " type .")" cr
Bhash dup =: zerostate_fhash
Bhashu dup =: zerostate_fhash
."file hash=" dup x. space 256 u>B dup B>base64url type cr
"zerostate" +suffix +".fhash" B>file
hash dup =: zerostate_rhash ."root hash=" dup x. space 256 u>B dup B>base64url type cr
hashu dup =: zerostate_rhash ."root hash=" dup x. space 256 u>B dup B>base64url type cr
"zerostate" +suffix +".rhash" B>file
basestate0_rhash ."Basestate0 root hash=" dup x. space 256 u>B B>base64url type cr
basestate0_fhash ."Basestate0 file hash=" dup x. space 256 u>B B>base64url type cr

View File

@ -15,10 +15,10 @@ cr ."initial basechain state is:" cr dup <s csr. cr
dup dup 31 boc+>B dup Bx. cr
dup "basestate0" +suffix +".boc" tuck B>file
."(Initial basechain state saved to file " type .")" cr
Bhash dup =: basestate0_fhash
Bhashu dup =: basestate0_fhash
."file hash=" dup x. space 256 u>B dup B>base64url type cr
"basestate0" +suffix +".fhash" B>file
hash dup =: basestate0_rhash
hashu dup =: basestate0_rhash
."root hash=" dup x. space 256 u>B dup B>base64url type cr
"basestate0" +suffix +".rhash" B>file
@ -231,10 +231,10 @@ cr cr ."new state is:" cr dup <s csr. cr
dup 31 boc+>B dup Bx. cr
dup "zerostate" +suffix +".boc" tuck B>file
."(Initial masterchain state saved to file " type .")" cr
Bhash dup =: zerostate_fhash
Bhashu dup =: zerostate_fhash
."file hash= " dup X. space 256 u>B dup B>base64url type cr
"zerostate" +suffix +".fhash" B>file
hash dup =: zerostate_rhash ."root hash= " dup X. space 256 u>B dup B>base64url type cr
hashu dup =: zerostate_rhash ."root hash= " dup X. space 256 u>B dup B>base64url type cr
"zerostate" +suffix +".rhash" B>file
basestate0_rhash ."Basestate0 root hash= " dup X. space 256 u>B B>base64url type cr
basestate0_fhash ."Basestate0 file hash= " dup X. space 256 u>B B>base64url type cr

View File

@ -0,0 +1,41 @@
;; Heavy-duty wallet for mass transfers (e.g., for cryptocurrency exchanges)
;; accepts orders for up to 254 internal messages (transfers) in one external message
() recv_internal(slice in_msg) impure {
;; do nothing for internal messages
}
() recv_external(slice in_msg) impure {
var signature = in_msg~load_bits(512);
var cs = in_msg;
var (subwallet_id, valid_until, msg_seqno) = (cs~load_uint(32), cs~load_uint(32), cs~load_uint(32));
throw_if(35, valid_until <= now());
var ds = get_data().begin_parse();
var (stored_seqno, stored_subwallet, public_key) = (ds~load_uint(32), ds~load_uint(32), ds~load_uint(256));
ds.end_parse();
throw_unless(33, msg_seqno == stored_seqno);
throw_unless(34, subwallet_id == stored_subwallet);
throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key));
var dict = cs~load_dict();
cs.end_parse();
accept_message();
int i = -1;
do {
(i, var cs, var f) = dict.idict_get_next?(16, i);
if (f) {
var mode = cs~load_uint(8);
send_raw_message(cs~load_ref(), mode);
}
} until (~ f);
set_data(begin_cell()
.store_uint(stored_seqno + 1, 32)
.store_uint(stored_subwallet, 32)
.store_uint(public_key, 256)
.end_cell());
}
;; Get methods
int seqno() method_id {
return get_data().begin_parse().preload_uint(32);
}

View File

@ -0,0 +1,79 @@
// automatically generated from `smartcont/stdlib.fc` `smartcont/highload-wallet-code.fc`
PROGRAM{
DECLPROC recv_internal
DECLPROC recv_external
85143 DECLMETHOD seqno
recv_internal PROC:<{
// in_msg
DROP //
}>
recv_external PROC:<{
// in_msg
9 PUSHPOW2 // in_msg _3=512
LDSLICEX // signature in_msg
DUP // signature in_msg cs
32 LDU // signature in_msg _9 cs
32 LDU // signature in_msg _9 _12 cs
32 LDU // signature in_msg subwallet_id valid_until msg_seqno cs
NOW // signature in_msg subwallet_id valid_until msg_seqno cs _19
s1 s3 XCHG // signature in_msg subwallet_id cs msg_seqno valid_until _19
LEQ // signature in_msg subwallet_id cs msg_seqno _20
35 THROWIF
c4 PUSH // signature in_msg subwallet_id cs msg_seqno _23
CTOS // signature in_msg subwallet_id cs msg_seqno ds
32 LDU // signature in_msg subwallet_id cs msg_seqno _28 ds
32 LDU // signature in_msg subwallet_id cs msg_seqno _28 _31 ds
256 LDU // signature in_msg subwallet_id cs msg_seqno stored_seqno stored_subwallet public_key ds
ENDS
s3 s2 XCPU // signature in_msg subwallet_id cs public_key stored_seqno stored_subwallet msg_seqno stored_seqno
EQUAL // signature in_msg subwallet_id cs public_key stored_seqno stored_subwallet _39
33 THROWIFNOT
s4 s4 XCPU // signature in_msg stored_subwallet cs public_key stored_seqno subwallet_id stored_subwallet
EQUAL // signature in_msg stored_subwallet cs public_key stored_seqno _42
34 THROWIFNOT
s0 s4 XCHG // signature stored_seqno stored_subwallet cs public_key in_msg
HASHSU // signature stored_seqno stored_subwallet cs public_key _45
s0 s5 s5 XC2PU // public_key stored_seqno stored_subwallet cs _45 signature public_key
CHKSIGNU // public_key stored_seqno stored_subwallet cs _46
35 THROWIFNOT
LDDICT // public_key stored_seqno stored_subwallet dict cs
ENDS
ACCEPT
-1 PUSHINT // public_key stored_seqno stored_subwallet dict i=-1
UNTIL:<{
OVER
16 PUSHINT // public_key stored_seqno stored_subwallet dict i dict _57=16
DICTIGETNEXT
NULLSWAPIFNOT
NULLSWAPIFNOT // public_key stored_seqno stored_subwallet dict cs i f
DUP // public_key stored_seqno stored_subwallet dict cs i f f
IF:<{ // public_key stored_seqno stored_subwallet dict cs i f
s0 s2 XCHG // public_key stored_seqno stored_subwallet dict f i cs
8 LDU // public_key stored_seqno stored_subwallet dict f i mode cs
LDREF // public_key stored_seqno stored_subwallet dict f i mode _100 _99
DROP // public_key stored_seqno stored_subwallet dict f i mode _63
SWAP // public_key stored_seqno stored_subwallet dict f i _63 mode
SENDRAWMSG
}>ELSE<{
s2 POP // public_key stored_seqno stored_subwallet dict f i
}>
SWAP // public_key stored_seqno stored_subwallet dict i f
NOT // public_key stored_seqno stored_subwallet dict i _66
}> // public_key stored_seqno stored_subwallet dict i
2DROP // public_key stored_seqno stored_subwallet
SWAP // public_key stored_subwallet stored_seqno
INC // public_key stored_subwallet _68
NEWC // public_key stored_subwallet _68 _69
32 STU // public_key stored_subwallet _71
32 STU // public_key _73
256 STU // _75
ENDC // _76
c4 POP
}>
seqno PROC:<{
//
c4 PUSH // _0
CTOS // _1
32 PLDU // _3
}>
}END>c

View File

@ -0,0 +1,65 @@
;; Heavy-duty wallet for mass transfers (e.g., for cryptocurrency exchanges)
;; accepts orders for up to 254 internal messages (transfers) in one external message
;; this version does not use seqno for replay protection; instead, it remembers all recent query_ids
;; in this way several external messages with different query_id can be sent in parallel
() recv_internal(slice in_msg) impure {
;; do nothing for internal messages
}
() recv_external(slice in_msg) impure {
var signature = in_msg~load_bits(512);
var cs = in_msg;
var (subwallet_id, query_id) = (cs~load_uint(32), cs~load_uint(64));
var bound = (now() << 32);
throw_if(35, query_id < bound);
var ds = get_data().begin_parse();
var (stored_subwallet, last_cleaned, public_key, old_queries) = (ds~load_uint(32), ds~load_uint(64), ds~load_uint(256), ds~load_dict());
ds.end_parse();
(_, var found?) = old_queries.udict_get?(64, query_id);
throw_if(32, found?);
throw_unless(34, subwallet_id == stored_subwallet);
throw_unless(35, check_signature(slice_hash(in_msg), signature, public_key));
var dict = cs~load_dict();
cs.end_parse();
accept_message();
int i = -1;
do {
(i, var cs, var f) = dict.idict_get_next?(16, i);
if (f) {
var mode = cs~load_uint(8);
send_raw_message(cs~load_ref(), mode);
}
} until (~ f);
bound -= (64 << 32); ;; clean up records expired more than 64 seconds ago
old_queries~udict_set_builder(64, query_id, begin_cell());
var queries = old_queries;
do {
var (old_queries', i, _, f) = old_queries.udict_delete_get_min(64);
f~touch();
if (f) {
f = (i < bound);
}
if (f) {
old_queries = old_queries';
last_cleaned = i;
}
} until (~ f);
set_data(begin_cell()
.store_uint(stored_subwallet, 32)
.store_uint(last_cleaned, 64)
.store_uint(public_key, 256)
.store_dict(old_queries)
.end_cell());
}
;; Get methods
;; returns -1 for processed queries, 0 for unprocessed, 1 for unknown (forgotten)
int processed?(int query_id) method_id {
var ds = get_data().begin_parse();
var (_, last_cleaned, _, old_queries) = (ds~load_uint(32), ds~load_uint(64), ds~load_uint(256), ds~load_dict());
ds.end_parse();
(_, var found) = old_queries.udict_get?(64, query_id);
return found ? true : - (query_id <= last_cleaned);
}

View File

@ -0,0 +1,134 @@
// automatically generated from `smartcont/stdlib.fc` `smartcont/highload-wallet-v2-code.fc`
PROGRAM{
DECLPROC recv_internal
DECLPROC recv_external
117746 DECLMETHOD processed?
recv_internal PROC:<{
// in_msg
DROP //
}>
recv_external PROC:<{
// in_msg
9 PUSHPOW2 // in_msg _3=512
LDSLICEX // signature in_msg
DUP // signature in_msg cs
32 LDU // signature in_msg _8 cs
64 LDU // signature in_msg subwallet_id query_id cs
NOW // signature in_msg subwallet_id query_id cs _15
32 LSHIFT# // signature in_msg subwallet_id query_id cs bound
s2 s0 PUSH2 // signature in_msg subwallet_id query_id cs bound query_id bound
LESS // signature in_msg subwallet_id query_id cs bound _19
35 THROWIF
c4 PUSH // signature in_msg subwallet_id query_id cs bound _22
CTOS // signature in_msg subwallet_id query_id cs bound ds
32 LDU // signature in_msg subwallet_id query_id cs bound _28 ds
64 LDU // signature in_msg subwallet_id query_id cs bound _28 _31 ds
256 LDU // signature in_msg subwallet_id query_id cs bound _28 _31 _34 ds
LDDICT // signature in_msg subwallet_id query_id cs bound stored_subwallet last_cleaned public_key old_queries ds
ENDS
s6 s0 PUSH2
64 PUSHINT // signature in_msg subwallet_id query_id cs bound stored_subwallet last_cleaned public_key old_queries query_id old_queries _42=64
DICTUGET
NULLSWAPIFNOT // signature in_msg subwallet_id query_id cs bound stored_subwallet last_cleaned public_key old_queries _115 _116
NIP // signature in_msg subwallet_id query_id cs bound stored_subwallet last_cleaned public_key old_queries found?
32 THROWIF
s7 s3 XCPU // signature in_msg old_queries query_id cs bound stored_subwallet last_cleaned public_key subwallet_id stored_subwallet
EQUAL // signature in_msg old_queries query_id cs bound stored_subwallet last_cleaned public_key _47
34 THROWIFNOT
s0 s7 XCHG // signature public_key old_queries query_id cs bound stored_subwallet last_cleaned in_msg
HASHSU // signature public_key old_queries query_id cs bound stored_subwallet last_cleaned _50
s0 s8 s7 XC2PU // last_cleaned public_key old_queries query_id cs bound stored_subwallet _50 signature public_key
CHKSIGNU // last_cleaned public_key old_queries query_id cs bound stored_subwallet _51
35 THROWIFNOT
s0 s2 XCHG // last_cleaned public_key old_queries query_id stored_subwallet bound cs
LDDICT // last_cleaned public_key old_queries query_id stored_subwallet bound dict cs
ENDS
ACCEPT
-1 PUSHINT // last_cleaned public_key old_queries query_id stored_subwallet bound dict i=-1
UNTIL:<{
OVER
16 PUSHINT // last_cleaned public_key old_queries query_id stored_subwallet bound dict i dict _62=16
DICTIGETNEXT
NULLSWAPIFNOT
NULLSWAPIFNOT // last_cleaned public_key old_queries query_id stored_subwallet bound dict cs i f
DUP // last_cleaned public_key old_queries query_id stored_subwallet bound dict cs i f f
IF:<{ // last_cleaned public_key old_queries query_id stored_subwallet bound dict cs i f
s0 s2 XCHG // last_cleaned public_key old_queries query_id stored_subwallet bound dict f i cs
8 LDU // last_cleaned public_key old_queries query_id stored_subwallet bound dict f i mode cs
LDREF // last_cleaned public_key old_queries query_id stored_subwallet bound dict f i mode _125 _124
DROP // last_cleaned public_key old_queries query_id stored_subwallet bound dict f i mode _68
SWAP // last_cleaned public_key old_queries query_id stored_subwallet bound dict f i _68 mode
SENDRAWMSG
}>ELSE<{
s2 POP // last_cleaned public_key old_queries query_id stored_subwallet bound dict f i
}>
SWAP // last_cleaned public_key old_queries query_id stored_subwallet bound dict i f
NOT // last_cleaned public_key old_queries query_id stored_subwallet bound dict i _71
}> // last_cleaned public_key old_queries query_id stored_subwallet bound dict i
2DROP // last_cleaned public_key old_queries query_id stored_subwallet bound
38 PUSHPOW2 // last_cleaned public_key old_queries query_id stored_subwallet bound _74
SUB // last_cleaned public_key old_queries query_id stored_subwallet bound
NEWC // last_cleaned public_key old_queries query_id stored_subwallet bound _77
s0 s3 s4 XCHG3
64 PUSHINT // last_cleaned public_key stored_subwallet bound _77 query_id old_queries _78=64
DICTUSETB // last_cleaned public_key stored_subwallet bound old_queries
UNTIL:<{
DUP
64 PUSHINT // last_cleaned public_key stored_subwallet bound old_queries old_queries _85=64
DICTUREMMIN
NULLSWAPIFNOT
NULLSWAPIFNOT // last_cleaned public_key stored_subwallet bound old_queries _126 _128 _127 _129
s2 POP // last_cleaned public_key stored_subwallet bound old_queries old_queries' f i
s1 s0 XCPU // last_cleaned public_key stored_subwallet bound old_queries old_queries' i f f
IF:<{ // last_cleaned public_key stored_subwallet bound old_queries old_queries' i f
DROP // last_cleaned public_key stored_subwallet bound old_queries old_queries' i
s0 s3 PUSH2 // last_cleaned public_key stored_subwallet bound old_queries old_queries' i i bound
LESS // last_cleaned public_key stored_subwallet bound old_queries old_queries' i f
}> // last_cleaned public_key stored_subwallet bound old_queries old_queries' i f
DUP // last_cleaned public_key stored_subwallet bound old_queries old_queries' i f f
IF:<{ // last_cleaned public_key stored_subwallet bound old_queries old_queries' i f
s3 POP
s6 POP // last_cleaned public_key stored_subwallet bound f old_queries
}>ELSE<{
s3 s1 s3 XCHG3
2DROP // last_cleaned public_key stored_subwallet bound f old_queries
}>
SWAP // last_cleaned public_key stored_subwallet bound old_queries f
NOT // last_cleaned public_key stored_subwallet bound old_queries _90
}> // last_cleaned public_key stored_subwallet bound old_queries
NIP // last_cleaned public_key stored_subwallet old_queries
NEWC // last_cleaned public_key stored_subwallet old_queries _91
s1 s2 XCHG // last_cleaned public_key old_queries stored_subwallet _91
32 STU // last_cleaned public_key old_queries _93
s1 s3 XCHG // old_queries public_key last_cleaned _93
64 STU // old_queries public_key _95
256 STU // old_queries _97
STDICT // _98
ENDC // _99
c4 POP
}>
processed? PROC:<{
// query_id
c4 PUSH // query_id _2
CTOS // query_id ds
32 LDU // query_id _29 _28
NIP // query_id ds
64 LDU // query_id _11 ds
256 LDU // query_id _11 _33 _32
NIP // query_id _11 ds
LDDICT // query_id last_cleaned old_queries ds
ENDS
s2 s(-1) PUXC
64 PUSHINT // query_id last_cleaned query_id old_queries _22=64
DICTUGET
NULLSWAPIFNOT // query_id last_cleaned _36 _37
NIP // query_id last_cleaned found
IF:<{ // query_id last_cleaned
2DROP //
TRUE // _24
}>ELSE<{ // query_id last_cleaned
LEQ // _26
NEGATE // _24
}>
}>
}END>c

View File

@ -0,0 +1,69 @@
#!/usr/bin/env fift -s
"TonUtil.fif" include
{ ."usage: " @' $0 type ." <filename-base> <subwallet-id> <order-file> [<savefile>]" cr
."Creates a request with up to 254 orders loaded from <order-file> to high-load v2 (sub)wallet created by new-highload-v2-wallet.fif, with private key loaded from file <filename-base>.pk "
."and address from <filename-base><subwallet-id>.addr, and saves it into <savefile>.boc ('wallet-query.boc' by default)" cr
."<order-file> is a text file with lines `SEND <dest-addr> <amount>`" cr 1 halt
} : usage
$# dup 3 < swap 4 > or ' usage if
$1 =: file-base
$2 parse-int dup 32 fits ' usage ifnot =: subwallet-id // parse subwallet-id
{ subwallet-id (.) $+ } : +subwallet
$3 =: order-file
def? $4 { @' $4 } { "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 +subwallet +".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
variable orders dictnew orders !
variable order# order# 0!
// c --
{ <s order# @ dup 254 >= abort"more than 254 orders"
orders @ 16 udict!+ not abort"cannot add order to dictionary"
orders ! order# 1+!
} : add-order
// b body -- b'
{ tuck <s 2dup s-fits? not rot over 1 i, -rot
{ drop swap ref, } { s, nip } cond
} : append-msg-body
// ng wc addr bounce body -- c
{ <b b{01} s, rot 1 i, b{000100} s, 2swap addr, rot Gram,
0 9 64 32 + + 1+ u, swap append-msg-body b>
} : create-int-msg
// ng wc addr bnc --
{ ."Transferring " 3 roll .GR ."to account "
-rot 2dup 4 pick 7 + .Addr ." = " .addr ." bounce=" . cr
} : .transfer
// addr$ ng -- c
{ swap parse-smc-addr // ng wc addr bnc
2over 2over .transfer
<b 0 32 u, b> create-int-msg
} : create-simple-transfer
// c m -- c'
{ <b swap 8 u, swap ref, b> } : create-order
// addr$ ng --
{ create-simple-transfer send-mode create-order add-order } : send
{ bl word bl word $>GR send } : SEND
// parse order file
order-file include
// create external message
now timeout + 32 << <b orders @ dict, b> hashu 32 1<<1- and + =: query_id
<b subwallet-id 32 i, query_id 64 u, orders @ dict, b>
dup ."signing message: " <s csr. cr
dup hashu wallet_pk ed25519_sign_uint
<b b{1000100} s, wallet_addr addr, 0 Gram, b{00} s,
swap B, swap <s s, b>
dup ."resulting external message: " <s csr. cr
2 boc+>B dup Bx. cr
."Query_id is " query_id dup . ."= 0x" X. cr
savefile +".boc" tuck B>file
."(Saved to file " type .")" cr

View File

@ -0,0 +1,68 @@
#!/usr/bin/env fift -s
"TonUtil.fif" include
{ ."usage: " @' $0 type ." <filename-base> <subwallet-id> <seqno> <order-file> [<savefile>]" cr
."Creates a request with up to 254 orders loaded from <order-file> to high-load (sub)wallet created by new-highload-wallet.fif, with private key loaded from file <filename-base>.pk "
."and address from <filename-base><subwallet-id>.addr, and saves it into <savefile>.boc ('wallet-query.boc' by default)" cr
."<order-file> is a text file with lines `SEND <dest-addr> <amount>`" cr 1 halt
} : usage
$# dup 4 < swap 5 > or ' usage if
$1 =: file-base
$2 parse-int dup 32 fits ' usage ifnot =: subwallet-id // parse subwallet-id
{ subwallet-id (.) $+ } : +subwallet
$3 parse-int =: seqno
$4 =: order-file
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 +subwallet +".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
variable orders dictnew orders !
variable order# order# 0!
// c --
{ <s order# @ dup 254 >= abort"more than 254 orders"
orders @ 16 udict!+ not abort"cannot add order to dictionary"
orders ! order# 1+!
} : add-order
// b body -- b'
{ tuck <s 2dup s-fits? not rot over 1 i, -rot
{ drop swap ref, } { s, nip } cond
} : append-msg-body
// ng wc addr bounce body -- c
{ <b b{01} s, rot 1 i, b{000100} s, 2swap addr, rot Gram,
0 9 64 32 + + 1+ u, swap append-msg-body b>
} : create-int-msg
// ng wc addr bnc --
{ ."Transferring " 3 roll .GR ."to account "
-rot 2dup 4 pick 7 + .Addr ." = " .addr ." bounce=" . cr
} : .transfer
// addr$ ng -- c
{ swap parse-smc-addr // ng wc addr bnc
2over 2over .transfer
<b 0 32 u, b> create-int-msg
} : create-simple-transfer
// c m -- c'
{ <b swap 8 u, swap ref, b> } : create-order
// addr$ ng --
{ create-simple-transfer send-mode create-order add-order } : send
{ bl word bl word $>GR send } : SEND
// parse order file
order-file include
// create external message
<b subwallet-id 32 i, now timeout + 32 u, seqno 32 u, orders @ dict, b>
dup ."signing message: " <s csr. cr
dup hashu wallet_pk ed25519_sign_uint
<b b{1000100} s, wallet_addr addr, 0 Gram, b{00} s,
swap B, swap <s s, b>
dup ."resulting external message: " <s csr. cr
2 boc+>B dup Bx. cr
savefile +".boc" tuck B>file
."(Saved to file " type .")" cr

View File

@ -0,0 +1,47 @@
#!/usr/bin/env fift -s
"TonUtil.fif" include
"Asm.fif" include
{ ."usage: " @' $0 type ." <workchain-id> <subwallet-id> [<filename-base>]" cr
."Creates a new v2 high-load wallet in the specified workchain, with the controlling private key saved to or loaded from <filename-base>.pk "
."('new-wallet.pk' by default)" cr
."<subwallet-id> is the 32-bit identifier of this subwallet among all controlled by the same private key" cr 1 halt
} : usage
$# 2- -2 and ' usage if
$1 parse-workchain-id =: wc // set workchain id from command line argument
$2 parse-int dup =: subwallet-id // parse subwallet-id
32 fits ' usage ifnot
{ subwallet-id (.) $+ } : +subwallet
def? $3 { @' $3 } { "new-wallet" } cond constant file-base
65536 constant timeout // init query times out in 65536 seconds
."Creating new v2 high-load wallet in workchain " wc .
."with subwallet id " subwallet-id . cr
// Create new high-load wallet; source code included from `highload-wallet-v2-code.fif`
"highload-wallet-v2-code.fif" include
// code
<b subwallet-id 32 i, 0 64 u,
file-base +".pk" load-generate-keypair
constant wallet_pk
B, false 1 i,
b> // data
null // no libraries
<b b{0011} s, 3 roll ref, rot ref, swap dict, b> // create StateInit
dup ."StateInit: " <s csr. cr
dup hashu wc swap 2dup 2constant wallet_addr
."new wallet address = " 2dup .addr cr
2dup file-base +subwallet +".addr" save-address-verbose
."Non-bounceable address (for init): " 2dup 7 .Addr cr
."Bounceable address (for later access): " 6 .Addr cr
now timeout + 32 << 1- dup =: query_id
."Init query_id is " dup . ."(0x" X._ .")" cr
<b subwallet-id 32 i, query_id 64 u, false 1 i, b>
dup ."signing message: " <s csr. cr
dup hashu wallet_pk ed25519_sign_uint rot
<b b{1000100} s, wallet_addr addr, b{000010} s, swap <s s, b{0} s, swap B, swap <s s, b>
dup ."External message for initialization is " <s csr. cr
2 boc+>B dup Bx. cr
file-base +subwallet +"-query.boc" tuck B>file
."(Saved wallet creating query to file " type .")" cr

View File

@ -0,0 +1,44 @@
#!/usr/bin/env fift -s
"TonUtil.fif" include
"Asm.fif" include
{ ."usage: " @' $0 type ." <workchain-id> <subwallet-id> [<filename-base>]" cr
."Creates a new high-load wallet in the specified workchain, with the controlling private key saved to or loaded from <filename-base>.pk "
."('new-wallet.pk' by default)" cr
."<subwallet-id> is the 32-bit identifier of this subwallet among all controlled by the same private key" cr 1 halt
} : usage
$# 2- -2 and ' usage if
$1 parse-workchain-id =: wc // set workchain id from command line argument
$2 parse-int dup =: subwallet-id // parse subwallet-id
32 fits ' usage ifnot
{ subwallet-id (.) $+ } : +subwallet
def? $3 { @' $3 } { "new-wallet" } cond constant file-base
."Creating new high-load wallet in workchain " wc .
."with subwallet id " subwallet-id . cr
// Create new high-load wallet; source code included from `highload-wallet-code.fif`
"highload-wallet-code.fif" include
// code
<b 0 32 u, subwallet-id 32 i,
file-base +".pk" load-generate-keypair
constant wallet_pk
B,
b> // data
null // no libraries
<b b{0011} s, 3 roll ref, rot ref, swap dict, b> // create StateInit
dup ."StateInit: " <s csr. cr
dup hashu wc swap 2dup 2constant wallet_addr
."new wallet address = " 2dup .addr cr
2dup file-base +subwallet +".addr" save-address-verbose
."Non-bounceable address (for init): " 2dup 7 .Addr cr
."Bounceable address (for later access): " 6 .Addr cr
<b subwallet-id 32 i, -1 32 i, 0 32 u, false 1 i, b>
dup ."signing message: " <s csr. cr
dup hashu wallet_pk ed25519_sign_uint rot
<b b{1000100} s, wallet_addr addr, b{000010} s, swap <s s, b{0} s, swap B, swap <s s, b>
dup ."External message for initialization is " <s csr. cr
2 boc+>B dup Bx. cr
file-base +subwallet +"-query.boc" tuck B>file
."(Saved wallet creating query to file " type .")" cr

View File

@ -41,7 +41,7 @@ def? $3 { @' $3 } { "new-pinger" } cond constant file-base
// no libraries
<b b{00110} s, rot ref, swap ref, b> // create StateInit
dup ."StateInit: " <s csr. cr
dup hash wc swap 2dup 2constant pinger_addr
dup hashu wc swap 2dup 2constant pinger_addr
."new pinger address = " 2dup .addr cr
2dup file-base +".addr" save-address-verbose
."Non-bounceable address (for init): " 2dup 7 .Addr cr

View File

@ -37,7 +37,7 @@ def? $2 { @' $2 } { "new-testgiver" } cond constant file-base
null // no libraries
<b b{0011} s, 3 roll ref, rot ref, swap dict, b> // create StateInit
dup ."StateInit: " <s csr. cr
dup hash wc swap 2dup 2constant wallet_addr
dup hashu wc swap 2dup 2constant wallet_addr
."new money giver address = " 2dup .addr cr
2dup file-base +".addr" save-address-verbose
."Non-bounceable address (for init): " 2dup 7 .Addr cr

View File

@ -47,14 +47,14 @@ b> // data
null // no libraries
<b b{0011} s, 3 roll ref, rot ref, swap dict, b> // create StateInit
dup ."StateInit: " <s csr. cr
dup hash wc swap 2dup 2constant wallet_addr
dup hashu wc swap 2dup 2constant wallet_addr
."new wallet address = " 2dup .addr cr
2dup file-base +".addr" save-address-verbose
."Non-bounceable address (for init): " 2dup 7 .Addr cr
."Bounceable address (for later access): " 6 .Addr cr
<b 0 32 u, -1 32 i, b>
dup ."signing message: " <s csr. cr
dup hash wallet_pk ed25519_sign_uint rot
dup hashu wallet_pk ed25519_sign_uint rot
<b b{1000100} s, wallet_addr addr, b{000010} s, swap <s s, b{0} s, swap B, swap <s s, b>
dup ."External message for initialization is " <s csr. cr
2 boc+>B dup Bx. cr

View File

@ -47,14 +47,14 @@ null // no libraries
// Libs{ x{ABACABADABACABA} drop x{AAAA} s>c public_lib x{1234} x{5678} |_ s>c public_lib }Libs
<b b{0011} s, 3 roll ref, rot ref, swap dict, b> // create StateInit
dup ."StateInit: " <s csr. cr
dup hash wc swap 2dup 2constant wallet_addr
dup hashu wc swap 2dup 2constant wallet_addr
."new wallet address = " 2dup .addr cr
2dup file-base +".addr" save-address-verbose
."Non-bounceable address (for init): " 2dup 7 .Addr cr
."Bounceable address (for later access): " 6 .Addr cr
<b 0 32 u, b>
dup ."signing message: " <s csr. cr
dup hash wallet_pk ed25519_sign_uint rot
dup hashu wallet_pk ed25519_sign_uint rot
<b b{1000100} s, wallet_addr addr, b{000010} s, swap <s s, b{0} s, swap B, swap <s s, b>
dup ."External message for initialization is " <s csr. cr
2 boc+>B dup Bx. cr

View File

@ -86,6 +86,8 @@ cell dict_set_builder(cell dict, int key_len, slice index, builder value) asm(va
(cell, (slice, slice, int)) ~dict::delete_get_min(cell dict, int key_len) asm(-> 0 2 1 3) "DICTREMMIN" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
(int, slice, int) udict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETNEXT" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
(int, slice, int) udict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTUGETPREV" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
(int, slice, int) idict_get_next?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETNEXT" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
(int, slice, int) idict_get_prev?(cell dict, int key_len, int pivot) asm(pivot dict key_len -> 1 0 2) "DICTIGETPREV" "NULLSWAPIFNOT" "NULLSWAPIFNOT";
cell new_dict() asm "NEWDICT";
int dict_empty?(cell c) asm "DICTEMPTY";

View File

@ -6,7 +6,7 @@
."with private key loaded from file <filename-base>.pk, "
."and saves it into <savefile>.boc ('config-query.boc' by default)" cr 1 halt
} : usage
def? $# { @' $# dup 2 < swap 3 > or ' usage if } if
$# dup 2 < swap 3 > or ' usage if
"config-master" constant file-base
0 constant seqno
@ -15,18 +15,16 @@ true constant bounce
"config-code.fif" constant config-source
100 constant interval // valid for 100 seconds
def? $2 {
@' $1 =: file-base
@' $2 parse-int =: seqno
} if
def? $5 { @' $5 } { "config-query" } cond constant savefile
$1 =: file-base
$2 parse-int =: seqno
def? $3 { @' $3 } { "config-query" } cond constant savefile
file-base +".addr" load-address
2dup 2constant config_addr
."Configuration smart contract address = " 2dup .addr cr 6 .Addr cr
file-base +".pk" load-keypair nip constant config_pk
."Loading new configuration smart contract code from file file " config-source type cr
."Loading new configuration smart contract code from file " config-source type cr
"Asm.fif" include
config-source include
dup <s csr. cr
@ -34,7 +32,7 @@ dup <s csr. cr
// create a message
<b x{4e436f64} s, seqno 32 u, now interval + 32 u, swap ref, b>
dup ."signing message: " <s csr. cr
dup hash config_pk ed25519_sign_uint
dup hashu config_pk ed25519_sign_uint
<b b{1000100} s, config_addr addr, 0 Gram, b{00} s,
swap B, swap <s s, b>
dup ."resulting external message: " <s csr. cr

View File

@ -6,7 +6,7 @@
."with private key loaded from file <filename-base>.pk, "
."and saves it into <savefile>.boc ('config-query.boc' by default)" cr 1 halt
} : usage
def? $# { @' $# dup 4 < swap 5 > or ' usage if } if
$# dup 4 < swap 5 > or ' usage if
"config-master" constant file-base
0 constant seqno
@ -15,12 +15,10 @@ true constant bounce
"new-value.boc" constant boc-filename
100 constant interval // valid for 100 seconds
def? $4 {
@' $1 =: file-base
@' $2 parse-int =: seqno
@' $3 parse-int =: idx
@' $4 =: boc-filename
} if
$1 =: file-base
$2 parse-int =: seqno
$3 parse-int =: idx
$4 =: boc-filename
def? $5 { @' $5 } { "config-query" } cond constant savefile
file-base +".addr" load-address
@ -39,7 +37,7 @@ dup idx is-valid-config? not abort"not a valid value for chosen configuration pa
// create a message
<b x{43665021} s, seqno 32 u, now interval + 32 u, idx 32 i, swap ref, b>
dup ."signing message: " <s csr. cr
dup hash config_pk ed25519_sign_uint
dup hashu config_pk ed25519_sign_uint
<b b{1000100} s, config_addr addr, 0 Gram, b{00} s,
swap B, swap <s s, b>
dup ."resulting external message: " <s csr. cr

View File

@ -6,7 +6,7 @@
."with private key loaded from file <filename-base>.pk, "
."and saves it into <savefile>.boc ('config-query.boc' by default)" cr 1 halt
} : usage
def? $# { @' $# dup 2 < swap 3 > or ' usage if } if
$# dup 2 < swap 3 > or ' usage if
"config-master" constant file-base
0 constant seqno
@ -15,18 +15,16 @@ true constant bounce
"elector-code.fif" constant elector-source
100 constant interval // valid for 100 seconds
def? $2 {
@' $1 =: file-base
@' $2 parse-int =: seqno
} if
def? $5 { @' $5 } { "config-query" } cond constant savefile
$1 =: file-base
$2 parse-int =: seqno
def? $3 { @' $3 } { "config-query" } cond constant savefile
file-base +".addr" load-address
2dup 2constant config_addr
."Configuration smart contract address = " 2dup .addr cr 6 .Addr cr
file-base +".pk" load-keypair nip constant config_pk
."Loading new elector smart contract code from file file " elector-source type cr
."Loading new elector smart contract code from file " elector-source type cr
"Asm.fif" include
elector-source include
dup <s csr. cr
@ -34,7 +32,7 @@ dup <s csr. cr
// create a message
<b x{4e43ef05} s, seqno 32 u, now interval + 32 u, swap ref, b>
dup ."signing message: " <s csr. cr
dup hash config_pk ed25519_sign_uint
dup hashu config_pk ed25519_sign_uint
<b b{1000100} s, config_addr addr, 0 Gram, b{00} s,
swap B, swap <s s, b>
dup ."resulting external message: " <s csr. cr

View File

@ -38,7 +38,7 @@ dest_addr 2dup bounce 7 + .Addr ." = " .addr
b>
<b seqno 32 u, now timeout + 32 u, send-mode 8 u, swap ref, b>
dup ."signing message: " <s csr. cr
dup hash wallet_pk ed25519_sign_uint
dup hashu wallet_pk ed25519_sign_uint
<b b{1000100} s, wallet_addr addr, 0 Gram, b{00} s,
swap B, swap <s s, b>
dup ."resulting external message: " <s csr. cr

View File

@ -37,7 +37,7 @@ dest_addr 2dup bounce 7 + .Addr ." = " .addr
b>
<b seqno 32 u, send-mode 8 u, swap ref, b>
dup ."signing message: " <s csr. cr
dup hash wallet_pk ed25519_sign_uint
dup hashu wallet_pk ed25519_sign_uint
<b b{1000100} s, wallet_addr addr, 0 Gram, b{00} s,
swap B, swap <s s, b>
dup ."resulting external message: " <s csr. cr

View File

@ -745,6 +745,26 @@ Ref<Cell> CellSlice::fetch_ref() {
}
}
bool CellSlice::prefetch_maybe_ref(Ref<vm::Cell>& res) const {
auto z = prefetch_ulong(1);
if (!z) {
res.clear();
return true;
} else {
return z == 1 && prefetch_ref_to(res);
}
}
bool CellSlice::fetch_maybe_ref(Ref<vm::Cell>& res) {
auto z = prefetch_ulong(1);
if (!z) {
res.clear();
return advance(1);
} else {
return z == 1 && prefetch_ref_to(res) && advance_ext(1, 1);
}
}
bool CellSlice::begins_with(unsigned bits, unsigned long long value) const {
return have(bits) && !((prefetch_ulong(bits) ^ value) & ((1ULL << bits) - 1));
}

View File

@ -185,6 +185,8 @@ class CellSlice : public td::CntObject {
bool prefetch_ref_to(Ref<Cell>& ref, unsigned offset = 0) const {
return (ref = prefetch_ref(offset)).not_null();
}
bool fetch_maybe_ref(Ref<Cell>& ref);
bool prefetch_maybe_ref(Ref<Cell>& ref) const;
td::BitSlice fetch_bits(unsigned bits);
td::BitSlice prefetch_bits(unsigned bits) const;
td::Ref<CellSlice> fetch_subslice(unsigned bits, unsigned refs = 0);

View File

@ -267,16 +267,18 @@ MerkleProofBuilder::MerkleProofBuilder(Ref<Cell> root)
usage_root = UsageCell::create(orig_root, usage_tree->root_ptr());
}
void MerkleProofBuilder::reset(Ref<Cell> root) {
Ref<Cell> MerkleProofBuilder::init(Ref<Cell> root) {
usage_tree = std::make_shared<CellUsageTree>();
orig_root = std::move(root);
usage_root = UsageCell::create(orig_root, usage_tree->root_ptr());
return usage_root;
}
void MerkleProofBuilder::clear() {
bool MerkleProofBuilder::clear() {
usage_tree.reset();
orig_root.clear();
usage_root.clear();
return true;
}
Ref<Cell> MerkleProofBuilder::extract_proof() const {

View File

@ -51,9 +51,10 @@ class MerkleProofBuilder {
Ref<vm::Cell> orig_root, usage_root;
public:
MerkleProofBuilder() = default;
MerkleProofBuilder(Ref<Cell> root);
void reset(Ref<Cell> root);
void clear();
Ref<Cell> init(Ref<Cell> root);
bool clear();
Ref<Cell> root() const {
return usage_root;
}

View File

@ -914,8 +914,10 @@ bool TestNode::do_parse_line() {
return parse_block_id_ext(blkid) && parse_account_addr(workchain, addr) && parse_lt(lt) && seekeoln() &&
get_one_transaction(blkid, workchain, addr, lt, true);
} else if (word == "lasttrans" || word == "lasttransdump") {
return parse_account_addr(workchain, addr) && parse_lt(lt) && parse_hash(hash) && seekeoln() &&
get_last_transactions(workchain, addr, lt, hash, 10, word == "lasttransdump");
count = 10;
return parse_account_addr(workchain, addr) && parse_lt(lt) && parse_hash(hash) &&
(seekeoln() || parse_uint32(count)) && seekeoln() &&
get_last_transactions(workchain, addr, lt, hash, count, word == "lasttransdump");
} else if (word == "listblocktrans" || word == "listblocktransrev") {
lt = 0;
int mode = (word == "listblocktrans" ? 7 : 0x47);

View File

@ -364,7 +364,7 @@ inline void register_actor_info_ptr(core::ActorInfoPtr actor_info_ptr) {
}
template <class T, class... ArgsT>
core::ActorInfoPtr create_actor(core::ActorOptions &options, ArgsT &&... args) {
core::ActorInfoPtr create_actor(core::ActorOptions &options, ArgsT &&... args) noexcept {
auto *scheduler_context = core::SchedulerContext::get();
if (!options.has_scheduler()) {
options.on_scheduler(scheduler_context->get_scheduler_id());

View File

@ -75,7 +75,7 @@ void ActorExecutor::send(ActorSignals signals) {
pending_signals_.add_signals(signals);
}
void ActorExecutor::start() {
void ActorExecutor::start() noexcept {
//LOG(ERROR) << "START " << actor_info_.get_name() << " " << tag("from_queue", options.from_queue);
if (is_closed()) {
return;
@ -126,7 +126,7 @@ void ActorExecutor::start() {
}
}
void ActorExecutor::finish() {
void ActorExecutor::finish() noexcept {
//LOG(ERROR) << "FINISH " << actor_info_.get_name() << " " << tag("own_lock", actor_locker_.own_lock());
if (!actor_locker_.own_lock()) {
if (!pending_signals_.empty() && actor_locker_.add_signals(pending_signals_)) {

View File

@ -106,8 +106,8 @@ class ActorExecutor {
return flags_;
}
void start();
void finish();
void start() noexcept;
void finish() noexcept;
bool flush_one(ActorSignals &signals);
bool flush_one_signal(ActorSignals &signals);

View File

@ -50,6 +50,9 @@
#define TRY_RESULT(name, result) TRY_RESULT_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result)
#define TRY_RESULT_PROMISE(promise_name, name, result) \
TRY_RESULT_PROMISE_IMPL(promise_name, TD_CONCAT(TD_CONCAT(r_, name), __LINE__), auto name, result)
#define TRY_RESULT_ASSIGN(name, result) TRY_RESULT_IMPL(TD_CONCAT(TD_CONCAT(r_, name), __LINE__), name, result)
#define TRY_RESULT_PREFIX(name, result, prefix) \
@ -78,10 +81,18 @@
} \
name = r_name.move_as_ok();
#define TRY_RESULT_PROMISE_IMPL(promise_name, r_name, name, result) \
auto r_name = (result); \
if (r_name.is_error()) { \
promise_name.set_error(r_name.move_as_error()); \
return; \
} \
name = r_name.move_as_ok();
#define TRY_RESULT_PROMISE_PREFIX_IMPL(promise_name, r_name, name, result, prefix) \
auto r_name = (result); \
if (r_name.is_error()) { \
promise.set_error(r_name.move_as_error()); \
promise_name.set_error(r_name.move_as_error_prefix(prefix)); \
return; \
} \
name = r_name.move_as_ok();
@ -298,14 +309,31 @@ class Status {
return std::move(*this);
}
Status move_as_error_prefix(Slice prefix) TD_WARN_UNUSED_RESULT {
Status move_as_error_prefix(const Status &status) const TD_WARN_UNUSED_RESULT {
return status.move_as_error_suffix(message());
}
Status move_as_error_prefix(Slice prefix) const TD_WARN_UNUSED_RESULT {
CHECK(is_error());
Info info = get_info();
switch (info.error_type) {
case ErrorType::general:
return Error(code(), PSLICE() << prefix << message());
return Error(code(), PSLICE() << prefix << " " << message());
case ErrorType::os:
return Status(false, ErrorType::os, code(), PSLICE() << prefix << message());
return Status(false, ErrorType::os, code(), PSLICE() << prefix << " " << message());
default:
UNREACHABLE();
return {};
}
}
Status move_as_error_suffix(Slice suffix) const TD_WARN_UNUSED_RESULT {
CHECK(is_error());
Info info = get_info();
switch (info.error_type) {
case ErrorType::general:
return Error(code(), PSLICE() << message() << " " << suffix);
case ErrorType::os:
return Status(false, ErrorType::os, code(), PSLICE() << message() << " " << suffix);
default:
UNREACHABLE();
return {};
@ -488,6 +516,18 @@ class Result {
};
return status_.move_as_error_prefix(prefix);
}
Status move_as_error_prefix(const Status &prefix) TD_WARN_UNUSED_RESULT {
SCOPE_EXIT {
status_ = Status::Error<-5>();
};
return status_.move_as_error_prefix(prefix);
}
Status move_as_error_suffix(Slice suffix) TD_WARN_UNUSED_RESULT {
SCOPE_EXIT {
status_ = Status::Error<-5>();
};
return status_.move_as_error_suffix(suffix);
}
const T &ok() const {
LOG_CHECK(status_.is_ok()) << status_;
return value_;

View File

@ -32,10 +32,10 @@
#include <type_traits>
#include <unordered_set>
#define BEGIN_STORE_FLAGS() \
do { \
uint32 flags_store = 0; \
uint32 bit_offset_store = 0
#define BEGIN_STORE_FLAGS() \
do { \
td::uint32 flags_store = 0; \
td::uint32 bit_offset_store = 0
#define STORE_FLAG(flag) \
flags_store |= (flag) << bit_offset_store; \
@ -47,10 +47,10 @@
} \
while (false)
#define BEGIN_PARSE_FLAGS() \
do { \
uint32 flags_parse; \
uint32 bit_offset_parse = 0; \
#define BEGIN_PARSE_FLAGS() \
do { \
td::uint32 flags_parse; \
td::uint32 bit_offset_parse = 0; \
td::parse(flags_parse, parser)
#define PARSE_FLAG(flag) \

View File

@ -99,6 +99,16 @@ class TlParser {
}
}
bool can_prefetch_int() const {
return get_left_len() >= sizeof(int32);
}
int32 prefetch_int_unsafe() const {
int32 result;
std::memcpy(&result, data, sizeof(int32));
return result;
}
int32 fetch_int_unsafe() {
int32 result;
std::memcpy(&result, data, sizeof(int32));

View File

@ -244,6 +244,8 @@ object_ptr<Object> Object::fetch(td::TlParser &p) {
return db_state_destroyedSessions::fetch(p);
case db_state_gcBlockId::ID:
return db_state_gcBlockId::fetch(p);
case db_state_hardforks::ID:
return db_state_hardforks::fetch(p);
case db_state_initBlockId::ID:
return db_state_initBlockId::fetch(p);
case db_state_key_destroyedSessions::ID:
@ -256,6 +258,8 @@ object_ptr<Object> Object::fetch(td::TlParser &p) {
return db_state_key_shardClient::fetch(p);
case db_state_key_asyncSerializer::ID:
return db_state_key_asyncSerializer::fetch(p);
case db_state_key_hardforks::ID:
return db_state_key_hardforks::fetch(p);
case db_state_shardClient::ID:
return db_state_shardClient::fetch(p);
case dht_key::ID:
@ -498,6 +502,8 @@ object_ptr<Object> Object::fetch(td::TlParser &p) {
return tonNode_zeroStateIdExt::fetch(p);
case validator_group::ID:
return validator_group::fetch(p);
case validator_groupEx::ID:
return validator_groupEx::fetch(p);
case validator_config_global::ID:
return validator_config_global::fetch(p);
case validator_config_local::ID:
@ -5905,6 +5911,44 @@ void db_state_gcBlockId::store(td::TlStorerToString &s, const char *field_name)
}
}
db_state_hardforks::db_state_hardforks()
: blocks_()
{}
db_state_hardforks::db_state_hardforks(std::vector<object_ptr<tonNode_blockIdExt>> &&blocks_)
: blocks_(std::move(blocks_))
{}
const std::int32_t db_state_hardforks::ID;
object_ptr<db_state_hardforks> db_state_hardforks::fetch(td::TlParser &p) {
return make_object<db_state_hardforks>(p);
}
db_state_hardforks::db_state_hardforks(td::TlParser &p)
#define FAIL(error) p.set_error(error)
: blocks_(TlFetchVector<TlFetchObject<tonNode_blockIdExt>>::parse(p))
#undef FAIL
{}
void db_state_hardforks::store(td::TlStorerCalcLength &s) const {
(void)sizeof(s);
TlStoreVector<TlStoreObject>::store(blocks_, s);
}
void db_state_hardforks::store(td::TlStorerUnsafe &s) const {
(void)sizeof(s);
TlStoreVector<TlStoreObject>::store(blocks_, s);
}
void db_state_hardforks::store(td::TlStorerToString &s, const char *field_name) const {
if (!LOG_IS_STRIPPED(ERROR)) {
s.store_class_begin(field_name, "db_state_hardforks");
{ const std::vector<object_ptr<tonNode_blockIdExt>> &v = blocks_; const std::uint32_t multiplicity = static_cast<std::uint32_t>(v.size()); const auto vector_name = "vector[" + td::to_string(multiplicity)+ "]"; s.store_class_begin("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();
}
}
db_state_initBlockId::db_state_initBlockId()
: block_()
{}
@ -5957,6 +6001,8 @@ object_ptr<db_state_Key> db_state_Key::fetch(td::TlParser &p) {
return db_state_key_shardClient::fetch(p);
case db_state_key_asyncSerializer::ID:
return db_state_key_asyncSerializer::fetch(p);
case db_state_key_hardforks::ID:
return db_state_key_hardforks::fetch(p);
default:
FAIL(PSTRING() << "Unknown constructor found " << td::format::as_hex(constructor));
}
@ -6118,6 +6164,37 @@ void db_state_key_asyncSerializer::store(td::TlStorerToString &s, const char *fi
}
}
db_state_key_hardforks::db_state_key_hardforks() {
}
const std::int32_t db_state_key_hardforks::ID;
object_ptr<db_state_Key> db_state_key_hardforks::fetch(td::TlParser &p) {
return make_object<db_state_key_hardforks>(p);
}
db_state_key_hardforks::db_state_key_hardforks(td::TlParser &p)
#define FAIL(error) p.set_error(error)
#undef FAIL
{
(void)p;
}
void db_state_key_hardforks::store(td::TlStorerCalcLength &s) const {
(void)sizeof(s);
}
void db_state_key_hardforks::store(td::TlStorerUnsafe &s) const {
(void)sizeof(s);
}
void db_state_key_hardforks::store(td::TlStorerToString &s, const char *field_name) const {
if (!LOG_IS_STRIPPED(ERROR)) {
s.store_class_begin(field_name, "db_state_key_hardforks");
s.store_class_end();
}
}
db_state_shardClient::db_state_shardClient()
: block_()
{}
@ -11755,6 +11832,20 @@ void tonNode_zeroStateIdExt::store(td::TlStorerToString &s, const char *field_na
}
}
object_ptr<validator_Group> validator_Group::fetch(td::TlParser &p) {
#define FAIL(error) p.set_error(error); return nullptr;
int constructor = p.fetch_int();
switch (constructor) {
case validator_group::ID:
return validator_group::fetch(p);
case validator_groupEx::ID:
return validator_groupEx::fetch(p);
default:
FAIL(PSTRING() << "Unknown constructor found " << td::format::as_hex(constructor));
}
#undef FAIL
}
validator_group::validator_group()
: workchain_()
, shard_()
@ -11773,7 +11864,7 @@ validator_group::validator_group(std::int32_t workchain_, std::int64_t shard_, s
const std::int32_t validator_group::ID;
object_ptr<validator_group> validator_group::fetch(td::TlParser &p) {
object_ptr<validator_Group> validator_group::fetch(td::TlParser &p) {
return make_object<validator_group>(p);
}
@ -11817,6 +11908,74 @@ void validator_group::store(td::TlStorerToString &s, const char *field_name) con
}
}
validator_groupEx::validator_groupEx()
: workchain_()
, shard_()
, vertical_seqno_()
, catchain_seqno_()
, config_hash_()
, members_()
{}
validator_groupEx::validator_groupEx(std::int32_t workchain_, std::int64_t shard_, std::int32_t vertical_seqno_, std::int32_t catchain_seqno_, td::Bits256 const &config_hash_, std::vector<object_ptr<validator_groupMember>> &&members_)
: workchain_(workchain_)
, shard_(shard_)
, vertical_seqno_(vertical_seqno_)
, catchain_seqno_(catchain_seqno_)
, config_hash_(config_hash_)
, members_(std::move(members_))
{}
const std::int32_t validator_groupEx::ID;
object_ptr<validator_Group> validator_groupEx::fetch(td::TlParser &p) {
return make_object<validator_groupEx>(p);
}
validator_groupEx::validator_groupEx(td::TlParser &p)
#define FAIL(error) p.set_error(error)
: workchain_(TlFetchInt::parse(p))
, shard_(TlFetchLong::parse(p))
, vertical_seqno_(TlFetchInt::parse(p))
, catchain_seqno_(TlFetchInt::parse(p))
, config_hash_(TlFetchInt256::parse(p))
, members_(TlFetchVector<TlFetchObject<validator_groupMember>>::parse(p))
#undef FAIL
{}
void validator_groupEx::store(td::TlStorerCalcLength &s) const {
(void)sizeof(s);
TlStoreBinary::store(workchain_, s);
TlStoreBinary::store(shard_, s);
TlStoreBinary::store(vertical_seqno_, s);
TlStoreBinary::store(catchain_seqno_, s);
TlStoreBinary::store(config_hash_, s);
TlStoreVector<TlStoreObject>::store(members_, s);
}
void validator_groupEx::store(td::TlStorerUnsafe &s) const {
(void)sizeof(s);
TlStoreBinary::store(workchain_, s);
TlStoreBinary::store(shard_, s);
TlStoreBinary::store(vertical_seqno_, s);
TlStoreBinary::store(catchain_seqno_, s);
TlStoreBinary::store(config_hash_, s);
TlStoreVector<TlStoreObject>::store(members_, s);
}
void validator_groupEx::store(td::TlStorerToString &s, const char *field_name) const {
if (!LOG_IS_STRIPPED(ERROR)) {
s.store_class_begin(field_name, "validator_groupEx");
s.store_field("workchain", workchain_);
s.store_field("shard", shard_);
s.store_field("vertical_seqno", vertical_seqno_);
s.store_field("catchain_seqno", catchain_seqno_);
s.store_field("config_hash", config_hash_);
{ const std::vector<object_ptr<validator_groupMember>> &v = members_; const std::uint32_t multiplicity = static_cast<std::uint32_t>(v.size()); const auto vector_name = "vector[" + td::to_string(multiplicity)+ "]"; s.store_class_begin("members", 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();
}
}
validator_config_global::validator_config_global()
: zero_state_()
, init_block_()

View File

@ -162,6 +162,8 @@ class db_state_destroyedSessions;
class db_state_gcBlockId;
class db_state_hardforks;
class db_state_initBlockId;
class db_state_Key;
@ -344,7 +346,7 @@ class tonNode_success;
class tonNode_zeroStateIdExt;
class validator_group;
class validator_Group;
class validator_config_global;
@ -3215,6 +3217,30 @@ class db_state_gcBlockId final : public Object {
void store(td::TlStorerToString &s, const char *field_name) const final;
};
class db_state_hardforks final : public Object {
public:
std::vector<object_ptr<tonNode_blockIdExt>> blocks_;
db_state_hardforks();
explicit db_state_hardforks(std::vector<object_ptr<tonNode_blockIdExt>> &&blocks_);
static const std::int32_t ID = -2047668988;
std::int32_t get_id() const final {
return ID;
}
static object_ptr<db_state_hardforks> fetch(td::TlParser &p);
explicit db_state_hardforks(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 db_state_initBlockId final : public Object {
public:
object_ptr<tonNode_blockIdExt> block_;
@ -3350,6 +3376,27 @@ class db_state_key_asyncSerializer final : public db_state_Key {
void store(td::TlStorerToString &s, const char *field_name) const final;
};
class db_state_key_hardforks final : public db_state_Key {
public:
db_state_key_hardforks();
static const std::int32_t ID = -420206662;
std::int32_t get_id() const final {
return ID;
}
static object_ptr<db_state_Key> fetch(td::TlParser &p);
explicit db_state_key_hardforks(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 db_state_shardClient final : public Object {
public:
object_ptr<tonNode_blockIdExt> block_;
@ -6440,7 +6487,13 @@ class tonNode_zeroStateIdExt final : public Object {
void store(td::TlStorerToString &s, const char *field_name) const final;
};
class validator_group final : public Object {
class validator_Group: public Object {
public:
static object_ptr<validator_Group> fetch(td::TlParser &p);
};
class validator_group final : public validator_Group {
public:
std::int32_t workchain_;
std::int64_t shard_;
@ -6457,7 +6510,7 @@ class validator_group final : public Object {
return ID;
}
static object_ptr<validator_group> fetch(td::TlParser &p);
static object_ptr<validator_Group> fetch(td::TlParser &p);
explicit validator_group(td::TlParser &p);
@ -6468,6 +6521,35 @@ class validator_group final : public Object {
void store(td::TlStorerToString &s, const char *field_name) const final;
};
class validator_groupEx final : public validator_Group {
public:
std::int32_t workchain_;
std::int64_t shard_;
std::int32_t vertical_seqno_;
std::int32_t catchain_seqno_;
td::Bits256 config_hash_;
std::vector<object_ptr<validator_groupMember>> members_;
validator_groupEx();
validator_groupEx(std::int32_t workchain_, std::int64_t shard_, std::int32_t vertical_seqno_, std::int32_t catchain_seqno_, td::Bits256 const &config_hash_, std::vector<object_ptr<validator_groupMember>> &&members_);
static const std::int32_t ID = 479350270;
std::int32_t get_id() const final {
return ID;
}
static object_ptr<validator_Group> fetch(td::TlParser &p);
explicit validator_groupEx(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 validator_config_global final : public Object {
public:
object_ptr<tonNode_blockIdExt> zero_state_;

View File

@ -344,6 +344,9 @@ bool downcast_call(Object &obj, const T &func) {
case db_state_gcBlockId::ID:
func(static_cast<db_state_gcBlockId &>(obj));
return true;
case db_state_hardforks::ID:
func(static_cast<db_state_hardforks &>(obj));
return true;
case db_state_initBlockId::ID:
func(static_cast<db_state_initBlockId &>(obj));
return true;
@ -362,6 +365,9 @@ bool downcast_call(Object &obj, const T &func) {
case db_state_key_asyncSerializer::ID:
func(static_cast<db_state_key_asyncSerializer &>(obj));
return true;
case db_state_key_hardforks::ID:
func(static_cast<db_state_key_hardforks &>(obj));
return true;
case db_state_shardClient::ID:
func(static_cast<db_state_shardClient &>(obj));
return true;
@ -725,6 +731,9 @@ bool downcast_call(Object &obj, const T &func) {
case validator_group::ID:
func(static_cast<validator_group &>(obj));
return true;
case validator_groupEx::ID:
func(static_cast<validator_groupEx &>(obj));
return true;
case validator_config_global::ID:
func(static_cast<validator_config_global &>(obj));
return true;
@ -1470,6 +1479,9 @@ bool downcast_call(db_state_Key &obj, const T &func) {
case db_state_key_asyncSerializer::ID:
func(static_cast<db_state_key_asyncSerializer &>(obj));
return true;
case db_state_key_hardforks::ID:
func(static_cast<db_state_key_hardforks &>(obj));
return true;
default:
return false;
}
@ -1874,6 +1886,26 @@ bool downcast_call(tonNode_PreparedState &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 <class T>
bool downcast_call(validator_Group &obj, const T &func) {
switch (obj.get_id()) {
case validator_group::ID:
func(static_cast<validator_group &>(obj));
return true;
case validator_groupEx::ID:
func(static_cast<validator_groupEx &>(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.

View File

@ -226,7 +226,8 @@ Result<int32> tl_constructor_from_string(ton_api::db_state_Key *object, const st
{"db.state.key.initBlockId", 1971484899},
{"db.state.key.gcBlockId", -1015417890},
{"db.state.key.shardClient", -912576121},
{"db.state.key.asyncSerializer", 699304479}
{"db.state.key.asyncSerializer", 699304479},
{"db.state.key.hardforks", -420206662}
};
auto it = m.find(str);
if (it == m.end()) {
@ -445,6 +446,17 @@ Result<int32> tl_constructor_from_string(ton_api::tonNode_PreparedState *object,
}
return it->second;
}
Result<int32> tl_constructor_from_string(ton_api::validator_Group *object, const std::string &str) {
static const std::unordered_map<Slice, int32, SliceHash> m = {
{"validator.group", -120029535},
{"validator.groupEx", 479350270}
};
auto it = m.find(str);
if (it == m.end()) {
return Status::Error(str + "Unknown class");
}
return it->second;
}
Result<int32> tl_constructor_from_string(ton_api::validator_config_Local *object, const std::string &str) {
static const std::unordered_map<Slice, int32, SliceHash> m = {
{"validator.config.local", 1716256616},
@ -596,12 +608,14 @@ Result<int32> tl_constructor_from_string(ton_api::Object *object, const std::str
{"db.state.asyncSerializer", -751883871},
{"db.state.destroyedSessions", -1381443196},
{"db.state.gcBlockId", -550453937},
{"db.state.hardforks", -2047668988},
{"db.state.initBlockId", 1932303605},
{"db.state.key.destroyedSessions", -386404007},
{"db.state.key.initBlockId", 1971484899},
{"db.state.key.gcBlockId", -1015417890},
{"db.state.key.shardClient", -912576121},
{"db.state.key.asyncSerializer", 699304479},
{"db.state.key.hardforks", -420206662},
{"db.state.shardClient", 186033821},
{"dht.key", -160964977},
{"dht.keyDescription", 673009157},
@ -723,6 +737,7 @@ Result<int32> tl_constructor_from_string(ton_api::Object *object, const std::str
{"tonNode.success", -1063902129},
{"tonNode.zeroStateIdExt", 494024110},
{"validator.group", -120029535},
{"validator.groupEx", 479350270},
{"validator.config.global", -2038562966},
{"validator.config.local", 1716256616},
{"validator.config.random.local", 1501795426},
@ -2554,6 +2569,15 @@ Status from_json(ton_api::db_state_gcBlockId &to, JsonObject &from) {
}
return Status::OK();
}
Status from_json(ton_api::db_state_hardforks &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::db_state_initBlockId &to, JsonObject &from) {
{
TRY_RESULT(value, get_json_object_field(from, "block", JsonValue::Type::Null, true));
@ -2578,6 +2602,9 @@ Status from_json(ton_api::db_state_key_shardClient &to, JsonObject &from) {
Status from_json(ton_api::db_state_key_asyncSerializer &to, JsonObject &from) {
return Status::OK();
}
Status from_json(ton_api::db_state_key_hardforks &to, JsonObject &from) {
return Status::OK();
}
Status from_json(ton_api::db_state_shardClient &to, JsonObject &from) {
{
TRY_RESULT(value, get_json_object_field(from, "block", JsonValue::Type::Null, true));
@ -4507,6 +4534,45 @@ Status from_json(ton_api::validator_group &to, JsonObject &from) {
}
return Status::OK();
}
Status from_json(ton_api::validator_groupEx &to, JsonObject &from) {
{
TRY_RESULT(value, get_json_object_field(from, "workchain", JsonValue::Type::Null, true));
if (value.type() != JsonValue::Type::Null) {
TRY_STATUS(from_json(to.workchain_, value));
}
}
{
TRY_RESULT(value, get_json_object_field(from, "shard", JsonValue::Type::Null, true));
if (value.type() != JsonValue::Type::Null) {
TRY_STATUS(from_json(to.shard_, value));
}
}
{
TRY_RESULT(value, get_json_object_field(from, "vertical_seqno", JsonValue::Type::Null, true));
if (value.type() != JsonValue::Type::Null) {
TRY_STATUS(from_json(to.vertical_seqno_, value));
}
}
{
TRY_RESULT(value, get_json_object_field(from, "catchain_seqno", JsonValue::Type::Null, true));
if (value.type() != JsonValue::Type::Null) {
TRY_STATUS(from_json(to.catchain_seqno_, value));
}
}
{
TRY_RESULT(value, get_json_object_field(from, "config_hash", JsonValue::Type::Null, true));
if (value.type() != JsonValue::Type::Null) {
TRY_STATUS(from_json(to.config_hash_, value));
}
}
{
TRY_RESULT(value, get_json_object_field(from, "members", JsonValue::Type::Null, true));
if (value.type() != JsonValue::Type::Null) {
TRY_STATUS(from_json(to.members_, value));
}
}
return Status::OK();
}
Status from_json(ton_api::validator_config_global &to, JsonObject &from) {
{
TRY_RESULT(value, get_json_object_field(from, "zero_state", JsonValue::Type::Null, true));
@ -6631,6 +6697,11 @@ void to_json(JsonValueScope &jv, const ton_api::db_state_gcBlockId &object) {
jo << ctie("block", ToJson(object.block_));
}
}
void to_json(JsonValueScope &jv, const ton_api::db_state_hardforks &object) {
auto jo = jv.enter_object();
jo << ctie("@type", "db.state.hardforks");
jo << ctie("blocks", ToJson(object.blocks_));
}
void to_json(JsonValueScope &jv, const ton_api::db_state_initBlockId &object) {
auto jo = jv.enter_object();
jo << ctie("@type", "db.state.initBlockId");
@ -6661,6 +6732,10 @@ void to_json(JsonValueScope &jv, const ton_api::db_state_key_asyncSerializer &ob
auto jo = jv.enter_object();
jo << ctie("@type", "db.state.key.asyncSerializer");
}
void to_json(JsonValueScope &jv, const ton_api::db_state_key_hardforks &object) {
auto jo = jv.enter_object();
jo << ctie("@type", "db.state.key.hardforks");
}
void to_json(JsonValueScope &jv, const ton_api::db_state_shardClient &object) {
auto jo = jv.enter_object();
jo << ctie("@type", "db.state.shardClient");
@ -7535,6 +7610,9 @@ void to_json(JsonValueScope &jv, const ton_api::tonNode_zeroStateIdExt &object)
jo << ctie("root_hash", ToJson(object.root_hash_));
jo << ctie("file_hash", ToJson(object.file_hash_));
}
void to_json(JsonValueScope &jv, const ton_api::validator_Group &object) {
ton_api::downcast_call(const_cast<ton_api::validator_Group &>(object), [&jv](const auto &object) { to_json(jv, object); });
}
void to_json(JsonValueScope &jv, const ton_api::validator_group &object) {
auto jo = jv.enter_object();
jo << ctie("@type", "validator.group");
@ -7544,6 +7622,16 @@ void to_json(JsonValueScope &jv, const ton_api::validator_group &object) {
jo << ctie("config_hash", ToJson(object.config_hash_));
jo << ctie("members", ToJson(object.members_));
}
void to_json(JsonValueScope &jv, const ton_api::validator_groupEx &object) {
auto jo = jv.enter_object();
jo << ctie("@type", "validator.groupEx");
jo << ctie("workchain", ToJson(object.workchain_));
jo << ctie("shard", ToJson(JsonInt64{object.shard_}));
jo << ctie("vertical_seqno", ToJson(object.vertical_seqno_));
jo << ctie("catchain_seqno", ToJson(object.catchain_seqno_));
jo << ctie("config_hash", ToJson(object.config_hash_));
jo << ctie("members", ToJson(object.members_));
}
void to_json(JsonValueScope &jv, const ton_api::validator_config_global &object) {
auto jo = jv.enter_object();
jo << ctie("@type", "validator.config.global");

View File

@ -45,6 +45,7 @@ Result<int32> tl_constructor_from_string(ton_api::tonNode_DataFull *object, cons
Result<int32> tl_constructor_from_string(ton_api::tonNode_Prepared *object, const std::string &str);
Result<int32> tl_constructor_from_string(ton_api::tonNode_PreparedProof *object, const std::string &str);
Result<int32> tl_constructor_from_string(ton_api::tonNode_PreparedState *object, const std::string &str);
Result<int32> tl_constructor_from_string(ton_api::validator_Group *object, const std::string &str);
Result<int32> tl_constructor_from_string(ton_api::validator_config_Local *object, const std::string &str);
Result<int32> tl_constructor_from_string(ton_api::validatorSession_Message *object, const std::string &str);
Result<int32> tl_constructor_from_string(ton_api::validatorSession_round_Message *object, const std::string &str);
@ -160,12 +161,14 @@ Status from_json(ton_api::db_root_key_config &to, JsonObject &from);
Status from_json(ton_api::db_state_asyncSerializer &to, JsonObject &from);
Status from_json(ton_api::db_state_destroyedSessions &to, JsonObject &from);
Status from_json(ton_api::db_state_gcBlockId &to, JsonObject &from);
Status from_json(ton_api::db_state_hardforks &to, JsonObject &from);
Status from_json(ton_api::db_state_initBlockId &to, JsonObject &from);
Status from_json(ton_api::db_state_key_destroyedSessions &to, JsonObject &from);
Status from_json(ton_api::db_state_key_initBlockId &to, JsonObject &from);
Status from_json(ton_api::db_state_key_gcBlockId &to, JsonObject &from);
Status from_json(ton_api::db_state_key_shardClient &to, JsonObject &from);
Status from_json(ton_api::db_state_key_asyncSerializer &to, JsonObject &from);
Status from_json(ton_api::db_state_key_hardforks &to, JsonObject &from);
Status from_json(ton_api::db_state_shardClient &to, JsonObject &from);
Status from_json(ton_api::dht_key &to, JsonObject &from);
Status from_json(ton_api::dht_keyDescription &to, JsonObject &from);
@ -287,6 +290,7 @@ 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_groupEx &to, JsonObject &from);
Status from_json(ton_api::validator_config_global &to, JsonObject &from);
Status from_json(ton_api::validator_config_local &to, JsonObject &from);
Status from_json(ton_api::validator_config_random_local &to, JsonObject &from);
@ -505,6 +509,7 @@ void to_json(JsonValueScope &jv, const ton_api::db_root_key_config &object);
void to_json(JsonValueScope &jv, const ton_api::db_state_asyncSerializer &object);
void to_json(JsonValueScope &jv, const ton_api::db_state_destroyedSessions &object);
void to_json(JsonValueScope &jv, const ton_api::db_state_gcBlockId &object);
void to_json(JsonValueScope &jv, const ton_api::db_state_hardforks &object);
void to_json(JsonValueScope &jv, const ton_api::db_state_initBlockId &object);
void to_json(JsonValueScope &jv, const ton_api::db_state_Key &object);
void to_json(JsonValueScope &jv, const ton_api::db_state_key_destroyedSessions &object);
@ -512,6 +517,7 @@ void to_json(JsonValueScope &jv, const ton_api::db_state_key_initBlockId &object
void to_json(JsonValueScope &jv, const ton_api::db_state_key_gcBlockId &object);
void to_json(JsonValueScope &jv, const ton_api::db_state_key_shardClient &object);
void to_json(JsonValueScope &jv, const ton_api::db_state_key_asyncSerializer &object);
void to_json(JsonValueScope &jv, const ton_api::db_state_key_hardforks &object);
void to_json(JsonValueScope &jv, const ton_api::db_state_shardClient &object);
void to_json(JsonValueScope &jv, const ton_api::dht_key &object);
void to_json(JsonValueScope &jv, const ton_api::dht_keyDescription &object);
@ -650,7 +656,9 @@ 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_group &object);
void to_json(JsonValueScope &jv, const ton_api::validator_groupEx &object);
void to_json(JsonValueScope &jv, const ton_api::validator_config_global &object);
void to_json(JsonValueScope &jv, const ton_api::validator_config_Local &object);
void to_json(JsonValueScope &jv, const ton_api::validator_config_local &object);

View File

@ -199,6 +199,36 @@ void key::store(td::TlStorerToString &s, const char *field_name) const {
}
}
keyStoreTypeDirectory::keyStoreTypeDirectory()
: directory_()
{}
keyStoreTypeDirectory::keyStoreTypeDirectory(std::string const &directory_)
: directory_(std::move(directory_))
{}
const std::int32_t keyStoreTypeDirectory::ID;
void keyStoreTypeDirectory::store(td::TlStorerToString &s, const char *field_name) const {
if (!LOG_IS_STRIPPED(ERROR)) {
s.store_class_begin(field_name, "keyStoreTypeDirectory");
s.store_field("directory", directory_);
s.store_class_end();
}
}
keyStoreTypeInMemory::keyStoreTypeInMemory() {
}
const std::int32_t keyStoreTypeInMemory::ID;
void keyStoreTypeInMemory::store(td::TlStorerToString &s, const char *field_name) const {
if (!LOG_IS_STRIPPED(ERROR)) {
s.store_class_begin(field_name, "keyStoreTypeInMemory");
s.store_class_end();
}
}
logStreamDefault::logStreamDefault() {
}
@ -294,12 +324,12 @@ void ok::store(td::TlStorerToString &s, const char *field_name) const {
options::options()
: config_()
, keystore_directory_()
, keystore_type_()
{}
options::options(object_ptr<config> &&config_, std::string const &keystore_directory_)
options::options(object_ptr<config> &&config_, object_ptr<KeyStoreType> &&keystore_type_)
: config_(std::move(config_))
, keystore_directory_(std::move(keystore_directory_))
, keystore_type_(std::move(keystore_type_))
{}
const std::int32_t options::ID;
@ -308,17 +338,19 @@ void options::store(td::TlStorerToString &s, const char *field_name) const {
if (!LOG_IS_STRIPPED(ERROR)) {
s.store_class_begin(field_name, "options");
if (config_ == nullptr) { s.store_field("config", "null"); } else { config_->store(s, "config"); }
s.store_field("keystore_directory", keystore_directory_);
if (keystore_type_ == nullptr) { s.store_field("keystore_type", "null"); } else { keystore_type_->store(s, "keystore_type"); }
s.store_class_end();
}
}
sendGramsResult::sendGramsResult()
: sent_until_()
, body_hash_()
{}
sendGramsResult::sendGramsResult(std::int64_t sent_until_)
sendGramsResult::sendGramsResult(std::int64_t sent_until_, std::string const &body_hash_)
: sent_until_(sent_until_)
, body_hash_(std::move(body_hash_))
{}
const std::int32_t sendGramsResult::ID;
@ -327,6 +359,7 @@ void sendGramsResult::store(td::TlStorerToString &s, const char *field_name) con
if (!LOG_IS_STRIPPED(ERROR)) {
s.store_class_begin(field_name, "sendGramsResult");
s.store_field("sent_until", sent_until_);
s.store_bytes_field("body_hash", body_hash_);
s.store_class_end();
}
}
@ -545,13 +578,21 @@ raw_message::raw_message()
: source_()
, destination_()
, value_()
, fwd_fee_()
, ihr_fee_()
, created_lt_()
, body_hash_()
, message_()
{}
raw_message::raw_message(std::string const &source_, std::string const &destination_, std::int64_t value_, std::string const &message_)
raw_message::raw_message(std::string const &source_, std::string const &destination_, std::int64_t value_, std::int64_t fwd_fee_, std::int64_t ihr_fee_, std::int64_t created_lt_, std::string const &body_hash_, std::string const &message_)
: source_(std::move(source_))
, destination_(std::move(destination_))
, value_(value_)
, fwd_fee_(fwd_fee_)
, ihr_fee_(ihr_fee_)
, created_lt_(created_lt_)
, body_hash_(std::move(body_hash_))
, message_(std::move(message_))
{}
@ -563,6 +604,10 @@ void raw_message::store(td::TlStorerToString &s, const char *field_name) const {
s.store_field("source", source_);
s.store_field("destination", destination_);
s.store_field("value", value_);
s.store_field("fwd_fee", fwd_fee_);
s.store_field("ihr_fee", ihr_fee_);
s.store_field("created_lt", created_lt_);
s.store_bytes_field("body_hash", body_hash_);
s.store_bytes_field("message", message_);
s.store_class_end();
}
@ -573,15 +618,19 @@ raw_transaction::raw_transaction()
, data_()
, transaction_id_()
, fee_()
, storage_fee_()
, other_fee_()
, in_msg_()
, out_msgs_()
{}
raw_transaction::raw_transaction(std::int64_t utime_, std::string const &data_, object_ptr<internal_transactionId> &&transaction_id_, std::int64_t fee_, object_ptr<raw_message> &&in_msg_, std::vector<object_ptr<raw_message>> &&out_msgs_)
raw_transaction::raw_transaction(std::int64_t utime_, std::string const &data_, object_ptr<internal_transactionId> &&transaction_id_, std::int64_t fee_, std::int64_t storage_fee_, std::int64_t other_fee_, object_ptr<raw_message> &&in_msg_, std::vector<object_ptr<raw_message>> &&out_msgs_)
: utime_(utime_)
, data_(std::move(data_))
, transaction_id_(std::move(transaction_id_))
, fee_(fee_)
, storage_fee_(storage_fee_)
, other_fee_(other_fee_)
, in_msg_(std::move(in_msg_))
, out_msgs_(std::move(out_msgs_))
{}
@ -595,6 +644,8 @@ void raw_transaction::store(td::TlStorerToString &s, const char *field_name) con
s.store_bytes_field("data", data_);
if (transaction_id_ == nullptr) { s.store_field("transaction_id", "null"); } else { transaction_id_->store(s, "transaction_id"); }
s.store_field("fee", fee_);
s.store_field("storage_fee", storage_fee_);
s.store_field("other_fee", other_fee_);
if (in_msg_ == nullptr) { s.store_field("in_msg", "null"); } else { in_msg_->store(s, "in_msg"); }
{ const std::vector<object_ptr<raw_message>> &v = out_msgs_; const std::uint32_t multiplicity = static_cast<std::uint32_t>(v.size()); const auto vector_name = "vector[" + td::to_string(multiplicity)+ "]"; s.store_class_begin("out_msgs", 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();
@ -841,6 +892,18 @@ void createNewKey::store(td::TlStorerToString &s, const char *field_name) const
}
}
deleteAllKeys::deleteAllKeys() {
}
const std::int32_t deleteAllKeys::ID;
void deleteAllKeys::store(td::TlStorerToString &s, const char *field_name) const {
if (!LOG_IS_STRIPPED(ERROR)) {
s.store_class_begin(field_name, "deleteAllKeys");
s.store_class_end();
}
}
deleteKey::deleteKey()
: key_()
{}

View File

@ -62,6 +62,8 @@ class inputKey;
class key;
class KeyStoreType;
class LogStream;
class logTags;
@ -264,6 +266,39 @@ class key final : public Object {
void store(td::TlStorerToString &s, const char *field_name) const final;
};
class KeyStoreType: public Object {
public:
};
class keyStoreTypeDirectory final : public KeyStoreType {
public:
std::string directory_;
keyStoreTypeDirectory();
explicit keyStoreTypeDirectory(std::string const &directory_);
static const std::int32_t ID = -378990038;
std::int32_t get_id() const final {
return ID;
}
void store(td::TlStorerToString &s, const char *field_name) const final;
};
class keyStoreTypeInMemory final : public KeyStoreType {
public:
keyStoreTypeInMemory();
static const std::int32_t ID = -2106848825;
std::int32_t get_id() const final {
return ID;
}
void store(td::TlStorerToString &s, const char *field_name) const final;
};
class LogStream: public Object {
public:
};
@ -359,13 +394,13 @@ class ok final : public Object {
class options final : public Object {
public:
object_ptr<config> config_;
std::string keystore_directory_;
object_ptr<KeyStoreType> keystore_type_;
options();
options(object_ptr<config> &&config_, std::string const &keystore_directory_);
options(object_ptr<config> &&config_, object_ptr<KeyStoreType> &&keystore_type_);
static const std::int32_t ID = 789823302;
static const std::int32_t ID = -1924388359;
std::int32_t get_id() const final {
return ID;
}
@ -376,12 +411,13 @@ class options final : public Object {
class sendGramsResult final : public Object {
public:
std::int64_t sent_until_;
std::string body_hash_;
sendGramsResult();
explicit sendGramsResult(std::int64_t sent_until_);
sendGramsResult(std::int64_t sent_until_, std::string const &body_hash_);
static const std::int32_t ID = -858318471;
static const std::int32_t ID = 426872238;
std::int32_t get_id() const final {
return ID;
}
@ -568,13 +604,17 @@ class raw_message final : public Object {
std::string source_;
std::string destination_;
std::int64_t value_;
std::int64_t fwd_fee_;
std::int64_t ihr_fee_;
std::int64_t created_lt_;
std::string body_hash_;
std::string message_;
raw_message();
raw_message(std::string const &source_, std::string const &destination_, std::int64_t value_, std::string const &message_);
raw_message(std::string const &source_, std::string const &destination_, std::int64_t value_, std::int64_t fwd_fee_, std::int64_t ihr_fee_, std::int64_t created_lt_, std::string const &body_hash_, std::string const &message_);
static const std::int32_t ID = -259956097;
static const std::int32_t ID = -906281442;
std::int32_t get_id() const final {
return ID;
}
@ -588,14 +628,16 @@ class raw_transaction final : public Object {
std::string data_;
object_ptr<internal_transactionId> transaction_id_;
std::int64_t fee_;
std::int64_t storage_fee_;
std::int64_t other_fee_;
object_ptr<raw_message> in_msg_;
std::vector<object_ptr<raw_message>> out_msgs_;
raw_transaction();
raw_transaction(std::int64_t utime_, std::string const &data_, object_ptr<internal_transactionId> &&transaction_id_, std::int64_t fee_, object_ptr<raw_message> &&in_msg_, std::vector<object_ptr<raw_message>> &&out_msgs_);
raw_transaction(std::int64_t utime_, std::string const &data_, object_ptr<internal_transactionId> &&transaction_id_, std::int64_t fee_, std::int64_t storage_fee_, std::int64_t other_fee_, object_ptr<raw_message> &&in_msg_, std::vector<object_ptr<raw_message>> &&out_msgs_);
static const std::int32_t ID = -1159530820;
static const std::int32_t ID = 1887601793;
std::int32_t get_id() const final {
return ID;
}
@ -612,7 +654,7 @@ class raw_transactions final : public Object {
raw_transactions(std::vector<object_ptr<raw_transaction>> &&transactions_, object_ptr<internal_transactionId> &&previous_transaction_id_);
static const std::int32_t ID = 240548986;
static const std::int32_t ID = -2063931155;
std::int32_t get_id() const final {
return ID;
}
@ -800,6 +842,21 @@ class createNewKey final : public Function {
void store(td::TlStorerToString &s, const char *field_name) const final;
};
class deleteAllKeys final : public Function {
public:
deleteAllKeys();
static const std::int32_t ID = 1608776483;
std::int32_t get_id() const final {
return ID;
}
using ReturnType = object_ptr<ok>;
void store(td::TlStorerToString &s, const char *field_name) const final;
};
class deleteKey final : public Function {
public:
object_ptr<key> key_;

View File

@ -41,6 +41,12 @@ bool downcast_call(Object &obj, const T &func) {
case key::ID:
func(static_cast<key &>(obj));
return true;
case keyStoreTypeDirectory::ID:
func(static_cast<keyStoreTypeDirectory &>(obj));
return true;
case keyStoreTypeInMemory::ID:
func(static_cast<keyStoreTypeInMemory &>(obj));
return true;
case logStreamDefault::ID:
func(static_cast<logStreamDefault &>(obj));
return true;
@ -148,6 +154,9 @@ bool downcast_call(Function &obj, const T &func) {
case createNewKey::ID:
func(static_cast<createNewKey &>(obj));
return true;
case deleteAllKeys::ID:
func(static_cast<deleteAllKeys &>(obj));
return true;
case deleteKey::ID:
func(static_cast<deleteKey &>(obj));
return true;
@ -270,6 +279,26 @@ bool downcast_call(Function &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 <class T>
bool downcast_call(KeyStoreType &obj, const T &func) {
switch (obj.get_id()) {
case keyStoreTypeDirectory::ID:
func(static_cast<keyStoreTypeDirectory &>(obj));
return true;
case keyStoreTypeInMemory::ID:
func(static_cast<keyStoreTypeInMemory &>(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.

View File

@ -14,6 +14,17 @@
namespace ton {
namespace tonlib_api{
using namespace td;
Result<int32> tl_constructor_from_string(tonlib_api::KeyStoreType *object, const std::string &str) {
static const std::unordered_map<Slice, int32, SliceHash> m = {
{"keyStoreTypeDirectory", -378990038},
{"keyStoreTypeInMemory", -2106848825}
};
auto it = m.find(str);
if (it == m.end()) {
return Status::Error(str + "Unknown class");
}
return it->second;
}
Result<int32> tl_constructor_from_string(tonlib_api::LogStream *object, const std::string &str) {
static const std::unordered_map<Slice, int32, SliceHash> m = {
{"logStreamDefault", 1390581436},
@ -51,14 +62,16 @@ Result<int32> tl_constructor_from_string(tonlib_api::Object *object, const std::
{"exportedPemKey", 1425473725},
{"inputKey", 869287093},
{"key", -1978362923},
{"keyStoreTypeDirectory", -378990038},
{"keyStoreTypeInMemory", -2106848825},
{"logStreamDefault", 1390581436},
{"logStreamFile", -1880085930},
{"logStreamEmpty", -499912244},
{"logTags", -1604930601},
{"logVerbosityLevel", 1734624234},
{"ok", -722616727},
{"options", 789823302},
{"sendGramsResult", -858318471},
{"options", -1924388359},
{"sendGramsResult", 426872238},
{"unpackedAccountAddress", 1892946998},
{"updateSendLiteServerQuery", -1555130916},
{"generic.accountStateRaw", -1387096685},
@ -69,9 +82,9 @@ Result<int32> tl_constructor_from_string(tonlib_api::Object *object, const std::
{"internal.transactionId", -989527262},
{"raw.accountState", 461615898},
{"raw.initialAccountState", 777456197},
{"raw.message", -259956097},
{"raw.transaction", -1159530820},
{"raw.transactions", 240548986},
{"raw.message", -906281442},
{"raw.transaction", 1887601793},
{"raw.transactions", -2063931155},
{"testGiver.accountState", 860930426},
{"testWallet.accountState", 305698744},
{"testWallet.initialAccountState", -1231516227},
@ -91,6 +104,7 @@ Result<int32> tl_constructor_from_string(tonlib_api::Function *object, const std
{"changeLocalPassword", -1685491421},
{"close", -1187782273},
{"createNewKey", -1861385712},
{"deleteAllKeys", 1608776483},
{"deleteKey", -1579595571},
{"exportEncryptedKey", 155352861},
{"exportKey", 399723440},
@ -254,6 +268,18 @@ Status from_json(tonlib_api::key &to, JsonObject &from) {
}
return Status::OK();
}
Status from_json(tonlib_api::keyStoreTypeDirectory &to, JsonObject &from) {
{
TRY_RESULT(value, get_json_object_field(from, "directory", JsonValue::Type::Null, true));
if (value.type() != JsonValue::Type::Null) {
TRY_STATUS(from_json(to.directory_, value));
}
}
return Status::OK();
}
Status from_json(tonlib_api::keyStoreTypeInMemory &to, JsonObject &from) {
return Status::OK();
}
Status from_json(tonlib_api::logStreamDefault &to, JsonObject &from) {
return Status::OK();
}
@ -304,9 +330,9 @@ Status from_json(tonlib_api::options &to, JsonObject &from) {
}
}
{
TRY_RESULT(value, get_json_object_field(from, "keystore_directory", JsonValue::Type::Null, true));
TRY_RESULT(value, get_json_object_field(from, "keystore_type", JsonValue::Type::Null, true));
if (value.type() != JsonValue::Type::Null) {
TRY_STATUS(from_json(to.keystore_directory_, value));
TRY_STATUS(from_json(to.keystore_type_, value));
}
}
return Status::OK();
@ -318,6 +344,12 @@ Status from_json(tonlib_api::sendGramsResult &to, JsonObject &from) {
TRY_STATUS(from_json(to.sent_until_, value));
}
}
{
TRY_RESULT(value, get_json_object_field(from, "body_hash", JsonValue::Type::Null, true));
if (value.type() != JsonValue::Type::Null) {
TRY_STATUS(from_json_bytes(to.body_hash_, value));
}
}
return Status::OK();
}
Status from_json(tonlib_api::unpackedAccountAddress &to, JsonObject &from) {
@ -489,6 +521,30 @@ Status from_json(tonlib_api::raw_message &to, JsonObject &from) {
TRY_STATUS(from_json(to.value_, value));
}
}
{
TRY_RESULT(value, get_json_object_field(from, "fwd_fee", JsonValue::Type::Null, true));
if (value.type() != JsonValue::Type::Null) {
TRY_STATUS(from_json(to.fwd_fee_, value));
}
}
{
TRY_RESULT(value, get_json_object_field(from, "ihr_fee", JsonValue::Type::Null, true));
if (value.type() != JsonValue::Type::Null) {
TRY_STATUS(from_json(to.ihr_fee_, value));
}
}
{
TRY_RESULT(value, get_json_object_field(from, "created_lt", JsonValue::Type::Null, true));
if (value.type() != JsonValue::Type::Null) {
TRY_STATUS(from_json(to.created_lt_, value));
}
}
{
TRY_RESULT(value, get_json_object_field(from, "body_hash", JsonValue::Type::Null, true));
if (value.type() != JsonValue::Type::Null) {
TRY_STATUS(from_json_bytes(to.body_hash_, value));
}
}
{
TRY_RESULT(value, get_json_object_field(from, "message", JsonValue::Type::Null, true));
if (value.type() != JsonValue::Type::Null) {
@ -522,6 +578,18 @@ Status from_json(tonlib_api::raw_transaction &to, JsonObject &from) {
TRY_STATUS(from_json(to.fee_, value));
}
}
{
TRY_RESULT(value, get_json_object_field(from, "storage_fee", JsonValue::Type::Null, true));
if (value.type() != JsonValue::Type::Null) {
TRY_STATUS(from_json(to.storage_fee_, value));
}
}
{
TRY_RESULT(value, get_json_object_field(from, "other_fee", JsonValue::Type::Null, true));
if (value.type() != JsonValue::Type::Null) {
TRY_STATUS(from_json(to.other_fee_, value));
}
}
{
TRY_RESULT(value, get_json_object_field(from, "in_msg", JsonValue::Type::Null, true));
if (value.type() != JsonValue::Type::Null) {
@ -725,6 +793,9 @@ Status from_json(tonlib_api::createNewKey &to, JsonObject &from) {
}
return Status::OK();
}
Status from_json(tonlib_api::deleteAllKeys &to, JsonObject &from) {
return Status::OK();
}
Status from_json(tonlib_api::deleteKey &to, JsonObject &from) {
{
TRY_RESULT(value, get_json_object_field(from, "key", JsonValue::Type::Null, true));
@ -1291,6 +1362,18 @@ 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::KeyStoreType &object) {
tonlib_api::downcast_call(const_cast<tonlib_api::KeyStoreType &>(object), [&jv](const auto &object) { to_json(jv, object); });
}
void to_json(JsonValueScope &jv, const tonlib_api::keyStoreTypeDirectory &object) {
auto jo = jv.enter_object();
jo << ctie("@type", "keyStoreTypeDirectory");
jo << ctie("directory", ToJson(object.directory_));
}
void to_json(JsonValueScope &jv, const tonlib_api::keyStoreTypeInMemory &object) {
auto jo = jv.enter_object();
jo << ctie("@type", "keyStoreTypeInMemory");
}
void to_json(JsonValueScope &jv, const tonlib_api::LogStream &object) {
tonlib_api::downcast_call(const_cast<tonlib_api::LogStream &>(object), [&jv](const auto &object) { to_json(jv, object); });
}
@ -1328,12 +1411,15 @@ void to_json(JsonValueScope &jv, const tonlib_api::options &object) {
if (object.config_) {
jo << ctie("config", ToJson(object.config_));
}
jo << ctie("keystore_directory", ToJson(object.keystore_directory_));
if (object.keystore_type_) {
jo << ctie("keystore_type", ToJson(object.keystore_type_));
}
}
void to_json(JsonValueScope &jv, const tonlib_api::sendGramsResult &object) {
auto jo = jv.enter_object();
jo << ctie("@type", "sendGramsResult");
jo << ctie("sent_until", ToJson(object.sent_until_));
jo << ctie("body_hash", ToJson(JsonBytes{object.body_hash_}));
}
void to_json(JsonValueScope &jv, const tonlib_api::unpackedAccountAddress &object) {
auto jo = jv.enter_object();
@ -1416,6 +1502,10 @@ void to_json(JsonValueScope &jv, const tonlib_api::raw_message &object) {
jo << ctie("source", ToJson(object.source_));
jo << ctie("destination", ToJson(object.destination_));
jo << ctie("value", ToJson(JsonInt64{object.value_}));
jo << ctie("fwd_fee", ToJson(JsonInt64{object.fwd_fee_}));
jo << ctie("ihr_fee", ToJson(JsonInt64{object.ihr_fee_}));
jo << ctie("created_lt", ToJson(JsonInt64{object.created_lt_}));
jo << ctie("body_hash", ToJson(JsonBytes{object.body_hash_}));
jo << ctie("message", ToJson(JsonBytes{object.message_}));
}
void to_json(JsonValueScope &jv, const tonlib_api::raw_transaction &object) {
@ -1427,6 +1517,8 @@ void to_json(JsonValueScope &jv, const tonlib_api::raw_transaction &object) {
jo << ctie("transaction_id", ToJson(object.transaction_id_));
}
jo << ctie("fee", ToJson(JsonInt64{object.fee_}));
jo << ctie("storage_fee", ToJson(JsonInt64{object.storage_fee_}));
jo << ctie("other_fee", ToJson(JsonInt64{object.other_fee_}));
if (object.in_msg_) {
jo << ctie("in_msg", ToJson(object.in_msg_));
}
@ -1514,6 +1606,10 @@ void to_json(JsonValueScope &jv, const tonlib_api::createNewKey &object) {
jo << ctie("mnemonic_password", ToJson(JsonBytes{object.mnemonic_password_}));
jo << ctie("random_extra_seed", ToJson(JsonBytes{object.random_extra_seed_}));
}
void to_json(JsonValueScope &jv, const tonlib_api::deleteAllKeys &object) {
auto jo = jv.enter_object();
jo << ctie("@type", "deleteAllKeys");
}
void to_json(JsonValueScope &jv, const tonlib_api::deleteKey &object) {
auto jo = jv.enter_object();
jo << ctie("@type", "deleteKey");

View File

@ -11,6 +11,7 @@
namespace ton {
namespace tonlib_api{
using namespace td;
Result<int32> tl_constructor_from_string(tonlib_api::KeyStoreType *object, const std::string &str);
Result<int32> tl_constructor_from_string(tonlib_api::LogStream *object, const std::string &str);
Result<int32> tl_constructor_from_string(tonlib_api::generic_AccountState *object, const std::string &str);
Result<int32> tl_constructor_from_string(tonlib_api::Object *object, const std::string &str);
@ -24,6 +25,8 @@ 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::keyStoreTypeDirectory &to, JsonObject &from);
Status from_json(tonlib_api::keyStoreTypeInMemory &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);
@ -55,6 +58,7 @@ 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);
Status from_json(tonlib_api::deleteAllKeys &to, JsonObject &from);
Status from_json(tonlib_api::deleteKey &to, JsonObject &from);
Status from_json(tonlib_api::exportEncryptedKey &to, JsonObject &from);
Status from_json(tonlib_api::exportKey &to, JsonObject &from);
@ -103,6 +107,9 @@ 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::KeyStoreType &object);
void to_json(JsonValueScope &jv, const tonlib_api::keyStoreTypeDirectory &object);
void to_json(JsonValueScope &jv, const tonlib_api::keyStoreTypeInMemory &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);
@ -136,6 +143,7 @@ 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);
void to_json(JsonValueScope &jv, const tonlib_api::deleteAllKeys &object);
void to_json(JsonValueScope &jv, const tonlib_api::deleteKey &object);
void to_json(JsonValueScope &jv, const tonlib_api::exportEncryptedKey &object);
void to_json(JsonValueScope &jv, const tonlib_api::exportKey &object);

View File

@ -444,12 +444,14 @@ db.state.initBlockId block:tonNode.blockIdExt = db.state.InitBlockId;
db.state.gcBlockId block:tonNode.blockIdExt = db.state.GcBlockId;
db.state.shardClient block:tonNode.blockIdExt = db.state.ShardClient;
db.state.asyncSerializer block:tonNode.blockIdExt last:tonNode.blockIdExt last_ts:int = db.state.AsyncSerializer;
db.state.hardforks blocks:(vector tonNode.blockIdExt) = db.state.Hardforks;
db.state.key.destroyedSessions = db.state.Key;
db.state.key.initBlockId = db.state.Key;
db.state.key.gcBlockId = db.state.Key;
db.state.key.shardClient = db.state.Key;
db.state.key.asyncSerializer = db.state.Key;
db.state.key.hardforks = db.state.Key;
db.lt.el.key workchain:int shard:long idx:int = db.lt.Key;
db.lt.desc.key workchain:int shard:long = db.lt.Key;
@ -466,6 +468,7 @@ db.lt.status.value total_shards:int = db.lt.status.Value;
validator.groupMember public_key_hash:int256 adnl:int256 weight:long = engine.validator.GroupMember;
validator.group workchain:int shard:long catchain_seqno:int config_hash:int256 members:(vector validator.groupMember) = validator.Group;
validator.groupEx workchain:int shard:long vertical_seqno:int catchain_seqno:int config_hash:int256 members:(vector validator.groupMember) = validator.Group;
---functions---

View File

@ -16,8 +16,12 @@ vector {t:Type} # [ t ] = Vector t;
error code:int32 message:string = Error;
ok = Ok;
keyStoreTypeDirectory directory:string = KeyStoreType;
keyStoreTypeInMemory = KeyStoreType;
config config:string blockchain_name:string use_callbacks_for_network:Bool ignore_cache:Bool = Config;
options config:config keystore_directory:string = Options;
options config:config keystore_type:KeyStoreType = Options;
key public_key:string secret:secureBytes = Key;
inputKey key:key local_password:secureBytes = InputKey;
@ -35,9 +39,9 @@ internal.transactionId lt:int64 hash:bytes = internal.TransactionId;
raw.initialAccountState code:bytes data:bytes = raw.InitialAccountState;
raw.accountState balance:int64 code:bytes data:bytes last_transaction_id:internal.transactionId sync_utime:int53 = raw.AccountState;
raw.message source:string destination:string value:int64 message:bytes = raw.Message;
raw.transaction utime:int53 data:bytes transaction_id:internal.transactionId fee:int64 in_msg:raw.message out_msgs:vector<raw.message> = raw.Transaction;
raw.transactions transactions:vector<raw.Transaction> previous_transaction_id:internal.transactionId = raw.Transactions;
raw.message source:string destination:string value:int64 fwd_fee:int64 ihr_fee:int64 created_lt:int64 body_hash:bytes message:bytes = raw.Message;
raw.transaction utime:int53 data:bytes transaction_id:internal.transactionId fee:int64 storage_fee:int64 other_fee:int64 in_msg:raw.message out_msgs:vector<raw.message> = raw.Transaction;
raw.transactions transactions:vector<raw.transaction> previous_transaction_id:internal.transactionId = raw.Transactions;
testWallet.initialAccountState public_key:string = testWallet.InitialAccountState;
testWallet.accountState balance:int64 seqno:int32 last_transaction_id:internal.transactionId sync_utime:int53 = testWallet.AccountState;
@ -59,7 +63,7 @@ generic.accountStateWallet account_state:wallet.accountState = generic.AccountSt
generic.accountStateTestGiver account_state:testGiver.accountState = generic.AccountState;
generic.accountStateUninited account_state:uninited.accountState = generic.AccountState;
sendGramsResult sent_until:int53 = SendGramsResult;
sendGramsResult sent_until:int53 body_hash:bytes = SendGramsResult;
updateSendLiteServerQuery id:int64 data:bytes = Update;
@ -90,6 +94,7 @@ options.setConfig config:config = Ok;
createNewKey local_password:secureBytes mnemonic_password:secureBytes random_extra_seed:secureBytes = Key;
deleteKey key:key = Ok;
deleteAllKeys = Ok;
exportKey input_key:inputKey = ExportedKey;
exportPemKey input_key:inputKey key_password:secureBytes = ExportedPemKey;
exportEncryptedKey input_key:inputKey key_password:secureBytes = ExportedEncryptedKey;

View File

@ -5,6 +5,7 @@ if (NOT OPENSSL_FOUND)
endif()
set(TONLIB_SOURCE
tonlib/CellString.cpp
tonlib/Client.cpp
tonlib/Config.cpp
tonlib/ExtClient.cpp
@ -12,6 +13,7 @@ set(TONLIB_SOURCE
tonlib/ExtClientOutbound.cpp
tonlib/GenericAccount.cpp
tonlib/KeyStorage.cpp
tonlib/KeyValue.cpp
tonlib/LastBlock.cpp
tonlib/LastBlockStorage.cpp
tonlib/Logging.cpp
@ -21,6 +23,7 @@ set(TONLIB_SOURCE
tonlib/utils.cpp
tonlib/Wallet.cpp
tonlib/CellString.h
tonlib/Client.h
tonlib/Config.h
tonlib/ExtClient.h
@ -28,6 +31,7 @@ set(TONLIB_SOURCE
tonlib/ExtClientOutbound.h
tonlib/GenericAccount.h
tonlib/KeyStorage.h
tonlib/KeyValue.h
tonlib/LastBlock.h
tonlib/LastBlockStorage.h
tonlib/Logging.h

View File

@ -28,6 +28,7 @@
#include "vm/boc.h"
#include "vm/cells/MerkleProof.h"
#include "tonlib/CellString.h"
#include "tonlib/utils.h"
#include "tonlib/TestGiver.h"
#include "tonlib/TestWallet.h"
@ -53,6 +54,20 @@
#include "tonlib/keys/Mnemonic.h"
#include "tonlib/keys/SimpleEncryption.h"
TEST(Tonlib, CellString) {
for (unsigned size :
{0, 1, 7, 8, 35, 127, 128, 255, 256, (int)vm::CellString::max_bytes - 1, (int)vm::CellString::max_bytes}) {
auto str = td::rand_string('a', 'z', size);
for (unsigned head : {0, 1, 7, 8, 127, 35 * 8, 127 * 8, 1023, 1024}) {
vm::CellBuilder cb;
vm::CellString::store(cb, str, head).ensure();
auto cs = vm::load_cell_slice(cb.finalize());
auto got_str = vm::CellString::load(cs, head).move_as_ok();
ASSERT_EQ(str, got_str);
}
}
};
using namespace tonlib;
std::string current_dir() {
@ -268,20 +283,23 @@ static auto sync_send = [](auto &client, auto query) {
TEST(Tonlib, InitClose) {
using tonlib_api::make_object;
auto cfg = [](auto str) { return make_object<tonlib_api::config>(str, "", false, false); };
auto dir = [](auto str) { return make_object<tonlib_api::keyStoreTypeDirectory>(str); };
{
Client client;
sync_send(client, make_object<tonlib_api::close>()).ensure();
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "."))).ensure_error();
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, dir("."))))
.ensure_error();
}
{
Client client;
sync_send(client, make_object<tonlib_api::init>(nullptr)).ensure_error();
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(cfg("fdajkfldsjkafld"), ".")))
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(cfg("fdajkfldsjkafld"), dir("."))))
.ensure_error();
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "fdhskfds")))
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, dir("fdhskfds"))))
.ensure_error();
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, dir(".")))).ensure();
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, dir("."))))
.ensure_error();
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "."))).ensure();
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "."))).ensure_error();
td::Slice bad_config = R"abc(
{
@ -294,7 +312,8 @@ TEST(Tonlib, InitClose) {
sync_send(client, make_object<tonlib_api::testGiver_getAccountState>()).ensure_error();
sync_send(client, make_object<tonlib_api::close>()).ensure();
sync_send(client, make_object<tonlib_api::close>()).ensure_error();
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "."))).ensure_error();
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, dir("."))))
.ensure_error();
}
}
@ -389,7 +408,9 @@ TEST(Tonlib, ParseAddres) {
Client client;
// init
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "."))).ensure();
sync_send(client, make_object<tonlib_api::init>(
make_object<tonlib_api::options>(nullptr, make_object<tonlib_api::keyStoreTypeDirectory>("."))))
.ensure();
sync_send(client, make_object<tonlib_api::unpackAccountAddress>("hello")).ensure_error();
auto addr =
@ -409,7 +430,9 @@ TEST(Tonlib, KeysApi) {
Client client;
// init
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(nullptr, "."))).ensure();
sync_send(client, make_object<tonlib_api::init>(
make_object<tonlib_api::options>(nullptr, make_object<tonlib_api::keyStoreTypeDirectory>("."))))
.ensure();
auto local_password = td::SecureString("local password");
auto mnemonic_password = td::SecureString("mnemonic password");
{

View File

@ -197,7 +197,8 @@ int main(int argc, char* argv[]) {
Client client;
{
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(
make_object<tonlib_api::config>(global_config_str, "", false, false), ".")))
make_object<tonlib_api::config>(global_config_str, "", false, false),
make_object<tonlib_api::keyStoreTypeDirectory>("."))))
.ensure();
}
//dump_transaction_history(client, get_test_giver_address(client));
@ -211,7 +212,8 @@ int main(int argc, char* argv[]) {
{
// init
sync_send(client, make_object<tonlib_api::init>(make_object<tonlib_api::options>(
make_object<tonlib_api::config>(global_config_str, "", false, false), ".")))
make_object<tonlib_api::config>(global_config_str, "", false, false),
make_object<tonlib_api::keyStoreTypeDirectory>("."))))
.ensure();
auto key = sync_send(client, make_object<tonlib_api::createNewKey>(

View File

@ -0,0 +1,64 @@
#include "CellString.h"
#include "td/utils/misc.h"
#include "vm/cells/CellSlice.h"
namespace vm {
td::Status CellString::store(CellBuilder &cb, td::Slice slice, unsigned int top_bits) {
td::uint32 size = td::narrow_cast<td::uint32>(slice.size() * 8);
return store(cb, td::BitSlice(slice.ubegin(), size), top_bits);
}
td::Status CellString::store(CellBuilder &cb, td::BitSlice slice, unsigned int top_bits) {
if (slice.size() > max_bytes * 8) {
return td::Status::Error("String is too long (1)");
}
unsigned int head = td::min(slice.size(), td::min(cb.remaining_bits(), top_bits)) / 8 * 8;
auto max_bits = vm::Cell::max_bits / 8 * 8;
auto depth = 1 + (slice.size() - head + max_bits - 1) / max_bits;
if (depth > max_chain_length) {
return td::Status::Error("String is too long (2)");
}
cb.append_bitslice(slice.subslice(0, head));
slice.advance(head);
if (slice.size() == 0) {
return td::Status::OK();
}
CellBuilder child_cb;
store(child_cb, std::move(slice));
cb.store_ref(child_cb.finalize());
return td::Status::OK();
}
template <class F>
void CellString::for_each(F &&f, CellSlice &cs, unsigned int top_bits) {
unsigned int head = td::min(cs.size(), top_bits);
f(cs.prefetch_bits(head));
if (!cs.have_refs()) {
return;
}
auto ref = cs.prefetch_ref();
while (true) {
auto cs = vm::load_cell_slice(ref);
f(cs.prefetch_bits(cs.size()));
if (!cs.have_refs()) {
return;
}
ref = cs.prefetch_ref();
}
}
td::Result<td::string> CellString::load(CellSlice &cs, unsigned int top_bits) {
unsigned int size = 0;
for_each([&](auto slice) { size += slice.size(); }, cs, top_bits);
if (size % 8 != 0) {
return td::Status::Error("Size is not divisible by 8");
}
std::string res(size / 8, 0);
td::BitPtr to(td::MutableSlice(res).ubegin());
for_each([&](auto slice) { to.concat(slice); }, cs, top_bits);
CHECK(to.offs == (int)size);
return res;
}
} // namespace vm

View File

@ -0,0 +1,22 @@
#pragma once
#include "td/utils/Status.h"
#include "vm/cells/CellBuilder.h"
namespace vm {
class CellString {
public:
static constexpr unsigned int max_bytes = 1024;
static constexpr unsigned int max_chain_length = 16;
static td::Status store(CellBuilder &cb, td::Slice slice, unsigned int top_bits = Cell::max_bits);
static td::Status store(CellBuilder &cb, td::BitSlice slice, unsigned int top_bits = Cell::max_bits);
static td::Result<td::string> load(CellSlice &cs, unsigned int top_bits = Cell::max_bits);
private:
template <class F>
static void for_each(F &&f, CellSlice &cs, unsigned int top_bits = Cell::max_bits);
};
} // namespace vm

View File

@ -30,7 +30,7 @@ void ExtClient::with_last_block(td::Promise<LastBlockState> promise) {
});
};
if (client_.last_block_actor_.empty()) {
return P.set_error(td::Status::Error(500, "No lite clients"));
return P.set_error(TonlibError::NoLiteServers());
}
td::actor::send_closure(client_.last_block_actor_, &LastBlock::get_last_block, std::move(P));
}
@ -44,7 +44,7 @@ void ExtClient::send_raw_query(td::BufferSlice query, td::Promise<td::BufferSlic
});
};
if (client_.andl_ext_client_.empty()) {
return P.set_error(td::Status::Error(500, "No lite clients"));
return P.set_error(TonlibError::NoLiteServers());
}
td::actor::send_closure(client_.andl_ext_client_, &ton::adnl::AdnlExtClient::send_query, "query", std::move(query),
td::Timestamp::in(10.0), std::move(P));

View File

@ -26,6 +26,10 @@
#include "td/actor/actor.h"
#include "td/utils/Container.h"
#include "td/utils/Random.h"
#include "TonlibError.h"
#include "utils.h"
namespace tonlib {
class LastBlock;
@ -55,21 +59,28 @@ class ExtClient {
template <class QueryT>
void send_query(QueryT query, td::Promise<typename QueryT::ReturnType> promise) {
auto raw_query = ton::serialize_tl_object(&query, true);
LOG(ERROR) << "send query to liteserver: " << to_string(query);
td::uint32 tag = td::Random::fast_uint32();
VLOG(lite_server) << "send query to liteserver: " << tag << " " << to_string(query);
td::BufferSlice liteserver_query =
ton::serialize_tl_object(ton::create_tl_object<ton::lite_api::liteServer_query>(std::move(raw_query)), true);
send_raw_query(std::move(liteserver_query), [promise = std::move(promise)](td::Result<td::BufferSlice> R) mutable {
promise.set_result([&]() -> td::Result<typename QueryT::ReturnType> {
TRY_RESULT(data, std::move(R));
auto r_error = ton::fetch_tl_object<ton::lite_api::liteServer_error>(data.clone(), true);
if (r_error.is_ok()) {
auto f = r_error.move_as_ok();
return td::Status::Error(f->code_, f->message_);
}
return ton::fetch_result<QueryT>(std::move(data));
}());
});
send_raw_query(
std::move(liteserver_query), [promise = std::move(promise), tag](td::Result<td::BufferSlice> R) mutable {
auto res = [&]() -> td::Result<typename QueryT::ReturnType> {
TRY_RESULT_PREFIX(data, std::move(R), TonlibError::LiteServerNetwork());
auto r_error = ton::fetch_tl_object<ton::lite_api::liteServer_error>(data.clone(), true);
if (r_error.is_ok()) {
auto f = r_error.move_as_ok();
return TonlibError::LiteServer(f->code_, f->message_);
}
return ton::fetch_result<QueryT>(std::move(data));
}
();
VLOG_IF(lite_server, res.is_ok())
<< "got result from liteserver: " << tag << " " << td::Slice(to_string(res.ok())).truncate(1 << 12);
VLOG_IF(lite_server, res.is_error()) << "got error from liteserver: " << tag << " " << res.error();
promise.set_result(std::move(res));
});
}
private:

View File

@ -18,6 +18,7 @@
Copyright 2017-2019 Telegram Systems LLP
*/
#include "ExtClientOutbound.h"
#include "TonlibError.h"
#include <map>
namespace tonlib {
@ -40,7 +41,7 @@ class ExtClientOutboundImp : public ExtClientOutbound {
void on_query_result(td::int64 id, td::Result<td::BufferSlice> r_data, td::Promise<td::Unit> promise) override {
auto it = queries_.find(id);
if (it == queries_.end()) {
promise.set_error(td::Status::Error(400, "Unknown query id"));
promise.set_error(TonlibError::Internal("Unknown query id"));
}
it->second.set_result(std::move(r_data));
queries_.erase(it);
@ -54,7 +55,7 @@ class ExtClientOutboundImp : public ExtClientOutbound {
void tear_down() override {
for (auto &it : queries_) {
it.second.set_error(td::Status::Error(400, "Query cancelled"));
it.second.set_error(TonlibError::Cancelled());
}
queries_.clear();
}

View File

@ -20,18 +20,19 @@
#include "tonlib/utils.h"
#include "block/block-auto.h"
namespace tonlib {
td::Ref<vm::Cell> GenericAccount::get_init_state(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data) {
td::Ref<vm::Cell> GenericAccount::get_init_state(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data) noexcept {
return vm::CellBuilder()
.append_cellslice(binary_bitstring_to_cellslice("b{00110}").move_as_ok())
.store_ref(std::move(code))
.store_ref(std::move(data))
.finalize();
}
block::StdAddress GenericAccount::get_address(ton::WorkchainId workchain_id, const td::Ref<vm::Cell>& init_state) {
block::StdAddress GenericAccount::get_address(ton::WorkchainId workchain_id,
const td::Ref<vm::Cell>& init_state) noexcept {
return block::StdAddress(workchain_id, init_state->get_hash().bits(), true /*bounce*/);
}
td::Ref<vm::Cell> GenericAccount::create_ext_message(const block::StdAddress& address, td::Ref<vm::Cell> new_state,
td::Ref<vm::Cell> body) {
td::Ref<vm::Cell> body) noexcept {
block::gen::Message::Record message;
/*info*/ {
block::gen::CommonMsgInfo::Record_ext_in_msg_info info;

View File

@ -22,9 +22,9 @@
namespace tonlib {
class GenericAccount {
public:
static td::Ref<vm::Cell> get_init_state(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data);
static block::StdAddress get_address(ton::WorkchainId workchain_id, const td::Ref<vm::Cell>& init_state);
static td::Ref<vm::Cell> get_init_state(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data) noexcept;
static block::StdAddress get_address(ton::WorkchainId workchain_id, const td::Ref<vm::Cell>& init_state) noexcept;
static td::Ref<vm::Cell> create_ext_message(const block::StdAddress& address, td::Ref<vm::Cell> new_state,
td::Ref<vm::Cell> body);
td::Ref<vm::Cell> body) noexcept;
};
} // namespace tonlib

View File

@ -22,34 +22,26 @@
#include "tonlib/keys/DecryptedKey.h"
#include "tonlib/keys/EncryptedKey.h"
#include "tonlib/TonlibError.h"
#include "td/utils/filesystem.h"
#include "td/utils/port/path.h"
#include "td/utils/crypto.h"
#include "td/utils/PathView.h"
namespace tonlib {
namespace {
std::string to_file_name_old(const KeyStorage::Key &key) {
return td::buffer_to_hex(key.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));
}
} // namespace
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));
TRY_RESULT(stat, td::stat(path));
if (!stat.is_dir_) {
return td::Status::Error("not a directory");
}
directory_ = std::move(path);
return td::Status::OK();
void KeyStorage::set_key_value(std::shared_ptr<KeyValue> kv) {
kv_ = std::move(kv);
}
td::Result<KeyStorage::Key> KeyStorage::save_key(const DecryptedKey &decrypted_key, td::Slice local_password) {
@ -58,17 +50,7 @@ td::Result<KeyStorage::Key> KeyStorage::save_key(const DecryptedKey &decrypted_k
Key res;
res.public_key = encrypted_key.public_key.as_octet_string();
res.secret = std::move(encrypted_key.secret);
auto size = encrypted_key.encrypted_data.size();
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_t>(size)) {
return td::Status::Error(PSLICE() << "Failed to write file: written " << written << " bytes instead of " << size);
}
to_file.close();
TRY_STATUS_PREFIX(kv_->set(to_file_name(res), encrypted_key.encrypted_data), TonlibError::Internal());
return std::move(res);
}
@ -83,19 +65,22 @@ td::Result<KeyStorage::Key> KeyStorage::create_new_key(td::Slice local_password,
}
td::Result<DecryptedKey> KeyStorage::export_decrypted_key(InputKey input_key) {
auto r_encrypted_data = td::read_file_secure(to_file_path(input_key.key));
auto r_encrypted_data = kv_->get(to_file_name(input_key.key));
if (r_encrypted_data.is_error()) {
r_encrypted_data = td::read_file_secure(to_file_path_old(input_key.key));
r_encrypted_data = kv_->get(to_file_name_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();
LOG(WARNING) << "Restore private from deprecated location " << to_file_name_old(input_key.key) << " --> "
<< to_file_name(input_key.key);
TRY_STATUS_PREFIX(kv_->set(to_file_name(input_key.key), r_encrypted_data.ok()), TonlibError::Internal());
kv_->erase(to_file_name_old(input_key.key)).ignore();
}
}
TRY_RESULT(encrypted_data, std::move(r_encrypted_data));
TRY_RESULT_PREFIX(encrypted_data, std::move(r_encrypted_data), TonlibError::KeyUnknown());
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));
TRY_RESULT_PREFIX(decrypted_key, encrypted_key.decrypt(std::move(input_key.local_password)),
TonlibError::KeyDecrypt());
return decrypted_key;
}
td::Result<KeyStorage::ExportedKey> KeyStorage::export_key(InputKey input_key) {
@ -113,7 +98,26 @@ td::Result<KeyStorage::PrivateKey> KeyStorage::load_private_key(InputKey input_k
}
td::Status KeyStorage::delete_key(const Key &key) {
return td::unlink(to_file_path(key));
LOG(WARNING) << "Delete private key stored at " << to_file_name(key);
return kv_->erase(to_file_name(key));
}
td::Status KeyStorage::delete_all_keys() {
std::vector<std::string> keys;
kv_->foreach_key([&](td::Slice key) {
if (td::PathView(key).extension().empty()) {
keys.push_back(key.str());
}
});
td::Status status;
for (auto key : keys) {
LOG(WARNING) << "Delete private key stored at " << key;
auto err = kv_->erase(key);
if (err.is_error() && status.is_ok()) {
status = std::move(err);
}
}
return status;
}
td::Result<KeyStorage::Key> KeyStorage::import_key(td::Slice local_password, td::Slice mnemonic_password,
@ -121,16 +125,16 @@ td::Result<KeyStorage::Key> KeyStorage::import_key(td::Slice local_password, td:
TRY_RESULT(mnemonic, Mnemonic::create(std::move(exported_key.mnemonic_words), td::SecureString(mnemonic_password)));
if (!mnemonic.is_basic_seed()) {
if (mnemonic_password.empty() && mnemonic.is_password_seed()) {
return td::Status::Error("Mnemonic password is expected");
return TonlibError::NeedMnemonicPassword();
}
return td::Status::Error("Invalid mnemonic words or password (invalid checksum)");
return TonlibError::InvalidMnemonic();
}
return save_key(DecryptedKey(std::move(mnemonic)), local_password);
}
td::Result<KeyStorage::ExportedPemKey> KeyStorage::export_pem_key(InputKey input_key, td::Slice key_password) {
TRY_RESULT(decrypted_key, export_decrypted_key(std::move(input_key)));
TRY_RESULT(pem, decrypted_key.private_key.as_pem(key_password));
TRY_RESULT_PREFIX(pem, decrypted_key.private_key.as_pem(key_password), TonlibError::Internal());
return ExportedPemKey{std::move(pem)};
}
@ -140,13 +144,15 @@ td::Result<KeyStorage::Key> 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)));
TRY_RESULT_PREFIX(value, kv_->get(to_file_name(input_key.key)), TonlibError::KeyUnknown());
TRY_STATUS_PREFIX(kv_->add(to_file_name(res), value), TonlibError::Internal());
return std::move(res);
}
td::Result<KeyStorage::Key> KeyStorage::import_pem_key(td::Slice local_password, td::Slice key_password,
ExportedPemKey exported_key) {
TRY_RESULT(key, td::Ed25519::PrivateKey::from_pem(exported_key.pem, key_password));
TRY_RESULT_PREFIX(key, td::Ed25519::PrivateKey::from_pem(exported_key.pem, key_password),
TonlibError::InvalidPemKey());
return save_key(DecryptedKey({}, std::move(key)), local_password);
}
@ -162,7 +168,7 @@ td::Result<KeyStorage::Key> KeyStorage::import_encrypted_key(td::Slice local_pas
ExportedEncryptedKey exported_key) {
EncryptedKey encrypted_key{std::move(exported_key.data), td::Ed25519::PublicKey(td::SecureString()),
td::SecureString(dummy_secret)};
TRY_RESULT(decrypted_key, encrypted_key.decrypt(key_password, false));
TRY_RESULT_PREFIX(decrypted_key, encrypted_key.decrypt(key_password, false), TonlibError::KeyDecrypt());
return save_key(std::move(decrypted_key), local_password);
}

View File

@ -21,6 +21,8 @@
#include "td/utils/Status.h"
#include "td/utils/SharedSlice.h"
#include "KeyValue.h"
#include <string>
namespace tonlib {
@ -48,7 +50,7 @@ class KeyStorage {
td::SecureString private_key;
};
td::Status set_directory(std::string directory);
void set_key_value(std::shared_ptr<KeyValue> kv);
td::Result<Key> create_new_key(td::Slice local_password, td::Slice key_password, td::Slice entropy);
@ -58,6 +60,7 @@ class KeyStorage {
td::Result<Key> change_local_password(InputKey input_key, td::Slice new_local_password);
td::Status delete_key(const Key& key);
td::Status delete_all_keys();
td::Result<Key> import_key(td::Slice local_password, td::Slice mnemonic_password, ExportedKey exported_key);
td::Result<Key> import_pem_key(td::Slice local_password, td::Slice key_password, ExportedPemKey exported_key);
@ -67,12 +70,9 @@ class KeyStorage {
td::Result<PrivateKey> load_private_key(InputKey input_key);
private:
std::string directory_;
std::shared_ptr<KeyValue> kv_;
td::Result<Key> save_key(const DecryptedKey& mnemonic, td::Slice local_password);
td::Result<DecryptedKey> export_decrypted_key(InputKey input_key);
std::string to_file_path(const Key& key);
std::string to_file_path_old(const Key& key);
};
} // namespace tonlib

View File

@ -0,0 +1,124 @@
#include "KeyValue.h"
#include "td/utils/filesystem.h"
#include "td/utils/port/path.h"
#include <map>
#include <utility>
namespace tonlib {
namespace detail {
class KeyValueDir : public KeyValue {
public:
static td::Result<td::unique_ptr<KeyValueDir>> create(td::CSlice directory) {
TRY_RESULT(path, td::realpath(directory));
TRY_RESULT(stat, td::stat(path));
if (!stat.is_dir_) {
return td::Status::Error("not a directory");
}
return td::make_unique<KeyValueDir>(path);
}
KeyValueDir(std::string directory) : directory_(std::move(directory)) {
}
td::Status add(td::Slice key, td::Slice value) override {
auto path = to_file_path(key.str());
if (td::stat(path).is_ok()) {
return td::Status::Error(PSLICE() << "File " << path << "already exists");
}
return td::atomic_write_file(path, value);
}
td::Status set(td::Slice key, td::Slice value) override {
return td::atomic_write_file(to_file_path(key.str()), value);
}
td::Result<td::SecureString> get(td::Slice key) override {
return td::read_file_secure(to_file_path(key.str()));
}
td::Status erase(td::Slice key) override {
return td::unlink(key.str());
}
void foreach_key(std::function<void(td::Slice)> f) override {
int cnt = 0;
td::WalkPath::run(directory_, [&](td::Slice path, td::WalkPath::Type type) {
cnt++;
if (type == td::WalkPath::Type::EnterDir) {
if (cnt != 1) {
return td::WalkPath::Action::SkipDir;
}
} else if (type == td::WalkPath::Type::NotDir) {
f(path);
}
return td::WalkPath::Action::Continue;
}).ignore();
}
private:
std::string directory_;
std::string to_file_path(std::string key) {
return directory_ + TD_DIR_SLASH + key;
}
};
class KeyValueInmemory : public KeyValue {
public:
td::Status add(td::Slice key, td::Slice value) override {
auto res = map_.insert(std::make_pair(key.str(), td::SecureString(value)));
if (!res.second) {
return td::Status::Error(PSLICE() << "Add failed: value with key=`" << key << "` already exists");
}
return td::Status::OK();
}
td::Status set(td::Slice key, td::Slice value) override {
map_[key.str()] = td::SecureString(value);
return td::Status::OK();
}
td::Result<td::SecureString> get(td::Slice key) override {
auto it = map_.find(key);
if (it == map_.end()) {
return td::Status::Error("Unknown key");
}
return it->second.copy();
}
static td::Result<td::unique_ptr<KeyValueInmemory>> create() {
return td::make_unique<KeyValueInmemory>();
}
td::Status erase(td::Slice key) override {
auto it = map_.find(key);
if (it == map_.end()) {
return td::Status::Error("Unknown key");
}
map_.erase(it);
return td::Status::OK();
}
void foreach_key(std::function<void(td::Slice)> f) override {
for (auto &it : map_) {
f(it.first);
}
}
private:
class Cmp : public std::less<> {
public:
using is_transparent = void;
};
std::map<std::string, td::SecureString, Cmp> map_;
};
} // namespace detail
td::Result<td::unique_ptr<KeyValue>> KeyValue::create_dir(td::CSlice dir) {
TRY_RESULT(res, detail::KeyValueDir::create(dir.str()));
return std::move(res);
}
td::Result<td::unique_ptr<KeyValue>> KeyValue::create_inmemory() {
TRY_RESULT(res, detail::KeyValueInmemory::create());
return std::move(res);
}
} // namespace tonlib

View File

@ -0,0 +1,19 @@
#pragma once
#include "td/utils/SharedSlice.h"
#include "td/utils/Slice.h"
#include "td/utils/Status.h"
namespace tonlib {
class KeyValue {
public:
virtual ~KeyValue() = default;
virtual td::Status add(td::Slice key, td::Slice value) = 0;
virtual td::Status set(td::Slice key, td::Slice value) = 0;
virtual td::Status erase(td::Slice key) = 0;
virtual td::Result<td::SecureString> get(td::Slice key) = 0;
virtual void foreach_key(std::function<void(td::Slice)> f) = 0;
static td::Result<td::unique_ptr<KeyValue>> create_dir(td::CSlice dir);
static td::Result<td::unique_ptr<KeyValue>> create_inmemory();
};
} // namespace tonlib

View File

@ -18,12 +18,18 @@
*/
#include "tonlib/LastBlock.h"
#include "tonlib/utils.h"
#include "ton/lite-tl.hpp"
#include "lite-client/lite-client-common.h"
namespace tonlib {
// init_state <-> last_key_block
// state.valitated_init_state
// last_key_block ->
//
td::StringBuilder& operator<<(td::StringBuilder& sb, const LastBlockState& state) {
return sb << td::tag("last_block", state.last_block_id.to_str())
<< td::tag("last_key_block", state.last_key_block_id.to_str()) << td::tag("utime", state.utime);
@ -32,9 +38,10 @@ td::StringBuilder& operator<<(td::StringBuilder& sb, const LastBlockState& state
LastBlock::LastBlock(ExtClientRef client, LastBlockState state, Config config, td::unique_ptr<Callback> callback)
: state_(std::move(state)), config_(std::move(config)), callback_(std::move(callback)) {
client_.set_client(client);
if (!config_.init_block_id.is_valid()) {
check_init_block_state_ = QueryState::Done;
}
state_.last_block_id = state_.last_key_block_id;
VLOG(last_block) << "check_init_block: skip - FIXME before release";
check_init_block_state_ = QueryState::Done;
}
void LastBlock::get_last_block(td::Promise<LastBlockState> promise) {
@ -42,9 +49,13 @@ void LastBlock::get_last_block(td::Promise<LastBlockState> promise) {
promise.set_error(fatal_error_.clone());
return;
}
if (promises_.empty() && get_last_block_state_ == QueryState::Done) {
VLOG(last_block) << "sync: start";
VLOG(last_block) << "get_last_block: reset";
get_last_block_state_ = QueryState::Empty;
}
promises_.push_back(std::move(promise));
sync_loop();
}
@ -54,36 +65,43 @@ void LastBlock::sync_loop() {
return;
}
update_zero_state(state_.zero_state_id);
update_zero_state(state_.zero_state_id, "cache");
update_zero_state(ton::ZeroStateIdExt(config_.zero_state_id.id.workchain, config_.zero_state_id.root_hash,
config_.zero_state_id.file_hash));
config_.zero_state_id.file_hash),
"config");
if (get_mc_info_state_ == QueryState::Empty) {
VLOG(last_block) << "get_masterchain_info: start";
get_mc_info_state_ = QueryState::Active;
client_.send_query(ton::lite_api::liteServer_getMasterchainInfo(),
[this](auto r_info) { this->on_masterchain_info(std::move(r_info)); });
}
if (get_last_block_state_ == QueryState::Empty) {
get_last_block_state_ = QueryState::Active;
total_sync_ = td::Timer();
validate_ = td::Timer(true);
queries_ = 0;
LOG(INFO) << "Begin last block synchronization " << state_;
do_get_last_block();
if (check_init_block_state_ == QueryState::Empty) {
if (!config_.init_block_id.is_valid()) {
check_init_block_state_ = QueryState::Done;
VLOG(last_block) << "check_init_block: skip - no init_block in config";
} else if (config_.init_block_id == state_.init_block_id) {
check_init_block_state_ = QueryState::Done;
VLOG(last_block) << "check_init_block: skip - was checked before";
} else {
check_init_block_state_ = QueryState::Active;
check_init_block_stats_.start();
if (state_.last_block_id.id.seqno >= config_.init_block_id.id.seqno) {
VLOG(last_block) << "check_init_block: start - init_block -> last_block";
do_check_init_block(config_.init_block_id, state_.last_key_block_id);
} else {
VLOG(last_block) << "check_init_block: start - last_block -> init_block";
do_check_init_block(state_.last_key_block_id, config_.init_block_id);
}
}
}
if (check_init_block_state_ == QueryState::Empty) {
if (state_.last_block_id.id.seqno >= config_.init_block_id.id.seqno) {
check_init_block_state_ = QueryState::Active;
// validate
//total_sync_ = td::Timer();
//validate_ = td::Timer(true);
//queries_ = 0;
LOG(INFO) << "Begin last block synchronization (check init_block)" << state_;
do_check_init_block(state_.last_key_block_id);
} else {
}
if (get_last_block_state_ == QueryState::Empty && check_init_block_state_ == QueryState::Done) {
VLOG(last_block) << "get_last_block: start";
get_last_block_stats_.start();
get_last_block_state_ = QueryState::Active;
do_get_last_block();
}
if (get_mc_info_state_ == QueryState::Done && get_last_block_state_ == QueryState::Done &&
@ -94,7 +112,8 @@ void LastBlock::sync_loop() {
void LastBlock::do_get_last_block() {
//liteServer.getBlockProof mode:# known_block:tonNode.blockIdExt target_block:mode.0?tonNode.blockIdExt = liteServer.PartialBlockProof;
queries_++;
VLOG(last_block) << "get_last_block: continue " << state_.last_key_block_id.to_str() << " -> ?";
get_last_block_stats_.queries_++;
client_.send_query(
ton::lite_api::liteServer_getBlockProof(0, create_tl_lite_block_id(state_.last_key_block_id), nullptr),
[this, from = state_.last_key_block_id](auto r_block_proof) {
@ -102,64 +121,69 @@ void LastBlock::do_get_last_block() {
});
}
void LastBlock::do_check_init_block(ton::BlockIdExt from) {
void LastBlock::do_check_init_block(ton::BlockIdExt from, ton::BlockIdExt to) {
VLOG(last_block) << "check_init_block: continue " << from.to_str() << " -> " << to.to_str();
//liteServer.getBlockProof mode:# known_block:tonNode.blockIdExt target_block:mode.0?tonNode.blockIdExt = liteServer.PartialBlockProof;
//queries_++;
client_.send_query(ton::lite_api::liteServer_getBlockProof(1, create_tl_lite_block_id(from),
create_tl_lite_block_id(config_.init_block_id)),
[this, from = state_.last_key_block_id](auto r_block_proof) {
this->on_init_block_proof(from, std::move(r_block_proof));
});
check_init_block_stats_.queries_++;
client_.send_query(
ton::lite_api::liteServer_getBlockProof(1, create_tl_lite_block_id(from), create_tl_lite_block_id(to)),
[this, from, to](auto r_block_proof) { this->on_init_block_proof(from, to, std::move(r_block_proof)); });
}
td::Result<std::unique_ptr<block::BlockProofChain>> LastBlock::process_block_proof(
ton::BlockIdExt from,
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof) {
TRY_RESULT(block_proof, std::move(r_block_proof));
LOG(DEBUG) << "Got proof FROM\n" << to_string(block_proof->from_) << "TO\n" << to_string(block_proof->to_);
TRY_RESULT(block_proof, std::move(r_block_proof)); //TODO: it is fatal?
TRY_RESULT_PREFIX(chain, TRY_VM(process_block_proof(from, std::move(block_proof))),
TonlibError::ValidateBlockProof());
return chain;
}
td::Result<std::unique_ptr<block::BlockProofChain>> LastBlock::process_block_proof(
ton::BlockIdExt from, ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof> block_proof) {
VLOG(last_block) << "Got proof FROM\n" << to_string(block_proof->from_) << "TO\n" << to_string(block_proof->to_);
TRY_RESULT(chain, liteclient::deserialize_proof_chain(std::move(block_proof)));
if (chain->from != from) {
return td::Status::Error(PSLICE() << "block proof chain starts from block " << chain->from.to_str()
<< ", not from requested block " << from.to_str());
}
TRY_STATUS(chain->validate());
return chain;
}
void LastBlock::update_state(block::BlockProofChain& chain) {
// Update state_
bool is_changed = false;
is_changed |= update_mc_last_block(chain->to);
if (chain->has_key_block) {
is_changed |= update_mc_last_key_block(chain->key_blkid);
is_changed |= update_mc_last_block(chain.to);
if (chain.has_key_block) {
is_changed |= update_mc_last_key_block(chain.key_blkid);
}
if (chain->has_utime) {
update_utime(chain->last_utime);
if (chain.has_utime) {
update_utime(chain.last_utime);
}
if (is_changed) {
callback_->on_state_changed(state_);
}
return std::move(chain);
}
void LastBlock::on_block_proof(
ton::BlockIdExt from,
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof) {
validate_.resume();
get_last_block_stats_.validate_.resume();
auto r_chain = process_block_proof(from, std::move(r_block_proof));
validate_.pause();
bool is_ready;
get_last_block_stats_.validate_.pause();
if (r_chain.is_error()) {
LOG(WARNING) << "Error during last block synchronization " << r_chain.error();
if (config_.init_block_id.is_valid()) {
if (state_.last_key_block_id.id.seqno < config_.init_block_id.id.seqno) {
on_sync_error(td::Status::Error(PSLICE() << "Sync failed and we can't validate config.init_block: "
<< r_chain.move_as_error()));
}
}
is_ready = true;
} else {
is_ready = r_chain.ok()->complete;
get_last_block_state_ = QueryState::Empty;
VLOG(last_block) << "get_last_block: error " << r_chain.error();
on_sync_error(r_chain.move_as_error_suffix("(during last block synchronization)"));
return;
}
if (is_ready) {
LOG(INFO) << "End last block synchronization " << state_ << "\n"
<< " net queries: " << queries_ << "\n"
<< " total: " << total_sync_ << " validation: " << validate_;
auto chain = r_chain.move_as_ok();
CHECK(chain);
update_state(*chain);
if (chain->complete) {
VLOG(last_block) << "get_last_block: done\n" << get_last_block_stats_;
get_last_block_state_ = QueryState::Done;
sync_loop();
} else {
@ -168,26 +192,26 @@ void LastBlock::on_block_proof(
}
void LastBlock::on_init_block_proof(
ton::BlockIdExt from,
ton::BlockIdExt from, ton::BlockIdExt to,
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof) {
validate_.resume();
check_init_block_stats_.validate_.resume();
auto r_chain = process_block_proof(from, std::move(r_block_proof));
validate_.pause();
check_init_block_stats_.validate_.pause();
if (r_chain.is_error()) {
check_init_block_state_ = QueryState::Empty;
on_sync_error(
td::Status::Error(PSLICE() << "Error during last block synchronization (check init_block)" << r_chain.error()));
VLOG(last_block) << "check_init_block: error " << r_chain.error();
on_sync_error(r_chain.move_as_error_suffix("(during check init block)"));
return;
}
auto chain = r_chain.move_as_ok();
CHECK(chain);
update_state(*chain);
if (chain->complete) {
LOG(INFO) << "End last block synchronization " << state_ << "\n"
<< " net queries: " << queries_ << "\n"
<< " total: " << total_sync_ << " validation: " << validate_;
get_last_block_state_ = QueryState::Done;
VLOG(last_block) << "check_init_block: done\n" << check_init_block_stats_;
check_init_block_state_ = QueryState::Done;
sync_loop();
} else {
do_check_init_block(chain->to);
do_check_init_block(chain->to, to);
}
}
@ -195,28 +219,30 @@ void LastBlock::on_masterchain_info(
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_masterchainInfo>> r_info) {
if (r_info.is_ok()) {
auto info = r_info.move_as_ok();
update_zero_state(create_zero_state_id(info->init_));
update_mc_last_block(create_block_id(info->last_));
update_zero_state(create_zero_state_id(info->init_), "masterchain info");
// last block is not validated! Do not update it
get_mc_info_state_ = QueryState::Done;
VLOG(last_block) << "get_masterchain_info: done";
} else {
get_mc_info_state_ = QueryState::Empty;
VLOG(last_block) << "get_masterchain_info: error " << r_info.error();
LOG(WARNING) << "Failed liteServer_getMasterchainInfo " << r_info.error();
on_sync_error(r_info.move_as_error());
}
sync_loop();
}
void LastBlock::update_zero_state(ton::ZeroStateIdExt zero_state_id) {
void LastBlock::update_zero_state(ton::ZeroStateIdExt zero_state_id, td::Slice source) {
if (has_fatal_error()) {
return;
}
if (!zero_state_id.is_valid()) {
LOG(ERROR) << "Ignore invalid zero state update";
LOG(ERROR) << "Ignore invalid zero state update from " << source;
return;
}
if (!state_.zero_state_id.is_valid()) {
LOG(INFO) << "Init zerostate: " << zero_state_id.to_str();
LOG(INFO) << "Init zerostate from " << source << ": " << zero_state_id.to_str();
state_.zero_state_id = std::move(zero_state_id);
return;
}
@ -225,8 +251,9 @@ void LastBlock::update_zero_state(ton::ZeroStateIdExt zero_state_id) {
return;
}
on_fatal_error(td::Status::Error(PSLICE() << "Masterchain zerostate mismatch: expected: "
<< state_.zero_state_id.to_str() << ", found " << zero_state_id.to_str()));
on_fatal_error(TonlibError::ValidateZeroState(PSLICE() << "Masterchain zerostate mismatch: expected: "
<< state_.zero_state_id.to_str() << ", found "
<< zero_state_id.to_str() << " from " << source));
}
bool LastBlock::update_mc_last_block(ton::BlockIdExt mc_block_id) {
@ -256,6 +283,9 @@ bool LastBlock::update_mc_last_key_block(ton::BlockIdExt mc_key_block_id) {
if (!state_.last_key_block_id.is_valid() || state_.last_key_block_id.id.seqno < mc_key_block_id.id.seqno) {
state_.last_key_block_id = mc_key_block_id;
LOG(INFO) << "Update masterchain key block id: " << state_.last_key_block_id.to_str();
//LOG(ERROR) << td::int64(state_.last_key_block_id.id.shard) << " "
//<< td::base64_encode(state_.last_key_block_id.file_hash.as_slice()) << " "
//<< td::base64_encode(state_.last_key_block_id.root_hash.as_slice());
return true;
}
return false;
@ -268,6 +298,7 @@ void LastBlock::update_utime(td::int64 utime) {
}
void LastBlock::on_sync_ok() {
VLOG(last_block) << "sync: ok " << state_;
for (auto& promise : promises_) {
auto state = state_;
promise.set_value(std::move(state));
@ -275,12 +306,14 @@ void LastBlock::on_sync_ok() {
promises_.clear();
}
void LastBlock::on_sync_error(td::Status status) {
VLOG(last_block) << "sync: error " << status;
for (auto& promise : promises_) {
promise.set_error(status.clone());
}
promises_.clear();
}
void LastBlock::on_fatal_error(td::Status status) {
VLOG(last_block) << "sync: fatal error " << status;
fatal_error_ = std::move(status);
on_sync_error(fatal_error_.clone());
}

View File

@ -22,6 +22,8 @@
#include "tonlib/Config.h"
#include "tonlib/ExtClient.h"
#include "td/utils/tl_helpers.h"
namespace block {
struct BlockProofChain;
}
@ -89,25 +91,44 @@ struct LastBlockState {
ton::BlockIdExt last_key_block_id;
ton::BlockIdExt last_block_id;
td::int64 utime{0};
ton::BlockIdExt init_block_id;
static constexpr td::int32 magic = 0xa7f171a4;
enum Version { None = 0, Magic, InitBlock, Next };
static constexpr td::int32 version = Version::Next - 1;
template <class StorerT>
void store(StorerT &storer) const {
using td::store;
using tonlib::store;
store(magic, storer);
store(version, storer);
store(zero_state_id, storer);
store(last_key_block_id, storer);
store(last_block_id, storer);
store(utime, storer);
store(init_block_id, storer);
}
template <class ParserT>
void parse(ParserT &parser) {
using td::parse;
using tonlib::parse;
td::int32 version = 0;
if (parser.can_prefetch_int() && parser.prefetch_int_unsafe() == magic) {
td::int32 magic;
parse(magic, parser);
parse(version, parser);
}
parse(zero_state_id, parser);
parse(last_key_block_id, parser);
parse(last_block_id, parser);
parse(utime, parser);
if (version >= InitBlock) {
parse(init_block_id, parser);
}
}
};
@ -132,20 +153,36 @@ class LastBlock : public td::actor::Actor {
td::Status fatal_error_;
enum class QueryState { Empty, Active, Done };
QueryState get_mc_info_state_{QueryState::Empty};
QueryState get_last_block_state_{QueryState::Empty};
QueryState check_init_block_state_{QueryState::Empty};
QueryState get_mc_info_state_{QueryState::Empty}; // just to check zero state
QueryState check_init_block_state_{QueryState::Empty}; // init_block <---> last_key_block (from older to newer)
QueryState get_last_block_state_{QueryState::Empty}; // last_key_block_id --> ?
// stats
td::Timer total_sync_;
td::Timer validate_;
td::uint32 queries_;
struct Stats {
td::Timer total_sync_;
td::Timer validate_;
td::uint32 queries_;
void start() {
total_sync_ = td::Timer();
validate_ = td::Timer(true);
queries_ = 0;
}
friend td::StringBuilder &operator<<(td::StringBuilder &sb, const Stats &stats) {
return sb << " net queries: " << stats.queries_ << "\n"
<< " total: " << stats.total_sync_ << " validation: " << stats.validate_;
}
};
Stats check_init_block_stats_;
Stats get_last_block_stats_;
std::vector<td::Promise<LastBlockState>> promises_;
void do_check_init_block(ton::BlockIdExt from);
void do_check_init_block(ton::BlockIdExt from, ton::BlockIdExt to);
void on_init_block_proof(
ton::BlockIdExt from,
ton::BlockIdExt from, ton::BlockIdExt to,
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof);
void on_masterchain_info(td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_masterchainInfo>> r_info);
void do_get_last_block();
@ -155,7 +192,11 @@ class LastBlock : public td::actor::Actor {
ton::BlockIdExt from,
td::Result<ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof>> r_block_proof);
void update_zero_state(ton::ZeroStateIdExt zero_state_id);
td::Result<std::unique_ptr<block::BlockProofChain>> process_block_proof(
ton::BlockIdExt from, ton::ton_api::object_ptr<ton::lite_api::liteServer_partialBlockProof> block_proof);
void update_state(block::BlockProofChain &chain);
void update_zero_state(ton::ZeroStateIdExt zero_state_id, td::Slice source);
bool update_mc_last_block(ton::BlockIdExt mc_block_id);
bool update_mc_last_key_block(ton::BlockIdExt mc_key_block_id);

View File

@ -18,6 +18,8 @@
*/
#include "LastBlockStorage.h"
#include "tonlib/utils.h"
#include "td/utils/as.h"
#include "td/utils/filesystem.h"
#include "td/utils/port/path.h"
@ -25,22 +27,18 @@
namespace tonlib {
td::Status LastBlockStorage::set_directory(std::string directory) {
TRY_RESULT(path, td::realpath(directory));
TRY_RESULT(stat, td::stat(path));
if (!stat.is_dir_) {
return td::Status::Error("not a directory");
}
directory_ = std::move(path);
return td::Status::OK();
void LastBlockStorage::set_key_value(std::shared_ptr<KeyValue> kv) {
kv_ = std::move(kv);
}
std::string LastBlockStorage::get_file_name(td::Slice name) {
return directory_ + TD_DIR_SLASH + td::buffer_to_hex(name) + ".blkstate";
namespace {
std::string get_file_name(td::Slice name) {
return td::buffer_to_hex(name) + ".blkstate";
}
} // namespace
td::Result<LastBlockState> LastBlockStorage::get_state(td::Slice name) {
TRY_RESULT(data, td::read_file(get_file_name(name)));
TRY_RESULT(data, kv_->get(get_file_name(name)));
if (data.size() < 8) {
return td::Status::Error("too short");
}
@ -53,10 +51,11 @@ td::Result<LastBlockState> LastBlockStorage::get_state(td::Slice name) {
}
void LastBlockStorage::save_state(td::Slice name, LastBlockState state) {
VLOG(last_block) << "Save to cache: " << state;
auto x = td::serialize(state);
std::string y(x.size() + 8, 0);
td::MutableSlice(y).substr(8).copy_from(x);
td::as<td::uint64>(td::MutableSlice(y).data()) = td::crc64(x);
td::atomic_write_file(get_file_name(name), y);
kv_->set(get_file_name(name), y);
}
} // namespace tonlib

View File

@ -20,15 +20,16 @@
#include "tonlib/LastBlock.h"
#include "tonlib/KeyValue.h"
namespace tonlib {
class LastBlockStorage {
public:
td::Status set_directory(std::string directory);
void set_key_value(std::shared_ptr<KeyValue> kv);
td::Result<LastBlockState> get_state(td::Slice name);
void save_state(td::Slice name, LastBlockState state);
private:
std::string directory_;
std::string get_file_name(td::Slice name);
std::shared_ptr<KeyValue> kv_;
};
} // namespace tonlib

Some files were not shown because too many files have changed in this diff Show More