Update tonlib

This commit is contained in:
Ali 2020-04-23 23:29:22 +04:00
parent fb07f06a26
commit 69fb8c7603
734 changed files with 23389 additions and 164826 deletions

View File

@ -757,15 +757,28 @@ final class AppDelegate: NSObject, UIApplicationDelegate {
|> deliverOnMainQueue).start(next: { records, publicKey in
if let record = records.first {
if let publicKey = publicKey {
if record.info.encryptedSecret.publicKey == publicKey {
if record.exportCompleted {
let _ = (walletAddress(publicKey: record.info.publicKey, tonInstance: walletContext.tonInstance)
|> deliverOnMainQueue).start(next: { address in
let infoScreen = WalletInfoScreen(context: walletContext, walletInfo: record.info, address: address, enableDebugActions: false)
beginWithController(infoScreen)
})
} else {
let createdScreen = WalletSplashScreen(context: walletContext, mode: .created(walletInfo: record.info, words: nil), walletCreatedPreloadState: nil)
let recordPublicKey: Data
switch record.info {
case let .ready(info, _, _):
recordPublicKey = info.encryptedSecret.publicKey
case let .imported(info):
recordPublicKey = info.encryptedSecret.publicKey
}
if recordPublicKey == publicKey {
switch record.info {
case let .ready(info, exportCompleted, _):
if exportCompleted {
let _ = (walletAddress(walletInfo: info, tonInstance: walletContext.tonInstance)
|> deliverOnMainQueue).start(next: { address in
let infoScreen = WalletInfoScreen(context: walletContext, walletInfo: info, address: address, enableDebugActions: false)
beginWithController(infoScreen)
})
} else {
let createdScreen = WalletSplashScreen(context: walletContext, mode: .created(walletInfo: info, words: nil), walletCreatedPreloadState: nil)
beginWithController(createdScreen)
}
case let .imported(info):
let createdScreen = WalletSplashScreen(context: walletContext, mode: .successfullyImported(importedInfo: info), walletCreatedPreloadState: nil)
beginWithController(createdScreen)
}
} else {

View File

@ -1004,6 +1004,8 @@ public func channelAdminController(context: AccountContext, peerId: PeerId, admi
if let peer = adminView.peers[adminView.peerId] {
text = presentationData.strings.Privacy_GroupsAndChannels_InviteToGroupError(peer.compactDisplayTitle, peer.compactDisplayTitle).0
}
case .notMutualContact:
text = presentationData.strings.GroupInfo_AddUserLeftError
default:
break
}

View File

@ -396,6 +396,8 @@ public func channelMembersController(context: AccountContext, peerId: PeerId) ->
text = presentationData.strings.Login_UnknownError
case .restricted:
text = presentationData.strings.Channel_ErrorAddBlocked
case .notMutualContact:
text = presentationData.strings.GroupInfo_AddUserLeftError
case let .bot(memberId):
let _ = (context.account.postbox.transaction { transaction in
return transaction.getPeer(peerId)

View File

@ -1750,7 +1750,7 @@ public func groupInfoController(context: AccountContext, peerId originalPeerId:
return state.withUpdatedTemporaryParticipants(temporaryParticipants).withUpdatedSuccessfullyAddedParticipantIds(successfullyAddedParticipantIds)
}
return .complete()
case .privacy:
case .privacy, .notMutualContact:
let _ = (context.account.postbox.loadedPeerWithId(memberId)
|> deliverOnMainQueue).start(next: { peer in
presentControllerImpl?(textAlertController(context: context, title: nil, text: presentationData.strings.Privacy_GroupsAndChannels_InviteToGroupError(peer.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), nil)

View File

@ -10,6 +10,7 @@ public enum AddGroupMemberError {
case generic
case groupFull
case privacy
case notMutualContact
case tooManyChannels
}
@ -26,6 +27,8 @@ public func addGroupMember(account: Account, peerId: PeerId, memberId: PeerId) -
return .privacy
case "USER_CHANNELS_TOO_MUCH":
return .tooManyChannels
case "USER_NOT_MUTUAL_CONTACT":
return .notMutualContact
default:
return .generic
}
@ -68,6 +71,7 @@ public func addGroupMember(account: Account, peerId: PeerId, memberId: PeerId) -
public enum AddChannelMemberError {
case generic
case restricted
case notMutualContact
case limitExceeded
case tooMuchJoined
case bot(PeerId)
@ -98,8 +102,10 @@ public func addChannelMember(account: Account, peerId: PeerId, memberId: PeerId)
return .fail(.tooMuchJoined)
case "USERS_TOO_MUCH":
return .fail(.limitExceeded)
case "USER_PRIVACY_RESTRICTED", "USER_NOT_MUTUAL_CONTACT":
case "USER_PRIVACY_RESTRICTED":
return .fail(.restricted)
case "USER_NOT_MUTUAL_CONTACT":
return .fail(.notMutualContact)
case "USER_BOT":
return .fail(.bot(memberId))
case "BOT_GROUPS_BLOCKED":
@ -194,8 +200,10 @@ public func addChannelMembers(account: Account, peerId: PeerId, memberIds: [Peer
switch error.errorDescription {
case "CHANNELS_TOO_MUCH":
return .tooMuchJoined
case "USER_PRIVACY_RESTRICTED", "USER_NOT_MUTUAL_CONTACT":
case "USER_PRIVACY_RESTRICTED":
return .restricted
case "USER_NOT_MUTUAL_CONTACT":
return .notMutualContact
case "USERS_TOO_MUCH":
return .limitExceeded
default:

View File

@ -3342,6 +3342,12 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
self?.controller?.present(textAlertController(context: context, title: nil, text: presentationData.strings.Privacy_GroupsAndChannels_InviteToGroupError(peer.compactDisplayTitle, peer.compactDisplayTitle).0, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
})
return .complete()
case .notMutualContact:
let _ = (context.account.postbox.loadedPeerWithId(memberId)
|> deliverOnMainQueue).start(next: { peer in
self?.controller?.present(textAlertController(context: context, title: nil, text: presentationData.strings.GroupInfo_AddUserLeftError, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
})
return .complete()
case .tooManyChannels:
let _ = (context.account.postbox.loadedPeerWithId(memberId)
|> deliverOnMainQueue).start(next: { peer in
@ -3399,7 +3405,7 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
return context.account.postbox.multiplePeersView(memberIds)
|> take(1)
|> deliverOnMainQueue
|> castError(AddChannelMemberError.self)
|> castError(AddChannelMemberError.self)
|> mapToSignal { view -> Signal<Void, AddChannelMemberError> in
if memberIds.count == 1 {
return context.peerChannelMemberCategoriesContextsManager.addMember(account: context.account, peerId: groupPeer.id, memberId: memberIds[0])
@ -3479,6 +3485,8 @@ private final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewD
default:
break
}
} else if peers.count == 1, case .notMutualContact = error {
self?.controller?.present(textAlertController(context: context, title: nil, text: presentationData.strings.GroupInfo_AddUserLeftError, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
} else if case .tooMuchJoined = error {
self?.controller?.present(textAlertController(context: context, title: nil, text: presentationData.strings.Invite_ChannelsTooMuch, actions: [TextAlertAction(type: .genericAction, title: presentationData.strings.Common_OK, action: {})]), in: .window(.root))
}

View File

@ -30,12 +30,15 @@ NS_ASSUME_NONNULL_BEGIN
@interface TONAccountState : NSObject
@property (nonatomic, readonly) bool isInitialized;
@property (nonatomic, readonly) bool isRWallet;
@property (nonatomic, readonly) int64_t balance;
@property (nonatomic, readonly) int64_t unlockedBalance;
@property (nonatomic, readonly) int32_t seqno;
@property (nonatomic, strong, readonly) TONTransactionId * _Nullable lastTransactionId;
@property (nonatomic, readonly) int64_t syncUtime;
- (instancetype)initWithIsInitialized:(bool)isInitialized balance:(int64_t)balance seqno:(int32_t)seqno lastTransactionId:(TONTransactionId * _Nullable)lastTransactionId syncUtime:(int64_t)syncUtime;
- (instancetype)initWithIsInitialized:(bool)isInitialized isRWallet:(bool)isRWallet balance:(int64_t)balance unlockedBalance:(int64_t)unlockedBalance seqno:(int32_t)seqno lastTransactionId:(TONTransactionId * _Nullable)lastTransactionId syncUtime:(int64_t)syncUtime;
@end
@ -166,7 +169,7 @@ NS_ASSUME_NONNULL_BEGIN
- (SSignal *)validateConfig:(NSString *)config blockchainName:(NSString *)blockchainName;
- (SSignal *)createKeyWithLocalPassword:(NSData *)localPassword mnemonicPassword:(NSData *)mnemonicPassword;
- (SSignal *)getWalletAccountAddressWithPublicKey:(NSString *)publicKey initialWalletId:(int64_t)initialWalletId;
- (SSignal *)getWalletAccountAddressWithPublicKey:(NSString *)publicKey initialWalletId:(int64_t)initialWalletId rwalletInitialPublicKey:(NSString * _Nullable)rwalletInitialPublicKey;
- (SSignal *)getAccountStateWithAddress:(NSString *)accountAddress;
- (SSignal *)generateSendGramsQueryFromKey:(TONKey *)key localPassword:(NSData *)localPassword fromAddress:(NSString *)fromAddress toAddress:(NSString *)address amount:(int64_t)amount comment:(NSData *)comment encryptComment:(bool)encryptComment forceIfDestinationNotInitialized:(bool)forceIfDestinationNotInitialized timeout:(int32_t)timeout randomId:(int64_t)randomId;
- (SSignal *)generateFakeSendGramsQueryFromAddress:(NSString *)fromAddress toAddress:(NSString *)address amount:(int64_t)amount comment:(NSData *)comment encryptComment:(bool)encryptComment forceIfDestinationNotInitialized:(bool)forceIfDestinationNotInitialized timeout:(int32_t)timeout;

View File

@ -98,11 +98,13 @@ static TONTransactionMessage * _Nullable parseTransactionMessage(tonlib_api::obj
@implementation TONAccountState
- (instancetype)initWithIsInitialized:(bool)isInitialized balance:(int64_t)balance seqno:(int32_t)seqno lastTransactionId:(TONTransactionId * _Nullable)lastTransactionId syncUtime:(int64_t)syncUtime {
- (instancetype)initWithIsInitialized:(bool)isInitialized isRWallet:(bool)isRWallet balance:(int64_t)balance unlockedBalance:(int64_t)unlockedBalance seqno:(int32_t)seqno lastTransactionId:(TONTransactionId * _Nullable)lastTransactionId syncUtime:(int64_t)syncUtime {
self = [super init];
if (self != nil) {
_isInitialized = isInitialized;
_isRWallet = isRWallet;
_balance = balance;
_unlockedBalance = unlockedBalance;
_seqno = seqno;
_lastTransactionId = lastTransactionId;
_syncUtime = syncUtime;
@ -632,7 +634,7 @@ typedef enum {
}] startOn:[SQueue mainQueue]] deliverOn:[SQueue mainQueue]];
}
- (SSignal *)getWalletAccountAddressWithPublicKey:(NSString *)publicKey initialWalletId:(int64_t)initialWalletId {
- (SSignal *)getWalletAccountAddressWithPublicKey:(NSString *)publicKey initialWalletId:(int64_t)initialWalletId rwalletInitialPublicKey:(NSString * _Nullable)rwalletInitialPublicKey {
return [[[[SSignal alloc] initWithGenerator:^id<SDisposable>(SSubscriber *subscriber) {
NSData *publicKeyData = [publicKey dataUsingEncoding:NSUTF8StringEncoding];
if (publicKeyData == nil) {
@ -640,6 +642,15 @@ typedef enum {
return [[SBlockDisposable alloc] initWithBlock:^{}];
}
NSData *rwalletInitialPublicKeyData = nil;
if (rwalletInitialPublicKey != nil) {
rwalletInitialPublicKeyData = [rwalletInitialPublicKey dataUsingEncoding:NSUTF8StringEncoding];
if (rwalletInitialPublicKeyData == nil) {
[subscriber putError:[[TONError alloc] initWithText:@"Error encoding UTF8 string for rwalletInitialPublicKey in getWalletAccountAddressWithPublicKey"]];
return [[SBlockDisposable alloc] initWithBlock:^{}];
}
}
uint64_t requestId = _nextRequestId;
_nextRequestId += 1;
@ -656,14 +667,27 @@ typedef enum {
}
}];
auto initialAccountState = make_object<tonlib_api::wallet_v3_initialAccountState>(
makeString(publicKeyData),
initialWalletId
);
tonlib_api::object_ptr<tonlib_api::InitialAccountState> initialAccountState;
std::int32_t revision;
if (rwalletInitialPublicKey != nil) {
initialAccountState = make_object<tonlib_api::rwallet_initialAccountState>(
makeString(rwalletInitialPublicKeyData),
makeString(publicKeyData),
initialWalletId
);
revision = -1;
} else {
initialAccountState = tonlib_api::move_object_as<tonlib_api::InitialAccountState>(make_object<tonlib_api::wallet_v3_initialAccountState>(
makeString(publicKeyData),
initialWalletId
));
revision = 1;
}
auto query = make_object<tonlib_api::getAccountAddress>(
tonlib_api::move_object_as<tonlib_api::InitialAccountState>(initialAccountState),
1
revision
);
_client->send({ requestId, std::move(query) });
@ -684,12 +708,21 @@ typedef enum {
} else if (object->get_id() == tonlib_api::fullAccountState::ID) {
auto fullAccountState = tonlib_api::move_object_as<tonlib_api::fullAccountState>(object);
int32_t seqNo = -1;
bool isRWallet = false;
int64_t unlockedBalance = INT64_MAX;
if (fullAccountState->account_state_->get_id() == tonlib_api::uninited_accountState::ID) {
seqNo = -1;
} else if (fullAccountState->account_state_->get_id() == tonlib_api::wallet_v3_accountState::ID) {
auto v3AccountState = tonlib_api::move_object_as<tonlib_api::wallet_v3_accountState>(fullAccountState->account_state_);
seqNo = v3AccountState->seqno_;
} else {
} else if (fullAccountState->account_state_->get_id() == tonlib_api::rwallet_accountState::ID) {
auto rwalletAccountState = tonlib_api::move_object_as<tonlib_api::rwallet_accountState>(fullAccountState->account_state_);
isRWallet = true;
unlockedBalance = rwalletAccountState->unlocked_balance_;
seqNo = rwalletAccountState->seqno_;
}else {
[subscriber putError:[[TONError alloc] initWithText:@"Unknown type"]];
return;
}
@ -698,7 +731,7 @@ typedef enum {
if (fullAccountState->last_transaction_id_ != nullptr) {
lastTransactionId = [[TONTransactionId alloc] initWithLt:fullAccountState->last_transaction_id_->lt_ transactionHash:makeData(fullAccountState->last_transaction_id_->hash_)];
}
[subscriber putNext:[[TONAccountState alloc] initWithIsInitialized:false balance:fullAccountState->balance_ seqno:-1 lastTransactionId:lastTransactionId syncUtime:fullAccountState->sync_utime_]];
[subscriber putNext:[[TONAccountState alloc] initWithIsInitialized:false isRWallet:isRWallet balance:fullAccountState->balance_ unlockedBalance:unlockedBalance seqno:-1 lastTransactionId:lastTransactionId syncUtime:fullAccountState->sync_utime_]];
[subscriber putCompletion];
} else {
assert(false);
@ -785,7 +818,8 @@ typedef enum {
),
make_object<tonlib_api::accountAddress>(fromAddress.UTF8String),
timeout,
tonlib_api::move_object_as<tonlib_api::Action>(inputAction)
tonlib_api::move_object_as<tonlib_api::Action>(inputAction),
nil
);
_client->send({ requestId, std::move(query) });
@ -840,7 +874,8 @@ typedef enum {
make_object<tonlib_api::inputKeyFake>(),
make_object<tonlib_api::accountAddress>(fromAddress.UTF8String),
timeout,
tonlib_api::move_object_as<tonlib_api::Action>(inputAction)
tonlib_api::move_object_as<tonlib_api::Action>(inputAction),
nil
);
_client->send({ requestId, std::move(query) });

View File

@ -198,7 +198,7 @@ public final class TonInstance {
}
let _ = keychain.encrypt(key.secret).start(next: { encryptedSecretData in
let _ = self.exportKey(key: key, localPassword: localPassword).start(next: { wordList in
subscriber.putNext((WalletInfo(publicKey: WalletPublicKey(rawValue: key.publicKey), encryptedSecret: encryptedSecretData), wordList))
subscriber.putNext((WalletInfo(publicKey: WalletPublicKey(rawValue: key.publicKey), rWalletInitialPublicKey: nil, encryptedSecret: encryptedSecretData), wordList))
subscriber.putCompletion()
}, error: { error in
subscriber.putError(.generic)
@ -220,7 +220,7 @@ public final class TonInstance {
}
}
fileprivate func importWallet(keychain: TonKeychain, wordList: [String], localPassword: Data) -> Signal<WalletInfo, ImportWalletInternalError> {
fileprivate func importWallet(keychain: TonKeychain, wordList: [String], localPassword: Data) -> Signal<ImportedWalletInfo, ImportWalletInternalError> {
return Signal { subscriber in
let disposable = MetaDisposable()
@ -232,7 +232,7 @@ public final class TonInstance {
return
}
let _ = keychain.encrypt(key.secret).start(next: { encryptedSecretData in
subscriber.putNext(WalletInfo(publicKey: WalletPublicKey(rawValue: key.publicKey), encryptedSecret: encryptedSecretData))
subscriber.putNext(ImportedWalletInfo(publicKey: WalletPublicKey(rawValue: key.publicKey), encryptedSecret: encryptedSecretData))
subscriber.putCompletion()
}, error: { _ in
subscriber.putError(.generic)
@ -283,7 +283,7 @@ public final class TonInstance {
}
}
fileprivate func walletAddress(publicKey: WalletPublicKey) -> Signal<String, NoError> {
fileprivate func walletAddress(publicKey: WalletPublicKey, rwalletInitialPublicKey: WalletInitialPublicKey?) -> Signal<String, NoError> {
return self.getInitialWalletId()
|> `catch` { _ -> Signal<Int64, NoError> in
return .single(0)
@ -294,7 +294,7 @@ public final class TonInstance {
self.impl.with { impl in
impl.withInstance { ton in
let cancel = ton.getWalletAccountAddress(withPublicKey: publicKey.rawValue, initialWalletId: initialWalletId).start(next: { address in
let cancel = ton.getWalletAccountAddress(withPublicKey: publicKey.rawValue, initialWalletId: initialWalletId, rwalletInitialPublicKey: rwalletInitialPublicKey?.rawValue).start(next: { address in
guard let address = address as? String else {
return
}
@ -353,7 +353,7 @@ public final class TonInstance {
fileprivate func getWalletState(address: String) -> Signal<(WalletState, Int64), GetWalletStateError> {
return self.getWalletStateRaw(address: address)
|> map { state in
return (WalletState(balance: state.balance, lastTransactionId: state.lastTransactionId.flatMap(WalletTransactionId.init(tonTransactionId:))), state.syncUtime)
return (WalletState(totalBalance: state.balance, unlockedBalance: state.isRWallet ? state.unlockedBalance : nil, lastTransactionId: state.lastTransactionId.flatMap(WalletTransactionId.init(tonTransactionId:))), state.syncUtime)
}
}
@ -717,7 +717,27 @@ public struct WalletPublicKey: Codable, Hashable {
}
}
public struct WalletInitialPublicKey: Codable, Hashable {
public var rawValue: String
public init(rawValue: String) {
self.rawValue = rawValue
}
}
public struct WalletInfo: Codable, Equatable {
public let publicKey: WalletPublicKey
public let rWalletInitialPublicKey: WalletInitialPublicKey?
public let encryptedSecret: TonKeychainEncryptedData
public init(publicKey: WalletPublicKey, rWalletInitialPublicKey: WalletInitialPublicKey?, encryptedSecret: TonKeychainEncryptedData) {
self.publicKey = publicKey
self.rWalletInitialPublicKey = rWalletInitialPublicKey
self.encryptedSecret = encryptedSecret
}
}
public struct ImportedWalletInfo: Codable, Equatable {
public let publicKey: WalletPublicKey
public let encryptedSecret: TonKeychainEncryptedData
@ -743,15 +763,50 @@ public struct CombinedWalletState: Codable, Equatable {
}
}
public enum WalletStateRecordDecodingError: Error {
case generic
}
public struct WalletStateRecord: Codable, Equatable {
public let info: WalletInfo
public var exportCompleted: Bool
public var state: CombinedWalletState?
enum Key: CodingKey {
case info
case exportCompleted
case state
case importedInfo
}
public init(info: WalletInfo, exportCompleted: Bool, state: CombinedWalletState?) {
public enum Info: Equatable {
case ready(info: WalletInfo, exportCompleted: Bool, state: CombinedWalletState?)
case imported(info: ImportedWalletInfo)
}
public var info: WalletStateRecord.Info
public init(info: WalletStateRecord.Info) {
self.info = info
self.exportCompleted = exportCompleted
self.state = state
}
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: Key.self)
if let info = try? container.decode(WalletInfo.self, forKey: .info) {
self.info = .ready(info: info, exportCompleted: (try? container.decode(Bool.self, forKey: .exportCompleted)) ?? false, state: try? container.decode(Optional<CombinedWalletState>.self, forKey: .state))
} else if let info = try? container.decode(ImportedWalletInfo.self, forKey: .importedInfo) {
self.info = .imported(info: info)
} else {
throw WalletStateRecordDecodingError.generic
}
}
public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: Key.self)
switch info {
case let .ready(info, exportCompleted, state):
try container.encode(info, forKey: .info)
try container.encode(exportCompleted, forKey: .exportCompleted)
try container.encode(state, forKey: .state)
case let .imported(info):
try container.encode(info, forKey: .importedInfo)
}
}
}
@ -771,7 +826,7 @@ public func createWallet(storage: WalletStorageInterface, tonInstance: TonInstan
|> mapToSignal { walletInfo, wordList -> Signal<(WalletInfo, [String]), CreateWalletError> in
return storage.updateWalletRecords({ records in
var records = records
records.append(WalletStateRecord(info: walletInfo, exportCompleted: false, state: nil))
records.append(WalletStateRecord(info: .ready(info: walletInfo, exportCompleted: false, state: nil)))
return records
})
|> map { _ -> (WalletInfo, [String]) in
@ -785,8 +840,13 @@ public func confirmWalletExported(storage: WalletStorageInterface, publicKey: Wa
return storage.updateWalletRecords { records in
var records = records
for i in 0 ..< records.count {
if records[i].info.publicKey == publicKey {
records[i].exportCompleted = true
switch records[i].info {
case let .ready(info, _, state):
if info.publicKey == publicKey {
records[i].info = .ready(info: info, exportCompleted: true, state: state)
}
case .imported:
break
}
}
return records
@ -802,21 +862,21 @@ public enum ImportWalletError {
case generic
}
public func importWallet(storage: WalletStorageInterface, tonInstance: TonInstance, keychain: TonKeychain, wordList: [String], localPassword: Data) -> Signal<WalletInfo, ImportWalletError> {
public func importWallet(storage: WalletStorageInterface, tonInstance: TonInstance, keychain: TonKeychain, wordList: [String], localPassword: Data) -> Signal<ImportedWalletInfo, ImportWalletError> {
return tonInstance.importWallet(keychain: keychain, wordList: wordList, localPassword: localPassword)
|> `catch` { error -> Signal<WalletInfo, ImportWalletError> in
|> `catch` { error -> Signal<ImportedWalletInfo, ImportWalletError> in
switch error {
case .generic:
return .fail(.generic)
}
}
|> mapToSignal { walletInfo -> Signal<WalletInfo, ImportWalletError> in
|> mapToSignal { walletInfo -> Signal<ImportedWalletInfo, ImportWalletError> in
return storage.updateWalletRecords { records in
var records = records
records.append(WalletStateRecord(info: walletInfo, exportCompleted: true, state: nil))
records.append(WalletStateRecord(info: .imported(info: walletInfo)))
return records
}
|> map { _ -> WalletInfo in
|> map { _ -> ImportedWalletInfo in
return walletInfo
}
|> castError(ImportWalletError.self)
@ -848,17 +908,29 @@ public func walletRestoreWords(tonInstance: TonInstance, publicKey: WalletPublic
}
public struct WalletState: Codable, Equatable {
public let balance: Int64
public let totalBalance: Int64
public let unlockedBalance: Int64?
public let lastTransactionId: WalletTransactionId?
public init(balance: Int64, lastTransactionId: WalletTransactionId?) {
self.balance = balance
public init(totalBalance: Int64, unlockedBalance: Int64?, lastTransactionId: WalletTransactionId?) {
self.totalBalance = totalBalance
self.unlockedBalance = unlockedBalance
self.lastTransactionId = lastTransactionId
}
}
public func walletAddress(publicKey: WalletPublicKey, tonInstance: TonInstance) -> Signal<String, NoError> {
return tonInstance.walletAddress(publicKey: publicKey)
public extension WalletState {
var effectiveAvailableBalance: Int64 {
if let unlockedBalance = self.unlockedBalance {
return unlockedBalance
} else {
return self.totalBalance
}
}
}
public func walletAddress(walletInfo: WalletInfo, tonInstance: TonInstance) -> Signal<String, NoError> {
return tonInstance.walletAddress(publicKey: walletInfo.publicKey, rwalletInitialPublicKey: walletInfo.rWalletInitialPublicKey)
}
private enum GetWalletStateError {
@ -882,7 +954,39 @@ public enum CombinedWalletStateResult {
public enum CombinedWalletStateSubject {
case wallet(WalletInfo)
case address(String)
}
public enum GetWalletInfoError {
case generic
case network
}
public func getWalletInfo(importedInfo: ImportedWalletInfo, tonInstance: TonInstance) -> Signal<WalletInfo, GetWalletInfoError> {
let rwalletInitialPublicKey = WalletInitialPublicKey(rawValue: "Pua8zmvG8934jf2mAysxTMUJUaxoXQskZKfqsAoGUjS2Kj4J")
return tonInstance.walletAddress(publicKey: importedInfo.publicKey, rwalletInitialPublicKey: rwalletInitialPublicKey)
|> castError(GetWalletInfoError.self)
|> mapToSignal { address -> Signal<WalletInfo, GetWalletInfoError> in
return tonInstance.getWalletState(address: address)
|> mapError { error -> GetWalletInfoError in
switch error {
case .generic:
return .generic
case .network:
return .network
}
}
|> mapToSignal { state -> Signal<WalletInfo, GetWalletInfoError> in
if state.0.unlockedBalance != nil {
return .single(WalletInfo(publicKey: importedInfo.publicKey, rWalletInitialPublicKey: rwalletInitialPublicKey, encryptedSecret: importedInfo.encryptedSecret))
} else {
return tonInstance.walletAddress(publicKey: importedInfo.publicKey, rwalletInitialPublicKey: nil)
|> castError(GetWalletInfoError.self)
|> map { address -> WalletInfo in
return WalletInfo(publicKey: importedInfo.publicKey, rWalletInitialPublicKey: nil, encryptedSecret: importedInfo.encryptedSecret)
}
}
}
}
}
public func getCombinedWalletState(storage: WalletStorageInterface, subject: CombinedWalletStateSubject, tonInstance: TonInstance, onlyCached: Bool) -> Signal<CombinedWalletStateResult, GetCombinedWalletStateError> {
@ -891,8 +995,13 @@ public func getCombinedWalletState(storage: WalletStorageInterface, subject: Com
return storage.getWalletRecords()
|> map { records -> CombinedWalletState? in
for item in records {
if item.info.publicKey == walletInfo.publicKey {
return item.state
switch item.info {
case let .ready(itemInfo, _, state):
if itemInfo.publicKey == walletInfo.publicKey {
return state
}
case .imported:
break
}
}
return nil
@ -904,7 +1013,7 @@ public func getCombinedWalletState(storage: WalletStorageInterface, subject: Com
}
return .single(.cached(cachedState))
|> then(
tonInstance.walletAddress(publicKey: walletInfo.publicKey)
tonInstance.walletAddress(publicKey: walletInfo.publicKey, rwalletInitialPublicKey: walletInfo.rWalletInitialPublicKey)
|> castError(GetCombinedWalletStateError.self)
|> mapToSignal { address -> Signal<CombinedWalletStateResult, GetCombinedWalletStateError> in
@ -981,8 +1090,13 @@ public func getCombinedWalletState(storage: WalletStorageInterface, subject: Com
return storage.updateWalletRecords { records in
var records = records
for i in 0 ..< records.count {
if records[i].info.publicKey == walletInfo.publicKey {
records[i].state = combinedState
switch records[i].info {
case let .ready(itemInfo, exportCompleted, _):
if itemInfo.publicKey == walletInfo.publicKey {
records[i].info = .ready(info: itemInfo, exportCompleted: exportCompleted, state: combinedState)
}
case .imported:
break
}
}
return records
@ -996,26 +1110,6 @@ public func getCombinedWalletState(storage: WalletStorageInterface, subject: Com
}
)
}
case let .address(address):
let updated = getWalletState(address: address, tonInstance: tonInstance)
|> mapError { _ -> GetCombinedWalletStateError in
return .generic
}
|> mapToSignal { walletState, syncUtime -> Signal<CombinedWalletStateResult, GetCombinedWalletStateError> in
let topTransactions: Signal<[WalletTransaction], GetCombinedWalletStateError>
topTransactions = getWalletTransactions(address: address, previousId: nil, tonInstance: tonInstance)
|> mapError { _ -> GetCombinedWalletStateError in
return .generic
}
return topTransactions
|> mapToSignal { topTransactions -> Signal<CombinedWalletStateResult, GetCombinedWalletStateError> in
let combinedState = CombinedWalletState(walletState: walletState, timestamp: syncUtime, topTransactions: topTransactions, pendingTransactions: [])
return .single(.updated(combinedState))
}
}
return .single(.cached(nil))
|> then(updated)
}
}
@ -1042,7 +1136,7 @@ public struct SendGramsVerificationResult {
}
public func verifySendGramsRequestAndEstimateFees(tonInstance: TonInstance, walletInfo: WalletInfo, toAddress: String, amount: Int64, comment: Data, encryptComment: Bool, timeout: Int32) -> Signal<SendGramsVerificationResult, SendGramsFromWalletError> {
return walletAddress(publicKey: walletInfo.publicKey, tonInstance: tonInstance)
return walletAddress(walletInfo: walletInfo, tonInstance: tonInstance)
|> castError(SendGramsFromWalletError.self)
|> mapToSignal { fromAddress -> Signal<SendGramsVerificationResult, SendGramsFromWalletError> in
struct QueryWithInfo {
@ -1074,7 +1168,7 @@ public func verifySendGramsRequestAndEstimateFees(tonInstance: TonInstance, wall
}
public func sendGramsFromWallet(storage: WalletStorageInterface, tonInstance: TonInstance, walletInfo: WalletInfo, decryptedSecret: Data, localPassword: Data, toAddress: String, amount: Int64, comment: Data, encryptComment: Bool, forceIfDestinationNotInitialized: Bool, timeout: Int32, randomId: Int64) -> Signal<PendingWalletTransaction, SendGramsFromWalletError> {
return walletAddress(publicKey: walletInfo.publicKey, tonInstance: tonInstance)
return walletAddress(walletInfo: walletInfo, tonInstance: tonInstance)
|> castError(SendGramsFromWalletError.self)
|> mapToSignal { fromAddress -> Signal<PendingWalletTransaction, SendGramsFromWalletError> in
return tonInstance.prepareSendGramsFromWalletQuery(decryptedSecret: decryptedSecret, localPassword: localPassword, walletInfo: walletInfo, fromAddress: fromAddress, toAddress: toAddress, amount: amount, comment: comment, encryptComment: encryptComment, forceIfDestinationNotInitialized: forceIfDestinationNotInitialized, timeout: timeout, randomId: randomId)
@ -1094,11 +1188,14 @@ public func sendGramsFromWallet(storage: WalletStorageInterface, tonInstance: To
return storage.updateWalletRecords { records in
var records = records
for i in 0 ..< records.count {
if records[i].info.publicKey == walletInfo.publicKey {
if var state = records[i].state {
switch records[i].info {
case let .ready(itemInfo, exportCompleted, itemState):
if itemInfo.publicKey == walletInfo.publicKey, var state = itemState {
state.pendingTransactions.insert(result, at: 0)
records[i].state = state
records[i].info = .ready(info: itemInfo, exportCompleted: exportCompleted, state: state)
}
case .imported:
break
}
}
return records

View File

@ -44,7 +44,7 @@ private class WalletInfoTitleView: UIView, NavigationBarTitleView {
public final class WalletInfoScreen: ViewController {
private let context: WalletContext
private let walletInfo: WalletInfo?
private let walletInfo: WalletInfo
private let address: String
private let enableDebugActions: Bool
@ -55,7 +55,7 @@ public final class WalletInfoScreen: ViewController {
return self._ready
}
public init(context: WalletContext, walletInfo: WalletInfo?, address: String, enableDebugActions: Bool) {
public init(context: WalletContext, walletInfo: WalletInfo, address: String, enableDebugActions: Bool) {
self.context = context
self.walletInfo = walletInfo
self.address = address
@ -73,9 +73,7 @@ public final class WalletInfoScreen: ViewController {
self.navigationBar?.intrinsicCanTransitionInline = false
self.navigationItem.backBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Wallet_Navigation_Back, style: .plain, target: nil, action: nil)
if let _ = walletInfo {
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: generateTintedImage(image: UIImage(bundleImageName: "Wallet/NavigationSettingsIcon"), color: .white), style: .plain, target: self, action: #selector(self.settingsPressed))
}
self.navigationItem.rightBarButtonItem = UIBarButtonItem(image: generateTintedImage(image: UIImage(bundleImageName: "Wallet/NavigationSettingsIcon"), color: .white), style: .plain, target: self, action: #selector(self.settingsPressed))
self.navigationItem.titleView = WalletInfoTitleView(action: { [weak self] in self?.scrollToTop?() })
@ -93,16 +91,15 @@ public final class WalletInfoScreen: ViewController {
}
@objc private func settingsPressed() {
if let walletInfo = self.walletInfo {
self.push(walletSettingsController(context: self.context, walletInfo: walletInfo))
}
self.push(walletSettingsController(context: self.context, walletInfo: self.walletInfo))
}
override public func loadDisplayNode() {
self.displayNode = WalletInfoScreenNode(context: self.context, presentationData: self.presentationData, walletInfo: self.walletInfo, address: self.address, sendAction: { [weak self] in
guard let strongSelf = self, let walletInfo = strongSelf.walletInfo else {
guard let strongSelf = self else {
return
}
let walletInfo = strongSelf.walletInfo
guard let combinedState = (strongSelf.displayNode as! WalletInfoScreenNode).combinedState else {
return
}
@ -122,10 +119,16 @@ public final class WalletInfoScreen: ViewController {
strongSelf.push(walletSendScreen(context: strongSelf.context, randomId: randomId, walletInfo: walletInfo))
}
}, receiveAction: { [weak self] in
guard let strongSelf = self, let _ = strongSelf.walletInfo else {
guard let strongSelf = self else {
return
}
strongSelf.push(WalletReceiveScreen(context: strongSelf.context, mode: .receive(address: strongSelf.address)))
let _ = (walletAddress(walletInfo: strongSelf.walletInfo, tonInstance: strongSelf.context.tonInstance)
|> deliverOnMainQueue).start(next: { address in
guard let strongSelf = self else {
return
}
strongSelf.push(WalletReceiveScreen(context: strongSelf.context, mode: .receive(address: address)))
})
}, openTransaction: { [weak self] transaction in
guard let strongSelf = self else {
return
@ -274,7 +277,8 @@ private final class WalletInfoHeaderNode: ASDisplayNode {
let balanceNode: WalletInfoBalanceNode
let refreshNode: WalletRefreshNode
private let balanceSubtitleNode: ImmediateTextNode
let balanceSubtitleNode: ImmediateTextNode
let balanceSubtitleIconNode: AnimatedStickerNode
private let receiveButtonNode: SolidRoundedButtonNode
private let receiveGramsButtonNode: SolidRoundedButtonNode
private let sendButtonNode: SolidRoundedButtonNode
@ -288,7 +292,15 @@ private final class WalletInfoHeaderNode: ASDisplayNode {
self.balanceSubtitleNode = ImmediateTextNode()
self.balanceSubtitleNode.displaysAsynchronously = false
self.balanceSubtitleNode.attributedText = NSAttributedString(string: hasActions ? presentationData.strings.Wallet_Info_YourBalance : "balance", font: Font.regular(13), textColor: UIColor(white: 1.0, alpha: 0.6))
self.balanceSubtitleNode.attributedText = NSAttributedString(string: presentationData.strings.Wallet_Info_YourBalance, font: Font.regular(13), textColor: UIColor(white: 1.0, alpha: 0.6))
self.balanceSubtitleIconNode = AnimatedStickerNode()
self.balanceSubtitleIconNode.isHidden = true
if let path = getAppBundle().path(forResource: "WalletIntroStatic", ofType: "tgs") {
self.balanceSubtitleIconNode.setup(source: AnimatedStickerNodeLocalFileSource(path: path), width: 36, height: 36, mode: .direct)
self.balanceSubtitleIconNode.visibility = true
}
self.balanceSubtitleNode.addSubnode(self.balanceSubtitleIconNode)
self.headerBackgroundNode = ASDisplayNode()
self.headerBackgroundNode.backgroundColor = .black
@ -353,6 +365,9 @@ private final class WalletInfoHeaderNode: ASDisplayNode {
let buttonAlpha: CGFloat = buttonTransition
let balanceSubtitleSize = self.balanceSubtitleNode.updateLayout(CGSize(width: size.width - sideInset * 2.0, height: 200.0))
let balanceSubtitleIconSize = CGSize(width: 18.0, height: 18.0)
self.balanceSubtitleIconNode.frame = CGRect(origin: CGPoint(x: -balanceSubtitleIconSize.width - 2.0, y: -2.0), size: balanceSubtitleIconSize)
self.balanceSubtitleIconNode.updateLayout(size: balanceSubtitleIconSize)
let headerScaleTransition: CGFloat = max(0.0, min(1.0, (effectiveOffset - minHeaderOffset) / (maxHeaderOffset - minHeaderOffset)))
@ -544,7 +559,7 @@ private func preparedTransition(from fromEntries: [WalletInfoListEntry], to toEn
private final class WalletInfoScreenNode: ViewControllerTracingNode {
private let context: WalletContext
private var presentationData: WalletPresentationData
private let walletInfo: WalletInfo?
private let walletInfo: WalletInfo
private let address: String
private let openTransaction: (WalletInfoTransaction) -> Void
@ -585,7 +600,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
private var watchCombinedStateDisposable: Disposable?
private var refreshProgressDisposable: Disposable?
init(context: WalletContext, presentationData: WalletPresentationData, walletInfo: WalletInfo?, address: String, sendAction: @escaping () -> Void, receiveAction: @escaping () -> Void, openTransaction: @escaping (WalletInfoTransaction) -> Void, present: @escaping (ViewController, Any?) -> Void) {
init(context: WalletContext, presentationData: WalletPresentationData, walletInfo: WalletInfo, address: String, sendAction: @escaping () -> Void, receiveAction: @escaping () -> Void, openTransaction: @escaping (WalletInfoTransaction) -> Void, present: @escaping (ViewController, Any?) -> Void) {
self.context = context
self.presentationData = presentationData
self.walletInfo = walletInfo
@ -593,7 +608,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
self.openTransaction = openTransaction
self.present = present
self.headerNode = WalletInfoHeaderNode(presentationData: presentationData, hasActions: walletInfo != nil, sendAction: sendAction, receiveAction: receiveAction)
self.headerNode = WalletInfoHeaderNode(presentationData: presentationData, hasActions: true, sendAction: sendAction, receiveAction: receiveAction)
self.listNode = ListView()
self.listNode.verticalScrollIndicatorColor = UIColor(white: 0.0, alpha: 0.3)
@ -681,60 +696,55 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
}, queue: .mainQueue())
self.updateTimestampTimer?.start()
let subject: CombinedWalletStateSubject
if let walletInfo = walletInfo {
subject = .wallet(walletInfo)
let subject: CombinedWalletStateSubject = .wallet(walletInfo)
let watchCombinedStateSignal = context.storage.watchWalletRecords()
|> map { records -> WalletStateRecord? in
for record in records {
if record.info.publicKey == walletInfo.publicKey {
return record
let watchCombinedStateSignal = context.storage.watchWalletRecords()
|> map { records -> CombinedWalletState? in
for record in records {
switch record.info {
case let .ready(itemInfo, _, state):
if itemInfo.publicKey == walletInfo.publicKey {
return state
}
}
return nil
}
|> distinctUntilChanged
|> mapToSignal { wallet -> Signal<CombinedWalletState?, NoError> in
guard let wallet = wallet, let state = wallet.state else {
return .single(nil)
}
return .single(state)
}
let tonInstance = self.context.tonInstance
let decryptedWalletState = combineLatest(queue: .mainQueue(),
watchCombinedStateSignal,
self.transactionDecryptionKey.get()
)
|> mapToSignal { maybeState, decryptionKey -> Signal<CombinedWalletState?, NoError> in
guard let state = maybeState, let decryptionKey = decryptionKey else {
return .single(maybeState)
}
return decryptWalletTransactions(decryptionKey: decryptionKey, transactions: state.topTransactions, tonInstance: tonInstance)
|> `catch` { _ -> Signal<[WalletTransaction], NoError> in
return .single(state.topTransactions)
}
|> map { transactions -> CombinedWalletState? in
return state.withTopTransactions(transactions)
case .imported:
break
}
}
self.watchCombinedStateDisposable = (decryptedWalletState
|> deliverOnMainQueue).start(next: { [weak self] state in
guard let strongSelf = self, let state = state else {
return
}
if state.pendingTransactions != strongSelf.combinedState?.pendingTransactions || state.timestamp != strongSelf.combinedState?.timestamp {
if !strongSelf.reloadingState {
strongSelf.updateCombinedState(combinedState: state, isUpdated: true)
}
}
})
} else {
subject = .address(address)
return nil
}
|> distinctUntilChanged
let tonInstance = self.context.tonInstance
let decryptedWalletState = combineLatest(queue: .mainQueue(),
watchCombinedStateSignal,
self.transactionDecryptionKey.get()
)
|> mapToSignal { maybeState, decryptionKey -> Signal<CombinedWalletState?, NoError> in
guard let state = maybeState, let decryptionKey = decryptionKey else {
return .single(maybeState)
}
return decryptWalletTransactions(decryptionKey: decryptionKey, transactions: state.topTransactions, tonInstance: tonInstance)
|> `catch` { _ -> Signal<[WalletTransaction], NoError> in
return .single(state.topTransactions)
}
|> map { transactions -> CombinedWalletState? in
return state.withTopTransactions(transactions)
}
}
self.watchCombinedStateDisposable = (decryptedWalletState
|> deliverOnMainQueue).start(next: { [weak self] state in
guard let strongSelf = self, let state = state else {
return
}
if state.pendingTransactions != strongSelf.combinedState?.pendingTransactions || state.timestamp != strongSelf.combinedState?.timestamp {
if !strongSelf.reloadingState {
strongSelf.updateCombinedState(combinedState: state, isUpdated: true)
}
}
})
let pollCombinedState: Signal<Never, NoError> = (
getCombinedWalletState(storage: context.storage, subject: subject, tonInstance: context.tonInstance, onlyCached: false)
|> ignoreValues
@ -908,12 +918,7 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
self.headerNode.isRefreshing = true
self.headerNode.refreshNode.refreshProgress = 0.0
let subject: CombinedWalletStateSubject
if let walletInfo = self.walletInfo {
subject = .wallet(walletInfo)
} else {
subject = .address(self.address)
}
let subject: CombinedWalletStateSubject = .wallet(self.walletInfo)
let transactionDecryptionKey = self.transactionDecryptionKey
let tonInstance = self.context.tonInstance
@ -1017,8 +1022,23 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
private func updateCombinedState(combinedState: CombinedWalletState?, isUpdated: Bool) {
self.combinedState = combinedState
if let combinedState = combinedState {
self.headerNode.balanceNode.balance = (formatBalanceText(max(0, combinedState.walletState.balance), decimalSeparator: self.presentationData.dateTimeFormat.decimalSeparator), .white)
self.headerNode.balance = max(0, combinedState.walletState.balance)
self.headerNode.balanceNode.balance = (formatBalanceText(max(0, combinedState.walletState.effectiveAvailableBalance), decimalSeparator: self.presentationData.dateTimeFormat.decimalSeparator), .white)
if let unlockedBalance = combinedState.walletState.unlockedBalance {
let lockedBalance = combinedState.walletState.totalBalance - unlockedBalance
let balanceText = formatBalanceText(max(0, lockedBalance), decimalSeparator: self.presentationData.dateTimeFormat.decimalSeparator)
let string = NSMutableAttributedString()
string.append(NSAttributedString(string: "\(balanceText)", font: Font.semibold(13), textColor: .white))
string.append(NSAttributedString(string: " locked", font: Font.regular(13), textColor: .white))
self.headerNode.balanceSubtitleNode.attributedText = string
self.headerNode.balanceSubtitleIconNode.isHidden = false
} else {
self.headerNode.balanceSubtitleNode.attributedText = NSAttributedString(string: self.presentationData.strings.Wallet_Info_YourBalance, font: Font.regular(13), textColor: UIColor(white: 1.0, alpha: 0.6))
self.headerNode.balanceSubtitleIconNode.isHidden = true
}
self.headerNode.balance = max(0, combinedState.walletState.effectiveAvailableBalance)
if self.isReady, let (layout, navigationHeight) = self.validLayout {
self.containerLayoutUpdated(layout: layout, navigationHeight: navigationHeight, transition: .immediate)
@ -1117,7 +1137,12 @@ private final class WalletInfoScreenNode: ViewControllerTracingNode {
}
let transactionDecryptionKey = self.transactionDecryptionKey
let tonInstance = self.context.tonInstance
let processedTransactions = getWalletTransactions(address: self.address, previousId: lastTransactionId, tonInstance: self.context.tonInstance)
let requestTransactions = walletAddress(walletInfo: self.walletInfo, tonInstance: self.context.tonInstance)
|> castError(GetWalletTransactionsError.self)
|> mapToSignal { address -> Signal<[WalletTransaction], GetWalletTransactionsError> in
getWalletTransactions(address: address, previousId: lastTransactionId, tonInstance: tonInstance)
}
let processedTransactions = requestTransactions
|> mapToSignal { transactions -> Signal<[WalletTransaction], GetWalletTransactionsError> in
return transactionDecryptionKey.get()
|> castError(GetWalletTransactionsError.self)

View File

@ -501,7 +501,7 @@ public func walletSendScreen(context: WalletContext, randomId: Int64, walletInfo
})
}
let _ = (walletAddress(publicKey: walletInfo.publicKey, tonInstance: context.tonInstance)
let _ = (walletAddress(walletInfo: walletInfo, tonInstance: context.tonInstance)
|> deliverOnMainQueue).start(next: { walletAddress in
let presentationData = context.presentationData
let state = stateValue.with { $0 }
@ -572,7 +572,7 @@ public func walletSendScreen(context: WalletContext, randomId: Int64, walletInfo
var emptyItem: ItemListControllerEmptyStateItem?
if let walletState = walletState {
let textLength: Int = state.comment.data(using: .utf8, allowLossyConversion: true)?.count ?? 0
sendEnabled = isValidAddress(state.address, exactLength: true) && amount > 0 && amount <= walletState.balance && textLength <= walletTextLimit
sendEnabled = isValidAddress(state.address, exactLength: true) && amount > 0 && amount <= walletState.effectiveAvailableBalance && textLength <= walletTextLimit
rightNavigationButton = ItemListNavigationButton(content: .text(presentationData.strings.Wallet_Send_Send), style: .bold, enabled: sendEnabled, action: {
arguments.proceed()
@ -583,7 +583,7 @@ public func walletSendScreen(context: WalletContext, randomId: Int64, walletInfo
}
let controllerState = ItemListControllerState(theme: presentationData.theme, title: .text(presentationData.strings.Wallet_Send_Title), leftNavigationButton: leftNavigationButton, rightNavigationButton: rightNavigationButton, backNavigationButton: ItemListBackButton(title: presentationData.strings.Wallet_Navigation_Back), animateChanges: false)
let listState = ItemListNodeState(entries: walletSendScreenEntries(presentationData: presentationData, balance: walletState?.balance, state: state, sendEnabled: sendEnabled), style: .blocks, focusItemTag: focusItemTag, emptyStateItem: emptyItem, animateChanges: false)
let listState = ItemListNodeState(entries: walletSendScreenEntries(presentationData: presentationData, balance: walletState?.effectiveAvailableBalance, state: state, sendEnabled: sendEnabled), style: .blocks, focusItemTag: focusItemTag, emptyStateItem: emptyItem, animateChanges: false)
return (controllerState, (listState, arguments))
}

View File

@ -48,7 +48,8 @@ public struct WalletSplashModeSending {
public enum WalletSplashMode {
case intro
case created(walletInfo: WalletInfo, words: [String]?)
case success(walletInfo: WalletInfo)
case successfullyCreated(walletInfo: WalletInfo)
case successfullyImported(importedInfo: ImportedWalletInfo)
case restoreFailed
case sending(WalletSplashModeSending)
case sent(walletInfo: WalletInfo, amount: Int64)
@ -56,16 +57,21 @@ public enum WalletSplashMode {
case secureStorageReset(WalletSecureStorageResetReason)
}
public struct WalletCreatedPreloadState {
let info: WalletInfo
let state: CombinedWalletStateResult
}
public final class WalletSplashScreen: ViewController {
private let context: WalletContext
private var presentationData: WalletPresentationData
private var mode: WalletSplashMode
private let walletCreatedPreloadState: Promise<CombinedWalletStateResult?>?
private let walletCreatedPreloadState: Promise<WalletCreatedPreloadState?>?
private let actionDisposable = MetaDisposable()
public init(context: WalletContext, mode: WalletSplashMode, walletCreatedPreloadState: Promise<CombinedWalletStateResult?>?) {
public init(context: WalletContext, mode: WalletSplashMode, walletCreatedPreloadState: Promise<WalletCreatedPreloadState?>?) {
self.context = context
self.mode = mode
@ -81,22 +87,70 @@ public final class WalletSplashScreen: ViewController {
} else {
self.walletCreatedPreloadState = Promise()
self.walletCreatedPreloadState?.set(getCombinedWalletState(storage: context.storage, subject: .wallet(walletInfo), tonInstance: context.tonInstance, onlyCached: false)
|> map(Optional.init)
|> `catch` { _ -> Signal<CombinedWalletStateResult?, NoError> in
|> map { state -> WalletCreatedPreloadState? in
return WalletCreatedPreloadState(info: walletInfo, state: state)
}
|> `catch` { _ -> Signal<WalletCreatedPreloadState?, NoError> in
return .single(nil)
})
}
case let .success(walletInfo):
case let .successfullyCreated(walletInfo):
if let walletCreatedPreloadState = walletCreatedPreloadState {
self.walletCreatedPreloadState = walletCreatedPreloadState
} else {
self.walletCreatedPreloadState = Promise()
self.walletCreatedPreloadState?.set(getCombinedWalletState(storage: context.storage, subject: .wallet(walletInfo), tonInstance: context.tonInstance, onlyCached: false)
|> map(Optional.init)
|> `catch` { _ -> Signal<CombinedWalletStateResult?, NoError> in
|> map { state -> WalletCreatedPreloadState? in
return WalletCreatedPreloadState(info: walletInfo, state: state)
}
|> `catch` { _ -> Signal<WalletCreatedPreloadState?, NoError> in
return .single(nil)
})
}
case let .successfullyImported(importedInfo):
if let walletCreatedPreloadState = walletCreatedPreloadState {
self.walletCreatedPreloadState = walletCreatedPreloadState
} else {
self.walletCreatedPreloadState = Promise()
let signal = getWalletInfo(importedInfo: importedInfo, tonInstance: context.tonInstance)
|> mapError { error -> GetCombinedWalletStateError in
switch error {
case .generic:
return .generic
case .network:
return .network
}
}
|> mapToSignal { walletInfo -> Signal<WalletCreatedPreloadState?, GetCombinedWalletStateError> in
return context.storage.updateWalletRecords { records in
var records = records
for i in 0 ..< records.count {
switch records[i].info {
case .ready:
break
case let .imported(info):
if info.publicKey == importedInfo.publicKey {
records[i].info = .ready(info: walletInfo, exportCompleted: true, state: nil)
}
}
}
return records
}
|> castError(GetCombinedWalletStateError.self)
|> mapToSignal { _ -> Signal<WalletCreatedPreloadState?, GetCombinedWalletStateError> in
return getCombinedWalletState(storage: context.storage, subject: .wallet(walletInfo), tonInstance: context.tonInstance, onlyCached: false)
|> map { state -> WalletCreatedPreloadState? in
return WalletCreatedPreloadState(info: walletInfo, state: state)
}
}
}
|> `catch` { _ -> Signal<WalletCreatedPreloadState?, NoError> in
return .single(nil)
}
self.walletCreatedPreloadState?.set(signal)
}
default:
self.walletCreatedPreloadState = nil
}
@ -139,7 +193,7 @@ public final class WalletSplashScreen: ViewController {
self.navigationItem.setLeftBarButton(UIBarButtonItem(customDisplayNode: ASDisplayNode())!, animated: false)
case .restoreFailed, .secureStorageNotAvailable, .secureStorageReset, .created:
break
case .success:
case .successfullyCreated, .successfullyImported:
break
}
@ -197,7 +251,7 @@ public final class WalletSplashScreen: ViewController {
return
}
if let navigationController = strongSelf.navigationController as? NavigationController {
let _ = (walletAddress(publicKey: walletInfo.publicKey, tonInstance: strongSelf.context.tonInstance)
let _ = (walletAddress(walletInfo: walletInfo, tonInstance: strongSelf.context.tonInstance)
|> deliverOnMainQueue).start(next: { [weak self] address in
guard let strongSelf = self else {
return
@ -222,7 +276,7 @@ public final class WalletSplashScreen: ViewController {
return
}
if let navigationController = strongSelf.navigationController as? NavigationController {
let _ = (walletAddress(publicKey: walletInfo.publicKey, tonInstance: strongSelf.context.tonInstance)
let _ = (walletAddress(walletInfo: walletInfo, tonInstance: strongSelf.context.tonInstance)
|> deliverOnMainQueue).start(next: { [weak self] address in
guard let strongSelf = self else {
return
@ -315,7 +369,7 @@ public final class WalletSplashScreen: ViewController {
return true
}
let _ = (walletAddress(publicKey: sending.walletInfo.publicKey, tonInstance: self.context.tonInstance)
let _ = (walletAddress(walletInfo: sending.walletInfo, tonInstance: self.context.tonInstance)
|> deliverOnMainQueue).start(next: { [weak self] address in
guard let strongSelf = self else {
return
@ -426,8 +480,8 @@ public final class WalletSplashScreen: ViewController {
}
})
}
case let .success(walletInfo):
let _ = (walletAddress(publicKey: walletInfo.publicKey, tonInstance: strongSelf.context.tonInstance)
case let .successfullyCreated(walletInfo):
let _ = (walletAddress(walletInfo: walletInfo, tonInstance: strongSelf.context.tonInstance)
|> deliverOnMainQueue).start(next: { address in
guard let strongSelf = self else {
return
@ -452,6 +506,46 @@ public final class WalletSplashScreen: ViewController {
navigationController.setViewControllers(controllers, animated: true)
}
})
case .successfullyImported:
let controller = OverlayStatusController(theme: strongSelf.presentationData.theme, type: .loading(cancelled: nil))
strongSelf.present(controller, in: .window(.root))
let _ = (strongSelf.walletCreatedPreloadState!.get()
|> filter { $0 != nil }
|> take(1)
|> deliverOnMainQueue).start(next: { state in
controller.dismiss()
guard let state = state else {
return
}
let walletInfo = state.info
let _ = (walletAddress(walletInfo: walletInfo, tonInstance: strongSelf.context.tonInstance)
|> deliverOnMainQueue).start(next: { address in
guard let strongSelf = self else {
return
}
if let navigationController = strongSelf.navigationController as? NavigationController {
var controllers = navigationController.viewControllers
controllers = controllers.filter { controller in
if controller is WalletSplashScreen {
return false
}
if controller is WalletWordDisplayScreen {
return false
}
if controller is WalletWordCheckScreen {
return false
}
return true
}
controllers.append(WalletInfoScreen(context: strongSelf.context, walletInfo: walletInfo, address: address, enableDebugActions: false))
strongSelf.view.endEditing(true)
navigationController.setViewControllers(controllers, animated: true)
}
})
})
case let .sent(walletInfo, _):
if let navigationController = strongSelf.navigationController as? NavigationController {
var controllers = navigationController.viewControllers
@ -474,7 +568,7 @@ public final class WalletSplashScreen: ViewController {
return true
}
let _ = (walletAddress(publicKey: walletInfo.publicKey, tonInstance: strongSelf.context.tonInstance)
let _ = (walletAddress(walletInfo: walletInfo, tonInstance: strongSelf.context.tonInstance)
|> deliverOnMainQueue).start(next: { [weak self] address in
guard let strongSelf = self else {
return
@ -527,7 +621,7 @@ public final class WalletSplashScreen: ViewController {
return true
}
let _ = (walletAddress(publicKey: sending.walletInfo.publicKey, tonInstance: strongSelf.context.tonInstance)
let _ = (walletAddress(walletInfo: sending.walletInfo, tonInstance: strongSelf.context.tonInstance)
|> deliverOnMainQueue).start(next: { [weak self] address in
guard let strongSelf = self else {
return
@ -627,7 +721,7 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
}
}
init(context: WalletContext, walletCreatedPreloadState: Promise<CombinedWalletStateResult?>?, presentationData: WalletPresentationData, mode: WalletSplashMode, action: @escaping () -> Void, secondaryAction: @escaping () -> Void, openTerms: @escaping () -> Void) {
init(context: WalletContext, walletCreatedPreloadState: Promise<WalletCreatedPreloadState?>?, presentationData: WalletPresentationData, mode: WalletSplashMode, action: @escaping () -> Void, secondaryAction: @escaping () -> Void, openTerms: @escaping () -> Void) {
self.presentationData = presentationData
self.mode = mode
self.secondaryAction = secondaryAction
@ -680,7 +774,7 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
self.animationNode.visibility = true
}
secondaryActionText = ""
case .success:
case .successfullyCreated, .successfullyImported:
title = self.presentationData.strings.Wallet_Completed_Title
text = NSAttributedString(string: self.presentationData.strings.Wallet_Completed_Text, font: textFont, textColor: textColor)
buttonText = self.presentationData.strings.Wallet_Completed_ViewWallet
@ -899,7 +993,7 @@ private final class WalletSplashScreenNode: ViewControllerTracingNode {
let iconSize: CGSize = self.animationSize
var iconOffset = CGPoint()
switch self.mode {
case .success:
case .successfullyCreated, .successfullyImported:
iconOffset.x = 10.0
default:
break

View File

@ -2073,9 +2073,9 @@ public final class WalletWordCheckScreen: ViewController {
private let startTime: Double
private let walletCreatedPreloadState: Promise<CombinedWalletStateResult?>?
private let walletCreatedPreloadState: Promise<WalletCreatedPreloadState?>?
public init(context: WalletContext, mode: WalletWordCheckMode, walletCreatedPreloadState: Promise<CombinedWalletStateResult?>?) {
public init(context: WalletContext, mode: WalletWordCheckMode, walletCreatedPreloadState: Promise<WalletCreatedPreloadState?>?) {
self.context = context
self.mode = mode
self.walletCreatedPreloadState = walletCreatedPreloadState
@ -2137,7 +2137,7 @@ public final class WalletWordCheckScreen: ViewController {
return true
}
let _ = confirmWalletExported(storage: strongSelf.context.storage, publicKey: walletInfo.publicKey).start()
controllers.append(WalletSplashScreen(context: strongSelf.context, mode: .success(walletInfo: walletInfo), walletCreatedPreloadState: strongSelf.walletCreatedPreloadState))
controllers.append(WalletSplashScreen(context: strongSelf.context, mode: .successfullyCreated(walletInfo: walletInfo), walletCreatedPreloadState: strongSelf.walletCreatedPreloadState))
strongSelf.view.endEditing(true)
navigationController.setViewControllers(controllers, animated: true)
}
@ -2205,7 +2205,7 @@ public final class WalletWordCheckScreen: ViewController {
}
return true
}
controllers.append(WalletSplashScreen(context: strongSelf.context, mode: .success(walletInfo: walletInfo), walletCreatedPreloadState: strongSelf.walletCreatedPreloadState))
controllers.append(WalletSplashScreen(context: strongSelf.context, mode: .successfullyImported(importedInfo: walletInfo), walletCreatedPreloadState: strongSelf.walletCreatedPreloadState))
strongSelf.view.endEditing(true)
navigationController.setViewControllers(controllers, animated: true)
}

View File

@ -24,9 +24,9 @@ public final class WalletWordDisplayScreen: ViewController {
private let startTime: Double
private let idleTimerExtensionDisposable: Disposable
private let walletCreatedPreloadState: Promise<CombinedWalletStateResult?>?
private let walletCreatedPreloadState: Promise<WalletCreatedPreloadState?>?
public init(context: WalletContext, walletInfo: WalletInfo, wordList: [String], mode: WalletWordDisplayScreenMode, walletCreatedPreloadState: Promise<CombinedWalletStateResult?>?) {
public init(context: WalletContext, walletInfo: WalletInfo, wordList: [String], mode: WalletWordDisplayScreenMode, walletCreatedPreloadState: Promise<WalletCreatedPreloadState?>?) {
self.context = context
self.walletInfo = walletInfo
self.wordList = wordList

View File

@ -104,6 +104,7 @@ if (TON_USE_ROCKSDB)
if (ANDROID)
set(PORTABLE ON CACHE BOOL "portable")
endif()
set(WITH_GFLAGS OFF CACHE BOOL "build with GFlags")
set(WITH_TESTS OFF CACHE BOOL "build with tests")
set(WITH_TOOLS OFF CACHE BOOL "build with tools")
set(FAIL_ON_WARNINGS OFF CACHE BOOL "fail on warnings")
@ -220,7 +221,7 @@ elseif (CLANG OR GCC)
if (APPLE)
#use "-Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/export_list" for exported symbols
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -fvisibility=hidden -Wl,-dead_strip,-x,-S")
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fvisibility=hidden -Wl,-dead_strip,-x,-S")
#set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fvisibility=hidden -Wl,-dead_strip,-x,-S")
else()
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffunction-sections -fdata-sections")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,--gc-sections -Wl,--exclude-libs,ALL")

View File

@ -49,6 +49,7 @@ set(TON_CRYPTO_SOURCE
common/bigexp.h
common/util.h
common/linalloc.hpp
common/promiseop.hpp
ellcurve/Ed25519.h
ellcurve/Fp25519.h
@ -212,6 +213,7 @@ set(SMC_ENVELOPE_SOURCE
smc-envelope/HighloadWalletV2.cpp
smc-envelope/ManualDns.cpp
smc-envelope/MultisigWallet.cpp
smc-envelope/PaymentChannel.cpp
smc-envelope/SmartContract.cpp
smc-envelope/SmartContractCode.cpp
smc-envelope/TestGiver.cpp
@ -341,7 +343,7 @@ if (NOT CMAKE_CROSSCOMPILING)
set(multiValueArgs SOURCE)
set(FUNC_LIB_SOURCE smartcont/stdlib.fc)
cmake_parse_arguments(ARG "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN} )
string(REGEX REPLACE "[^a-zA-Z_]" "_" ID ${ARG_DEST})
string(REGEX REPLACE "[^0-9a-zA-Z_]" "_" ID ${ARG_DEST})
set(ARG_DEST_FIF "${ARG_DEST}.fif")
add_custom_command(
COMMENT "Generate ${ARG_DEST_FIF}"
@ -374,10 +376,13 @@ if (NOT CMAKE_CROSSCOMPILING)
GenFif(DEST smartcont/auto/multisig-code SOURCE smartcont/multisig-code.fc NAME multisig)
GenFif(DEST smartcont/auto/restricted-wallet-code SOURCE smartcont/restricted-wallet-code.fc NAME restricted-wallet)
GenFif(DEST smartcont/auto/restricted-wallet2-code SOURCE smartcont/restricted-wallet2-code.fc NAME restricted-wallet2)
GenFif(DEST smartcont/auto/restricted-wallet3-code SOURCE smartcont/restricted-wallet3-code.fc NAME restricted-wallet3)
GenFif(DEST smartcont/auto/dns-manual-code SOURCE smartcont/dns-manual-code.fc NAME dns-manual)
GenFif(DEST smartcont/auto/dns-auto-code SOURCE smartcont/dns-auto-code.fc NAME dns-auto)
GenFif(DEST smartcont/auto/payment-channel-code SOURCE smartcont/payment-channel-code.fc NAME payment-channel)
GenFif(DEST smartcont/auto/simple-wallet-ext-code SOURCE smartcont/simple-wallet-ext-code.fc NAME simple-wallet-ext)
endif()
@ -420,5 +425,13 @@ if (WINGETOPT_FOUND)
target_link_libraries_system(dump-block wingetopt)
endif()
add_executable(test-weight-distr block/test-weight-distr.cpp)
target_include_directories(test-weight-distr PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
$<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/..>)
target_link_libraries(test-weight-distr PUBLIC ton_crypto fift-lib ton_block)
if (WINGETOPT_FOUND)
target_link_libraries_system(test-weight-distr wingetopt)
endif()
install(TARGETS fift func RUNTIME DESTINATION bin)
install(DIRECTORY fift/lib/ DESTINATION lib/fift)

File diff suppressed because it is too large Load Diff

View File

@ -28,6 +28,7 @@
// uses built-in type `uint256`
// uses built-in type `int257`
// uses built-in type `bits256`
// uses built-in type `bits512`
namespace block {
@ -3284,6 +3285,54 @@ struct HASH_UPDATE final : TLB_Complex {
}
};
//
// headers for type `MERKLE_PROOF`
//
struct MERKLE_PROOF final : TLB_Complex {
enum { _merkle_proof };
static constexpr int cons_len_exact = 8;
static constexpr unsigned char cons_tag[1] = { 3 };
const TLB &X_;
MERKLE_PROOF(const TLB& X) : X_(X) {}
struct Record;
bool always_special() const override {
return true;
}
int get_size(const vm::CellSlice& cs) const override {
return 0x10118;
}
bool skip(vm::CellSlice& cs) const override {
return cs.advance_ext(0x10118);
}
bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
bool unpack(vm::CellSlice& cs, Record& data) const;
bool unpack__merkle_proof(vm::CellSlice& cs, td::BitArray<256>& virtual_hash, int& depth, Ref<Cell>& virtual_root) const;
bool cell_unpack(Ref<vm::Cell> cell_ref, Record& data) const;
bool cell_unpack__merkle_proof(Ref<vm::Cell> cell_ref, td::BitArray<256>& virtual_hash, int& depth, Ref<Cell>& virtual_root) const;
bool pack(vm::CellBuilder& cb, const Record& data) const;
bool pack__merkle_proof(vm::CellBuilder& cb, td::BitArray<256> virtual_hash, int depth, Ref<Cell> virtual_root) const;
bool cell_pack(Ref<vm::Cell>& cell_ref, const Record& data) const;
bool cell_pack__merkle_proof(Ref<vm::Cell>& cell_ref, td::BitArray<256> virtual_hash, int depth, Ref<Cell> virtual_root) const;
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override;
std::ostream& print_type(std::ostream& os) const override {
return os << "(MERKLE_PROOF " << X_ << ")";
}
int check_tag(const vm::CellSlice& cs) const override;
int get_tag(const vm::CellSlice& cs) const override {
return 0;
}
};
struct MERKLE_PROOF::Record {
typedef MERKLE_PROOF type_class;
td::BitArray<256> virtual_hash; // virtual_hash : bits256
int depth; // depth : uint16
Ref<Cell> virtual_root; // virtual_root : ^X
Record() = default;
Record(const td::BitArray<256>& _virtual_hash, int _depth, Ref<Cell> _virtual_root) : virtual_hash(_virtual_hash), depth(_depth), virtual_root(std::move(_virtual_root)) {}
};
//
// headers for type `AccountBlock`
//
@ -7727,19 +7776,19 @@ struct TopBlockDescrSet final : TLB_Complex {
extern const TopBlockDescrSet t_TopBlockDescrSet;
//
// headers for type `ComplaintDescr`
// headers for type `ProducerInfo`
//
struct ComplaintDescr final : TLB_Complex {
enum { no_blk_gen };
static constexpr int cons_len_exact = 32;
static constexpr unsigned cons_tag[1] = { 0x7e545dda };
struct ProducerInfo final : TLB_Complex {
enum { prod_info };
static constexpr int cons_len_exact = 8;
static constexpr unsigned char cons_tag[1] = { 0x34 };
struct Record;
int get_size(const vm::CellSlice& cs) const override {
return 0x202c0;
return 0x20288;
}
bool skip(vm::CellSlice& cs) const override {
return cs.advance_ext(0x202c0);
return cs.advance_ext(0x20288);
}
bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
bool unpack(vm::CellSlice& cs, Record& data) const;
@ -7748,7 +7797,7 @@ struct ComplaintDescr final : TLB_Complex {
bool cell_pack(Ref<vm::Cell>& cell_ref, const Record& data) const;
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override;
std::ostream& print_type(std::ostream& os) const override {
return os << "ComplaintDescr";
return os << "ProducerInfo";
}
int check_tag(const vm::CellSlice& cs) const override;
int get_tag(const vm::CellSlice& cs) const override {
@ -7756,15 +7805,66 @@ struct ComplaintDescr final : TLB_Complex {
}
};
struct ComplaintDescr::Record {
typedef ComplaintDescr type_class;
struct ProducerInfo::Record {
typedef ProducerInfo type_class;
unsigned utime; // utime : uint32
Ref<CellSlice> mc_blk_ref; // mc_blk_ref : ExtBlkRef
unsigned from_utime; // from_utime : uint32
unsigned to_utime; // to_utime : uint32
Ref<Cell> state_proof; // state_proof : ^Cell
Ref<Cell> prod_proof; // prod_proof : ^Cell
Ref<Cell> state_proof; // state_proof : ^(MERKLE_PROOF Block)
Ref<Cell> prod_proof; // prod_proof : ^(MERKLE_PROOF ShardState)
Record() = default;
Record(Ref<CellSlice> _mc_blk_ref, unsigned _from_utime, unsigned _to_utime, Ref<Cell> _state_proof, Ref<Cell> _prod_proof) : mc_blk_ref(std::move(_mc_blk_ref)), from_utime(_from_utime), to_utime(_to_utime), state_proof(std::move(_state_proof)), prod_proof(std::move(_prod_proof)) {}
Record(unsigned _utime, Ref<CellSlice> _mc_blk_ref, Ref<Cell> _state_proof, Ref<Cell> _prod_proof) : utime(_utime), mc_blk_ref(std::move(_mc_blk_ref)), state_proof(std::move(_state_proof)), prod_proof(std::move(_prod_proof)) {}
};
extern const ProducerInfo t_ProducerInfo;
//
// headers for type `ComplaintDescr`
//
struct ComplaintDescr final : TLB_Complex {
enum { no_blk_gen, no_blk_gen_diff };
static constexpr int cons_len_exact = 32;
static constexpr unsigned cons_tag[2] = { 0x450e8bd9, 0xc737b0caU };
struct Record_no_blk_gen {
typedef ComplaintDescr type_class;
unsigned from_utime; // from_utime : uint32
Ref<Cell> prod_info; // prod_info : ^ProducerInfo
Record_no_blk_gen() = default;
Record_no_blk_gen(unsigned _from_utime, Ref<Cell> _prod_info) : from_utime(_from_utime), prod_info(std::move(_prod_info)) {}
};
struct Record_no_blk_gen_diff {
typedef ComplaintDescr type_class;
Ref<Cell> prod_info_old; // prod_info_old : ^ProducerInfo
Ref<Cell> prod_info_new; // prod_info_new : ^ProducerInfo
Record_no_blk_gen_diff() = default;
Record_no_blk_gen_diff(Ref<Cell> _prod_info_old, Ref<Cell> _prod_info_new) : prod_info_old(std::move(_prod_info_old)), prod_info_new(std::move(_prod_info_new)) {}
};
bool skip(vm::CellSlice& cs) const override;
bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
bool unpack(vm::CellSlice& cs, Record_no_blk_gen& data) const;
bool unpack_no_blk_gen(vm::CellSlice& cs, unsigned& from_utime, Ref<Cell>& prod_info) const;
bool cell_unpack(Ref<vm::Cell> cell_ref, Record_no_blk_gen& data) const;
bool cell_unpack_no_blk_gen(Ref<vm::Cell> cell_ref, unsigned& from_utime, Ref<Cell>& prod_info) const;
bool pack(vm::CellBuilder& cb, const Record_no_blk_gen& data) const;
bool pack_no_blk_gen(vm::CellBuilder& cb, unsigned from_utime, Ref<Cell> prod_info) const;
bool cell_pack(Ref<vm::Cell>& cell_ref, const Record_no_blk_gen& data) const;
bool cell_pack_no_blk_gen(Ref<vm::Cell>& cell_ref, unsigned from_utime, Ref<Cell> prod_info) const;
bool unpack(vm::CellSlice& cs, Record_no_blk_gen_diff& data) const;
bool unpack_no_blk_gen_diff(vm::CellSlice& cs, Ref<Cell>& prod_info_old, Ref<Cell>& prod_info_new) const;
bool cell_unpack(Ref<vm::Cell> cell_ref, Record_no_blk_gen_diff& data) const;
bool cell_unpack_no_blk_gen_diff(Ref<vm::Cell> cell_ref, Ref<Cell>& prod_info_old, Ref<Cell>& prod_info_new) const;
bool pack(vm::CellBuilder& cb, const Record_no_blk_gen_diff& data) const;
bool pack_no_blk_gen_diff(vm::CellBuilder& cb, Ref<Cell> prod_info_old, Ref<Cell> prod_info_new) const;
bool cell_pack(Ref<vm::Cell>& cell_ref, const Record_no_blk_gen_diff& data) const;
bool cell_pack_no_blk_gen_diff(Ref<vm::Cell>& cell_ref, Ref<Cell> prod_info_old, Ref<Cell> prod_info_new) const;
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override;
std::ostream& print_type(std::ostream& os) const override {
return os << "ComplaintDescr";
}
int check_tag(const vm::CellSlice& cs) const override;
int get_tag(const vm::CellSlice& cs) const override {
return (int)cs.prefetch_ulong(1);
}
};
extern const ComplaintDescr t_ComplaintDescr;
@ -9035,6 +9135,344 @@ struct SmcCapability final : TLB_Complex {
extern const SmcCapability t_SmcCapability;
//
// headers for type `ChanConfig`
//
struct ChanConfig final : TLB_Complex {
enum { chan_config };
static constexpr int cons_len_exact = 0;
struct Record;
int get_size(const vm::CellSlice& cs) const override {
return 0x20280;
}
bool skip(vm::CellSlice& cs) const override {
return cs.advance_ext(0x20280);
}
bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
bool unpack(vm::CellSlice& cs, Record& data) const;
bool cell_unpack(Ref<vm::Cell> cell_ref, Record& data) const;
bool pack(vm::CellBuilder& cb, const Record& data) const;
bool cell_pack(Ref<vm::Cell>& cell_ref, const Record& data) const;
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override;
std::ostream& print_type(std::ostream& os) const override {
return os << "ChanConfig";
}
int check_tag(const vm::CellSlice& cs) const override;
int get_tag(const vm::CellSlice& cs) const override {
return 0;
}
};
struct ChanConfig::Record {
typedef ChanConfig type_class;
unsigned init_timeout; // init_timeout : uint32
unsigned close_timeout; // close_timeout : uint32
td::BitArray<256> a_key; // a_key : bits256
td::BitArray<256> b_key; // b_key : bits256
Ref<Cell> a_addr; // a_addr : ^MsgAddressInt
Ref<Cell> b_addr; // b_addr : ^MsgAddressInt
unsigned long long channel_id; // channel_id : uint64
Record() = default;
Record(unsigned _init_timeout, unsigned _close_timeout, const td::BitArray<256>& _a_key, const td::BitArray<256>& _b_key, Ref<Cell> _a_addr, Ref<Cell> _b_addr, unsigned long long _channel_id) : init_timeout(_init_timeout), close_timeout(_close_timeout), a_key(_a_key), b_key(_b_key), a_addr(std::move(_a_addr)), b_addr(std::move(_b_addr)), channel_id(_channel_id) {}
};
extern const ChanConfig t_ChanConfig;
//
// headers for type `ChanState`
//
struct ChanState final : TLB_Complex {
enum { chan_state_init, chan_state_close, chan_state_payout };
static constexpr int cons_len_exact = 3;
struct Record_chan_state_init;
struct Record_chan_state_close;
struct Record_chan_state_payout {
typedef ChanState type_class;
Ref<CellSlice> A; // A : Grams
Ref<CellSlice> B; // B : Grams
Record_chan_state_payout() = default;
Record_chan_state_payout(Ref<CellSlice> _A, Ref<CellSlice> _B) : A(std::move(_A)), B(std::move(_B)) {}
};
bool skip(vm::CellSlice& cs) const override;
bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
bool unpack(vm::CellSlice& cs, Record_chan_state_init& data) const;
bool cell_unpack(Ref<vm::Cell> cell_ref, Record_chan_state_init& data) const;
bool pack(vm::CellBuilder& cb, const Record_chan_state_init& data) const;
bool cell_pack(Ref<vm::Cell>& cell_ref, const Record_chan_state_init& data) const;
bool unpack(vm::CellSlice& cs, Record_chan_state_close& data) const;
bool cell_unpack(Ref<vm::Cell> cell_ref, Record_chan_state_close& data) const;
bool pack(vm::CellBuilder& cb, const Record_chan_state_close& data) const;
bool cell_pack(Ref<vm::Cell>& cell_ref, const Record_chan_state_close& data) const;
bool unpack(vm::CellSlice& cs, Record_chan_state_payout& data) const;
bool unpack_chan_state_payout(vm::CellSlice& cs, Ref<CellSlice>& A, Ref<CellSlice>& B) const;
bool cell_unpack(Ref<vm::Cell> cell_ref, Record_chan_state_payout& data) const;
bool cell_unpack_chan_state_payout(Ref<vm::Cell> cell_ref, Ref<CellSlice>& A, Ref<CellSlice>& B) const;
bool pack(vm::CellBuilder& cb, const Record_chan_state_payout& data) const;
bool pack_chan_state_payout(vm::CellBuilder& cb, Ref<CellSlice> A, Ref<CellSlice> B) const;
bool cell_pack(Ref<vm::Cell>& cell_ref, const Record_chan_state_payout& data) const;
bool cell_pack_chan_state_payout(Ref<vm::Cell>& cell_ref, Ref<CellSlice> A, Ref<CellSlice> B) const;
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override;
std::ostream& print_type(std::ostream& os) const override {
return os << "ChanState";
}
int check_tag(const vm::CellSlice& cs) const override;
int get_tag(const vm::CellSlice& cs) const override {
return cs.bselect(3, 7);
}
};
struct ChanState::Record_chan_state_init {
typedef ChanState type_class;
bool signed_A; // signed_A : Bool
bool signed_B; // signed_B : Bool
Ref<CellSlice> min_A; // min_A : Grams
Ref<CellSlice> min_B; // min_B : Grams
unsigned expire_at; // expire_at : uint32
Ref<CellSlice> A; // A : Grams
Ref<CellSlice> B; // B : Grams
Record_chan_state_init() = default;
Record_chan_state_init(bool _signed_A, bool _signed_B, Ref<CellSlice> _min_A, Ref<CellSlice> _min_B, unsigned _expire_at, Ref<CellSlice> _A, Ref<CellSlice> _B) : signed_A(_signed_A), signed_B(_signed_B), min_A(std::move(_min_A)), min_B(std::move(_min_B)), expire_at(_expire_at), A(std::move(_A)), B(std::move(_B)) {}
};
struct ChanState::Record_chan_state_close {
typedef ChanState type_class;
bool signed_A; // signed_A : Bool
bool signed_B; // signed_B : Bool
Ref<CellSlice> promise_A; // promise_A : Grams
Ref<CellSlice> promise_B; // promise_B : Grams
unsigned expire_at; // expire_at : uint32
Ref<CellSlice> A; // A : Grams
Ref<CellSlice> B; // B : Grams
Record_chan_state_close() = default;
Record_chan_state_close(bool _signed_A, bool _signed_B, Ref<CellSlice> _promise_A, Ref<CellSlice> _promise_B, unsigned _expire_at, Ref<CellSlice> _A, Ref<CellSlice> _B) : signed_A(_signed_A), signed_B(_signed_B), promise_A(std::move(_promise_A)), promise_B(std::move(_promise_B)), expire_at(_expire_at), A(std::move(_A)), B(std::move(_B)) {}
};
extern const ChanState t_ChanState;
//
// headers for type `ChanPromise`
//
struct ChanPromise final : TLB_Complex {
enum { chan_promise };
static constexpr int cons_len_exact = 0;
struct Record;
bool skip(vm::CellSlice& cs) const override;
bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
bool unpack(vm::CellSlice& cs, Record& data) const;
bool unpack_chan_promise(vm::CellSlice& cs, unsigned long long& channel_id, Ref<CellSlice>& promise_A, Ref<CellSlice>& promise_B) const;
bool cell_unpack(Ref<vm::Cell> cell_ref, Record& data) const;
bool cell_unpack_chan_promise(Ref<vm::Cell> cell_ref, unsigned long long& channel_id, Ref<CellSlice>& promise_A, Ref<CellSlice>& promise_B) const;
bool pack(vm::CellBuilder& cb, const Record& data) const;
bool pack_chan_promise(vm::CellBuilder& cb, unsigned long long channel_id, Ref<CellSlice> promise_A, Ref<CellSlice> promise_B) const;
bool cell_pack(Ref<vm::Cell>& cell_ref, const Record& data) const;
bool cell_pack_chan_promise(Ref<vm::Cell>& cell_ref, unsigned long long channel_id, Ref<CellSlice> promise_A, Ref<CellSlice> promise_B) const;
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override;
std::ostream& print_type(std::ostream& os) const override {
return os << "ChanPromise";
}
int check_tag(const vm::CellSlice& cs) const override;
int get_tag(const vm::CellSlice& cs) const override {
return 0;
}
};
struct ChanPromise::Record {
typedef ChanPromise type_class;
unsigned long long channel_id; // channel_id : uint64
Ref<CellSlice> promise_A; // promise_A : Grams
Ref<CellSlice> promise_B; // promise_B : Grams
Record() = default;
Record(unsigned long long _channel_id, Ref<CellSlice> _promise_A, Ref<CellSlice> _promise_B) : channel_id(_channel_id), promise_A(std::move(_promise_A)), promise_B(std::move(_promise_B)) {}
};
extern const ChanPromise t_ChanPromise;
//
// headers for type `ChanSignedPromise`
//
struct ChanSignedPromise final : TLB_Complex {
enum { chan_signed_promise };
static constexpr int cons_len_exact = 0;
struct Record {
typedef ChanSignedPromise type_class;
Ref<CellSlice> sig; // sig : Maybe ^bits512
Ref<CellSlice> promise; // promise : ChanPromise
Record() = default;
Record(Ref<CellSlice> _sig, Ref<CellSlice> _promise) : sig(std::move(_sig)), promise(std::move(_promise)) {}
};
bool skip(vm::CellSlice& cs) const override;
bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
bool unpack(vm::CellSlice& cs, Record& data) const;
bool unpack_chan_signed_promise(vm::CellSlice& cs, Ref<CellSlice>& sig, Ref<CellSlice>& promise) const;
bool cell_unpack(Ref<vm::Cell> cell_ref, Record& data) const;
bool cell_unpack_chan_signed_promise(Ref<vm::Cell> cell_ref, Ref<CellSlice>& sig, Ref<CellSlice>& promise) const;
bool pack(vm::CellBuilder& cb, const Record& data) const;
bool pack_chan_signed_promise(vm::CellBuilder& cb, Ref<CellSlice> sig, Ref<CellSlice> promise) const;
bool cell_pack(Ref<vm::Cell>& cell_ref, const Record& data) const;
bool cell_pack_chan_signed_promise(Ref<vm::Cell>& cell_ref, Ref<CellSlice> sig, Ref<CellSlice> promise) const;
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override;
std::ostream& print_type(std::ostream& os) const override {
return os << "ChanSignedPromise";
}
int check_tag(const vm::CellSlice& cs) const override;
int get_tag(const vm::CellSlice& cs) const override {
return 0;
}
};
extern const ChanSignedPromise t_ChanSignedPromise;
//
// headers for type `ChanMsg`
//
struct ChanMsg final : TLB_Complex {
enum { chan_msg_init, chan_msg_timeout, chan_msg_close };
static constexpr int cons_len_exact = 32;
static constexpr unsigned cons_tag[3] = { 0x27317822, 0x43278a28, 0xf28ae183U };
struct Record_chan_msg_init;
struct Record_chan_msg_close;
struct Record_chan_msg_timeout {
typedef ChanMsg type_class;
};
bool skip(vm::CellSlice& cs) const override;
bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
bool unpack(vm::CellSlice& cs, Record_chan_msg_init& data) const;
bool cell_unpack(Ref<vm::Cell> cell_ref, Record_chan_msg_init& data) const;
bool pack(vm::CellBuilder& cb, const Record_chan_msg_init& data) const;
bool cell_pack(Ref<vm::Cell>& cell_ref, const Record_chan_msg_init& data) const;
bool unpack(vm::CellSlice& cs, Record_chan_msg_close& data) const;
bool unpack_chan_msg_close(vm::CellSlice& cs, Ref<CellSlice>& extra_A, Ref<CellSlice>& extra_B, Ref<CellSlice>& promise) const;
bool cell_unpack(Ref<vm::Cell> cell_ref, Record_chan_msg_close& data) const;
bool cell_unpack_chan_msg_close(Ref<vm::Cell> cell_ref, Ref<CellSlice>& extra_A, Ref<CellSlice>& extra_B, Ref<CellSlice>& promise) const;
bool pack(vm::CellBuilder& cb, const Record_chan_msg_close& data) const;
bool pack_chan_msg_close(vm::CellBuilder& cb, Ref<CellSlice> extra_A, Ref<CellSlice> extra_B, Ref<CellSlice> promise) const;
bool cell_pack(Ref<vm::Cell>& cell_ref, const Record_chan_msg_close& data) const;
bool cell_pack_chan_msg_close(Ref<vm::Cell>& cell_ref, Ref<CellSlice> extra_A, Ref<CellSlice> extra_B, Ref<CellSlice> promise) const;
bool unpack(vm::CellSlice& cs, Record_chan_msg_timeout& data) const;
bool unpack_chan_msg_timeout(vm::CellSlice& cs) const;
bool cell_unpack(Ref<vm::Cell> cell_ref, Record_chan_msg_timeout& data) const;
bool cell_unpack_chan_msg_timeout(Ref<vm::Cell> cell_ref) const;
bool pack(vm::CellBuilder& cb, const Record_chan_msg_timeout& data) const;
bool pack_chan_msg_timeout(vm::CellBuilder& cb) const;
bool cell_pack(Ref<vm::Cell>& cell_ref, const Record_chan_msg_timeout& data) const;
bool cell_pack_chan_msg_timeout(Ref<vm::Cell>& cell_ref) const;
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override;
std::ostream& print_type(std::ostream& os) const override {
return os << "ChanMsg";
}
int check_tag(const vm::CellSlice& cs) const override;
int get_tag(const vm::CellSlice& cs) const override {
return cs.bselect(2, 11);
}
};
struct ChanMsg::Record_chan_msg_init {
typedef ChanMsg type_class;
Ref<CellSlice> inc_A; // inc_A : Grams
Ref<CellSlice> inc_B; // inc_B : Grams
Ref<CellSlice> min_A; // min_A : Grams
Ref<CellSlice> min_B; // min_B : Grams
unsigned long long channel_id; // channel_id : uint64
Record_chan_msg_init() = default;
Record_chan_msg_init(Ref<CellSlice> _inc_A, Ref<CellSlice> _inc_B, Ref<CellSlice> _min_A, Ref<CellSlice> _min_B, unsigned long long _channel_id) : inc_A(std::move(_inc_A)), inc_B(std::move(_inc_B)), min_A(std::move(_min_A)), min_B(std::move(_min_B)), channel_id(_channel_id) {}
};
struct ChanMsg::Record_chan_msg_close {
typedef ChanMsg type_class;
Ref<CellSlice> extra_A; // extra_A : Grams
Ref<CellSlice> extra_B; // extra_B : Grams
Ref<CellSlice> promise; // promise : ChanSignedPromise
Record_chan_msg_close() = default;
Record_chan_msg_close(Ref<CellSlice> _extra_A, Ref<CellSlice> _extra_B, Ref<CellSlice> _promise) : extra_A(std::move(_extra_A)), extra_B(std::move(_extra_B)), promise(std::move(_promise)) {}
};
extern const ChanMsg t_ChanMsg;
//
// headers for type `ChanSignedMsg`
//
struct ChanSignedMsg final : TLB_Complex {
enum { chan_signed_msg };
static constexpr int cons_len_exact = 0;
struct Record;
bool skip(vm::CellSlice& cs) const override;
bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
bool unpack(vm::CellSlice& cs, Record& data) const;
bool unpack_chan_signed_msg(vm::CellSlice& cs, Ref<CellSlice>& sig_A, Ref<CellSlice>& sig_B, Ref<CellSlice>& msg) const;
bool cell_unpack(Ref<vm::Cell> cell_ref, Record& data) const;
bool cell_unpack_chan_signed_msg(Ref<vm::Cell> cell_ref, Ref<CellSlice>& sig_A, Ref<CellSlice>& sig_B, Ref<CellSlice>& msg) const;
bool pack(vm::CellBuilder& cb, const Record& data) const;
bool pack_chan_signed_msg(vm::CellBuilder& cb, Ref<CellSlice> sig_A, Ref<CellSlice> sig_B, Ref<CellSlice> msg) const;
bool cell_pack(Ref<vm::Cell>& cell_ref, const Record& data) const;
bool cell_pack_chan_signed_msg(Ref<vm::Cell>& cell_ref, Ref<CellSlice> sig_A, Ref<CellSlice> sig_B, Ref<CellSlice> msg) const;
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override;
std::ostream& print_type(std::ostream& os) const override {
return os << "ChanSignedMsg";
}
int check_tag(const vm::CellSlice& cs) const override;
int get_tag(const vm::CellSlice& cs) const override {
return 0;
}
};
struct ChanSignedMsg::Record {
typedef ChanSignedMsg type_class;
Ref<CellSlice> sig_A; // sig_A : Maybe ^bits512
Ref<CellSlice> sig_B; // sig_B : Maybe ^bits512
Ref<CellSlice> msg; // msg : ChanMsg
Record() = default;
Record(Ref<CellSlice> _sig_A, Ref<CellSlice> _sig_B, Ref<CellSlice> _msg) : sig_A(std::move(_sig_A)), sig_B(std::move(_sig_B)), msg(std::move(_msg)) {}
};
extern const ChanSignedMsg t_ChanSignedMsg;
//
// headers for type `ChanData`
//
struct ChanData final : TLB_Complex {
enum { chan_data };
static constexpr int cons_len_exact = 0;
struct Record {
typedef ChanData type_class;
Ref<Cell> config; // config : ^ChanConfig
Ref<Cell> state; // state : ^ChanState
Record() = default;
Record(Ref<Cell> _config, Ref<Cell> _state) : config(std::move(_config)), state(std::move(_state)) {}
};
int get_size(const vm::CellSlice& cs) const override {
return 0x20000;
}
bool skip(vm::CellSlice& cs) const override {
return cs.advance_ext(0x20000);
}
bool validate_skip(int* ops, vm::CellSlice& cs, bool weak = false) const override;
bool unpack(vm::CellSlice& cs, Record& data) const;
bool unpack_chan_data(vm::CellSlice& cs, Ref<Cell>& config, Ref<Cell>& state) const;
bool cell_unpack(Ref<vm::Cell> cell_ref, Record& data) const;
bool cell_unpack_chan_data(Ref<vm::Cell> cell_ref, Ref<Cell>& config, Ref<Cell>& state) const;
bool pack(vm::CellBuilder& cb, const Record& data) const;
bool pack_chan_data(vm::CellBuilder& cb, Ref<Cell> config, Ref<Cell> state) const;
bool cell_pack(Ref<vm::Cell>& cell_ref, const Record& data) const;
bool cell_pack_chan_data(Ref<vm::Cell>& cell_ref, Ref<Cell> config, Ref<Cell> state) const;
bool print_skip(PrettyPrinter& pp, vm::CellSlice& cs) const override;
std::ostream& print_type(std::ostream& os) const override {
return os << "ChanData";
}
int check_tag(const vm::CellSlice& cs) const override;
int get_tag(const vm::CellSlice& cs) const override {
return 0;
}
};
extern const ChanData t_ChanData;
// declarations of constant types used
// ## 1
@ -9123,6 +9561,8 @@ extern const HASH_UPDATE t_HASH_UPDATE_Account;
extern const RefT t_Ref_HASH_UPDATE_Account;
// ^TransactionDescr
extern const RefT t_Ref_TransactionDescr;
// uint16
extern const UInt t_uint16;
// HashmapAug 64 ^Transaction CurrencyCollection
extern const HashmapAug t_HashmapAug_64_Ref_Transaction_CurrencyCollection;
// HashmapAugE 256 AccountBlock CurrencyCollection
@ -9134,9 +9574,7 @@ extern const Maybe t_Maybe_VarUInteger_3;
// Maybe int32
extern const Maybe t_Maybe_int32;
// ^[$_ gas_used:(VarUInteger 7) gas_limit:(VarUInteger 7) gas_credit:(Maybe (VarUInteger 3)) mode:int8 exit_code:int32 exit_arg:(Maybe int32) vm_steps:uint32 vm_init_state_hash:bits256 vm_final_state_hash:bits256 ]
extern const RefT t_Ref_TYPE_1625;
// uint16
extern const UInt t_uint16;
extern const RefT t_Ref_TYPE_1626;
// Maybe TrStoragePhase
extern const Maybe t_Maybe_TrStoragePhase;
// Maybe TrCreditPhase
@ -9168,7 +9606,7 @@ extern const HashmapE t_HashmapE_256_LibDescr;
// Maybe BlkMasterInfo
extern const Maybe t_Maybe_BlkMasterInfo;
// ^[$_ overload_history:uint64 underload_history:uint64 total_balance:CurrencyCollection total_validator_fees:CurrencyCollection libraries:(HashmapE 256 LibDescr) master_ref:(Maybe BlkMasterInfo) ]
extern const RefT t_Ref_TYPE_1639;
extern const RefT t_Ref_TYPE_1640;
// ^McStateExtra
extern const RefT t_Ref_McStateExtra;
// Maybe ^McStateExtra
@ -9206,13 +9644,13 @@ extern const RefT t_Ref_McBlockExtra;
// Maybe ^McBlockExtra
extern const Maybe t_Maybe_Ref_McBlockExtra;
// ^[$_ from_prev_blk:CurrencyCollection to_next_blk:CurrencyCollection imported:CurrencyCollection exported:CurrencyCollection ]
extern const RefT t_Ref_TYPE_1650;
// ^[$_ fees_imported:CurrencyCollection recovered:CurrencyCollection created:CurrencyCollection minted:CurrencyCollection ]
extern const RefT t_Ref_TYPE_1651;
// ^[$_ fees_imported:CurrencyCollection recovered:CurrencyCollection created:CurrencyCollection minted:CurrencyCollection ]
extern const RefT t_Ref_TYPE_1652;
// ## 3
extern const NatWidth t_natwidth_3;
// ^[$_ fees_collected:CurrencyCollection funds_created:CurrencyCollection ]
extern const RefT t_Ref_TYPE_1655;
extern const RefT t_Ref_TYPE_1656;
// BinTree ShardDescr
extern const BinTree t_BinTree_ShardDescr;
// ^(BinTree ShardDescr)
@ -9236,7 +9674,7 @@ extern const NatWidth t_natwidth_16;
// Maybe ExtBlkRef
extern const Maybe t_Maybe_ExtBlkRef;
// ^[$_ flags:(## 16) {<= flags 1} validator_info:ValidatorInfo prev_blocks:OldMcBlocksInfo after_key_block:Bool last_key_block:(Maybe ExtBlkRef) block_create_stats:flags.0?BlockCreateStats ]
extern const RefT t_Ref_TYPE_1669;
extern const RefT t_Ref_TYPE_1670;
// ^SignedCertificate
extern const RefT t_Ref_SignedCertificate;
// HashmapE 16 CryptoSignaturePair
@ -9244,7 +9682,7 @@ extern const HashmapE t_HashmapE_16_CryptoSignaturePair;
// Maybe ^InMsg
extern const Maybe t_Maybe_Ref_InMsg;
// ^[$_ prev_blk_signatures:(HashmapE 16 CryptoSignaturePair) recover_create_msg:(Maybe ^InMsg) mint_msg:(Maybe ^InMsg) ]
extern const RefT t_Ref_TYPE_1677;
extern const RefT t_Ref_TYPE_1678;
// Hashmap 16 ValidatorDescr
extern const Hashmap t_Hashmap_16_ValidatorDescr;
// HashmapE 16 ValidatorDescr
@ -9289,6 +9727,16 @@ extern const Maybe t_Maybe_Ref_BlockSignatures;
extern const RefT t_Ref_TopBlockDescr;
// HashmapE 96 ^TopBlockDescr
extern const HashmapE t_HashmapE_96_Ref_TopBlockDescr;
// MERKLE_PROOF Block
extern const MERKLE_PROOF t_MERKLE_PROOF_Block;
// ^(MERKLE_PROOF Block)
extern const RefT t_Ref_MERKLE_PROOF_Block;
// MERKLE_PROOF ShardState
extern const MERKLE_PROOF t_MERKLE_PROOF_ShardState;
// ^(MERKLE_PROOF ShardState)
extern const RefT t_Ref_MERKLE_PROOF_ShardState;
// ^ProducerInfo
extern const RefT t_Ref_ProducerInfo;
// ^ComplaintDescr
extern const RefT t_Ref_ComplaintDescr;
// ^ValidatorComplaint
@ -9306,7 +9754,7 @@ extern const NatWidth t_natwidth_24;
// HashmapE 4 VmStackValue
extern const HashmapE t_HashmapE_4_VmStackValue;
// ^[$_ max_limit:int64 cur_limit:int64 credit:int64 ]
extern const RefT t_Ref_TYPE_1715;
extern const RefT t_Ref_TYPE_1717;
// HashmapE 256 ^Cell
extern const HashmapE t_HashmapE_256_Ref_Cell;
// uint13
@ -9325,6 +9773,18 @@ extern const RefT t_Ref_VmCont;
extern const RefT t_Ref_DNSRecord;
// HashmapE 16 ^DNSRecord
extern const HashmapE t_HashmapE_16_Ref_DNSRecord;
// ^MsgAddressInt
extern const RefT t_Ref_MsgAddressInt;
// bits512
extern const Bits t_bits512;
// ^bits512
extern const RefT t_Ref_bits512;
// Maybe ^bits512
extern const Maybe t_Maybe_Ref_bits512;
// ^ChanConfig
extern const RefT t_Ref_ChanConfig;
// ^ChanState
extern const RefT t_Ref_ChanState;
// declaration of type name registration function
extern bool register_simple_types(std::function<bool(const char*, const TLB*)> func);

View File

@ -241,6 +241,14 @@ bool MsgAddressInt::extract_std_address(vm::CellSlice& cs, ton::WorkchainId& wor
return false;
}
bool MsgAddressInt::extract_std_address(Ref<vm::CellSlice> cs_ref, block::StdAddress& addr, bool rewrite) const {
return extract_std_address(std::move(cs_ref), addr.workchain, addr.addr, rewrite);
}
bool MsgAddressInt::extract_std_address(vm::CellSlice& cs, block::StdAddress& addr, bool rewrite) const {
return extract_std_address(cs, addr.workchain, addr.addr, rewrite);
}
bool MsgAddressInt::store_std_address(vm::CellBuilder& cb, ton::WorkchainId workchain,
const ton::StdSmcAddress& addr) const {
if (workchain >= -128 && workchain < 128) {
@ -263,6 +271,14 @@ Ref<vm::CellSlice> MsgAddressInt::pack_std_address(ton::WorkchainId workchain, c
}
}
bool MsgAddressInt::store_std_address(vm::CellBuilder& cb, const block::StdAddress& addr) const {
return store_std_address(cb, addr.workchain, addr.addr);
}
Ref<vm::CellSlice> MsgAddressInt::pack_std_address(const block::StdAddress& addr) const {
return pack_std_address(addr.workchain, addr.addr);
}
const MsgAddressInt t_MsgAddressInt;
bool MsgAddress::validate_skip(int* ops, vm::CellSlice& cs, bool weak) const {

View File

@ -298,8 +298,13 @@ struct MsgAddressInt final : TLB_Complex {
bool rewrite = true) const;
bool extract_std_address(vm::CellSlice& cs, ton::WorkchainId& workchain, ton::StdSmcAddress& addr,
bool rewrite = true) const;
bool extract_std_address(Ref<vm::CellSlice> cs_ref, block::StdAddress& addr, bool rewrite = true) const;
bool extract_std_address(vm::CellSlice& cs, block::StdAddress& addr, bool rewrite = true) const;
bool store_std_address(vm::CellBuilder& cb, ton::WorkchainId workchain, const ton::StdSmcAddress& addr) const;
Ref<vm::CellSlice> pack_std_address(ton::WorkchainId workchain, const ton::StdSmcAddress& addr) const;
bool store_std_address(vm::CellBuilder& cb, const block::StdAddress& addr) const;
Ref<vm::CellSlice> pack_std_address(const block::StdAddress& addr) const;
};
extern const MsgAddressInt t_MsgAddressInt;

View File

@ -27,6 +27,7 @@
#include "td/utils/crypto.h"
#include "td/utils/tl_storers.h"
#include "td/utils/misc.h"
#include "td/utils/Random.h"
namespace block {
using namespace std::literals::string_literals;
@ -369,14 +370,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)));
}
@ -1546,6 +1547,89 @@ bool unpack_CreatorStats(Ref<vm::CellSlice> cs, DiscountedCounter& mc_cnt, Disco
}
}
/*
*
* Monte Carlo simulator for computing the share of shardchain blocks generated by each validator
*
*/
bool MtCarloComputeShare::compute() {
ok = false;
if (W.size() >= (1U << 31) || W.empty()) {
return false;
}
K = std::min(K, N);
if (K <= 0 || iterations <= 0) {
return false;
}
double tot_weight = 0., acc = 0.;
for (int i = 0; i < N; i++) {
if (W[i] <= 0.) {
return false;
}
tot_weight += W[i];
}
CW.resize(N);
RW.resize(N);
for (int i = 0; i < N; i++) {
CW[i] = acc;
acc += W[i] /= tot_weight;
RW[i] = 0.;
}
R0 = 0.;
H.resize(N);
A.resize(K);
for (long long it = 0; it < iterations; ++it) {
gen_vset();
}
for (int i = 0; i < N; i++) {
RW[i] = W[i] * (RW[i] + R0) / (double)iterations;
}
return ok = true;
}
void MtCarloComputeShare::gen_vset() {
double total_wt = 1.;
int hc = 0;
for (int i = 0; i < K; i++) {
CHECK(total_wt > 0);
double inv_wt = 1. / total_wt;
R0 += inv_wt;
for (int j = 0; j < i; j++) {
RW[A[j]] -= inv_wt;
}
// double p = drand48() * total_wt;
double p = (double)td::Random::fast_uint64() * total_wt / (1. * (1LL << 32) * (1LL << 32));
for (int h = 0; h < hc; h++) {
if (p < H[h].first) {
break;
}
p += H[h].second;
}
int a = -1, b = N, c;
while (b - a > 1) {
c = ((a + b) >> 1);
if (CW[c] <= p) {
a = c;
} else {
b = c;
}
}
CHECK(a >= 0 && a < N);
CHECK(total_wt >= W[a]);
total_wt -= W[a];
double x = CW[a];
c = hc++;
while (c > 0 && H[c - 1].first > x) {
H[c] = H[c - 1];
--c;
}
H[c].first = x;
H[c].second = W[a];
A[i] = a;
}
}
/*
*
* Other block-related functions
@ -1723,7 +1807,7 @@ ton::AccountIdPrefixFull interpolate_addr(const ton::AccountIdPrefixFull& src, c
unsigned long long mask = (std::numeric_limits<td::uint64>::max() >> (d - 32));
return ton::AccountIdPrefixFull{dest.workchain, (dest.account_id_prefix & ~mask) | (src.account_id_prefix & mask)};
} else {
int mask = (-1 >> d);
int mask = (int)(~0U >> d);
return ton::AccountIdPrefixFull{(dest.workchain & ~mask) | (src.workchain & mask), src.account_id_prefix};
}
}

View File

@ -163,12 +163,12 @@ struct MsgProcessedUpto {
MsgProcessedUpto(ton::ShardId _shard, ton::BlockSeqno _mcseqno, ton::LogicalTime _lt, td::ConstBitPtr _hash)
: shard(_shard), mc_seqno(_mcseqno), last_inmsg_lt(_lt), last_inmsg_hash(_hash) {
}
bool operator<(const MsgProcessedUpto& other) const & {
bool operator<(const MsgProcessedUpto& other) const& {
return shard < other.shard || (shard == other.shard && mc_seqno < other.mc_seqno);
}
bool contains(const MsgProcessedUpto& other) const &;
bool contains(const MsgProcessedUpto& other) const&;
bool 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&;
// NB: this is for checking whether we have already imported an internal message
bool already_processed(const EnqueuedMsgDescr& msg) const;
bool can_check_processed() const {
@ -596,6 +596,62 @@ struct BlockProofChain {
td::Status validate(td::CancellationToken cancellation_token = {});
};
// compute the share of shardchain blocks generated by each validator using Monte Carlo method
class MtCarloComputeShare {
int K, N;
long long iterations;
std::vector<double> W;
std::vector<double> CW, RW;
std::vector<std::pair<double, double>> H;
std::vector<int> A;
double R0;
bool ok;
public:
MtCarloComputeShare(int subset_size, const std::vector<double>& weights, long long iteration_count = 1000000)
: K(subset_size), N((int)weights.size()), iterations(iteration_count), W(weights), ok(false) {
compute();
}
MtCarloComputeShare(int subset_size, int set_size, const double* weights, long long iteration_count = 1000000)
: K(subset_size), N(set_size), iterations(iteration_count), W(weights, weights + set_size), ok(false) {
compute();
}
bool is_ok() const {
return ok;
}
const double* share_array() const {
return ok ? RW.data() : nullptr;
}
const double* weights_array() const {
return ok ? W.data() : nullptr;
}
double operator[](int i) const {
return ok ? RW.at(i) : -1.;
}
double share(int i) const {
return ok ? RW.at(i) : -1.;
}
double weight(int i) const {
return ok ? W.at(i) : -1.;
}
int size() const {
return N;
}
int subset_size() const {
return K;
}
long long performed_iterations() const {
return iterations;
}
private:
bool set_error() {
return ok = false;
}
bool compute();
void gen_vset();
};
int filter_out_msg_queue(vm::AugmentedDictionary& out_queue, ton::ShardIdFull old_shard, ton::ShardIdFull subshard);
std::ostream& operator<<(std::ostream& os, const ShardId& shard_id);

View File

@ -272,6 +272,7 @@ transaction$0111 account_addr:bits256 lt:uint64
old:^X new:^X = MERKLE_UPDATE X;
update_hashes#72 {X:Type} old_hash:bits256 new_hash:bits256
= HASH_UPDATE X;
!merkle_proof#03 {X:Type} virtual_hash:bits256 depth:uint16 virtual_root:^X = MERKLE_PROOF X;
acc_trans#5 account_addr:bits256
transactions:(HashmapAug 64 ^Transaction CurrencyCollection)
@ -731,7 +732,10 @@ top_block_descr_set#4ac789f3 collection:(HashmapE 96 ^TopBlockDescr) = TopBlockD
//
// VALIDATOR MISBEHAVIOR COMPLAINTS
//
no_blk_gen mc_blk_ref:ExtBlkRef from_utime:uint32 to_utime:uint32 state_proof:^Cell prod_proof:^Cell = ComplaintDescr;
prod_info#34 utime:uint32 mc_blk_ref:ExtBlkRef state_proof:^(MERKLE_PROOF Block)
prod_proof:^(MERKLE_PROOF ShardState) = ProducerInfo;
no_blk_gen from_utime:uint32 prod_info:^ProducerInfo = ComplaintDescr;
no_blk_gen_diff prod_info_old:^ProducerInfo prod_info_new:^ProducerInfo = ComplaintDescr;
validator_complaint#bc validator_pubkey:uint256 description:^ComplaintDescr created_at:uint32 severity:uint8 reward_addr:uint256 paid:Grams suggested_fine:Grams suggested_fine_part:uint32 = ValidatorComplaint;
complaint_status#2d complaint:^ValidatorComplaint voters:(HashmapE 16 True) vset_id:uint256 weight_remaining:int64 = ValidatorComplaintStatus;
@ -808,3 +812,25 @@ cap_method_pubkey#71f4 = SmcCapability;
cap_is_wallet#2177 = SmcCapability;
cap_name#ff name:Text = SmcCapability;
//
// PAYMENT CHANNELS
//
chan_config$_ init_timeout:uint32 close_timeout:uint32 a_key:bits256 b_key:bits256
a_addr:^MsgAddressInt b_addr:^MsgAddressInt channel_id:uint64 = ChanConfig;
chan_state_init$000 signed_A:Bool signed_B:Bool min_A:Grams min_B:Grams expire_at:uint32 A:Grams B:Grams = ChanState;
chan_state_close$001 signed_A:Bool signed_B:Bool promise_A:Grams promise_B:Grams expire_at:uint32 A:Grams B:Grams = ChanState;
chan_state_payout$010 A:Grams B:Grams = ChanState;
chan_promise$_ channel_id:uint64 promise_A:Grams promise_B:Grams = ChanPromise;
chan_signed_promise#_ sig:(Maybe ^bits512) promise:ChanPromise = ChanSignedPromise;
chan_msg_init#27317822 inc_A:Grams inc_B:Grams min_A:Grams min_B:Grams channel_id:uint64 = ChanMsg;
chan_msg_close#f28ae183 extra_A:Grams extra_B:Grams promise:ChanSignedPromise = ChanMsg;
chan_msg_timeout#43278a28 = ChanMsg;
chan_signed_msg$_ sig_A:(Maybe ^bits512) sig_B:(Maybe ^bits512) msg:ChanMsg = ChanSignedMsg;
chan_data$_ config:^ChanConfig state:^ChanState = ChanData;

View File

@ -31,7 +31,7 @@
namespace block {
using namespace std::literals::string_literals;
td::Status check_block_header_proof(td::Ref<vm::Cell> root, ton::BlockIdExt blkid, ton::Bits256* store_shard_hash_to,
td::Status check_block_header_proof(td::Ref<vm::Cell> root, ton::BlockIdExt blkid, ton::Bits256* store_state_hash_to,
bool check_state_hash, td::uint32* save_utime, ton::LogicalTime* save_lt) {
ton::RootHash vhash{root->get_hash().bits()};
if (vhash != blkid.root_hash) {
@ -53,7 +53,7 @@ td::Status check_block_header_proof(td::Ref<vm::Cell> root, ton::BlockIdExt blki
if (save_lt) {
*save_lt = info.end_lt;
}
if (store_shard_hash_to) {
if (store_state_hash_to) {
vm::CellSlice upd_cs{vm::NoVmSpec(), blk.state_update};
if (!(upd_cs.is_special() && upd_cs.prefetch_long(8) == 4 // merkle update
&& upd_cs.size_ext() == 0x20228)) {
@ -61,11 +61,11 @@ td::Status check_block_header_proof(td::Ref<vm::Cell> root, ton::BlockIdExt blki
}
auto upd_hash = upd_cs.prefetch_ref(1)->get_hash(0);
if (!check_state_hash) {
*store_shard_hash_to = upd_hash.bits();
} else if (store_shard_hash_to->compare(upd_hash.bits())) {
*store_state_hash_to = upd_hash.bits();
} else if (store_state_hash_to->compare(upd_hash.bits())) {
return td::Status::Error(PSTRING() << "state hash mismatch in block header of " << blkid.to_str()
<< " : header declares " << upd_hash.bits().to_hex(256) << " expected "
<< store_shard_hash_to->to_hex());
<< store_state_hash_to->to_hex());
}
}
return td::Status::OK();

View File

@ -25,7 +25,7 @@ namespace block {
using td::Ref;
td::Status check_block_header_proof(td::Ref<vm::Cell> root, ton::BlockIdExt blkid,
ton::Bits256* store_shard_hash_to = nullptr, bool check_state_hash = false,
ton::Bits256* store_state_hash_to = nullptr, bool check_state_hash = false,
td::uint32* save_utime = nullptr, ton::LogicalTime* save_lt = nullptr);
td::Status check_shard_proof(ton::BlockIdExt blk, ton::BlockIdExt shard_blk, td::Slice shard_proof);
td::Status check_account_proof(td::Slice proof, ton::BlockIdExt shard_blk, const block::StdAddress& addr,

View File

@ -1785,6 +1785,22 @@ std::vector<ton::ValidatorDescr> ValidatorSet::export_validator_set() const {
return l;
}
std::map<ton::Bits256, int> ValidatorSet::compute_validator_map() const {
std::map<ton::Bits256, int> res;
for (int i = 0; i < (int)list.size(); i++) {
res.emplace(list[i].pubkey.as_bits256(), i);
}
return res;
}
std::vector<double> ValidatorSet::export_scaled_validator_weights() const {
std::vector<double> res;
for (const auto& node : list) {
res.push_back((double)node.weight / (double)total_weight);
}
return res;
}
std::vector<ton::ValidatorDescr> Config::do_compute_validator_set(const block::CatchainValidatorsConfig& ccv_conf,
ton::ShardIdFull shard,
const block::ValidatorSet& vset, ton::UnixTime time,

View File

@ -71,6 +71,8 @@ struct ValidatorSet {
}
const ValidatorDescr& at_weight(td::uint64 weight_pos) const;
std::vector<ton::ValidatorDescr> export_validator_set() const;
std::map<ton::Bits256, int> compute_validator_map() const;
std::vector<double> export_scaled_validator_weights() const;
};
#pragma pack(push, 1)

View File

@ -1,248 +0,0 @@
/*
This file is part of TON Blockchain source code.
TON Blockchain is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
TON Blockchain is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
You must obey the GNU General Public License in all respects for all
of the code used other than OpenSSL. If you modify file(s) with this
exception, you may extend this exception to your version of the file(s),
but you are not obligated to do so. If you do not wish to do so, delete this
exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
Copyright 2017-2019 Telegram Systems LLP
*/
#include "block/block.h"
#include "vm/boc.h"
#include <iostream>
#include "block-db.h"
#include "block-auto.h"
#include "block-parse.h"
#include "vm/cp0.h"
#include <getopt.h>
using td::Ref;
int verbosity;
struct IntError {
std::string err_msg;
IntError(std::string _msg) : err_msg(_msg) {
}
IntError(const char* _msg) : err_msg(_msg) {
}
};
td::Ref<vm::Cell> load_boc(std::string filename) {
std::cerr << "loading bag-of-cell file " << filename << std::endl;
auto bytes_res = block::load_binary_file(filename);
if (bytes_res.is_error()) {
throw IntError{PSTRING() << "cannot load file `" << filename << "` : " << bytes_res.move_as_error()};
}
vm::BagOfCells boc;
auto res = boc.deserialize(bytes_res.move_as_ok());
if (res.is_error()) {
throw IntError{PSTRING() << "cannot deserialize bag-of-cells " << res.move_as_error()};
}
if (res.move_as_ok() <= 0 || boc.get_root_cell().is_null()) {
throw IntError{"cannot deserialize bag-of-cells "};
}
return boc.get_root_cell();
}
void test1() {
block::ShardId id{ton::masterchainId}, id2{ton::basechainId, 0x11efULL << 48};
std::cout << '[' << id << "][" << id2 << ']' << std::endl;
vm::CellBuilder cb;
cb << id << id2;
std::cout << "ShardIdent.pack() = " << block::tlb::t_ShardIdent.pack(cb, {12, 3, 0x3aeULL << 52}) << std::endl;
std::cout << cb << std::endl;
auto cref = cb.finalize();
td::Ref<vm::CellSlice> cs{true, cref}, cs2;
block::ShardId id3{cs.write()}, id4, id5;
cs >> id4 >> id5;
std::cout << '[' << id3 << "][" << id4 << "][" << id5 << ']' << std::endl;
vm::CellSlice csl{std::move(cref)};
std::cout << "ShardIdent.get_size() = " << block::tlb::t_ShardIdent.get_size(csl) << std::endl;
std::cout << "MsgAddress.get_size() = " << block::tlb::t_MsgAddress.get_size(csl) << std::endl;
std::cout << "Grams.get_size() = " << block::tlb::t_Grams.get_size(csl) << std::endl;
std::cout << "Grams.as_integer() = " << block::tlb::t_Grams.as_integer(csl) << std::endl;
(csl + 8).print_rec(std::cout);
std::cout << "Grams.get_size() = " << block::tlb::t_Grams.get_size(csl + 8) << std::endl;
std::cout << "Grams.as_integer() = " << block::tlb::t_Grams.as_integer(csl + 8) << std::endl;
vm::CellSlice csl2{csl};
block::gen::ShardIdent::Record sh_id;
for (int i = 0; i < 3; i++) {
std::cout << csl2 << std::endl;
bool ok = tlb::unpack(csl2, sh_id);
std::cout << "block::gen::ShardIdent.unpack() = " << ok << std::endl;
if (ok) {
std::cout << " (shard_ident shard_pfx_bits:" << sh_id.shard_pfx_bits << " workchain_id:" << sh_id.workchain_id
<< " shard_prefix:" << std::hex << sh_id.shard_prefix << std::dec << ")" << std::endl;
}
}
block::tlb::ShardIdent::Record shard_id;
for (int i = 0; i < 3; i++) {
std::cout << "ShardIdent.validate() = " << block::tlb::t_ShardIdent.validate(csl) << std::endl;
csl.print_rec(std::cerr);
csl.dump(std::cerr, 7);
std::cout << "ShardIdent.unpack() = " << block::tlb::t_ShardIdent.unpack(csl, shard_id) << std::endl;
if (shard_id.is_valid()) {
std::cout << "shard_pfx_bits:" << shard_id.shard_pfx_bits << " workchain_id:" << shard_id.workchain_id
<< " shard_prefix:" << shard_id.shard_prefix << std::endl;
}
}
std::cout << "ShardIdent.skip_validate() = " << block::tlb::t_ShardIdent.validate_skip(csl) << std::endl;
std::cout << "ShardIdent.skip_validate() = " << block::tlb::t_ShardIdent.validate_skip(csl) << std::endl;
std::cout << "ShardIdent.skip_validate() = " << block::tlb::t_ShardIdent.validate_skip(csl) << std::endl;
using namespace td::literals;
std::cout << "Grams.store_intval(239) = " << block::tlb::t_Grams.store_integer_value(cb, "239"_i256) << std::endl;
std::cout << "Grams.store_intval(17239) = " << block::tlb::t_Grams.store_integer_value(cb, "17239"_i256) << std::endl;
std::cout << "Grams.store_intval(-17) = " << block::tlb::t_Grams.store_integer_value(cb, "-17"_i256) << std::endl;
std::cout << "Grams.store_intval(0) = " << block::tlb::t_Grams.store_integer_value(cb, "0"_i256) << std::endl;
std::cout << cb << std::endl;
cs = td::Ref<vm::CellSlice>{true, cb.finalize()};
std::cout << "Grams.store_intval(666) = " << block::tlb::t_Grams.store_integer_value(cb, "666"_i256) << std::endl;
std::cout << cb << std::endl;
cs2 = td::Ref<vm::CellSlice>{true, cb.finalize()};
std::cout << "Grams.validate(cs) = " << block::tlb::t_Grams.validate(*cs) << std::endl;
std::cout << "Grams.validate(cs2) = " << block::tlb::t_Grams.validate(*cs2) << std::endl;
//
block::gen::SplitMergeInfo::Record data;
block::gen::Grams::Record data2;
std::cout << "block::gen::Grams.validate(cs) = " << block::gen::t_Grams.validate(*cs) << std::endl;
std::cout << "block::gen::Grams.validate(cs2) = " << block::gen::t_Grams.validate(*cs2) << std::endl;
std::cout << "[cs = " << cs << "]" << std::endl;
bool ok = tlb::csr_unpack_inexact(cs, data);
std::cout << "block::gen::SplitMergeInfo.unpack(cs, data) = " << ok << std::endl;
if (ok) {
std::cout << " cur_shard_pfx_len = " << data.cur_shard_pfx_len << "; acc_split_depth = " << data.acc_split_depth
<< "; this_addr = " << data.this_addr << "; sibling_addr = " << data.sibling_addr << std::endl;
}
ok = tlb::csr_unpack_inexact(cs, data2);
std::cout << "block::gen::Grams.unpack(cs, data2) = " << ok << std::endl;
if (ok) {
std::cout << " amount = " << data2.amount << std::endl;
block::gen::VarUInteger::Record data3;
ok = tlb::csr_type_unpack(data2.amount, block::gen::t_VarUInteger_16, data3);
std::cout << " block::gen::VarUInteger16.unpack(amount, data3) = " << ok << std::endl;
if (ok) {
std::cout << " len = " << data3.len << "; value = " << data3.value << std::endl;
vm::CellBuilder cb;
std::cout << " block::gen::VarUInteger16.pack(cb, data3) = "
<< tlb::type_pack(cb, block::gen::t_VarUInteger_16, data3) << std::endl;
std::cout << " cb = " << cb.finalize() << std::endl;
}
}
/*
{
vm::CellBuilder cb;
td::BitArray<256> hash;
std::memset(hash.data(), 0x69, 32);
bool ok = tlb::pack(
cb, block::gen::Test::Record{1000000000000, {170239, -888, {239017, "1000000000000000000"_ri256}, hash}, 17});
std::cout << " block::gen::Test::pack(cb, {1000000000000, ...}) = " << ok << std::endl;
std::cout << " cb = " << cb << std::endl;
auto cell = cb.finalize();
vm::CellSlice cs{cell};
cs.print_rec(std::cout);
block::gen::Test::Record data;
std::cout << " block::gen::Test::validate_ref(cell) = " << block::gen::t_Test.validate_ref(cell) << std::endl;
ok = tlb::unpack(cs, data);
std::cout << " block::gen::Test::unpack(cs, data) = " << ok << std::endl;
if (ok) {
std::cout << "a:" << data.a << " b:" << data.r1.b << " c:" << data.r1.c << " d:" << data.r1.r1.d
<< " e:" << data.r1.r1.e << " f:" << data.r1.f << " g:" << data.g << std::endl;
}
std::cout << " block::gen::Test::print_ref(cell) = ";
block::gen::t_Test.print_ref(std::cout, cell, 2);
block::gen::t_CurrencyCollection.print_ref(std::cout, cell, 2);
std::cout << std::endl;
}
*/
std::cout << "Grams.add_values() = " << block::tlb::t_Grams.add_values(cb, cs.write(), cs2.write()) << std::endl;
std::cout << cb << std::endl;
std::cout << "block::gen::t_HashmapAug_64_...print_type() = "
<< block::gen::t_HashmapAug_64_Ref_Transaction_CurrencyCollection << std::endl;
}
void test2(vm::CellSlice& cs) {
std::cout << "Bool.validate() = " << block::tlb::t_Bool.validate(cs) << std::endl;
std::cout << "UInt16.validate() = " << block::tlb::t_uint16.validate(cs) << std::endl;
std::cout << "HashmapE(32,UInt16).validate() = " << block::tlb::HashmapE(32, block::tlb::t_uint16).validate(cs)
<< std::endl;
std::cout << "block::gen::HashmapE(32,UInt16).validate() = "
<< block::gen::HashmapE{32, block::gen::t_uint16}.validate(cs) << std::endl;
}
void usage() {
std::cout << "usage: test-block [<boc-file>]\n\tor test-block -h\n";
std::exit(2);
}
int main(int argc, char* const argv[]) {
int i;
int new_verbosity_level = VERBOSITY_NAME(INFO);
auto zerostate = std::make_unique<block::ZerostateInfo>();
while ((i = getopt(argc, argv, "hv:")) != -1) {
switch (i) {
case 'v':
new_verbosity_level = VERBOSITY_NAME(FATAL) + (verbosity = td::to_integer<int>(td::Slice(optarg)));
break;
case 'h':
usage();
std::exit(2);
default:
usage();
std::exit(2);
}
}
SET_VERBOSITY_LEVEL(new_verbosity_level);
try {
bool done = false;
while (optind < argc) {
auto boc = load_boc(argv[optind++]);
if (boc.is_null()) {
std::cerr << "(invalid boc)" << std::endl;
std::exit(2);
} else {
done = true;
vm::CellSlice cs{vm::NoVm(), boc};
cs.print_rec(std::cout);
std::cout << std::endl;
block::gen::t_Block.print_ref(std::cout, boc);
std::cout << std::endl;
if (!block::gen::t_Block.validate_ref(boc)) {
std::cout << "(invalid Block)" << std::endl;
} else {
std::cout << "(valid Block)" << std::endl;
}
}
}
if (!done) {
test1();
}
} catch (IntError& err) {
std::cerr << "caught internal error " << err.err_msg << std::endl;
return 1;
} catch (vm::VmError& err) {
std::cerr << "caught vm error " << err.get_msg() << std::endl;
return 1;
}
return 0;
}

View File

@ -0,0 +1,199 @@
/*
This file is part of TON Blockchain source code.
TON Blockchain is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
TON Blockchain is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with TON Blockchain. If not, see <http://www.gnu.org/licenses/>.
In addition, as a special exception, the copyright holders give permission
to link the code of portions of this program with the OpenSSL library.
You must obey the GNU General Public License in all respects for all
of the code used other than OpenSSL. If you modify file(s) with this
exception, you may extend this exception to your version of the file(s),
but you are not obligated to do so. If you do not wish to do so, delete this
exception statement from your version. If you delete this exception statement
from all source files in the program, then also delete it here.
Copyright 2020 Telegram Systems LLP
*/
#include <iostream>
#include "td/utils/Random.h"
#include "td/utils/misc.h"
#include "block/block.h"
#include <getopt.h>
const int MAX_N = 1000, MAX_K = 100, DEFAULT_K = 7;
int verbosity;
int N, K = DEFAULT_K;
long long iterations = 1000000;
td::uint64 TWL, WL[MAX_N];
double W[MAX_N], CW[MAX_N + 1], RW[MAX_N], R0;
int A[MAX_N], C[MAX_N];
long long TC;
void gen_vset() {
static std::pair<double, double> H[MAX_N];
double total_wt = 1.;
int hc = 0;
for (int i = 0; i < K; i++) {
CHECK(total_wt > 0);
double inv_wt = 1. / total_wt;
R0 += inv_wt; // advanced mtcarlo stats
for (int j = 0; j < i; j++) {
RW[A[j]] -= inv_wt; // advanced mtcarlo stats
}
// double p = drand48() * total_wt;
double p = (double)td::Random::fast_uint64() * total_wt / (1. * (1LL << 32) * (1LL << 32));
for (int h = 0; h < hc; h++) {
if (p < H[h].first) {
break;
}
p += H[h].second;
}
int a = -1, b = N, c;
while (b - a > 1) {
c = ((a + b) >> 1);
if (CW[c] <= p) {
a = c;
} else {
b = c;
}
}
CHECK(a >= 0 && a < N);
CHECK(total_wt >= W[a]);
total_wt -= W[a];
double x = CW[a];
c = hc++;
while (c > 0 && H[c - 1].first > x) {
H[c] = H[c - 1];
--c;
}
H[c].first = x;
H[c].second = W[a];
A[i] = a;
C[a]++; // simple mtcarlo stats
// std::cout << a << ' ';
}
// std::cout << std::endl;
++TC; // simple mtcarlo stats
}
void mt_carlo() {
for (int i = 0; i < N; i++) {
C[i] = 0;
RW[i] = 0.;
}
TC = 0;
R0 = 0.;
std::cout << "running " << iterations << " steps of Monte Carlo simulation\n";
for (long long it = 0; it < iterations; ++it) {
gen_vset();
}
for (int i = 0; i < N; i++) {
RW[i] = W[i] * (RW[i] + R0) / (double)iterations;
}
}
double B[MAX_N];
void compute_bad_approx() {
static double S[MAX_K + 1];
S[0] = 1.;
for (int i = 1; i <= K; i++) {
S[i] = 0.;
}
for (int i = 0; i < N; i++) {
double p = W[i];
for (int j = K; j > 0; j--) {
S[j] += p * S[j - 1];
}
}
double Sk = S[K];
for (int i = 0; i < N; i++) {
double t = 1., p = W[i];
for (int j = 1; j <= K; j++) {
t = S[j] - p * t;
}
B[i] = 1. - t / Sk;
}
}
void usage() {
std::cout
<< "usage: test-weight-distr [-k<shard-val-num>][-m<iterations>][-s<rand-seed>]\nReads the set of validator "
"weights from stdin and emulates validator shard distribution load\n\t-k <shard-val-num>\tSets the number of "
"validators generating each shard\n\t-m <iterations>\tMonte Carlo simulation steps\n";
std::exit(2);
}
int main(int argc, char* const argv[]) {
int i;
int new_verbosity_level = VERBOSITY_NAME(INFO);
// long seed = 0;
while ((i = getopt(argc, argv, "hs:k:m:v:")) != -1) {
switch (i) {
case 'k':
K = td::to_integer<int>(td::Slice(optarg));
CHECK(K > 0 && K <= 100);
break;
case 'm':
iterations = td::to_integer<long long>(td::Slice(optarg));
CHECK(iterations > 0);
break;
case 's':
// seed = td::to_integer<long>(td::Slice(optarg));
// srand48(seed);
break;
case 'v':
new_verbosity_level = VERBOSITY_NAME(FATAL) + (verbosity = td::to_integer<int>(td::Slice(optarg)));
break;
case 'h':
usage();
std::exit(2);
default:
usage();
std::exit(2);
}
}
SET_VERBOSITY_LEVEL(new_verbosity_level);
for (N = 0; N < MAX_N && (std::cin >> WL[N]); N++) {
CHECK(WL[N] > 0);
TWL += WL[N];
}
CHECK(std::cin.eof());
CHECK(N > 0 && TWL > 0 && N <= MAX_N);
K = std::min(K, N);
CHECK(K > 0 && K <= MAX_K);
double acc = 0.;
for (i = 0; i < N; i++) {
CW[i] = acc;
acc += W[i] = (double)WL[i] / (double)TWL;
std::cout << "#" << i << ":\t" << W[i] << std::endl;
}
compute_bad_approx();
mt_carlo();
std::cout << "result of Monte Carlo simulation (" << iterations << " iterations):" << std::endl;
std::cout << "idx\tweight\tmtcarlo1\tmtcarlo2\tapprox\n";
for (i = 0; i < N; i++) {
std::cout << "#" << i << ":\t" << W[i] << '\t' << (double)C[i] / (double)iterations << '\t' << RW[i] << '\t' << B[i]
<< std::endl;
}
// same computation, but using a MtCarloComputeShare object
block::MtCarloComputeShare MT(K, N, W, iterations);
std::cout << "-----------------------\n";
for (i = 0; i < N; i++) {
std::cout << '#' << i << ":\t" << MT.weight(i) << '\t' << MT.share(i) << std::endl;
}
return 0;
}

View File

@ -1429,6 +1429,9 @@ int Transaction::try_action_send_msg(const vm::CellSlice& cs0, ActionPhase& ap,
if (!tlb::csr_unpack(msg.info, erec)) {
return -1;
}
if (act_rec.mode & ~3) {
return -1; // invalid mode for an external message
}
info.src = std::move(erec.src);
info.dest = std::move(erec.dest);
// created_lt and created_at are ignored

View File

@ -151,8 +151,6 @@ struct ActionPhaseConfig {
struct CreditPhase {
td::RefInt256 due_fees_collected;
block::CurrencyCollection credit;
// td::RefInt256 credit;
// Ref<vm::Cell> credit_extra;
};
struct ComputePhase {
@ -233,8 +231,6 @@ struct Account {
ton::UnixTime last_paid;
vm::CellStorageStat storage_stat;
block::CurrencyCollection balance;
// td::RefInt256 balance;
// Ref<vm::Cell> extra_balance;
td::RefInt256 due_payment;
Ref<vm::Cell> orig_total_state; // ^Account
Ref<vm::Cell> total_state; // ^Account
@ -325,8 +321,6 @@ struct Transaction {
Ref<vm::Cell> root;
Ref<vm::Cell> new_total_state;
Ref<vm::CellSlice> new_inner_state;
// Ref<vm::Cell> extra_balance;
// Ref<vm::Cell> msg_extra;
Ref<vm::Cell> new_code, new_data, new_library;
Ref<vm::Cell> in_msg, in_msg_state;
Ref<vm::CellSlice> in_msg_body;

View File

@ -0,0 +1,72 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2020 Telegram Systems LLP
*/
#pragma once
#include "refcnt.hpp"
#include "td/actor/PromiseFuture.h"
namespace td {
template <typename S, typename T>
class BinaryPromiseMerger : public CntObject {
Result<S> first_;
Result<T> second_;
Promise<std::pair<S, T>> promise_;
std::atomic<int> pending_;
public:
BinaryPromiseMerger(Promise<std::pair<S, T>> promise) : promise_(std::move(promise)), pending_(2) {
}
static std::pair<Promise<S>, Promise<T>> split(Promise<std::pair<S, T>> promise) {
auto ref = make_ref<BinaryPromiseMerger>(std::move(promise));
auto& obj = ref.write();
return std::make_pair(obj.left(), obj.right());
}
private:
Promise<S> left() {
return [this, self = Ref<BinaryPromiseMerger>(this)](Result<S> res) {
first_ = std::move(res);
work();
};
}
Promise<T> right() {
return [this, self = Ref<BinaryPromiseMerger>(this)](Result<T> res) {
second_ = std::move(res);
work();
};
}
void work() {
if (!--pending_) {
if (first_.is_error()) {
promise_.set_error(first_.move_as_error());
} else if (second_.is_error()) {
promise_.set_error(second_.move_as_error());
} else {
promise_.set_result(std::pair<S, T>(first_.move_as_ok(), second_.move_as_ok()));
}
}
}
};
template <typename S, typename T>
std::pair<Promise<S>, Promise<T>> split_promise(Promise<std::pair<S, T>> promise) {
return BinaryPromiseMerger<S, T>::split(std::move(promise));
}
} // namespace td

View File

@ -36,6 +36,7 @@ library TonUtil // TON Blockchain Fift Library
// ( x -- ) Displays a 64-digit hex number
{ 64 0x. } : 64x.
{ 64 0X. } : 64X.
// ( wc addr -- ) Show address in <workchain>:<account> form
{ swap ._ .":" 64x. } : .addr
// ( wc addr flags -- ) Show address in base64url form

View File

@ -0,0 +1,63 @@
#include <openssl/evp.h>
#include "openssl/digest_td.h"
#include <openssl/opensslv.h>
namespace digest {
const EVP_MD *OpensslEVP_SHA1::get_evp() {
return EVP_sha1();
}
const EVP_MD *OpensslEVP_SHA256::get_evp() {
return EVP_sha256();
}
const EVP_MD *OpensslEVP_SHA512::get_evp() {
return EVP_sha512();
}
template <typename H>
void HashCtx<H>::init() {
ctx = EVP_MD_CTX_create();
reset();
}
template <typename H>
void HashCtx<H>::reset() {
EVP_DigestInit_ex(ctx, H::get_evp(), 0);
}
template <typename H>
void HashCtx<H>::clear() {
EVP_MD_CTX_destroy(ctx);
ctx = nullptr;
}
template <typename H>
void HashCtx<H>::feed(const void *data, std::size_t len) {
EVP_DigestUpdate(ctx, data, len);
}
template <typename H>
std::size_t HashCtx<H>::extract(unsigned char buffer[digest_bytes]) {
unsigned olen = 0;
EVP_DigestFinal_ex(ctx, buffer, &olen);
assert(olen == digest_bytes);
return olen;
}
template <typename H>
std::size_t HashCtx<H>::extract(td::MutableSlice slice) {
return extract(slice.ubegin());
}
template <typename H>
std::string HashCtx<H>::extract() {
unsigned char buffer[digest_bytes];
unsigned olen = 0;
EVP_DigestFinal_ex(ctx, buffer, &olen);
assert(olen == digest_bytes);
return std::string((char *)buffer, olen);
}
} // namespace digest

View File

@ -230,21 +230,24 @@ variable special-dict
} : create-wallet1
// D x t -- D'
{ <b rot Gram, swap rot 16 b>idict! not abort"cannot add value"
{ <b rot Gram, swap rot 32 b>idict! not abort"cannot add value"
} : rdict-entry
{ 86400 * } : days*
// balance -- dict
{ dictnew
over -32768 rdict-entry
over 3/4 */ 92 rdict-entry
over 1/2 */ 183 rdict-entry
swap 1/4 */ 366 rdict-entry
0 548 rdict-entry
over 31 -1<< rdict-entry
over 3/4 */ 91 days* rdict-entry
over 1/2 */ 183 days* rdict-entry
swap 1/4 */ 365 days* rdict-entry
0 548 days* rdict-entry
} : make-rdict
variable wallet2-start-at wallet2-start-at 0!
now 86400 / 1+ 86400 * wallet2-start-at !
// pubkey amount --
{ over ."Key " pubkey>$ type ." -> "
RWCode2 // code
<b 1 32 u, 3 pick 256 u, 3 roll make-rdict dict, b> // data
<b 1 32 u, 3 pick 256 u, 3 roll wallet2-start-at @ 32 u, make-rdict dict, b> // data
empty_cell // libs
3 roll // balance
0 // split_depth

File diff suppressed because one or more lines are too long

View File

@ -52,6 +52,7 @@ PROGRAM{
130944 DECLMETHOD compute_returned_stake
104565 DECLMETHOD past_election_ids
81558 DECLMETHOD past_elections
74376 DECLMETHOD past_elections_list
DECLPROC complete_unpack_complaint
DECLPROC get_past_complaints
77853 DECLMETHOD show_complaint
@ -2263,7 +2264,7 @@ PROGRAM{
}>
past_elections PROC:<{
//
load_data INLINECALLDICT // _23 _24 _25 _26 _27 _28
load_data INLINECALLDICT // _21 _22 _23 _24 _25 _26
s3 s5 XCHG
5 BLKDROP // past_elections
32 PUSHPOW2 // past_elections id
@ -2276,19 +2277,51 @@ PROGRAM{
DUP // past_elections list fs id found found
IF:<{ // past_elections list fs id found
s0 s2 XCHG // past_elections list found id fs
unpack_past_election INLINECALLDICT // past_elections list found id _32 _33 _34 _35 _36 _37 _38
7 TUPLE // past_elections list found id info
s1 s(-1) PUXC // past_elections list found id id info
PAIR // past_elections list found id _20
s0 s3 XCHG2 // past_elections id found _20 list
unpack_past_election INLINECALLDICT // past_elections list found id _30 _31 _32 _33 _34 _35 _36
s7 PUSH
7 -ROLL // past_elections list found id id _30 _31 _32 _33 _34 _35 _36
8 TUPLE // past_elections list found id _17
s0 s3 XCHG2 // past_elections id found _17 list
CONS // past_elections id found list
s0 s2 XCHG // past_elections list found id
}>ELSE<{
s2 POP // past_elections list found id
}>
SWAP // past_elections list id found
NOT // past_elections list id _22
s1 s2 XCHG // past_elections id list _22
NOT // past_elections list id _20
s1 s2 XCHG // past_elections id list _20
}> // past_elections id list
2 1 BLKDROP2 // list
}>
past_elections_list PROC:<{
//
load_data INLINECALLDICT // _28 _29 _30 _31 _32 _33
s3 s5 XCHG
5 BLKDROP // past_elections
32 PUSHPOW2 // past_elections id
PUSHNULL // past_elections id list
UNTIL:<{
s1 s2 XCPU
32 PUSHINT // past_elections list id past_elections _15=32
DICTUGETPREV
NULLSWAPIFNOT2 // past_elections list fs id found
DUP // past_elections list fs id found found
IF:<{ // past_elections list fs id found
s0 s2 XCHG // past_elections list found id fs
unpack_past_election INLINECALLDICT // past_elections list found id _37 _38 _39 _40 _41 _42 _43
4 BLKDROP // past_elections list found id unfreeze_at stake_held vset_hash
s3 PUSH
s3 s1 s3 XCHG3 // past_elections list found id id unfreeze_at vset_hash stake_held
4 TUPLE // past_elections list found id _25
s0 s3 XCHG2 // past_elections id found _25 list
CONS // past_elections id found list
s0 s2 XCHG // past_elections list found id
}>ELSE<{
s2 POP // past_elections list found id
}>
SWAP // past_elections list id found
NOT // past_elections list id _27
s1 s2 XCHG // past_elections id list _27
}> // past_elections id list
2 1 BLKDROP2 // list
}>

View File

@ -0,0 +1 @@
with_tvm_code("payment-channel", "te6ccgECHgEAA2oAART/APSkE/S88sgLAQIBIAIDAgLOBAUACvJwAfANAgEgBgcCASAPEAIBIAgJAgEgCgsAB0MfANgAKV0NMf0x/T/9P/1AHQAdQB0AHTP9GAIBIAwOAgEgDQ4AiwC0gBTAZgw1AHQgwjXGd4B0gBTAZgw1AHQgwjXGd4h+QElnIAfVEFo+RAU8vQQJJI0NeIhmoAgQ1b5EBTy9BKTNTMw4gGAANRwyMoCF8oAFcoAUAP6AgH6AssfAfoCAfoCyYAAfNIA0gD6APoA0x/6APoAMIAIBIBESAgFIGBkCASATFAIBIBUWADUccjKAhfKABXKAFAD+gIB+gLLHwH6AgH6AsmAAQSCEDf+eBBwgBDIywVQBs8WUAT6AhTLahLLH8s/yQH7AIABnPgAUFahI6NSELmTMCKj3lMCvJIwId5RM6BQI6FUMSRz8AlUIgRz8AlyyMoCAfoCAfoCyYAH3AjwBSLAAJgy+CNQB6BQBpE34gvTH4IQQyeKKFIguo4WECZfBjQ0NYAjAvgjuRLy9HBRRAbwCuCAJIIQJzF4IhO6EvL0+gD6APoA+gDTPzCAJwFWErry9IAhU0OgARERAbkBERAB8vKAIlPYuVPYubHy9FDSoFB8oFBasYBcAhFMluZIyFJE14lAnsVNpuZM2EFiROeJTKLCOHjhbUVS8USO8ErGWcFFEBvAK4DBsInBUcAAgVRTwCOAzMzdBRAPwBgL3AnwByLAAJgy+CNQB6BQBpE34g3TH4IQQyeKKFIguo4YWzMzNjY2NoAjBPgjuRTy9BYQNRA0WfAK4IAlghDyiuGDE7oS8vSAIlPWuVPWubHy9FFcsVFLsQX6APoA0gABjhM7WzmAJlC6sBny9HAgEIoHUJkI4w0J0z/6AIBobAoc2zwh8ANUOUPwBCnQ0wJwUiC6jhE7VErgVGRAVHhwD1YS8AsJCJI6PeJxHbqeOBBpCBBqEEUQNEE68AyUXwhsIuLbPIBwdAGbUAdCDCNcZIfkBERCRPZ4zgB9Ub0/5EB3y9BtwAuINlRAtOjownDCAIE26+RAY8vRwB+IAevoAMIAnUT+6E/L0+ABQCaBcuZExkTDiUHagU3C5kTeRMOJTULCcMDQ1FhA1EDRAM/AK4GwiNxAmRUAS8AgADO1E0NTU0QAOAcjMzMntVA==");

View File

@ -0,0 +1,571 @@
// automatically generated from `smartcont/stdlib.fc` `smartcont/payment-channel-code.fc`
PROGRAM{
DECLPROC unpack_data
DECLPROC pack_data
DECLPROC unpack_config
DECLPROC unwrap_signatures
DECLPROC unpack_state_init
DECLPROC pack_state_init
DECLPROC unpack_state_close
DECLPROC pack_state_close
DECLPROC send_payout
DECLPROC do_payout
DECLPROC with_init
DECLPROC with_close
DECLPROC recv_any
DECLPROC recv_internal
DECLPROC recv_external
unpack_data PROCREF:<{
//
c4 PUSH // _1
CTOS // cs
LDREF // _4 cs
LDREF // res res cs
ENDS
}>
pack_data PROCREF:<{
// config state
SWAP
NEWC // state config _2
STREF // state _3
STREF // _4
ENDC // _5
c4 POP
}>
unpack_config PROC:<{
// config
CTOS // cs
32 LDU // _4 cs
32 LDU // _4 _7 cs
256 LDU // _4 _7 _10 cs
256 LDU // _4 _7 _10 _13 cs
LDREF // _4 _7 _10 _13 _16 cs
SWAP // _4 _7 _10 _13 cs _16
CTOS // _4 _7 _10 _13 cs _18
SWAP // _4 _7 _10 _13 _18 cs
LDREF // _4 _7 _10 _13 _18 _19 cs
SWAP // _4 _7 _10 _13 _18 cs _19
CTOS // _4 _7 _10 _13 _18 cs _21
SWAP // _4 _7 _10 _13 _18 _21 cs
64 LDU // res res res res res res res cs
ENDS
}>
unwrap_signatures PROC:<{
// cs a_key b_key
s0 s2 XCHG // b_key a_key cs
1 LDI // b_key a_key a? cs
s0 s1 PUSH2 // b_key a_key a? cs a_sig a?
IF:<{ // b_key a_key a? cs a_sig
DROP // b_key a_key a? cs
LDREF // b_key a_key a? _8 cs
SWAP // b_key a_key a? cs _8
CTOS // b_key a_key a? cs _10
9 PUSHPOW2 // b_key a_key a? cs _10 _11=512
PLDSLICEX // b_key a_key a? cs a_sig
}> // b_key a_key a? cs a_sig
SWAP // b_key a_key a? a_sig cs
1 LDI // b_key a_key a? a_sig b? cs
s0 s1 PUSH2 // b_key a_key a? a_sig b? cs b_sig b?
IF:<{ // b_key a_key a? a_sig b? cs b_sig
DROP // b_key a_key a? a_sig b? cs
LDREF // b_key a_key a? a_sig b? _18 cs
SWAP // b_key a_key a? a_sig b? cs _18
CTOS // b_key a_key a? a_sig b? cs _20
9 PUSHPOW2 // b_key a_key a? a_sig b? cs _20 _21=512
PLDSLICEX // b_key a_key a? a_sig b? cs b_sig
}> // b_key a_key a? a_sig b? cs b_sig
OVER // b_key a_key a? a_sig b? cs b_sig cs
HASHSU // b_key a_key a? a_sig b? cs b_sig hash
s5 PUSH // b_key a_key a? a_sig b? cs b_sig hash a?
IF:<{ // b_key a_key a? a_sig b? cs b_sig hash
31 PUSHINT // b_key a_key a? a_sig b? cs b_sig hash _25
s1 s5 s7 PUXC2 // b_key hash a? _25 b? cs b_sig hash a_sig a_key
CHKSIGNU // b_key hash a? _25 b? cs b_sig _26
s1 s4 XCHG // b_key hash a? b_sig b? cs _25 _26
THROWANYIFNOT
s2 s4 XCHG // b_key b_sig a? hash b? cs
}>ELSE<{
s4 POP
s5 POP // b_key b_sig a? hash b? cs
}>
OVER // b_key b_sig a? hash b? cs b?
IF:<{ // b_key b_sig a? hash b? cs
32 PUSHINT // b_key b_sig a? hash b? cs _28
s3 s5 s6 XCHG3 // _28 cs a? b? hash b_sig b_key
CHKSIGNU // _28 cs a? b? _29
s1 s4 XCHG // b? cs a? _28 _29
THROWANYIFNOT
s1 s2 XCHG // cs b? a?
}>ELSE<{
s5 POP
s3 POP
DROP // cs b? a?
}>
SWAP // cs a? b?
}>
unpack_state_init PROC:<{
// state
1 LDI // _1 state
1 LDI // _1 _4 state
LDGRAMS // _1 _4 _7 state
LDGRAMS // _1 _4 _7 _9 state
32 LDU // _1 _4 _7 _9 _11 state
LDGRAMS // _1 _4 _7 _9 _11 _14 state
LDGRAMS // _1 _4 _7 _9 _11 _14 _31 _30
DROP // _1 _4 _7 _9 _11 _14 _16
}>
pack_state_init PROC:<{
// signed_A? signed_B? min_A min_B expire_at A B
0 PUSHINT // signed_A? signed_B? min_A min_B expire_at A B _7
NEWC // signed_A? signed_B? min_A min_B expire_at A B _7 _8
3 STI // signed_A? signed_B? min_A min_B expire_at A B _10
s1 s7 XCHG // B signed_B? min_A min_B expire_at A signed_A? _10
1 STI // B signed_B? min_A min_B expire_at A _12
s1 s5 XCHG // B A min_A min_B expire_at signed_B? _12
1 STI // B A min_A min_B expire_at _14
s0 s3 XCHG2 // B A expire_at min_B _14 min_A
STGRAMS // B A expire_at min_B _15
SWAP // B A expire_at _15 min_B
STGRAMS // B A expire_at _16
32 STU // B A _18
SWAP // B _18 A
STGRAMS // B _19
SWAP // _19 B
STGRAMS // _20
ENDC // _21
}>
unpack_state_close PROC:<{
// state
1 LDI // _1 state
1 LDI // _1 _4 state
LDGRAMS // _1 _4 _7 state
LDGRAMS // _1 _4 _7 _9 state
32 LDU // _1 _4 _7 _9 _11 state
LDGRAMS // _1 _4 _7 _9 _11 _14 state
LDGRAMS // _1 _4 _7 _9 _11 _14 _31 _30
DROP // _1 _4 _7 _9 _11 _14 _16
}>
pack_state_close PROC:<{
// signed_A? signed_B? promise_A promise_B expire_at A B
1 PUSHINT // signed_A? signed_B? promise_A promise_B expire_at A B _7
NEWC // signed_A? signed_B? promise_A promise_B expire_at A B _7 _8
3 STI // signed_A? signed_B? promise_A promise_B expire_at A B _10
s1 s7 XCHG // B signed_B? promise_A promise_B expire_at A signed_A? _10
1 STI // B signed_B? promise_A promise_B expire_at A _12
s1 s5 XCHG // B A promise_A promise_B expire_at signed_B? _12
1 STI // B A promise_A promise_B expire_at _14
s0 s3 XCHG2 // B A expire_at promise_B _14 promise_A
STGRAMS // B A expire_at promise_B _15
SWAP // B A expire_at _15 promise_B
STGRAMS // B A expire_at _16
32 STU // B A _18
SWAP // B _18 A
STGRAMS // B _19
SWAP // _19 B
STGRAMS // _20
ENDC // _21
}>
send_payout PROC:<{
// s_addr amount channel_id flags
0x37fe7810 PUSHINT // s_addr amount channel_id flags _4
0 PUSHINT // s_addr amount channel_id flags _4 _5=0
16 PUSHINT // s_addr amount channel_id flags _4 _5=0 _6=16
NEWC // s_addr amount channel_id flags _4 _5=0 _6=16 _7
6 STU // s_addr amount channel_id flags _4 _5=0 _9
s0 s6 XCHG2 // _5=0 amount channel_id flags _4 _9 s_addr
STSLICER // _5=0 amount channel_id flags _4 _10
s0 s4 XCHG2 // _5=0 _4 channel_id flags _10 amount
STGRAMS // _5=0 _4 channel_id flags _11
s1 s4 XCHG // flags _4 channel_id _5=0 _11
107 STU // flags _4 channel_id _25
s1 s2 XCHG // flags channel_id _4 _25
32 STU // flags channel_id _27
64 STU // flags _29
ENDC // flags _30
SWAP // _30 flags
SENDRAWMSG
}>
do_payout PROC:<{
// promise_A promise_B A B a_addr b_addr channel_id
ACCEPT
s5 s6 XCHG2 // channel_id b_addr A B a_addr promise_B promise_A
SUB // channel_id b_addr A B a_addr diff
s3 PUSH // channel_id b_addr A B a_addr diff A
NEGATE // channel_id b_addr A B a_addr diff _10
s1 s(-1) PUXC // channel_id b_addr A B a_addr diff diff _10
LESS // channel_id b_addr A B a_addr diff _11
IF:<{ // channel_id b_addr A B a_addr diff
DROP // channel_id b_addr A B a_addr
s2 PUSH // channel_id b_addr A B a_addr A
NEGATE // channel_id b_addr A B a_addr diff
}> // channel_id b_addr A B a_addr diff
s0 s2 PUSH2 // channel_id b_addr A B a_addr diff diff B
GREATER // channel_id b_addr A B a_addr diff _13
IF:<{ // channel_id b_addr A B a_addr diff
DROP // channel_id b_addr A B a_addr
OVER // channel_id b_addr A B a_addr diff
}> // channel_id b_addr A B a_addr diff
s3 s3 XCPU // channel_id b_addr diff B a_addr A diff
ADD // channel_id b_addr diff B a_addr A
s2 s3 XCHG2 // channel_id b_addr A a_addr B diff
SUB // channel_id b_addr A a_addr B
s1 s2 s4 XCPU2
3 PUSHINT // channel_id b_addr A B a_addr A channel_id _16=3
send_payout CALLDICT
s2 s0 s3 XCPUXC
3 PUSHINT // B A b_addr B channel_id _18=3
send_payout CALLDICT
2 PUSHINT // B A _20
NEWC // B A _20 _21
3 STI // B A _23
SWAP // B _23 A
STGRAMS // B _24
SWAP // _24 B
STGRAMS // _25
ENDC // _26
}>
with_init PROC:<{
// state msg_value msg msg_signed_A? msg_signed_B? a_addr b_addr init_timeout channel_id
s0 s8 XCHG // channel_id msg_value msg msg_signed_A? msg_signed_B? a_addr b_addr init_timeout state
unpack_state_init CALLDICT // channel_id msg_value msg msg_signed_A? msg_signed_B? a_addr b_addr init_timeout signed_A? signed_B? min_A min_B expire_at A B
s2 PUSH // channel_id msg_value msg msg_signed_A? msg_signed_B? a_addr b_addr init_timeout signed_A? signed_B? min_A min_B expire_at A B expire_at
0 EQINT // channel_id msg_value msg msg_signed_A? msg_signed_B? a_addr b_addr init_timeout signed_A? signed_B? min_A min_B expire_at A B _18
IF:<{ // channel_id msg_value msg msg_signed_A? msg_signed_B? a_addr b_addr init_timeout signed_A? signed_B? min_A min_B expire_at A B
s2 POP // channel_id msg_value msg msg_signed_A? msg_signed_B? a_addr b_addr init_timeout signed_A? signed_B? min_A min_B B A
NOW // channel_id msg_value msg msg_signed_A? msg_signed_B? a_addr b_addr init_timeout signed_A? signed_B? min_A min_B B A _19
s0 s7 XCHG2 // channel_id msg_value msg msg_signed_A? msg_signed_B? a_addr b_addr A signed_A? signed_B? min_A min_B B _19 init_timeout
ADD // channel_id msg_value msg msg_signed_A? msg_signed_B? a_addr b_addr A signed_A? signed_B? min_A min_B B expire_at
s0 s6 XCHG2 // channel_id msg_value msg msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at A
}>ELSE<{
s7 POP // channel_id msg_value msg msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at A
}>
s0 s11 XCHG // channel_id msg_value A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at msg
32 LDU // channel_id msg_value A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at op msg
0x43278a28 PUSHINT // channel_id msg_value A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at op msg _25
s2 s(-1) PUXC // channel_id msg_value A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at op msg op _25
EQUAL // channel_id msg_value A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at op msg _26
IFJMP:<{ // channel_id msg_value A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at op msg
s2 s6 XCHG
6 BLKDROP
s4 POP
s4 POP
s5 POP // channel_id b_addr A B expire_at a_addr
35 PUSHINT // channel_id b_addr A B expire_at a_addr _27
s0 s2 XCHG
NOW // channel_id b_addr A B _27 a_addr expire_at _28
LESS // channel_id b_addr A B _27 a_addr _29
s1 s2 XCHG // channel_id b_addr A B a_addr _27 _29
THROWANYIFNOT
0 PUSHINT // channel_id b_addr A B a_addr _31=0
s4 s4 XCPU
s0 s6 XCHG // _31=0 _32=0 A B a_addr b_addr channel_id
do_payout CALLDICT // _33
}> // channel_id msg_value A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at op msg
36 PUSHINT // channel_id msg_value A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at op msg _34
0x27317822 PUSHINT // channel_id msg_value A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at op msg _34 _35
s1 s3 XCHG // channel_id msg_value A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at _34 msg op _35
EQUAL // channel_id msg_value A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at _34 msg _36
s1 s2 XCHG // channel_id msg_value A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at msg _34 _36
THROWANYIFNOT
LDGRAMS // channel_id msg_value A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at _43 msg
LDGRAMS // channel_id msg_value A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at _43 _45 msg
LDGRAMS // channel_id msg_value A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at _43 _45 _47 msg
LDGRAMS // channel_id msg_value A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at _43 _45 _47 _49 msg
64 LDU // channel_id msg_value A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at _43 _45 _47 _49 _104 _103
DROP // channel_id msg_value A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at inc_A inc_B upd_min_A upd_min_B got_channel_id
39 PUSHINT // channel_id msg_value A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at inc_A inc_B upd_min_A upd_min_B got_channel_id _54
SWAP
18 s() PUSH // channel_id msg_value A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at inc_A inc_B upd_min_A upd_min_B _54 got_channel_id channel_id
EQUAL // channel_id msg_value A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at inc_A inc_B upd_min_A upd_min_B _54 _55
THROWANYIFNOT
33 PUSHINT // channel_id msg_value A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at inc_A inc_B upd_min_A upd_min_B _57
s4 s3 PUSH2 // channel_id msg_value A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at inc_A inc_B upd_min_A upd_min_B _57 inc_A inc_B
ADD // channel_id msg_value A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at inc_A inc_B upd_min_A upd_min_B _57 _58
s1 17 s() XCHG // channel_id _57 A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at inc_A inc_B upd_min_A upd_min_B msg_value _58
LESS // channel_id _57 A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at inc_A inc_B upd_min_A upd_min_B _59
s1 16 s() XCHG // channel_id upd_min_B A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at inc_A inc_B upd_min_A _57 _59
THROWANYIF
34 PUSHINT // channel_id upd_min_B A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at inc_A inc_B upd_min_A _61
s13 s8 PUSH2 // channel_id upd_min_B A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at inc_A inc_B upd_min_A _61 msg_signed_A? signed_A?
LESS // channel_id upd_min_B A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at inc_A inc_B upd_min_A _61 _62
s13 s8 PUSH2 // channel_id upd_min_B A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at inc_A inc_B upd_min_A _61 _62 msg_signed_B? signed_B?
LESS // channel_id upd_min_B A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at inc_A inc_B upd_min_A _61 _62 _63
OR // channel_id upd_min_B A msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at inc_A inc_B upd_min_A _61 _64
THROWANYIFNOT
s13 s2 XCHG2 // channel_id upd_min_B inc_B msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at upd_min_A A inc_A
ADD // channel_id upd_min_B inc_B msg_signed_A? msg_signed_B? a_addr b_addr B signed_A? signed_B? min_A min_B expire_at upd_min_A A
s7 s12 XCHG2 // channel_id upd_min_B A msg_signed_A? msg_signed_B? a_addr b_addr upd_min_A signed_A? signed_B? min_A min_B expire_at B inc_B
ADD // channel_id upd_min_B A msg_signed_A? msg_signed_B? a_addr b_addr upd_min_A signed_A? signed_B? min_A min_B expire_at B
s5 s10 XCHG2 // channel_id upd_min_B A B msg_signed_B? a_addr b_addr upd_min_A expire_at signed_B? min_A min_B signed_A? msg_signed_A?
OR // channel_id upd_min_B A B msg_signed_B? a_addr b_addr upd_min_A expire_at signed_B? min_A min_B signed_A?
s2 s5 PUSH2 // channel_id upd_min_B A B msg_signed_B? a_addr b_addr upd_min_A expire_at signed_B? min_A min_B signed_A? min_A upd_min_A
LESS // channel_id upd_min_B A B msg_signed_B? a_addr b_addr upd_min_A expire_at signed_B? min_A min_B signed_A? _69
IF:<{ // channel_id upd_min_B A B msg_signed_B? a_addr b_addr upd_min_A expire_at signed_B? min_A min_B signed_A?
s2 POP // channel_id upd_min_B A B msg_signed_B? a_addr b_addr min_A expire_at signed_B? signed_A? min_B
s1 s4 XCHG // channel_id upd_min_B A B msg_signed_B? a_addr b_addr signed_A? expire_at signed_B? min_A min_B
}>ELSE<{
s5 POP // channel_id upd_min_B A B msg_signed_B? a_addr b_addr signed_A? expire_at signed_B? min_A min_B
}>
s2 s7 XCHG2 // channel_id upd_min_B A B min_B a_addr b_addr signed_A? expire_at min_A signed_B? msg_signed_B?
OR // channel_id upd_min_B A B min_B a_addr b_addr signed_A? expire_at min_A signed_B?
s6 s9 PUSH2 // channel_id upd_min_B A B min_B a_addr b_addr signed_A? expire_at min_A signed_B? min_B upd_min_B
LESS // channel_id upd_min_B A B min_B a_addr b_addr signed_A? expire_at min_A signed_B? _71
IF:<{ // channel_id upd_min_B A B min_B a_addr b_addr signed_A? expire_at min_A signed_B?
s6 POP // channel_id min_B A B signed_B? a_addr b_addr signed_A? expire_at min_A
s5 s8 XCHG // channel_id signed_B? A B min_B a_addr b_addr signed_A? expire_at min_A
}>ELSE<{
s9 POP // channel_id signed_B? A B min_B a_addr b_addr signed_A? expire_at min_A
}>
s2 s8 PUSH2 // channel_id signed_B? A B min_B a_addr b_addr signed_A? expire_at min_A signed_A? signed_B?
AND // channel_id signed_B? A B min_B a_addr b_addr signed_A? expire_at min_A _72
IFJMP:<{ // channel_id signed_B? A B min_B a_addr b_addr signed_A? expire_at min_A
s8 POP
2DROP // channel_id min_A A B min_B a_addr b_addr
s5 s4 XCPU // channel_id b_addr A B min_B a_addr min_A A
GREATER // channel_id b_addr A B min_B a_addr _73
s2 s3 XCPU // channel_id b_addr A B _73 a_addr min_B B
GREATER // channel_id b_addr A B _73 a_addr _74
s1 s2 XCHG // channel_id b_addr A B a_addr _73 _74
OR // channel_id b_addr A B a_addr _75
IFJMP:<{ // channel_id b_addr A B a_addr
0 PUSHINT // channel_id b_addr A B a_addr _76=0
s4 s4 XCPU
s0 s6 XCHG // _76=0 _77=0 A B a_addr b_addr channel_id
do_payout CALLDICT // _78
}> // channel_id b_addr A B a_addr
DROP
2 2 BLKDROP2 // A B
0 PUSHINT // A B _79=0
s0 s0 s0 PUSH3 // A B _79=0 _80=0 _81=0 _82=0
DUP // A B _79=0 _80=0 _81=0 _82=0 _83=0
2 5 BLKSWAP // _79=0 _80=0 _81=0 _82=0 _83=0 A B
pack_state_close CALLDICT // _84
}> // channel_id signed_B? A B min_B a_addr b_addr signed_A? expire_at min_A
s3 POP
s3 POP
s7 POP // signed_A? signed_B? A B min_B expire_at min_A
s1 s4 s4 XCHG3
s0 s3 XCHG // signed_A? signed_B? min_A min_B expire_at A B
pack_state_init CALLDICT // _85
}>
with_close PROC:<{
// cs msg msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr expire_timeout channel_id
s0 s9 XCHG // channel_id msg msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr expire_timeout cs
unpack_state_close CALLDICT // channel_id msg msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr expire_timeout signed_A? signed_B? promise_A promise_B expire_at A B
s2 PUSH // channel_id msg msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr expire_timeout signed_A? signed_B? promise_A promise_B expire_at A B expire_at
0 EQINT // channel_id msg msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr expire_timeout signed_A? signed_B? promise_A promise_B expire_at A B _19
IF:<{ // channel_id msg msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr expire_timeout signed_A? signed_B? promise_A promise_B expire_at A B
s2 POP // channel_id msg msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr expire_timeout signed_A? signed_B? promise_A promise_B B A
NOW // channel_id msg msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr expire_timeout signed_A? signed_B? promise_A promise_B B A _20
s0 s7 XCHG2 // channel_id msg msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr A signed_A? signed_B? promise_A promise_B B _20 expire_timeout
ADD // channel_id msg msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr A signed_A? signed_B? promise_A promise_B B expire_at
s0 s6 XCHG2 // channel_id msg msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_A? signed_B? promise_A promise_B expire_at A
}>ELSE<{
s7 POP // channel_id msg msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_A? signed_B? promise_A promise_B expire_at A
}>
s0 s13 XCHG // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_A? signed_B? promise_A promise_B expire_at msg
32 LDU // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_A? signed_B? promise_A promise_B expire_at op msg
0x43278a28 PUSHINT // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_A? signed_B? promise_A promise_B expire_at op msg _26
s2 s(-1) PUXC // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_A? signed_B? promise_A promise_B expire_at op msg op _26
EQUAL // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_A? signed_B? promise_A promise_B expire_at op msg _27
IFJMP:<{ // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_A? signed_B? promise_A promise_B expire_at op msg
2DROP
s3 POP
s3 POP
s6 POP
s6 POP
s6 POP
s6 POP // channel_id A B promise_B expire_at promise_A a_addr b_addr
35 PUSHINT // channel_id A B promise_B expire_at promise_A a_addr b_addr _28
s0 s4 XCHG
NOW // channel_id A B promise_B _28 promise_A a_addr b_addr expire_at _29
LESS // channel_id A B promise_B _28 promise_A a_addr b_addr _30
s1 s4 XCHG // channel_id A B promise_B b_addr promise_A a_addr _28 _30
THROWANYIFNOT
s1 s6 XCHG
s3 s5 XCHG
s3 s4 XCHG
-ROT // promise_A promise_B A B a_addr b_addr channel_id
do_payout CALLDICT // _32
}> // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_A? signed_B? promise_A promise_B expire_at op msg
37 PUSHINT // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_A? signed_B? promise_A promise_B expire_at op msg _33
0xf28ae183 PUSHINT // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_A? signed_B? promise_A promise_B expire_at op msg _33 _34
s1 s3 XCHG // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_A? signed_B? promise_A promise_B expire_at _33 msg op _34
EQUAL // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_A? signed_B? promise_A promise_B expire_at _33 msg _35
s1 s2 XCHG // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_A? signed_B? promise_A promise_B expire_at msg _33 _35
THROWANYIFNOT
34 PUSHINT // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_A? signed_B? promise_A promise_B expire_at msg _37
s13 s6 PUSH2 // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_A? signed_B? promise_A promise_B expire_at msg _37 msg_signed_A? signed_A?
LESS // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_A? signed_B? promise_A promise_B expire_at msg _37 _38
s13 s6 PUSH2 // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_A? signed_B? promise_A promise_B expire_at msg _37 _38 msg_signed_B? signed_B?
LESS // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_A? signed_B? promise_A promise_B expire_at msg _37 _38 _39
OR // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_A? signed_B? promise_A promise_B expire_at msg _37 _40
THROWANYIFNOT
s5 s12 XCPU // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B msg signed_B? promise_A promise_B expire_at signed_A? msg_signed_A?
OR // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B msg signed_B? promise_A promise_B expire_at signed_A?
s4 s11 XCPU // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B msg signed_A? promise_A promise_B expire_at signed_B? msg_signed_B?
OR // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B msg signed_A? promise_A promise_B expire_at signed_B?
s0 s5 XCHG // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at msg
LDGRAMS // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at _46 msg
LDGRAMS // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at extra_A extra_B msg
1 LDI // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at extra_A extra_B has_sig msg
SWAP // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at extra_A extra_B msg has_sig
IF:<{ // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at extra_A extra_B msg
LDREF // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at extra_A extra_B _55 msg
SWAP // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at extra_A extra_B msg _55
CTOS // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at extra_A extra_B msg _57
9 PUSHPOW2 // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at extra_A extra_B msg _57 _58=512
PLDSLICEX // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at extra_A extra_B msg sig
OVER // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at extra_A extra_B msg sig msg
HASHSU // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at extra_A extra_B msg sig hash
s0 16 s() XCHG // channel_id A hash msg_signed_B? a_key b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at extra_A extra_B msg sig msg_signed_A?
IFNOT:<{ // channel_id A hash msg_signed_B? a_key b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at extra_A extra_B msg sig
s3 POP // channel_id A hash msg_signed_B? a_key b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at sig extra_B msg
31 PUSHINT // channel_id A hash msg_signed_B? a_key b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at sig extra_B msg _62
s15 s3 s13 PU2XC // channel_id A hash msg_signed_B? _62 b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at sig extra_B msg hash sig a_key
CHKSIGNU // channel_id A hash msg_signed_B? _62 b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at sig extra_B msg _63
s1 s13 XCHG // channel_id A hash msg_signed_B? msg b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at sig extra_B _62 _63
THROWANYIFNOT
s1 s11 XCHG
0 PUSHINT
s0 s2 XCHG // channel_id A hash msg_signed_B? sig b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at extra_A=0 extra_B msg
}>ELSE<{
s13 POP // channel_id A hash msg_signed_B? sig b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at extra_A extra_B msg
}>
s0 s13 XCHG // channel_id A hash msg sig b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at extra_A extra_B msg_signed_B?
IFNOT:<{ // channel_id A hash msg sig b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at extra_A extra_B
DROP // channel_id A hash msg sig b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at extra_A
32 PUSHINT // channel_id A hash msg sig b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at extra_A _66
s13 s11 s10 XCHG3 // channel_id A expire_at msg extra_A _66 a_addr b_addr B signed_B? signed_A? promise_A promise_B hash sig b_key
CHKSIGNU // channel_id A expire_at msg extra_A _66 a_addr b_addr B signed_B? signed_A? promise_A promise_B _67
s1 s8 XCHG // channel_id A expire_at msg extra_A promise_B a_addr b_addr B signed_B? signed_A? promise_A _66 _67
THROWANYIFNOT
0 PUSHINT // channel_id A expire_at msg extra_A promise_B a_addr b_addr B signed_B? signed_A? promise_A extra_B=0
s0 s7 XCHG // channel_id A expire_at msg extra_A extra_B=0 a_addr b_addr B signed_B? signed_A? promise_A promise_B
}>ELSE<{
s2 s13 XCHG
s10 POP
s10 POP
DROP // channel_id A expire_at msg extra_A extra_B a_addr b_addr B signed_B? signed_A? promise_A promise_B
}>
}>ELSE<{ // channel_id A msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr B signed_B? signed_A? promise_A promise_B expire_at extra_A extra_B msg
s11 POP
2DROP
s9 POP // channel_id A msg_signed_A? msg_signed_B? expire_at msg a_addr b_addr B signed_B? signed_A? promise_A promise_B
38 PUSHINT // channel_id A msg_signed_A? msg_signed_B? expire_at msg a_addr b_addr B signed_B? signed_A? promise_A promise_B _70
s11 s10 XCHG2 // channel_id A promise_B _70 expire_at msg a_addr b_addr B signed_B? signed_A? promise_A msg_signed_A? msg_signed_B?
AND // channel_id A promise_B _70 expire_at msg a_addr b_addr B signed_B? signed_A? promise_A _71
s1 s9 XCHG // channel_id A promise_B promise_A expire_at msg a_addr b_addr B signed_B? signed_A? _70 _71
THROWANYIFNOT
0 PUSHINT // channel_id A promise_B promise_A expire_at msg a_addr b_addr B signed_B? signed_A? extra_A=0
DUP // channel_id A promise_B promise_A expire_at msg a_addr b_addr B signed_B? signed_A? extra_A=0 extra_B=0
s8 s10 XCHG
s0 s7 XCHG
s9 s9 XCHG2
s0 s8 XCHG // channel_id A expire_at msg extra_A extra_B a_addr b_addr B signed_B? signed_A? promise_A promise_B
}>
s0 s9 XCHG // channel_id A expire_at promise_B extra_A extra_B a_addr b_addr B signed_B? signed_A? promise_A msg
64 LDU // channel_id A expire_at promise_B extra_A extra_B a_addr b_addr B signed_B? signed_A? promise_A _78 msg
LDGRAMS // channel_id A expire_at promise_B extra_A extra_B a_addr b_addr B signed_B? signed_A? promise_A _78 _81 msg
LDGRAMS // channel_id A expire_at promise_B extra_A extra_B a_addr b_addr B signed_B? signed_A? promise_A _78 _81 _118 _117
DROP // channel_id A expire_at promise_B extra_A extra_B a_addr b_addr B signed_B? signed_A? promise_A got_channel_id update_promise_A update_promise_B
39 PUSHINT // channel_id A expire_at promise_B extra_A extra_B a_addr b_addr B signed_B? signed_A? promise_A got_channel_id update_promise_A update_promise_B _85
s3 s15 XCPU // channel_id A expire_at promise_B extra_A extra_B a_addr b_addr B signed_B? signed_A? promise_A _85 update_promise_A update_promise_B got_channel_id channel_id
EQUAL // channel_id A expire_at promise_B extra_A extra_B a_addr b_addr B signed_B? signed_A? promise_A _85 update_promise_A update_promise_B _86
s1 s3 XCHG // channel_id A expire_at promise_B extra_A extra_B a_addr b_addr B signed_B? signed_A? promise_A update_promise_B update_promise_A _85 _86
THROWANYIFNOT
ACCEPT
s0 s9 XCHG2 // channel_id A expire_at promise_B update_promise_B extra_B a_addr b_addr B signed_B? signed_A? promise_A update_promise_A extra_A
ADD // channel_id A expire_at promise_B update_promise_B extra_B a_addr b_addr B signed_B? signed_A? promise_A update_promise_A
2DUP // channel_id A expire_at promise_B update_promise_B extra_B a_addr b_addr B signed_B? signed_A? promise_A update_promise_A promise_A update_promise_A
LESS // channel_id A expire_at promise_B update_promise_B extra_B a_addr b_addr B signed_B? signed_A? promise_A update_promise_A _90
IF:<{ // channel_id A expire_at promise_B update_promise_B extra_B a_addr b_addr B signed_B? signed_A? promise_A update_promise_A
NIP // channel_id A expire_at promise_B update_promise_B extra_B a_addr b_addr B signed_B? signed_A? promise_A
}>ELSE<{
DROP // channel_id A expire_at promise_B update_promise_B extra_B a_addr b_addr B signed_B? signed_A? promise_A
}>
s7 s6 XCHG2 // channel_id A expire_at promise_B signed_A? promise_A a_addr b_addr B signed_B? update_promise_B extra_B
ADD // channel_id A expire_at promise_B signed_A? promise_A a_addr b_addr B signed_B? update_promise_B
s7 s0 PUSH2 // channel_id A expire_at promise_B signed_A? promise_A a_addr b_addr B signed_B? update_promise_B promise_B update_promise_B
LESS // channel_id A expire_at promise_B signed_A? promise_A a_addr b_addr B signed_B? update_promise_B _92
IF:<{ // channel_id A expire_at promise_B signed_A? promise_A a_addr b_addr B signed_B? update_promise_B
s7 POP // channel_id A expire_at promise_B signed_A? promise_A a_addr b_addr B signed_B?
}>ELSE<{
DROP // channel_id A expire_at promise_B signed_A? promise_A a_addr b_addr B signed_B?
}>
s5 s0 PUSH2 // channel_id A expire_at promise_B signed_A? promise_A a_addr b_addr B signed_B? signed_A? signed_B?
AND // channel_id A expire_at promise_B signed_A? promise_A a_addr b_addr B signed_B? _93
IFJMP:<{ // channel_id A expire_at promise_B signed_A? promise_A a_addr b_addr B signed_B?
DROP
s4 POP
s5 POP // channel_id A b_addr promise_B B promise_A a_addr
s1 s6 XCHG
s3 s5 XCHG
s3 s4 XCHG
s0 s3 s3 XCHG3 // promise_A promise_B A B a_addr b_addr channel_id
do_payout CALLDICT // _94
}> // channel_id A expire_at promise_B signed_A? promise_A a_addr b_addr B signed_B?
2 2 BLKDROP2
s7 POP // signed_B? A expire_at promise_B signed_A? promise_A B
s2 s6 XCHG
s5 s4 s0 XCHG3
s1 s2 XCHG // signed_A? signed_B? promise_A promise_B expire_at A B
pack_state_close CALLDICT // _95
}>
recv_any PROC:<{
// msg_value msg
unpack_data INLINECALLDICT // msg_value msg config state
OVER // msg_value msg config state config
unpack_config CALLDICT // msg_value msg config state init_timeout close_timeout a_key b_key a_addr b_addr channel_id
s9 s4 s3 XCPU2 // msg_value channel_id config state init_timeout close_timeout a_key b_key a_addr b_addr msg a_key b_key
unwrap_signatures CALLDICT // msg_value channel_id config state init_timeout close_timeout a_key b_key a_addr b_addr msg msg_signed_A? msg_signed_B?
s9 PUSH // msg_value channel_id config state init_timeout close_timeout a_key b_key a_addr b_addr msg msg_signed_A? msg_signed_B? state
CTOS // msg_value channel_id config state init_timeout close_timeout a_key b_key a_addr b_addr msg msg_signed_A? msg_signed_B? cs
3 LDU // msg_value channel_id config state init_timeout close_timeout a_key b_key a_addr b_addr msg msg_signed_A? msg_signed_B? state_type cs
0 PUSHINT // msg_value channel_id config state init_timeout close_timeout a_key b_key a_addr b_addr msg msg_signed_A? msg_signed_B? state_type cs _23
s2 s(-1) PUXC // msg_value channel_id config state init_timeout close_timeout a_key b_key a_addr b_addr msg msg_signed_A? msg_signed_B? state_type cs state_type _23
EQUAL // msg_value channel_id config state init_timeout close_timeout a_key b_key a_addr b_addr msg msg_signed_A? msg_signed_B? state_type cs _24
IF:<{ // msg_value channel_id config state init_timeout close_timeout a_key b_key a_addr b_addr msg msg_signed_A? msg_signed_B? state_type cs
s11 POP // msg_value channel_id config cs init_timeout close_timeout a_key b_key a_addr b_addr msg msg_signed_A? msg_signed_B? state_type
s10 s13 s(-1) PUXC2
s4 s3 s(-2) PU2XC
s8 s7 s0 PUSH3
s0 s15 XCHG
18 s() PUSH // state_type channel_id config cs msg_signed_B? close_timeout a_key b_key a_addr b_addr msg msg_signed_A? cs msg_value msg msg_signed_A? msg_signed_B? a_addr b_addr init_timeout channel_id
with_init CALLDICT // state_type channel_id config cs msg_signed_B? close_timeout a_key b_key a_addr b_addr msg msg_signed_A? state
s0 s9 XCHG
s0 s8 XCHG // state_type channel_id config state cs close_timeout a_key b_key a_addr b_addr msg msg_signed_A? msg_signed_B?
}>ELSE<{
s10 POP
s13 POP // state_type channel_id config state cs close_timeout a_key b_key a_addr b_addr msg msg_signed_A? msg_signed_B?
}>
1 PUSHINT // state_type channel_id config state cs close_timeout a_key b_key a_addr b_addr msg msg_signed_A? msg_signed_B? _26
s1 s13 XCHG // msg_signed_B? channel_id config state cs close_timeout a_key b_key a_addr b_addr msg msg_signed_A? state_type _26
EQUAL // msg_signed_B? channel_id config state cs close_timeout a_key b_key a_addr b_addr msg msg_signed_A? _27
IF:<{ // msg_signed_B? channel_id config state cs close_timeout a_key b_key a_addr b_addr msg msg_signed_A?
s8 POP // msg_signed_B? channel_id config msg_signed_A? cs close_timeout a_key b_key a_addr b_addr msg
s6 s9 XCHG
s0 s8 XCHG
s6 s10 XCHG
s4 s5 XCHG
s3 s4 XCHG
s1 s3 s10 XCHG3 // config cs msg msg_signed_A? msg_signed_B? a_key b_key a_addr b_addr close_timeout channel_id
with_close CALLDICT // config state
}>ELSE<{
8 BLKDROP
2 2 BLKDROP2 // config state
}>
pack_data INLINECALLDICT
}>
recv_internal PROC:<{
// msg_value in_msg_cell in_msg
NIP // msg_value in_msg
recv_any CALLDICT
}>
recv_external PROC:<{
// in_msg
0 PUSHINT // in_msg _1=0
SWAP // _1=0 in_msg
recv_any CALLDICT
}>
}END>c

View File

@ -1 +1 @@
with_tvm_code("restricted-wallet2", "te6ccgECCwEAAQgAART/APSkE/S88sgLAQIBIAIDAgFIBAUB+PKDCNcYINMf0x8B+CO78mPtRNDTH9P/9ATRU0K68qEEjhIyMzP4AAGkyMsfEsv/9ADJ7VThBPkBVBBU+RDyovgAgPP4MyBukjB/nvgjAdDXCx+hggFRgKkE4iKAEPR7b6UxlvoAMHL7ApEw4pMg10qW0wfUAvsA6NECpMgKAATQMAIBIAYHAgEgCAkAe7y2B2omhAgJBrkPoCaMB5/BmQN0kYP898EYDoa4WP0MEAqMBUgnF8E7eIAUAIej230pjL/QAYULhbBMiYcUABe7Oc7UTQ0x8x1wv/gAEbjJftRNDXCx+AASyx/L//QAye1U");
with_tvm_code("restricted-wallet2", "te6ccgECDgEAARoAART/APSkE/S88sgLAQIBIAIDAgFIBAUB8vKDCNcYINMf0x8B+CO78mPtRNDTH9P/0x/0BNFTU7ryoQWOFTM0NPgAA6TIyx8Sy/8Syx/0AMntVOEF+QFUEGb5EPKi+AAj+CPbPCOAIPR7b6UxlvoAMHL7ApEw4gGTINdKltMH1AL7AOjRpMjLHxPL/8sf9ADJ7VQNAATQMAIBIAYHAgEgCAkCAUgKCwAXuznO1E0NMfMdcL/4ABG4yX7UTQ1wsfgBDbbYHwR7Z5AMAQm1B1tnkAwBTu1E0IEBINch0x/0BNEC2zz4J28QAoAg9HtvpTGX+gAwoXC2CZEw4g0AOiGOETGA8/gzIG6SMHCU0NcLH+IB3yGSAaGSW3/i");

View File

@ -1,32 +1,42 @@
// automatically generated from `smartcont/stdlib.fc` `smartcont/restricted-wallet2-code.fc`
PROGRAM{
DECLPROC recv_internal
DECLPROC days_passed
DECLPROC seconds_passed
DECLPROC recv_external
85143 DECLMETHOD seqno
78748 DECLMETHOD get_public_key
DECLPROC compute_balance_at
108602 DECLMETHOD balance_at
104128 DECLMETHOD balance
recv_internal PROC:<{
// in_msg
DROP //
}>
days_passed PROC:<{
//
-13 PUSHINT // _1=-13
CONFIGOPTPARAM // p
DUP // p p
ISNULL // p _3
IF:<{ // p
DROP //
-1 PUSHINT // _4=-1
}>ELSE<{ // p
NOW // p _6
SWAP // _6 p
CTOS // _6 _7
32 PLDU // _6 _9
SUB // _10
86400 PUSHINT // _10 _11=86400
DIV // _4
seconds_passed PROCREF:<{
// start_at utime
OVER // start_at utime start_at
IFNOT:<{ // start_at utime
NIP // utime
-13 PUSHINT // utime _3=-13
CONFIGOPTPARAM // utime p
DUP // utime p p
ISNULL // utime p _5
IF:<{ // utime p
DROP // utime
0 PUSHINT // utime _6=0
}>ELSE<{ // utime p
CTOS // utime _8
32 PLDU // utime _6
}> // utime start_at
SWAP // start_at utime
}> // start_at utime
OVER // start_at utime start_at
IF:<{ // start_at utime
SWAP // utime start_at
SUB // _11
}>ELSE<{ // start_at utime
2DROP //
-1 PUSHINT // _11=-1
}>
}>
recv_external PROC:<{
@ -42,66 +52,73 @@ PROGRAM{
35 THROWIF
c4 PUSH // signature in_msg msg_seqno cs _19
CTOS // signature in_msg msg_seqno cs ds
32 LDU // signature in_msg msg_seqno cs _24 ds
256 LDU // signature in_msg msg_seqno cs _24 _27 ds
LDDICT // signature in_msg msg_seqno cs stored_seqno public_key rdict ds
32 LDU // signature in_msg msg_seqno cs _25 ds
256 LDU // signature in_msg msg_seqno cs _25 _28 ds
32 LDU // signature in_msg msg_seqno cs _25 _28 _31 ds
LDDICT // signature in_msg msg_seqno cs stored_seqno public_key start_at rdict ds
ENDS
s4 s2 PUSH2 // signature in_msg msg_seqno cs stored_seqno public_key rdict msg_seqno stored_seqno
EQUAL // signature in_msg msg_seqno cs stored_seqno public_key rdict _34
s5 s3 PUSH2 // signature in_msg msg_seqno cs stored_seqno public_key start_at rdict msg_seqno stored_seqno
EQUAL // signature in_msg msg_seqno cs stored_seqno public_key start_at rdict _38
33 THROWIFNOT
s0 s4 XCHG // signature in_msg rdict cs stored_seqno public_key msg_seqno
IFNOTJMP:<{ // signature in_msg rdict cs stored_seqno public_key
s2 POP
s0 s5 XCHG // signature in_msg rdict cs stored_seqno public_key start_at msg_seqno
IFNOTJMP:<{ // signature in_msg rdict cs stored_seqno public_key start_at
s3 POP
s3 POP // public_key stored_seqno rdict
s4 POP
s4 POP // stored_seqno public_key rdict start_at
ACCEPT
SWAP // public_key rdict stored_seqno
INC // public_key rdict _38
NEWC // public_key rdict _38 _39
32 STU // public_key rdict _41
s1 s2 XCHG // rdict public_key _41
256 STU // rdict _43
STDICT // _44
ENDC // _45
s0 s3 XCHG // start_at public_key rdict stored_seqno
INC // start_at public_key rdict _42
NEWC // start_at public_key rdict _42 _43
32 STU // start_at public_key rdict _45
s1 s2 XCHG // start_at rdict public_key _45
256 STU // start_at rdict _47
s1 s2 XCHG // rdict start_at _47
32 STU // rdict _49
STDICT // _50
ENDC // _51
c4 POP
}> // signature in_msg rdict cs stored_seqno public_key
s0 s4 XCHG // signature public_key rdict cs stored_seqno in_msg
HASHSU // signature public_key rdict cs stored_seqno _48
s0 s5 s4 XC2PU // stored_seqno public_key rdict cs _48 signature public_key
CHKSIGNU // stored_seqno public_key rdict cs _49
}> // signature in_msg rdict cs stored_seqno public_key start_at
s0 s5 XCHG // signature start_at rdict cs stored_seqno public_key in_msg
HASHSU // signature start_at rdict cs stored_seqno public_key _54
s0 s6 s6 XC2PU // public_key start_at rdict cs stored_seqno _54 signature public_key
CHKSIGNU // public_key start_at rdict cs stored_seqno _55
34 THROWIFNOT
ACCEPT
days_passed INLINECALLDICT // stored_seqno public_key rdict cs ts
s2 PUSH
16 PUSHINT // stored_seqno public_key rdict cs ts rdict _57=16
s3 PUSH
NOW // public_key start_at rdict cs stored_seqno start_at _59
seconds_passed INLINECALLDICT // public_key start_at rdict cs stored_seqno ts
s3 PUSH
32 PUSHINT // public_key start_at rdict cs stored_seqno ts rdict _64=32
DICTIGETPREVEQ
NULLSWAPIFNOT2 // stored_seqno public_key rdict cs _98 _97 _99
NIP // stored_seqno public_key rdict cs value found
IF:<{ // stored_seqno public_key rdict cs value
LDGRAMS // stored_seqno public_key rdict cs _101 _100
DROP // stored_seqno public_key rdict cs _59
2 PUSHINT // stored_seqno public_key rdict cs _59 _61=2
NULLSWAPIFNOT2 // public_key start_at rdict cs stored_seqno _109 _108 _110
NIP // public_key start_at rdict cs stored_seqno value found
IF:<{ // public_key start_at rdict cs stored_seqno value
LDGRAMS // public_key start_at rdict cs stored_seqno _112 _111
DROP // public_key start_at rdict cs stored_seqno _66
2 PUSHINT // public_key start_at rdict cs stored_seqno _66 _68=2
RAWRESERVE
}>ELSE<{
DROP // stored_seqno public_key rdict cs
}> // stored_seqno public_key rdict cs
DROP // public_key start_at rdict cs stored_seqno
}>
SWAP // public_key start_at rdict stored_seqno cs
WHILE:<{
DUP // stored_seqno public_key rdict cs cs
SREFS // stored_seqno public_key rdict cs _65
}>DO<{ // stored_seqno public_key rdict cs
8 LDU // stored_seqno public_key rdict mode cs
LDREF // stored_seqno public_key rdict mode msg cs
s0 s2 XCHG // stored_seqno public_key rdict cs msg mode
DUP // public_key start_at rdict stored_seqno cs cs
SREFS // public_key start_at rdict stored_seqno cs _72
}>DO<{ // public_key start_at rdict stored_seqno cs
8 LDU // public_key start_at rdict stored_seqno mode cs
LDREF // public_key start_at rdict stored_seqno mode msg cs
s0 s2 XCHG // public_key start_at rdict stored_seqno cs msg mode
SENDRAWMSG
}> // stored_seqno public_key rdict cs
}> // public_key start_at rdict stored_seqno cs
ENDS
s0 s2 XCHG // rdict public_key stored_seqno
INC // rdict public_key _76
NEWC // rdict public_key _76 _77
32 STU // rdict public_key _79
256 STU // rdict _81
STDICT // _82
ENDC // _83
INC // public_key start_at rdict _83
NEWC // public_key start_at rdict _83 _84
32 STU // public_key start_at rdict _86
s1 s3 XCHG // rdict start_at public_key _86
256 STU // rdict start_at _88
32 STU // rdict _90
STDICT // _91
ENDC // _92
c4 POP
}>
seqno PROC:<{
@ -118,30 +135,41 @@ PROGRAM{
NIP // cs
256 PLDU // _7
}>
balance PROC:<{
//
c4 PUSH // _1
CTOS // _2
288 PUSHINT // _2 _5
SDSKIPFIRST // ds
LDDICT // rdict ds
compute_balance_at PROCREF:<{
// utime
c4 PUSH // utime _2
CTOS // utime _3
288 PUSHINT // utime _3 _6
SDSKIPFIRST // utime ds
32 LDU // utime _10 ds
LDDICT // utime start_at rdict ds
ENDS
days_passed INLINECALLDICT // rdict ts
BALANCE // rdict ts _14
s0 s2 XCHG // rdict start_at utime
seconds_passed INLINECALLDICT // rdict ts
BALANCE // rdict ts _19
FIRST // rdict ts balance
s0 s2 XCHG
16 PUSHINT // balance ts rdict _19=16
32 PUSHINT // balance ts rdict _24=32
DICTIGETPREVEQ
NULLSWAPIFNOT2 // balance _29 _28 _30
NULLSWAPIFNOT2 // balance _36 _35 _37
NIP // balance value found
IF:<{ // balance value
LDGRAMS // balance _32 _31
DROP // balance _21
SUB // _23
0 PUSHINT // _23 _24=0
LDGRAMS // balance _39 _38
DROP // balance _26
SUB // _28
0 PUSHINT // _28 _29=0
MAX // balance
}>ELSE<{
DROP // balance
}>
}>
balance_at PROC:<{
// utime
compute_balance_at INLINECALLDICT // _1
}>
balance PROC:<{
//
NOW // _0
compute_balance_at INLINECALLDICT // _1
}>
}END>c

View File

@ -0,0 +1 @@
with_tvm_code("restricted-wallet3", "te6ccgECEgEAAUsAART/APSkE/S88sgLAQIBIAIDAgFIBAUD+PKDCNcYINMf0x/THwL4I7vyY+1E0NMf0x/T/1NDuvKhUWK68qIG+QFUEHb5EPKkAY4fMwHT/9EB0x/0BNH4AAOkyMsfFMsfy/8Syx/0AMntVOEC0x/0BNH4ACH4I9s8IYAg9HtvpTGW+gAwcvsCkTDiApMg10qK6NECpMgPEBEABNAwAgEgBgcCASAICQIBSAwNAgFuCgsAEbjJftRNDXCx+AAXrc52omhpn5jrhf/AABesePaiaGmPmOuFj8ABDbbYHwR7Z5AOAQm1B1tnkA4BTu1E0IEBQNch0x/0BNEC2zz4J28QAoAg9HtvpTGX+gAwoXC2CZEw4g8AOiGOETGA8/gzIG6SMHCU0NcLH+IB3yGSAaGSW3/iAAzTB9QC+wAAHssfFMsfEsv/yx/0AMntVA==");

View File

@ -0,0 +1,199 @@
// automatically generated from `smartcont/stdlib.fc` `smartcont/restricted-wallet3-code.fc`
PROGRAM{
DECLPROC recv_internal
DECLPROC seconds_passed
DECLPROC recv_external
85143 DECLMETHOD seqno
80113 DECLMETHOD wallet_id
78748 DECLMETHOD get_public_key
DECLPROC compute_balance_at
108602 DECLMETHOD balance_at
104128 DECLMETHOD balance
recv_internal PROC:<{
// in_msg
DROP //
}>
seconds_passed PROCREF:<{
// start_at utime
OVER // start_at utime start_at
IFNOT:<{ // start_at utime
NIP // utime
-13 PUSHINT // utime _3=-13
CONFIGOPTPARAM // utime p
DUP // utime p p
ISNULL // utime p _5
IF:<{ // utime p
DROP // utime
0 PUSHINT // utime _6=0
}>ELSE<{ // utime p
CTOS // utime _8
32 PLDU // utime _6
}> // utime start_at
SWAP // start_at utime
}> // start_at utime
OVER // start_at utime start_at
IF:<{ // start_at utime
SWAP // utime start_at
SUB // _11
}>ELSE<{ // start_at utime
2DROP //
-1 PUSHINT // _11=-1
}>
}>
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
s0 s2 XCHG
NOW // 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
s4 s3 PUSH2 // signature in_msg subwallet_id cs msg_seqno stored_seqno stored_subwallet public_key ds msg_seqno stored_seqno
EQUAL // signature in_msg subwallet_id cs msg_seqno stored_seqno stored_subwallet public_key ds _38
33 THROWIFNOT
s6 s2 XCPU // signature in_msg ds cs msg_seqno stored_seqno stored_subwallet public_key subwallet_id stored_subwallet
EQUAL // signature in_msg ds cs msg_seqno stored_seqno stored_subwallet public_key _41
34 THROWIFNOT
s0 s6 XCHG // signature public_key ds cs msg_seqno stored_seqno stored_subwallet in_msg
HASHSU // signature public_key ds cs msg_seqno stored_seqno stored_subwallet _44
s0 s7 s6 XC2PU // stored_subwallet public_key ds cs msg_seqno stored_seqno _44 signature public_key
CHKSIGNU // stored_subwallet public_key ds cs msg_seqno stored_seqno _45
36 THROWIFNOT
SWAP // stored_subwallet public_key ds cs stored_seqno msg_seqno
IFNOTJMP:<{ // stored_subwallet public_key ds cs stored_seqno
s3 POP // stored_subwallet stored_seqno ds cs
SWAP // stored_subwallet stored_seqno cs ds
256 LDU // stored_subwallet stored_seqno cs public_key ds
ENDS
SWAP // stored_subwallet stored_seqno public_key cs
32 LDU // stored_subwallet stored_seqno public_key _55 cs
LDDICT // stored_subwallet stored_seqno public_key start_at rdict cs
ENDS
ACCEPT
s0 s3 XCHG // stored_subwallet rdict public_key start_at stored_seqno
INC // stored_subwallet rdict public_key start_at _63
NEWC // stored_subwallet rdict public_key start_at _63 _64
32 STU // stored_subwallet rdict public_key start_at _66
s1 s4 XCHG // start_at rdict public_key stored_subwallet _66
32 STU // start_at rdict public_key _68
256 STU // start_at rdict _70
s1 s2 XCHG // rdict start_at _70
32 STU // rdict _72
STDICT // _73
ENDC // _74
c4 POP
}> // stored_subwallet public_key ds cs stored_seqno
s0 s2 XCHG // stored_subwallet public_key stored_seqno cs ds
32 LDU // stored_subwallet public_key stored_seqno cs _78 ds
LDDICT // stored_subwallet public_key stored_seqno cs start_at rdict ds
ENDS
ACCEPT
OVER
NOW // stored_subwallet public_key stored_seqno cs start_at rdict start_at _86
seconds_passed INLINECALLDICT // stored_subwallet public_key stored_seqno cs start_at rdict ts
OVER
32 PUSHINT // stored_subwallet public_key stored_seqno cs start_at rdict ts rdict _91=32
DICTIGETPREVEQ
NULLSWAPIFNOT2 // stored_subwallet public_key stored_seqno cs start_at rdict _148 _147 _149
NIP // stored_subwallet public_key stored_seqno cs start_at rdict value found
IF:<{ // stored_subwallet public_key stored_seqno cs start_at rdict value
LDGRAMS // stored_subwallet public_key stored_seqno cs start_at rdict _151 _150
DROP // stored_subwallet public_key stored_seqno cs start_at rdict _93
2 PUSHINT // stored_subwallet public_key stored_seqno cs start_at rdict _93 _95=2
RAWRESERVE
}>ELSE<{
DROP // stored_subwallet public_key stored_seqno cs start_at rdict
}>
s0 s2 XCHG // stored_subwallet public_key stored_seqno rdict start_at cs
WHILE:<{
DUP // stored_subwallet public_key stored_seqno rdict start_at cs cs
SREFS // stored_subwallet public_key stored_seqno rdict start_at cs _99
}>DO<{ // stored_subwallet public_key stored_seqno rdict start_at cs
8 LDU // stored_subwallet public_key stored_seqno rdict start_at mode cs
LDREF // stored_subwallet public_key stored_seqno rdict start_at mode msg cs
s0 s2 XCHG // stored_subwallet public_key stored_seqno rdict start_at cs msg mode
SENDRAWMSG
}> // stored_subwallet public_key stored_seqno rdict start_at cs
ENDS
s0 s2 XCHG // stored_subwallet public_key start_at rdict stored_seqno
INC // stored_subwallet public_key start_at rdict _110
NEWC // stored_subwallet public_key start_at rdict _110 _111
32 STU // stored_subwallet public_key start_at rdict _113
s1 s4 XCHG // rdict public_key start_at stored_subwallet _113
32 STU // rdict public_key start_at _115
s1 s2 XCHG // rdict start_at public_key _115
256 STU // rdict start_at _117
32 STU // rdict _119
STDICT // _120
ENDC // _121
c4 POP
}>
seqno PROC:<{
//
c4 PUSH // _0
CTOS // _1
32 PLDU // _3
}>
wallet_id PROC:<{
//
c4 PUSH // _1
CTOS // ds
32 LDU // _9 _8
NIP // ds
32 PLDU // _7
}>
get_public_key PROC:<{
//
c4 PUSH // _1
CTOS // ds
64 LDU // _11 _10
NIP // ds
256 PLDU // _9
}>
compute_balance_at PROCREF:<{
// utime
c4 PUSH // utime _2
CTOS // utime _3
320 PUSHINT // utime _3 _8
SDSKIPFIRST // utime ds
32 LDU // utime _12 ds
LDDICT // utime start_at rdict ds
ENDS
s0 s2 XCHG // rdict start_at utime
seconds_passed INLINECALLDICT // rdict ts
BALANCE // rdict ts _21
FIRST // rdict ts balance
s0 s2 XCHG
32 PUSHINT // balance ts rdict _26=32
DICTIGETPREVEQ
NULLSWAPIFNOT2 // balance _38 _37 _39
NIP // balance value found
IF:<{ // balance value
LDGRAMS // balance _41 _40
DROP // balance _28
SUB // _30
0 PUSHINT // _30 _31=0
MAX // balance
}>ELSE<{
DROP // balance
}>
}>
balance_at PROC:<{
// utime
compute_balance_at INLINECALLDICT // _1
}>
balance PROC:<{
//
NOW // _0
compute_balance_at INLINECALLDICT // _1
}>
}END>c

View File

@ -0,0 +1,323 @@
// automatically generated from `smartcont/stdlib.fc` `smartcont/config-code.fc`
PROGRAM{
DECLPROC set_conf_param
DECLPROC check_validator_set
DECLPROC send_answer
DECLPROC send_confirmation
DECLPROC send_error
DECLPROC recv_internal
DECLPROC change_elector_code
DECLPROC recv_external
DECLPROC run_ticktock
set_conf_param PROC:<{
// index value
c4 PUSH // index value _3
CTOS // index value cs
LDREF // index value cfg_dict cs
s3 s3 XCHG2
32 PUSHINT // cs value index cfg_dict _9=32
DICTISETREF // cs cfg_dict
NEWC // cs cfg_dict _11
STREF // cs _12
SWAP // _12 cs
STSLICER // _13
ENDC // _14
c4 POP
}>
check_validator_set PROC:<{
// vset
CTOS // cs
8 LDU // _4 cs
SWAP // cs _4
17 EQINT // cs _8
9 THROWIFNOT
32 LDU // utime_since cs
32 LDU // utime_since utime_until cs
16 LDU // utime_since utime_until total cs
16 LDU // utime_since utime_until total _42 _41
DROP // utime_since utime_until total main
DUP // utime_since utime_until total main main
0 GTINT // utime_since utime_until total main _28
9 THROWIFNOT
GEQ // utime_since utime_until _31
9 THROWIFNOT
}>
send_answer PROC:<{
// addr query_id ans_tag mode
0 PUSHINT // addr query_id ans_tag mode _4=0
24 PUSHINT // addr query_id ans_tag mode _4=0 _5=24
NEWC // addr query_id ans_tag mode _4=0 _5=24 _6
6 STU // addr query_id ans_tag mode _4=0 _8
s0 s5 XCHG2 // _4=0 query_id ans_tag mode _8 addr
STSLICER // _4=0 query_id ans_tag mode _9
s1 s4 XCHG // mode query_id ans_tag _4=0 _9
111 STU // mode query_id ans_tag _23
32 STU // mode query_id _25
64 STU // mode _27
ENDC // mode _28
SWAP // _28 mode
SENDRAWMSG
}>
send_confirmation PROC:<{
// addr query_id ans_tag
64 PUSHINT // addr query_id ans_tag _3=64
send_answer CALLDICT
}>
send_error PROC:<{
// addr query_id ans_tag
64 PUSHINT // addr query_id ans_tag _3=64
send_answer CALLDICT
}>
recv_internal PROC:<{
// in_msg_cell in_msg
SWAP // in_msg in_msg_cell
CTOS // in_msg cs
4 LDU // in_msg flags cs
LDMSGADDR // in_msg flags _74 _73
DROP // in_msg flags s_addr
DUP // in_msg flags s_addr s_addr
REWRITESTDADDR // in_msg flags s_addr src_wc src_addr
SWAP // in_msg flags s_addr src_addr src_wc
INC // in_msg flags s_addr src_addr _15
s0 s3 XCHG
1 PUSHINT // in_msg _15 s_addr src_addr flags _16=1
AND // in_msg _15 s_addr src_addr _17
s1 s3 XCHG // in_msg src_addr s_addr _15 _17
OR // in_msg src_addr s_addr _18
s3 PUSH // in_msg src_addr s_addr _18 in_msg
SEMPTY // in_msg src_addr s_addr _18 _19
OR // in_msg src_addr s_addr _20
IFJMP:<{ // in_msg src_addr s_addr
3 BLKDROP //
}> // in_msg src_addr s_addr
s0 s2 XCHG // s_addr src_addr in_msg
32 LDU // s_addr src_addr tag in_msg
64 LDU // s_addr src_addr tag query_id in_msg
s2 PUSH
1314280276 PUSHINT // s_addr src_addr tag query_id in_msg tag _29=1314280276
EQUAL // s_addr src_addr tag query_id in_msg _30
IFJMP:<{ // s_addr src_addr tag query_id in_msg
s2 POP // s_addr src_addr in_msg query_id
SWAP // s_addr src_addr query_id in_msg
LDREF // s_addr src_addr query_id vset in_msg
ENDS
1 PUSHINT // s_addr src_addr query_id vset _36=1
CONFIGOPTPARAM // s_addr src_addr query_id vset elector_param
DUP // s_addr src_addr query_id vset elector_param elector_param
ISNULL // s_addr src_addr query_id vset elector_param _39
IF:<{ // s_addr src_addr query_id vset elector_param
DROP // s_addr src_addr query_id vset
-1 PUSHINT // s_addr src_addr query_id vset _40=-1
}>ELSE<{ // s_addr src_addr query_id vset elector_param
CTOS // s_addr src_addr query_id vset _42
256 PLDU // s_addr src_addr query_id vset _40
}> // s_addr src_addr query_id vset elector_addr
s0 s3 XCHG
FALSE
s0 s4 XCHG // s_addr ok query_id vset src_addr elector_addr
EQUAL // s_addr ok query_id vset _47
IF:<{ // s_addr ok query_id vset
s2 POP // s_addr vset query_id
OVER // s_addr vset query_id vset
check_validator_set CALLDICT // s_addr vset query_id t_since t_until
OVER
NOW // s_addr vset query_id t_since t_until t_since t
GREATER // s_addr vset query_id t_since t_until _53
s0 s2 XCHG // s_addr vset query_id _53 t_until t_since
GREATER // s_addr vset query_id _53 _54
AND // s_addr vset query_id ok
}>ELSE<{
s0 s2 XCHG // s_addr vset query_id ok
}>
IFJMP:<{ // s_addr vset query_id
36 PUSHINT // s_addr vset query_id _56=36
ROT // s_addr query_id _56=36 vset
set_conf_param CALLDICT
4000730955 PUSHINT // s_addr query_id _58=4000730955
send_confirmation CALLDICT
}> // s_addr vset query_id
NIP // s_addr query_id
4000730991 PUSHINT // s_addr query_id _60=4000730991
send_error CALLDICT
}> // s_addr src_addr tag query_id in_msg
s2 s4 XCHG
4 BLKDROP // tag
DUP // tag tag
0 EQINT // tag _64
SWAP
31 PUSHPOW2 // _64 tag _67
AND // _64 _68
OR // _69
37 THROWIFNOT
}>
change_elector_code PROC:<{
// cs
1 PUSHINT // cs _2=1
CONFIGOPTPARAM // cs _3
CTOS // cs _4
256 PLDU // cs dest_addr
NOW // cs dest_addr query_id
1313042276 PUSHINT // cs dest_addr query_id _9=1313042276
0 PUSHINT // cs dest_addr query_id _9=1313042276 _10=0
50431 PUSHINT // cs dest_addr query_id _9=1313042276 _10=0 _11=50431
NEWC // cs dest_addr query_id _9=1313042276 _10=0 _11=50431 _12
17 STU // cs dest_addr query_id _9=1313042276 _10=0 _14
s1 s4 XCHG // cs _10=0 query_id _9=1313042276 dest_addr _14
256 STU // cs _10=0 query_id _9=1313042276 _16
30 PUSHPOW2 // cs _10=0 query_id _9=1313042276 _16 _19
STGRAMS // cs _10=0 query_id _9=1313042276 _20
s1 s3 XCHG // cs _9=1313042276 query_id _10=0 _20
107 STU // cs _9=1313042276 query_id _34
s1 s2 XCHG // cs query_id _9=1313042276 _34
32 STU // cs query_id _36
64 STU // cs _38
SWAP // _38 cs
STSLICER // _39
ENDC // _40
0 PUSHINT // _40 _41=0
SENDRAWMSG
}>
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 action cs
32 LDU // signature in_msg action msg_seqno cs
32 LDU // signature in_msg action msg_seqno valid_until cs
NOW // signature in_msg action msg_seqno valid_until cs _19
s1 s2 XCHG // signature in_msg action msg_seqno cs valid_until _19
LESS // signature in_msg action msg_seqno cs _20
35 THROWIF
c4 PUSH // signature in_msg action msg_seqno cs _23
CTOS // signature in_msg action msg_seqno cs cs2
LDREF // signature in_msg action msg_seqno cs cfg_dict cs2
32 LDU // signature in_msg action msg_seqno cs cfg_dict stored_seqno cs2
256 LDU // signature in_msg action msg_seqno cs cfg_dict stored_seqno public_key cs2
ENDS
s4 s1 XCPU // signature in_msg action public_key cs cfg_dict stored_seqno msg_seqno stored_seqno
EQUAL // signature in_msg action public_key cs cfg_dict stored_seqno _38
33 THROWIFNOT
s0 s5 XCHG // signature stored_seqno action public_key cs cfg_dict in_msg
HASHSU // signature stored_seqno action public_key cs cfg_dict _41
s0 s6 s3 XC2PU // cfg_dict stored_seqno action public_key cs _41 signature public_key
CHKSIGNU // cfg_dict stored_seqno action public_key cs _42
34 THROWIFNOT
ACCEPT
s2 PUSH
1130778657 PUSHINT // cfg_dict stored_seqno action public_key cs action _45=1130778657
EQUAL // cfg_dict stored_seqno action public_key cs _46
IF:<{ // cfg_dict stored_seqno action public_key cs
s2 POP // cfg_dict stored_seqno cs public_key
SWAP // cfg_dict stored_seqno public_key cs
32 LDU // cfg_dict stored_seqno public_key param_index cs
LDREF // cfg_dict stored_seqno public_key param_index param_value cs
ENDS
s0 s1 s4 XCHG3
32 PUSHINT // public_key stored_seqno param_value param_index cfg_dict _56=32
DICTISETREF // public_key stored_seqno cfg_dict
}>ELSE<{ // cfg_dict stored_seqno action public_key cs
s2 PUSH
1313042276 PUSHINT // cfg_dict stored_seqno action public_key cs action _58=1313042276
EQUAL // cfg_dict stored_seqno action public_key cs _59
IF:<{ // cfg_dict stored_seqno action public_key cs
s2 POP // cfg_dict stored_seqno cs public_key
SWAP // cfg_dict stored_seqno public_key cs
LDREF // cfg_dict stored_seqno public_key new_code cs
ENDS
SETCODE
}>ELSE<{ // cfg_dict stored_seqno action public_key cs
s2 PUSH
1348619041 PUSHINT // cfg_dict stored_seqno action public_key cs action _65=1348619041
EQUAL // cfg_dict stored_seqno action public_key cs _66
IF:<{ // cfg_dict stored_seqno action public_key cs
NIP
NIP // cfg_dict stored_seqno cs
256 LDU // cfg_dict stored_seqno public_key cs
ENDS
}>ELSE<{ // cfg_dict stored_seqno action public_key cs
s2 PUSH
1313074949 PUSHINT // cfg_dict stored_seqno action public_key cs action _71=1313074949
EQUAL // cfg_dict stored_seqno action public_key cs _72
IF:<{ // cfg_dict stored_seqno action public_key cs
s2 POP // cfg_dict stored_seqno cs public_key
SWAP // cfg_dict stored_seqno public_key cs
change_elector_code CALLDICT
}>ELSE<{ // cfg_dict stored_seqno action public_key cs
DROP // cfg_dict stored_seqno action public_key
SWAP // cfg_dict stored_seqno public_key action
32 THROWIF
}>
}>
}>
s0 s2 XCHG // public_key stored_seqno cfg_dict
}>
SWAP // public_key cfg_dict stored_seqno
INC // public_key cfg_dict _77
NEWC // public_key cfg_dict _77 _78
s1 s2 XCHG // public_key _77 cfg_dict _78
STREF // public_key _77 _79
32 STU // public_key _81
256 STU // _83
ENDC // _84
c4 POP
}>
run_ticktock PROC:<{
// is_tock
DROP //
c4 PUSH // _2
CTOS // cs
LDREF // cfg_dict cs
32 PUSHINT // cfg_dict cs kl=32
36 PUSHINT // cfg_dict cs kl=32 _10=36
s3 s1 PUSH2 // cfg_dict cs kl=32 _10=36 cfg_dict kl=32
DICTIGETOPTREF // cfg_dict cs kl=32 next_vset
DUP // cfg_dict cs kl=32 next_vset next_vset
ISNULL // cfg_dict cs kl=32 next_vset _12
IFNOT:<{ // cfg_dict cs kl=32 next_vset
DUP // cfg_dict cs kl=32 next_vset next_vset
CTOS // cfg_dict cs kl=32 next_vset ds
DUP // cfg_dict cs kl=32 next_vset ds ds
SBITS // cfg_dict cs kl=32 next_vset ds _15
39 GTINT // cfg_dict cs kl=32 next_vset ds _17
IF:<{ // cfg_dict cs kl=32 next_vset ds
8 LDU // cfg_dict cs kl=32 next_vset tag ds
32 PLDU // cfg_dict cs kl=32 next_vset tag since
SWAP // cfg_dict cs kl=32 next_vset since tag
17 EQINT // cfg_dict cs kl=32 next_vset since _26
NOW // cfg_dict cs kl=32 next_vset since _26 _27
s1 s2 XCHG // cfg_dict cs kl=32 next_vset _26 since _27
GEQ // cfg_dict cs kl=32 next_vset _26 _28
AND // cfg_dict cs kl=32 next_vset _29
IF:<{ // cfg_dict cs kl=32 next_vset
SWAP
34 PUSHINT
s0 s4 s4 XC2PU // kl=32 cs next_vset _32=34 cfg_dict kl=32
DICTISETGETOPTREF // kl=32 cs cfg_dict cur_vset
s3 s1 s0 PUXCPU // kl=32 cs cur_vset _35=32 cfg_dict kl=32
DICTISETGETOPTREF // kl=32 cs _51 _52
DROP // kl=32 cs cfg_dict
36 PUSHINT // kl=32 cs cfg_dict _38=36
s0 s1 s3 XCHG3 // cs _38=36 cfg_dict kl=32
DICTIDEL // cs _53 _54
DROP // cs cfg_dict
}>ELSE<{
s2 s3 XCHG
2DROP // cs cfg_dict
}>
}>ELSE<{
s3 s4 XCHG
3 BLKDROP // cs cfg_dict
}>
}>ELSE<{
s2 s3 XCHG
2DROP // cs cfg_dict
}>
NEWC // cs cfg_dict _40
STREF // cs _41
SWAP // _41 cs
STSLICER // _42
ENDC // _43
c4 POP
}>
}END>c

View File

@ -1097,8 +1097,21 @@ tuple past_elections() method_id {
do {
(id, var fs, var found) = past_elections.udict_get_prev?(32, id);
if (found) {
var info = [unpack_past_election(fs)];
list = cons(pair(id, info), list);
list = cons([id, unpack_past_election(fs)], list);
}
} until (~ found);
return list;
}
tuple past_elections_list() method_id {
var (elect, credits, past_elections, grams, active_id, active_hash) = load_data();
var id = (1 << 32);
var list = null();
do {
(id, var fs, var found) = past_elections.udict_get_prev?(32, id);
if (found) {
var (unfreeze_at, stake_held, vset_hash, frozen_dict, total_stake, bonuses, complaints) = unpack_past_election(fs);
list = cons([id, unfreeze_at, vset_hash, stake_held], list);
}
} until (~ found);
return list;

File diff suppressed because it is too large Load Diff

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,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

@ -29,8 +29,8 @@ def? $3 { @' $3 } { "rwallet" } cond constant file-base
} : make-rdict
// Create new restricted wallet; code taken from `auto/restricted-wallet2-code.fif`
"auto/restricted-wallet-code.fif" include // code
<b 0 32 u, PubKey 256 u, amount make-rdict dict, b> // data
"auto/restricted-wallet2-code.fif" include // code
<b 0 32 u, PubKey 256 u, 0 32 u, amount make-rdict dict, b> // data
null // no libraries
<b b{0011} s, 3 roll ref, rot ref, swap dict, b> // create StateInit
dup ."StateInit: " <s csr. cr

View File

@ -0,0 +1,315 @@
;; WARINIG: NOT READY FOR A PRODUCTION!
int err:wrong_a_signature() asm "31 PUSHINT";
int err:wrong_b_signature() asm "32 PUSHINT";
int err:msg_value_too_small() asm "33 PUSHINT";
int err:replay_protection() asm "34 PUSHINT";
int err:no_timeout() asm "35 PUSHINT";
int err:expected_init() asm "36 PUSHINT";
int err:expected_close() asm "37 PUSHINT";
int err:no_promise_signature() asm "38 PUSHINT";
int err:wrong_channel_id() asm "39 PUSHINT";
int msg:init() asm "0x27317822 PUSHINT";
int msg:close() asm "0xf28ae183 PUSHINT";
int msg:timeout() asm "0x43278a28 PUSHINT";
int msg:payout() asm "0x37fe7810 PUSHINT";
int state:init() asm "0 PUSHINT";
int state:close() asm "1 PUSHINT";
int state:payout() asm "2 PUSHINT";
;; A - initial balance of Alice,
;; B - initial balance of B
;;
;; To determine balance we track nondecreasing list of promises
;; promise_A ;; promised by Alice to Bob
;; promise_B ;; promised by Bob to Alice
;;
;; diff - balance between Alice and Bob. 0 in the beginning
;; diff = promise_B - promise_A;
;; diff = clamp(diff, -A, +B);
;;
;; final_A = A + diff;
;; final_B = B + diff;
;; Data pack/unpack
;;
_ unpack_data() inline_ref {
var cs = get_data().begin_parse();
var res = (cs~load_ref(), cs~load_ref());
cs.end_parse();
return res;
}
_ pack_data(cell config, cell state) impure inline_ref {
set_data(begin_cell().store_ref(config).store_ref(state).end_cell());
}
;; Config pack/unpack
;;
;; config$_ initTimeout:int exitTimeout:int a_key:int256 b_key:int256 a_addr b_addr channel_id:uint64 = Config;
;;
_ unpack_config(cell config) {
var cs = config.begin_parse();
var res = (
cs~load_uint(32),
cs~load_uint(32),
cs~load_uint(256),
cs~load_uint(256),
cs~load_ref().begin_parse(),
cs~load_ref().begin_parse(),
cs~load_uint(64));
cs.end_parse();
return res;
}
;; takes
;; signedMesage$_ a_sig:Maybe<int256> b_sig:Maybe<int256> msg:Message = SignedMessage;
;; checks signatures and unwap message.
(slice, (int, int)) unwrap_signatures(slice cs, int a_key, int b_key) {
int a? = cs~load_int(1);
slice a_sig = cs;
if (a?) {
a_sig = cs~load_ref().begin_parse().preload_bits(512);
}
var b? = cs~load_int(1);
slice b_sig = cs;
if (b?) {
b_sig = cs~load_ref().begin_parse().preload_bits(512);
}
int hash = cs.slice_hash();
if (a?) {
throw_unless(err:wrong_a_signature(), check_signature(hash, a_sig, a_key));
}
if (b?) {
throw_unless(err:wrong_b_signature(), check_signature(hash, b_sig, b_key));
}
return (cs, (a?, b?));
}
;; process message, give state is stateInit
;;
;; stateInit signed_A?:Bool signed_B?:Bool min_A:Grams min_B:Grams expire_at:uint32 A:Grams B:Grams = State;
_ unpack_state_init(slice state) {
return (
state~load_int(1),
state~load_int(1),
state~load_grams(),
state~load_grams(),
state~load_uint(32),
state~load_grams(),
state~load_grams());
}
_ pack_state_init(int signed_A?, int signed_B?, int min_A, int min_B, int expire_at, int A, int B) {
return begin_cell()
.store_int(state:init(), 3)
.store_int(signed_A?, 1)
.store_int(signed_B?, 1)
.store_grams(min_A)
.store_grams(min_B)
.store_uint(expire_at, 32)
.store_grams(A)
.store_grams(B).end_cell();
}
;; stateClosing$10 signed_A?:bool signed_B?:Bool promise_A:Grams promise_B:Grams exipire_at:uint32 A:Grams B:Grams = State;
_ unpack_state_close(slice state) {
return (
state~load_int(1),
state~load_int(1),
state~load_grams(),
state~load_grams(),
state~load_uint(32),
state~load_grams(),
state~load_grams());
}
_ pack_state_close(int signed_A?, int signed_B?, int promise_A, int promise_B, int expire_at, int A, int B) {
return begin_cell()
.store_int(state:close(), 3)
.store_int(signed_A?, 1)
.store_int(signed_B?, 1)
.store_grams(promise_A)
.store_grams(promise_B)
.store_uint(expire_at, 32)
.store_grams(A)
.store_grams(B).end_cell();
}
_ send_payout(slice s_addr, int amount, int channel_id, int flags) impure {
send_raw_message(begin_cell()
.store_uint(0x10, 6)
.store_slice(s_addr)
.store_grams(amount)
.store_uint(0, 1 + 4 + 4 + 64 + 32 + 1 + 1)
.store_uint(msg:payout(), 32)
.store_uint(channel_id, 64)
.end_cell(), flags);
}
cell do_payout(int promise_A, int promise_B, int A, int B, slice a_addr, slice b_addr, int channel_id) impure {
accept_message();
int diff = promise_B - promise_A;
if (diff < - A) {
diff = - A;
}
if (diff > B) {
diff = B;
}
A += diff;
B -= diff;
send_payout(a_addr, A, channel_id, 3);
send_payout(b_addr, B, channel_id, 3);
return begin_cell()
.store_int(state:payout(), 3)
.store_grams(A)
.store_grams(B)
.end_cell();
}
;;
;; init$000 inc_A:Grams inc_B:Grams min_A:Grams min_B:Grams = Message;
;;
cell with_init(slice state, int msg_value, slice msg, int msg_signed_A?, int msg_signed_B?,
slice a_addr, slice b_addr, int init_timeout, int channel_id) {
;; parse state
(int signed_A?, int signed_B?, int min_A, int min_B, int expire_at, int A, int B) = unpack_state_init(state);
if (expire_at == 0) {
expire_at = now() + init_timeout;
}
int op = msg~load_uint(32);
if (op == msg:timeout()) {
throw_unless(err:no_timeout(), expire_at < now());
return do_payout(0, 0, A, B, a_addr, b_addr, channel_id);
}
throw_unless(err:expected_init(), op == msg:init());
;; unpack init message
(int inc_A, int inc_B, int upd_min_A, int upd_min_B, int got_channel_id) =
(msg~load_grams(), msg~load_grams(), msg~load_grams(), msg~load_grams(), msg~load_uint(64));
throw_unless(err:wrong_channel_id(), got_channel_id == channel_id);
;; TODO: we should reserve some part of the value for comission
throw_if(err:msg_value_too_small(), msg_value < inc_A + inc_B);
throw_unless(err:replay_protection(), (msg_signed_A? < signed_A?) | (msg_signed_B? < signed_B?));
A += inc_A;
B += inc_B;
signed_A? |= msg_signed_A?;
if (min_A < upd_min_A) {
min_A = upd_min_A;
}
signed_B? |= msg_signed_B?;
if (min_B < upd_min_B) {
min_B = upd_min_B;
}
if (signed_A? & signed_B?) {
if ((min_A > A) | (min_B > B)) {
return do_payout(0, 0, A, B, a_addr, b_addr, channel_id);
}
return pack_state_close(0, 0, 0, 0, 0, A, B);
}
return pack_state_init(signed_A?, signed_B?, min_A, min_B, expire_at, A, B);
}
;; close$001 extra_A:Grams extra_B:Grams sig:Maybe<int256> promise_A:Grams promise_B:Grams = Message;
cell with_close(slice cs, slice msg, int msg_signed_A?, int msg_signed_B?, int a_key, int b_key,
slice a_addr, slice b_addr, int expire_timeout, int channel_id) {
;; parse state
(int signed_A?, int signed_B?, int promise_A, int promise_B, int expire_at, int A, int B) = unpack_state_close(cs);
if (expire_at == 0) {
expire_at = now() + expire_timeout;
}
int op = msg~load_uint(32);
if (op == msg:timeout()) {
throw_unless(err:no_timeout(), expire_at < now());
return do_payout(promise_A, promise_B, A, B, a_addr, b_addr, channel_id);
}
throw_unless(err:expected_close(), op == msg:close());
;; also ensures that (msg_signed_A? | msg_signed_B?) is true
throw_unless(err:replay_protection(), (msg_signed_A? < signed_A?) | (msg_signed_B? < signed_B?));
signed_A? |= msg_signed_A?;
signed_B? |= msg_signed_B?;
;; unpack close message
(int extra_A, int extra_B) = (msg~load_grams(), msg~load_grams());
int has_sig = msg~load_int(1);
if (has_sig) {
slice sig = msg~load_ref().begin_parse().preload_bits(512);
int hash = msg.slice_hash();
ifnot (msg_signed_A?) {
throw_unless(err:wrong_a_signature(), check_signature(hash, sig, a_key));
extra_A = 0;
}
ifnot (msg_signed_B?) {
throw_unless(err:wrong_b_signature(), check_signature(hash, sig, b_key));
extra_B = 0;
}
} else {
throw_unless(err:no_promise_signature(), msg_signed_A? & msg_signed_B?);
extra_A = 0;
extra_B = 0;
}
(int got_channel_id, int update_promise_A, int update_promise_B) = (msg~load_uint(64), msg~load_grams(), msg~load_grams());
throw_unless(err:wrong_channel_id(), got_channel_id == channel_id);
accept_message();
update_promise_A += extra_A;
if (promise_A < update_promise_A) {
promise_A = update_promise_A;
}
update_promise_B += extra_B;
if (promise_B < update_promise_B) {
promise_B = update_promise_B;
}
if (signed_A? & signed_B?) {
return do_payout(promise_A, promise_B, A, B, a_addr, b_addr, channel_id);
}
return pack_state_close(signed_A?, signed_B?, promise_A, promise_B, expire_at, A, B);
}
() recv_any(int msg_value, slice msg) impure {
(cell config, cell state) = unpack_data();
(int init_timeout, int close_timeout, int a_key, int b_key, slice a_addr, slice b_addr, int channel_id) = config.unpack_config();
(int msg_signed_A?, int msg_signed_B?) = msg~unwrap_signatures(a_key, b_key);
slice cs = state.begin_parse();
int state_type = cs~load_uint(3);
if (state_type == state:init()) { ;; init
state = with_init(cs, msg_value, msg, msg_signed_A?, msg_signed_B?, a_addr, b_addr, init_timeout, channel_id);
} if (state_type == state:close()) {
state = with_close(cs, msg, msg_signed_A?, msg_signed_B?, a_key, b_key, a_addr, b_addr, close_timeout, channel_id);
}
pack_data(config, state);
}
() recv_internal(int msg_value, cell in_msg_cell, slice in_msg) impure {
recv_any(msg_value, in_msg);
}
() recv_external(slice in_msg) impure {
recv_any(0, in_msg);
}

View File

@ -1,13 +1,16 @@
;; Restricted wallet (a variant of wallet-code.fc)
;; until configuration parameter -13 is set, accepts messages only to elector smc
;; restricts access to parts of balance until certain dates
() recv_internal(slice in_msg) impure {
;; do nothing for internal messages
}
_ days_passed() inline {
var p = config_param(-13);
return null?(p) ? -1 : (now() - begin_parse(p).preload_uint(32)) / 86400;
_ seconds_passed(int start_at, int utime) inline_ref {
ifnot (start_at) {
var p = config_param(-13);
start_at = null?(p) ? 0 : begin_parse(p).preload_uint(32);
}
return start_at ? utime - start_at : -1;
}
() recv_external(slice in_msg) impure {
@ -16,7 +19,7 @@ _ days_passed() inline {
var (msg_seqno, valid_until) = (cs~load_uint(32), cs~load_uint(32));
throw_if(35, valid_until <= now());
var ds = get_data().begin_parse();
var (stored_seqno, public_key, rdict) = (ds~load_uint(32), ds~load_uint(256), ds~load_dict());
var (stored_seqno, public_key, start_at, rdict) = (ds~load_uint(32), ds~load_uint(256), ds~load_uint(32), ds~load_dict());
ds.end_parse();
throw_unless(33, msg_seqno == stored_seqno);
ifnot (msg_seqno) {
@ -24,14 +27,15 @@ _ days_passed() inline {
set_data(begin_cell()
.store_uint(stored_seqno + 1, 32)
.store_uint(public_key, 256)
.store_uint(start_at, 32)
.store_dict(rdict)
.end_cell());
return ();
}
throw_unless(34, check_signature(slice_hash(in_msg), signature, public_key));
accept_message();
var ts = days_passed();
var (_, value, found) = rdict.idict_get_preveq?(16, ts);
var ts = seconds_passed(start_at, now());
var (_, value, found) = rdict.idict_get_preveq?(32, ts);
if (found) {
raw_reserve(value~load_grams(), 2);
}
@ -45,6 +49,7 @@ _ days_passed() inline {
set_data(begin_cell()
.store_uint(stored_seqno + 1, 32)
.store_uint(public_key, 256)
.store_uint(start_at, 32)
.store_dict(rdict)
.end_cell());
}
@ -61,15 +66,23 @@ int get_public_key() method_id {
return cs.preload_uint(256);
}
int balance() method_id {
int compute_balance_at(int utime) inline_ref {
var ds = get_data().begin_parse().skip_bits(32 + 256);
var rdict = ds~load_dict();
var (start_at, rdict) = (ds~load_uint(32), ds~load_dict());
ds.end_parse();
var ts = days_passed();
var ts = seconds_passed(start_at, utime);
var balance = get_balance().pair_first();
var (_, value, found) = rdict.idict_get_preveq?(16, ts);
var (_, value, found) = rdict.idict_get_preveq?(32, ts);
if (found) {
balance = max(balance - value~load_grams(), 0);
}
return balance;
}
int balance_at(int utime) method_id {
return compute_balance_at(utime);
}
int balance() method_id {
return compute_balance_at(now());
}

View File

@ -0,0 +1,103 @@
;; Restricted wallet initialized by a third party (a variant of restricted-wallet2-code.fc)
;; restricts access to parts of balance until certain dates
() recv_internal(slice in_msg) impure {
;; do nothing for internal messages
}
_ seconds_passed(int start_at, int utime) inline_ref {
ifnot (start_at) {
var p = config_param(-13);
start_at = null?(p) ? 0 : begin_parse(p).preload_uint(32);
}
return start_at ? utime - start_at : -1;
}
() 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));
throw_unless(33, msg_seqno == stored_seqno);
throw_unless(34, subwallet_id == stored_subwallet);
throw_unless(36, check_signature(slice_hash(in_msg), signature, public_key));
ifnot (msg_seqno) {
public_key = ds~load_uint(256); ;; load "final" public key
ds.end_parse();
cs~touch();
var (start_at, rdict) = (cs~load_uint(32), cs~load_dict());
cs.end_parse();
accept_message();
set_data(begin_cell()
.store_uint(stored_seqno + 1, 32)
.store_uint(stored_subwallet, 32)
.store_uint(public_key, 256)
.store_uint(start_at, 32)
.store_dict(rdict)
.end_cell());
return ();
}
var (start_at, rdict) = (ds~load_uint(32), ds~load_dict());
ds.end_parse();
accept_message();
var ts = seconds_passed(start_at, now());
var (_, value, found) = rdict.idict_get_preveq?(32, ts);
if (found) {
raw_reserve(value~load_grams(), 2);
}
cs~touch();
while (cs.slice_refs()) {
var mode = cs~load_uint(8);
var msg = cs~load_ref();
send_raw_message(msg, mode);
}
cs.end_parse();
set_data(begin_cell()
.store_uint(stored_seqno + 1, 32)
.store_uint(stored_subwallet, 32)
.store_uint(public_key, 256)
.store_uint(start_at, 32)
.store_dict(rdict)
.end_cell());
}
;; Get methods
int seqno() method_id {
return get_data().begin_parse().preload_uint(32);
}
int wallet_id() method_id {
var ds = get_data().begin_parse();
ds~load_uint(32);
return ds.preload_uint(32);
}
int get_public_key() method_id {
var ds = get_data().begin_parse();
ds~load_uint(32 + 32);
return ds.preload_uint(256);
}
int compute_balance_at(int utime) inline_ref {
var ds = get_data().begin_parse().skip_bits(32 + 32 + 256);
var (start_at, rdict) = (ds~load_uint(32), ds~load_dict());
ds.end_parse();
var ts = seconds_passed(start_at, utime);
var balance = get_balance().pair_first();
var (_, value, found) = rdict.idict_get_preveq?(32, ts);
if (found) {
balance = max(balance - value~load_grams(), 0);
}
return balance;
}
int balance_at(int utime) method_id {
return compute_balance_at(utime);
}
int balance() method_id {
return compute_balance_at(now());
}

View File

@ -18,5 +18,7 @@ dup Blen { 32 B>i@ } { drop Basechain } cond constant wallet_wc
file-base +".pk" dup file-exists? {
dup file>B dup Blen 32 <> abort"Private key must be exactly 32 bytes long"
=: wallet_pk ."Private key available in file " type cr
tuck =: wallet_pk ."Private key available in file " type cr
priv>pub 256 B>u@
dup ."Corresponding public key is " .pubkey ." = " 64X. cr
} { ."Private key file " type ." not found" cr } cond

View File

@ -0,0 +1,45 @@
// automatically generated from `smartcont/stdlib.fc` `smartcont/simple-wallet-code.fc`
PROGRAM{
DECLPROC recv_internal
DECLPROC recv_external
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 msg_seqno cs
c4 PUSH // signature in_msg msg_seqno cs _11
CTOS // signature in_msg msg_seqno cs cs2
32 LDU // signature in_msg msg_seqno cs stored_seqno cs2
256 LDU // signature in_msg msg_seqno cs stored_seqno public_key cs2
ENDS
s3 s1 XCPU // signature in_msg public_key cs stored_seqno msg_seqno stored_seqno
EQUAL // signature in_msg public_key cs stored_seqno _23
33 THROWIFNOT
s0 s3 XCHG // signature stored_seqno public_key cs in_msg
HASHSU // signature stored_seqno public_key cs _26
s0 s4 s2 XC2PU // cs stored_seqno public_key _26 signature public_key
CHKSIGNU // cs stored_seqno public_key _27
34 THROWIFNOT
ACCEPT
s2 s0 XCPU // public_key stored_seqno cs cs
SREFS // public_key stored_seqno cs _32
IF:<{ // public_key stored_seqno cs
8 LDU // public_key stored_seqno mode cs
LDREF // public_key stored_seqno mode _37 cs
s0 s2 XCHG // public_key stored_seqno cs _37 mode
SENDRAWMSG
}> // public_key stored_seqno cs
ENDS
INC // public_key _42
NEWC // public_key _42 _43
32 STU // public_key _45
256 STU // _47
ENDC // _48
c4 POP
}>
}END>c

View File

@ -0,0 +1,59 @@
// automatically generated from `smartcont/stdlib.fc` `smartcont/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 _8 cs
32 LDU // signature in_msg msg_seqno valid_until cs
NOW // signature in_msg msg_seqno valid_until cs _15
s1 s2 XCHG // signature in_msg msg_seqno cs valid_until _15
LEQ // signature in_msg msg_seqno cs _16
35 THROWIF
c4 PUSH // signature in_msg msg_seqno cs _19
CTOS // signature in_msg msg_seqno cs ds
32 LDU // signature in_msg msg_seqno cs _23 ds
256 LDU // signature in_msg msg_seqno cs stored_seqno public_key ds
ENDS
s3 s1 XCPU // signature in_msg public_key cs stored_seqno msg_seqno stored_seqno
EQUAL // signature in_msg public_key cs stored_seqno _31
33 THROWIFNOT
s0 s3 XCHG // signature stored_seqno public_key cs in_msg
HASHSU // signature stored_seqno public_key cs _34
s0 s4 s2 XC2PU // cs stored_seqno public_key _34 signature public_key
CHKSIGNU // cs stored_seqno public_key _35
34 THROWIFNOT
ACCEPT
s0 s2 XCHG // public_key stored_seqno cs
WHILE:<{
DUP // public_key stored_seqno cs cs
SREFS // public_key stored_seqno cs _40
}>DO<{ // public_key stored_seqno cs
8 LDU // public_key stored_seqno mode cs
LDREF // public_key stored_seqno mode _45 cs
s0 s2 XCHG // public_key stored_seqno cs _45 mode
SENDRAWMSG
}> // public_key stored_seqno cs
ENDS
INC // public_key _50
NEWC // public_key _50 _51
32 STU // public_key _53
256 STU // _55
ENDC // _56
c4 POP
}>
seqno PROC:<{
//
c4 PUSH // _0
CTOS // _1
32 PLDU // _3
}>
}END>c

View File

@ -21,6 +21,31 @@
#include "block/block-auto.h"
#include "block/block-parse.h"
namespace ton {
namespace smc {
td::Ref<vm::CellSlice> pack_grams(td::uint64 amount) {
vm::CellBuilder cb;
block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(amount));
return vm::load_cell_slice_ref(cb.finalize());
}
bool unpack_grams(td::Ref<vm::CellSlice> cs, td::uint64& amount) {
td::RefInt256 got;
if (!block::tlb::t_Grams.as_integer_to(cs, got)) {
return false;
}
if (!got->unsigned_fits_bits(63)) {
return false;
}
auto x = got->to_long();
if (x < 0) {
return false;
}
amount = x;
return true;
}
} // namespace smc
td::Ref<vm::Cell> GenericAccount::get_init_state(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data) noexcept {
return vm::CellBuilder()
.store_zeroes(2)
@ -47,7 +72,7 @@ void GenericAccount::store_int_message(vm::CellBuilder& cb, const block::StdAddr
.store_long(dest_address.workchain, 8)
.store_int256(dest_addr, 256);
block::tlb::t_Grams.store_integer_value(cb, td::BigInt256(gramms));
cb.store_zeroes(9 + 64 + 32 + 1 + 1);
cb.store_zeroes(9 + 64 + 32);
}
td::Ref<vm::Cell> GenericAccount::create_ext_message(const block::StdAddress& address, td::Ref<vm::Cell> new_state,

View File

@ -23,6 +23,10 @@
#include "SmartContract.h"
namespace ton {
namespace smc {
td::Ref<vm::CellSlice> pack_grams(td::uint64 amount);
bool unpack_grams(td::Ref<vm::CellSlice> cs, td::uint64& amount);
} // namespace smc
class GenericAccount {
public:
static td::Ref<vm::Cell> get_init_state(td::Ref<vm::Cell> code, td::Ref<vm::Cell> data) noexcept;

View File

@ -73,17 +73,11 @@ td::Ref<vm::Cell> HighloadWallet::make_a_gift_message(const td::Ed25519::Private
for (size_t i = 0; i < gifts.size(); i++) {
auto& gift = gifts[i];
td::int32 send_mode = 3;
auto gramms = gift.gramms;
if (gramms == -1) {
gramms = 0;
if (gift.gramms == -1) {
send_mode += 128;
}
auto message_inner = create_int_message(gift);
vm::CellBuilder cb;
GenericAccount::store_int_message(cb, gift.destination, gramms);
cb.store_bytes("\0\0\0\0", 4);
vm::CellString::store(cb, gift.message, 35 * 8).ensure();
auto message_inner = cb.finalize();
cb = {};
cb.store_long(send_mode, 8).store_ref(message_inner);
auto key = messages.integer_key(td::make_refint(i), 16, false);
messages.set_builder(key.bits(), 16, cb);

View File

@ -73,18 +73,11 @@ td::Ref<vm::Cell> HighloadWalletV2::make_a_gift_message(const td::Ed25519::Priva
for (size_t i = 0; i < gifts.size(); i++) {
auto& gift = gifts[i];
td::int32 send_mode = 3;
auto gramms = gift.gramms;
if (gramms == -1) {
gramms = 0;
if (gift.gramms == -1) {
send_mode += 128;
}
vm::CellBuilder cb;
GenericAccount::store_int_message(cb, gift.destination, gramms);
cb.store_bytes("\0\0\0\0", 4);
vm::CellString::store(cb, gift.message, 35 * 8).ensure();
auto message_inner = cb.finalize();
cb = {};
cb.store_long(send_mode, 8).store_ref(message_inner);
cb.store_long(send_mode, 8).store_ref(create_int_message(gift));
auto key = messages.integer_key(td::make_refint(i), 16, false);
messages.set_builder(key.bits(), 16, cb);
}

View File

@ -0,0 +1,264 @@
#include "PaymentChannel.h"
#include "GenericAccount.h"
#include "vm/cells.h"
#include "vm/cellslice.h"
#include "Ed25519.h"
#include "block/block-auto.h"
#include "block/block-parse.h"
#include "SmartContract.h"
#include "SmartContractCode.h"
namespace ton {
using smc::pack_grams;
using smc::unpack_grams;
namespace pchan {
td::Ref<vm::Cell> Config::serialize() const {
block::gen::ChanConfig::Record rec;
vm::CellBuilder a_addr_cb;
block::tlb::t_MsgAddressInt.store_std_address(a_addr_cb, a_addr);
rec.a_addr = a_addr_cb.finalize_novm();
vm::CellBuilder b_addr_cb;
block::tlb::t_MsgAddressInt.store_std_address(b_addr_cb, b_addr);
rec.b_addr = b_addr_cb.finalize_novm();
rec.a_key.as_slice().copy_from(a_key);
rec.b_key.as_slice().copy_from(b_key);
rec.init_timeout = init_timeout;
rec.close_timeout = close_timeout;
rec.channel_id = channel_id;
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
td::Ref<vm::Cell> MsgInit::serialize() const {
block::gen::ChanMsg::Record_chan_msg_init rec;
rec.min_A = pack_grams(min_A);
rec.min_B = pack_grams(min_B);
rec.inc_A = pack_grams(inc_A);
rec.inc_B = pack_grams(inc_B);
rec.channel_id = channel_id;
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
td::Ref<vm::Cell> Promise::serialize() const {
block::gen::ChanPromise::Record rec;
rec.channel_id = channel_id;
rec.promise_A = pack_grams(promise_A);
rec.promise_B = pack_grams(promise_B);
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
td::SecureString sign(const td::Ref<vm::Cell>& msg, const td::Ed25519::PrivateKey* key) {
return key->sign(msg->get_hash().as_slice()).move_as_ok();
}
td::Ref<vm::Cell> maybe_sign(const td::Ref<vm::Cell>& msg, const td::Ed25519::PrivateKey* key) {
if (!key) {
return {};
}
return vm::CellBuilder().store_bytes(sign(msg, key).as_slice()).finalize();
}
td::Ref<vm::CellSlice> maybe_ref(td::Ref<vm::Cell> msg) {
vm::CellBuilder cb;
CHECK(cb.store_maybe_ref(msg));
return vm::load_cell_slice_ref(cb.finalize());
}
td::Ref<vm::Cell> MsgClose::serialize() const {
block::gen::ChanMsg::Record_chan_msg_close rec;
rec.extra_A = pack_grams(extra_A);
rec.extra_B = pack_grams(extra_B);
rec.promise = signed_promise;
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
td::Ref<vm::Cell> MsgTimeout::serialize() const {
block::gen::ChanMsg::Record_chan_msg_timeout rec;
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
td::SecureString SignedPromise::signature(const td::Ed25519::PrivateKey* key, const td::Ref<vm::Cell>& promise) {
return sign(promise, key);
}
td::Ref<vm::Cell> SignedPromise::create_and_serialize(td::Slice signature, const td::Ref<vm::Cell>& promise) {
block::gen::ChanSignedPromise::Record rec;
rec.promise = vm::load_cell_slice_ref(promise);
LOG(ERROR) << "signature.size() = " << signature.size();
rec.sig = maybe_ref(vm::CellBuilder().store_bytes(signature).finalize());
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
td::Ref<vm::Cell> SignedPromise::create_and_serialize(const td::Ed25519::PrivateKey* key,
const td::Ref<vm::Cell>& promise) {
block::gen::ChanSignedPromise::Record rec;
rec.promise = vm::load_cell_slice_ref(promise);
rec.sig = maybe_ref(maybe_sign(promise, key));
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
bool SignedPromise::unpack(td::Ref<vm::Cell> cell) {
block::gen::ChanSignedPromise::Record rec;
if (!tlb::unpack_cell(cell, rec)) {
return false;
}
block::gen::ChanPromise::Record rec_promise;
if (!tlb::csr_unpack(rec.promise, rec_promise)) {
return false;
}
promise.channel_id = rec_promise.channel_id;
if (!unpack_grams(rec_promise.promise_A, promise.promise_A)) {
return false;
}
if (!unpack_grams(rec_promise.promise_B, promise.promise_B)) {
return false;
}
td::Ref<vm::Cell> sig_cell;
if (!rec.sig->prefetch_maybe_ref(sig_cell)) {
return false;
}
td::SecureString signature(64);
vm::CellSlice cs = vm::load_cell_slice(sig_cell);
if (!cs.prefetch_bytes(signature.as_mutable_slice())) {
return false;
}
o_signature = std::move(signature);
return true;
}
td::Ref<vm::Cell> StateInit::serialize() const {
block::gen::ChanState::Record_chan_state_init rec;
rec.expire_at = expire_at;
rec.min_A = pack_grams(min_A);
rec.min_B = pack_grams(min_B);
rec.A = pack_grams(A);
rec.B = pack_grams(B);
rec.signed_A = signed_A;
rec.signed_B = signed_B;
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
td::Ref<vm::Cell> Data::serialize() const {
block::gen::ChanData::Record rec;
rec.config = config;
rec.state = state;
td::Ref<vm::Cell> res;
CHECK(block::gen::t_ChanData.cell_pack(res, rec));
return res;
}
td::Ref<vm::Cell> Data::init_state() {
return StateInit().serialize();
}
} // namespace pchan
td::Result<PaymentChannel::Info> PaymentChannel::get_info() const {
block::gen::ChanData::Record data_rec;
if (!tlb::unpack_cell(get_state().data, data_rec)) {
return td::Status::Error("Can't unpack data");
}
block::gen::ChanConfig::Record config_rec;
if (!tlb::unpack_cell(data_rec.config, config_rec)) {
return td::Status::Error("Can't unpack config");
}
pchan::Config config;
config.a_key = td::SecureString(config_rec.a_key.as_slice());
config.b_key = td::SecureString(config_rec.b_key.as_slice());
block::tlb::t_MsgAddressInt.extract_std_address(vm::load_cell_slice_ref(config_rec.a_addr), config.a_addr);
block::tlb::t_MsgAddressInt.extract_std_address(vm::load_cell_slice_ref(config_rec.b_addr), config.b_addr);
config.init_timeout = static_cast<td::int32>(config_rec.init_timeout);
config.close_timeout = static_cast<td::int32>(config_rec.close_timeout);
config.channel_id = static_cast<td::int64>(config_rec.channel_id);
auto state_cs = vm::load_cell_slice(data_rec.state);
Info res;
switch (block::gen::t_ChanState.check_tag(state_cs)) {
case block::gen::ChanState::chan_state_init: {
pchan::StateInit state;
block::gen::ChanState::Record_chan_state_init state_rec;
if (!tlb::unpack_cell(data_rec.state, state_rec)) {
return td::Status::Error("Can't unpack state");
}
bool ok = unpack_grams(state_rec.A, state.A) && unpack_grams(state_rec.B, state.B) &&
unpack_grams(state_rec.min_A, state.min_A) && unpack_grams(state_rec.min_B, state.min_B);
state.expire_at = state_rec.expire_at;
state.signed_A = state_rec.signed_A;
state.signed_B = state_rec.signed_B;
if (!ok) {
return td::Status::Error("Can't unpack state");
}
res.state = std::move(state);
break;
}
case block::gen::ChanState::chan_state_close: {
pchan::StateClose state;
block::gen::ChanState::Record_chan_state_close state_rec;
if (!tlb::unpack_cell(data_rec.state, state_rec)) {
return td::Status::Error("Can't unpack state");
}
bool ok = unpack_grams(state_rec.A, state.A) && unpack_grams(state_rec.B, state.B) &&
unpack_grams(state_rec.promise_A, state.promise_A) &&
unpack_grams(state_rec.promise_B, state.promise_B);
state.expire_at = state_rec.expire_at;
state.signed_A = state_rec.signed_A;
state.signed_B = state_rec.signed_B;
if (!ok) {
return td::Status::Error("Can't unpack state");
}
res.state = std::move(state);
break;
}
case block::gen::ChanState::chan_state_payout: {
pchan::StatePayout state;
block::gen::ChanState::Record_chan_state_payout state_rec;
if (!tlb::unpack_cell(data_rec.state, state_rec)) {
return td::Status::Error("Can't unpack state");
}
bool ok = unpack_grams(state_rec.A, state.A) && unpack_grams(state_rec.B, state.B);
if (!ok) {
return td::Status::Error("Can't unpack state");
}
res.state = std::move(state);
break;
}
default:
return td::Status::Error("Can't unpack state");
}
res.config = std::move(config);
res.description = block::gen::t_ChanState.as_string_ref(data_rec.state);
return std::move(res);
} // namespace ton
td::optional<td::int32> PaymentChannel::guess_revision(const vm::Cell::Hash& code_hash) {
for (auto i : ton::SmartContractCode::get_revisions(ton::SmartContractCode::PaymentChannel)) {
auto code = SmartContractCode::get_code(SmartContractCode::PaymentChannel, i);
if (code->get_hash() == code_hash) {
return i;
}
}
return {};
}
} // namespace ton

View File

@ -0,0 +1,251 @@
#pragma once
#include "vm/cells.h"
#include "vm/cellslice.h"
#include "Ed25519.h"
#include "block/block-auto.h"
#include "block/block-parse.h"
#include "td/utils/Variant.h"
#include "SmartContract.h"
#include "SmartContractCode.h"
namespace ton {
namespace pchan {
//
// Payment channels
//
struct Config {
td::uint32 init_timeout{0};
td::uint32 close_timeout{0};
td::SecureString a_key;
td::SecureString b_key;
block::StdAddress a_addr;
block::StdAddress b_addr;
td::uint64 channel_id{0};
td::Ref<vm::Cell> serialize() const;
};
struct MsgInit {
td::uint64 inc_A{0};
td::uint64 inc_B{0};
td::uint64 min_A{0};
td::uint64 min_B{0};
td::uint64 channel_id{0};
td::Ref<vm::Cell> serialize() const;
};
struct Promise {
td::uint64 channel_id;
td::uint64 promise_A{0};
td::uint64 promise_B{0};
td::Ref<vm::Cell> serialize() const;
};
td::Ref<vm::Cell> maybe_sign(const td::Ref<vm::Cell>& msg, const td::Ed25519::PrivateKey* key);
td::Ref<vm::CellSlice> maybe_ref(td::Ref<vm::Cell> msg);
struct MsgClose {
td::uint64 extra_A{0};
td::uint64 extra_B{0};
td::Ref<vm::CellSlice> signed_promise;
td::Ref<vm::Cell> serialize() const;
};
struct MsgTimeout {
td::Ref<vm::Cell> serialize() const;
};
struct SignedPromise {
Promise promise;
td::optional<td::SecureString> o_signature;
bool unpack(td::Ref<vm::Cell> cell);
static td::SecureString signature(const td::Ed25519::PrivateKey* key, const td::Ref<vm::Cell>& promise);
static td::Ref<vm::Cell> create_and_serialize(td::Slice signature, const td::Ref<vm::Cell>& promise);
static td::Ref<vm::Cell> create_and_serialize(const td::Ed25519::PrivateKey* key, const td::Ref<vm::Cell>& promise);
};
struct StateInit {
bool signed_A{false};
bool signed_B{false};
td::uint64 min_A{0};
td::uint64 min_B{0};
td::uint64 A{0};
td::uint64 B{0};
td::uint32 expire_at{0};
td::Ref<vm::Cell> serialize() const;
};
struct StateClose {
bool signed_A{false};
bool signed_B{false};
td::uint64 promise_A{0};
td::uint64 promise_B{0};
td::uint64 A{0};
td::uint64 B{0};
td::uint32 expire_at{0};
};
struct StatePayout {
td::uint64 A{0};
td::uint64 B{0};
};
struct Data {
td::Ref<vm::Cell> config;
td::Ref<vm::Cell> state;
static td::Ref<vm::Cell> init_state();
td::Ref<vm::Cell> serialize() const;
};
template <class T>
struct MsgBuilder {
td::Ed25519::PrivateKey* a_key{nullptr};
td::Ed25519::PrivateKey* b_key{nullptr};
T&& with_a_key(td::Ed25519::PrivateKey* key) && {
a_key = key;
return static_cast<T&&>(*this);
}
T&& with_b_key(td::Ed25519::PrivateKey* key) && {
b_key = key;
return static_cast<T&&>(*this);
}
td::Ref<vm::Cell> finalize() && {
block::gen::ChanSignedMsg::Record rec;
auto msg = static_cast<T&&>(*this).msg.serialize();
rec.msg = vm::load_cell_slice_ref(msg);
rec.sig_A = maybe_ref(maybe_sign(msg, a_key));
rec.sig_B = maybe_ref(maybe_sign(msg, b_key));
td::Ref<vm::Cell> res;
CHECK(tlb::pack_cell(res, rec));
return res;
}
};
struct MsgInitBuilder : public MsgBuilder<MsgInitBuilder> {
MsgInit msg;
MsgInitBuilder&& min_A(td::uint64 value) && {
msg.min_A = value;
return std::move(*this);
}
MsgInitBuilder&& min_B(td::uint64 value) && {
msg.min_B = value;
return std::move(*this);
}
MsgInitBuilder&& inc_A(td::uint64 value) && {
msg.inc_A = value;
return std::move(*this);
}
MsgInitBuilder&& inc_B(td::uint64 value) && {
msg.inc_B = value;
return std::move(*this);
}
MsgInitBuilder&& channel_id(td::uint64 value) && {
msg.channel_id = value;
return std::move(*this);
}
};
struct MsgTimeoutBuilder : public MsgBuilder<MsgTimeoutBuilder> {
MsgTimeout msg;
};
struct MsgCloseBuilder : public MsgBuilder<MsgCloseBuilder> {
MsgClose msg;
MsgCloseBuilder&& extra_A(td::uint64 value) && {
msg.extra_A = value;
return std::move(*this);
}
MsgCloseBuilder&& extra_B(td::uint64 value) && {
msg.extra_B = value;
return std::move(*this);
}
MsgCloseBuilder&& signed_promise(td::Ref<vm::Cell> signed_promise) && {
msg.signed_promise = vm::load_cell_slice_ref(signed_promise);
return std::move(*this);
}
};
struct SignedPromiseBuilder {
Promise promise;
td::optional<td::SecureString> o_signature;
td::Ed25519::PrivateKey* key{nullptr};
SignedPromiseBuilder& with_key(td::Ed25519::PrivateKey* key) {
this->key = key;
return *this;
}
SignedPromiseBuilder& promise_A(td::uint64 value) {
promise.promise_A = value;
return *this;
}
SignedPromiseBuilder& promise_B(td::uint64 value) {
promise.promise_B = value;
return *this;
}
SignedPromiseBuilder& channel_id(td::uint64 value) {
promise.channel_id = value;
return *this;
}
SignedPromiseBuilder& signature(td::SecureString signature) {
o_signature = std::move(signature);
return *this;
}
bool check_signature(td::Slice signature, const td::Ed25519::PublicKey& pk) {
return pk.verify_signature(promise.serialize()->get_hash().as_slice(), signature).is_ok();
}
td::SecureString calc_signature() {
CHECK(key);
return SignedPromise::signature(key, promise.serialize());
}
td::Ref<vm::Cell> finalize() {
if (o_signature) {
return SignedPromise::create_and_serialize(o_signature.value().copy(), promise.serialize());
} else {
return SignedPromise::create_and_serialize(key, promise.serialize());
}
}
};
} // namespace pchan
class PaymentChannel : public SmartContract {
public:
PaymentChannel(State state) : SmartContract(std::move(state)) {
}
struct Info {
pchan::Config config;
td::Variant<pchan::StateInit, pchan::StateClose, pchan::StatePayout> state;
std::string description;
};
td::Result<Info> get_info() const;
static td::Ref<PaymentChannel> create(State state) {
return td::Ref<PaymentChannel>(true, std::move(state));
}
static td::optional<td::int32> guess_revision(const vm::Cell::Hash& code_hash);
static td::Ref<PaymentChannel> create(const pchan::Config& config, td::int32 revision) {
State state;
state.code = SmartContractCode::get_code(SmartContractCode::PaymentChannel, revision);
pchan::Data data;
data.config = config.serialize();
pchan::StateInit init;
data.state = init.serialize();
state.data = data.serialize();
return create(std::move(state));
}
};
} // namespace ton

View File

@ -32,19 +32,19 @@
namespace ton {
namespace {
td::Ref<vm::Stack> prepare_vm_stack(td::Ref<vm::CellSlice> body) {
td::Ref<vm::Stack> prepare_vm_stack(td::RefInt256 amount, td::Ref<vm::CellSlice> body) {
td::Ref<vm::Stack> stack_ref{true};
td::RefInt256 acc_addr{true};
//CHECK(acc_addr.write().import_bits(account.addr.cbits(), 256));
vm::Stack& stack = stack_ref.write();
stack.push_int(td::make_refint(10000000000));
stack.push_int(td::make_refint(10000000000));
stack.push_int(std::move(amount));
stack.push_cell(vm::CellBuilder().finalize());
stack.push_cellslice(std::move(body));
return stack_ref;
}
td::Ref<vm::Tuple> prepare_vm_c7(td::uint32 now) {
td::Ref<vm::Tuple> prepare_vm_c7(td::uint32 now, td::uint64 balance) {
// TODO: fix initialization of c7
td::BitArray<256> rand_seed;
rand_seed.as_slice().fill(0);
@ -58,7 +58,7 @@ td::Ref<vm::Tuple> prepare_vm_c7(td::uint32 now) {
td::make_refint(0), // block_lt:Integer
td::make_refint(0), // trans_lt:Integer
std::move(rand_seed_int), // rand_seed:Integer
block::CurrencyCollection(1000000000).as_vm_tuple(), // balance_remaining:[Integer (Maybe Cell)]
block::CurrencyCollection(balance).as_vm_tuple(), // balance_remaining:[Integer (Maybe Cell)]
vm::load_cell_slice_ref(vm::CellBuilder().finalize()) // myself:MsgAddressInt
//vm::StackEntry::maybe(td::Ref<vm::Cell>())
); // global_config:(Maybe Cell) ] = SmartContractInfo;
@ -66,6 +66,15 @@ td::Ref<vm::Tuple> prepare_vm_c7(td::uint32 now) {
return vm::make_tuple_ref(std::move(tuple));
}
static int output_actions_count(td::Ref<vm::Cell> list) {
int i = -1;
do {
++i;
list = load_cell_slice(std::move(list)).prefetch_ref();
} while (list.not_null());
return i;
}
SmartContract::Answer run_smartcont(SmartContract::State state, td::Ref<vm::Stack> stack, td::Ref<vm::Tuple> c7,
vm::GasLimits gas, bool ignore_chksig) {
auto gas_credit = gas.gas_credit;
@ -123,6 +132,8 @@ SmartContract::Answer run_smartcont(SmartContract::State state, td::Ref<vm::Stac
if (res.success) {
res.new_state.data = vm.get_c4();
res.actions = vm.get_d(5);
LOG(DEBUG) << "output actions:\n"
<< block::gen::OutList{output_actions_count(res.actions)}.as_string_ref(res.actions);
}
LOG_IF(ERROR, gas_credit != 0 && (res.accepted && !res.success))
<< "Accepted but failed with code " << res.code << "\n"
@ -171,10 +182,13 @@ SmartContract::Answer SmartContract::run_method(Args args) {
now = args.now.unwrap();
}
if (!args.c7) {
args.c7 = prepare_vm_c7(now);
args.c7 = prepare_vm_c7(now, args.balance);
}
if (!args.limits) {
args.limits = vm::GasLimits{(long long)0, (long long)1000000, (long long)10000};
bool is_internal = args.get_method_id().ok() == 0;
args.limits = vm::GasLimits{is_internal ? (long long)args.amount * 1000 : (long long)0, (long long)1000000,
is_internal ? 0 : (long long)10000};
}
CHECK(args.stack);
CHECK(args.method_id);
@ -191,7 +205,7 @@ SmartContract::Answer SmartContract::run_get_method(Args args) const {
now = args.now.unwrap();
}
if (!args.c7) {
args.c7 = prepare_vm_c7(now);
args.c7 = prepare_vm_c7(now, args.balance);
}
if (!args.limits) {
args.limits = vm::GasLimits{1000000};
@ -209,6 +223,11 @@ SmartContract::Answer SmartContract::run_get_method(td::Slice method, Args args)
}
SmartContract::Answer SmartContract::send_external_message(td::Ref<vm::Cell> cell, Args args) {
return run_method(args.set_stack(prepare_vm_stack(vm::load_cell_slice_ref(cell))).set_method_id(-1));
return run_method(
args.set_stack(prepare_vm_stack(td::make_refint(0), vm::load_cell_slice_ref(cell))).set_method_id(-1));
}
SmartContract::Answer SmartContract::send_internal_message(td::Ref<vm::Cell> cell, Args args) {
return run_method(
args.set_stack(prepare_vm_stack(td::make_refint(args.amount), vm::load_cell_slice_ref(cell))).set_method_id(0));
}
} // namespace ton

View File

@ -57,6 +57,8 @@ class SmartContract : public td::CntObject {
td::optional<td::Ref<vm::Stack>> stack;
td::optional<td::int32> now;
bool ignore_chksig{false};
td::uint64 amount{0};
td::uint64 balance{0};
Args() {
}
@ -95,6 +97,14 @@ class SmartContract : public td::CntObject {
this->ignore_chksig = ignore_chksig;
return std::move(*this);
}
Args&& set_amount(td::uint64 amount) {
this->amount = amount;
return std::move(*this);
}
Args&& set_balance(td::uint64 balance) {
this->balance = balance;
return std::move(*this);
}
td::Result<td::int32> get_method_id() const {
if (!method_id) {
@ -109,6 +119,7 @@ class SmartContract : public td::CntObject {
Answer run_get_method(Args args = {}) const;
Answer run_get_method(td::Slice method, Args args = {}) const;
Answer send_external_message(td::Ref<vm::Cell> cell, Args args = {});
Answer send_internal_message(td::Ref<vm::Cell> cell, Args args = {});
size_t code_size() const;
size_t data_size() const;
@ -122,6 +133,9 @@ class SmartContract : public td::CntObject {
const State& get_state() const {
return state_;
}
CntObject* make_copy() const override {
return new SmartContract(state_);
}
protected:
State state_;

View File

@ -44,6 +44,8 @@ const auto& get_map() {
#include "smartcont/auto/highload-wallet-code.cpp"
#include "smartcont/auto/highload-wallet-v2-code.cpp"
#include "smartcont/auto/dns-manual-code.cpp"
#include "smartcont/auto/payment-channel-code.cpp"
#include "smartcont/auto/restricted-wallet3-code.cpp"
with_tvm_code("highload-wallet-r1",
"te6ccgEBBgEAhgABFP8A9KQT9KDyyAsBAgEgAgMCAUgEBQC88oMI1xgg0x/TH9Mf+CMTu/Jj7UTQ0x/TH9P/"
@ -96,6 +98,13 @@ const auto& get_map() {
"FwCEMQLTAAHAAZPUAdCY0wUBqgLXGAHiINdJwg/"
"ypiB41yLXCwfyaHBTEddJqTYCmNMHAcAAEqEB5DDIywYBzxbJ0FADACBZ9KhvpSCUAvQEMJIybeICACg0A4AQ9FqZECOECUBE8AEBkjAx4gBmM"
"SLAFZwy9AQQI4QJUELwAQHgIsAWmDIChAn0czAB4DAyIMAfkzD0BODAIJJtAeDyLG0B");
with_tvm_code(
"restricted-wallet3-r1",
"te6ccgECEgEAAUwAART/APSkE/S88sgLAQIBIAIDAgFIBAUD+PKDCNcYINMf0x/THwL4I7vyY+1E0NMf0x/T/"
"1NDuvKhUWK68qIG+QFUEHb5EPKkAY4fMwHT/9EB0x/0BNH4AAOkyMsfFMsfy/8Syx/0AMntVOEC0x/"
"0BNH4ACH4I9s8IYAg9HtvpTGW+gAwcvsCkTDiApMg10qK6NECpMgPEBEABNAwAgEgBgcCASAICQIBSAwNAgFuCgsAEbjJftRNDXCx+"
"AAXrc52omhpn5jrhf/AABmsePaiaEAQa5DrhY/AAQ222B8Ee2eQDgEJtQdbZ5AOAU7tRNCBAUDXIdMf9ATRAts8+CdvEAKAIPR7b6Uxl/"
"oAMKFwtgmRMOIPADohjhExgPP4MyBukjBwlNDXCx/iAd8hkgGhklt/4gAM0wfUAvsAAB7LHxTLHxLL/8sf9ADJ7VQ=");
return map;
}();
return map;
@ -103,7 +112,6 @@ const auto& get_map() {
} // namespace
td::Result<td::Ref<vm::Cell>> SmartContractCode::load(td::Slice name) {
LOG(ERROR) << "LOAD " << name;
auto& map = get_map();
auto it = map.find(name);
if (it == map.end()) {
@ -146,6 +154,14 @@ td::Span<int> SmartContractCode::get_revisions(Type type) {
static int res[] = {-1, 1};
return res;
}
case Type::PaymentChannel: {
static int res[] = {-1};
return res;
}
case Type::RestrictedWallet: {
static int res[] = {-1, 1};
return res;
}
}
UNREACHABLE();
return {};
@ -190,6 +206,10 @@ td::Ref<vm::Cell> SmartContractCode::get_code(Type type, int ext_revision) {
return "multisig";
case Type::ManualDns:
return "dns-manual";
case Type::PaymentChannel:
return "payment-channel";
case Type::RestrictedWallet:
return "restricted-wallet3";
}
UNREACHABLE();
return "";

View File

@ -16,6 +16,7 @@
Copyright 2017-2020 Telegram Systems LLP
*/
#pragma once
#include "vm/cells.h"
#include "td/utils/Span.h"
@ -25,7 +26,18 @@ class SmartContractCode {
public:
static td::Result<td::Ref<vm::Cell>> load(td::Slice name);
enum Type { WalletV1 = 1, WalletV1Ext, WalletV2, WalletV3, HighloadWalletV1, HighloadWalletV2, ManualDns, Multisig };
enum Type {
WalletV1 = 1,
WalletV1Ext,
WalletV2,
WalletV3,
HighloadWalletV1,
HighloadWalletV2,
ManualDns,
Multisig,
PaymentChannel,
RestrictedWallet
};
static td::Span<int> get_revisions(Type type);
static td::Result<int> validate_revision(Type type, int revision);
static td::Ref<vm::Cell> get_code(Type type, int revision = 0);

View File

@ -43,12 +43,7 @@ td::Ref<vm::Cell> TestGiver::make_a_gift_message_static(td::uint32 seqno, td::Sp
for (auto& gift : gifts) {
td::int32 send_mode = 1;
auto gramms = gift.gramms;
vm::CellBuilder cbi;
GenericAccount::store_int_message(cbi, gift.destination, gramms);
store_gift_message(cbi, gift);
auto message_inner = cbi.finalize();
cb.store_long(send_mode, 8).store_ref(std::move(message_inner));
cb.store_long(send_mode, 8).store_ref(create_int_message(gift));
}
return cb.finalize();

View File

@ -46,18 +46,11 @@ td::Ref<vm::Cell> TestWallet::make_a_gift_message_static(const td::Ed25519::Priv
for (auto& gift : gifts) {
td::int32 send_mode = 3;
auto gramms = gift.gramms;
if (gramms == -1) {
gramms = 0;
if (gift.gramms == -1) {
send_mode += 128;
}
vm::CellBuilder cbi;
GenericAccount::store_int_message(cbi, gift.destination, gramms);
store_gift_message(cbi, gift);
auto message_inner = cbi.finalize();
cb.store_long(send_mode, 8).store_ref(std::move(message_inner));
cb.store_long(send_mode, 8).store_ref(create_int_message(gift));
}
auto message_outer = cb.finalize();
auto signature = private_key.sign(message_outer->get_hash().as_slice()).move_as_ok();
return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize();

View File

@ -52,16 +52,10 @@ td::Ref<vm::Cell> Wallet::make_a_gift_message(const td::Ed25519::PrivateKey& pri
for (auto& gift : gifts) {
td::int32 send_mode = 3;
auto gramms = gift.gramms;
if (gramms == -1) {
gramms = 0;
if (gift.gramms == -1) {
send_mode += 128;
}
vm::CellBuilder cbi;
GenericAccount::store_int_message(cbi, gift.destination, gramms);
store_gift_message(cbi, gift);
auto message_inner = cbi.finalize();
cb.store_long(send_mode, 8).store_ref(std::move(message_inner));
cb.store_long(send_mode, 8).store_ref(create_int_message(gift));
}
auto message_outer = cb.finalize();

View File

@ -0,0 +1,52 @@
/*
This file is part of TON Blockchain Library.
TON Blockchain Library is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 2 of the License, or
(at your option) any later version.
TON Blockchain Library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with TON Blockchain Library. If not, see <http://www.gnu.org/licenses/>.
Copyright 2017-2020 Telegram Systems LLP
*/
#include "WalletInterface.h"
#include "GenericAccount.h"
namespace ton {
td::Ref<vm::Cell> WalletInterfaceRaw::create_int_message(const Gift &gift) {
vm::CellBuilder cbi;
GenericAccount::store_int_message(cbi, gift.destination, gift.gramms < 0 ? 0 : gift.gramms);
if (gift.init_state.not_null()) {
cbi.store_ones(2);
cbi.store_ref(gift.init_state);
}
cbi.store_zeroes(0);
store_gift_message(cbi, gift);
return cbi.finalize();
}
td::Result<td::Ed25519::PublicKey> WalletInterfaceRaw::get_public_key() const {
auto sc = as_smart_constract();
auto answer = sc.run_get_method("get_public_key");
if (!answer.success) {
return td::Status::Error("get_public_key failed");
}
auto do_get_public_key = [&]() -> td::Result<td::Ed25519::PublicKey> {
auto key = answer.stack.write().pop_int_finite();
td::SecureString bytes(32);
if (!key->export_bytes(bytes.as_mutable_slice().ubegin(), bytes.size())) {
return td::Status::Error("get_public_key failed");
}
return td::Ed25519::PublicKey(std::move(bytes));
};
return TRY_VM(do_get_public_key());
}
} // namespace ton

View File

@ -24,6 +24,7 @@
#include "vm/cells/CellString.h"
#include "SmartContract.h"
#include "GenericAccount.h"
namespace ton {
class WalletInterface {
@ -36,6 +37,7 @@ class WalletInterface {
std::string message;
td::Ref<vm::Cell> body;
td::Ref<vm::Cell> init_state;
};
virtual ~WalletInterface() {
@ -48,15 +50,29 @@ class WalletInterface {
return td::Status::Error("Unsupported");
}
td::Result<td::Ref<vm::Cell>> get_init_message(const td::Ed25519::PrivateKey &private_key,
td::uint32 valid_until = std::numeric_limits<td::uint32>::max()) {
td::Result<td::Ref<vm::Cell>> get_init_message(
const td::Ed25519::PrivateKey &private_key,
td::uint32 valid_until = std::numeric_limits<td::uint32>::max()) const {
return make_a_gift_message(private_key, valid_until, {});
}
static td::Ref<vm::Cell> create_int_message(const Gift &gift) {
vm::CellBuilder cbi;
GenericAccount::store_int_message(cbi, gift.destination, gift.gramms < 0 ? 0 : gift.gramms);
if (gift.init_state.not_null()) {
cbi.store_ones(2);
cbi.store_ref(gift.init_state);
} else {
cbi.store_zeroes(1);
}
cbi.store_zeroes(1);
store_gift_message(cbi, gift);
return cbi.finalize();
}
static void store_gift_message(vm::CellBuilder &cb, const Gift &gift) {
if (gift.body.not_null()) {
auto body = vm::load_cell_slice(gift.body);
//TODO: handle error
cb.append_cellslice_bool(body);
CHECK(cb.append_cellslice_bool(body));
return;
}

View File

@ -62,16 +62,10 @@ td::Ref<vm::Cell> WalletV3::make_a_gift_message(const td::Ed25519::PrivateKey& p
for (auto& gift : gifts) {
td::int32 send_mode = 3;
auto gramms = gift.gramms;
if (gramms == -1) {
gramms = 0;
if (gift.gramms == -1) {
send_mode += 128;
}
vm::CellBuilder cbi;
GenericAccount::store_int_message(cbi, gift.destination, gramms);
store_gift_message(cbi, gift);
auto message_inner = cbi.finalize();
cb.store_long(send_mode, 8).store_ref(std::move(message_inner));
cb.store_long(send_mode, 8).store_ref(create_int_message(gift));
}
auto message_outer = cb.finalize();

View File

@ -70,3 +70,209 @@ class WalletV3 : public ton::SmartContract, public WalletInterface {
td::Result<td::Ed25519::PublicKey> get_public_key_or_throw() const;
};
} // namespace ton
#include "smc-envelope/SmartContractCode.h"
#include "smc-envelope/GenericAccount.h"
#include "block/block-parse.h"
#include <algorithm>
namespace ton {
template <class WalletT, class TraitsT>
class WalletBase : public SmartContract, public WalletInterface {
public:
using Traits = TraitsT;
using InitData = typename Traits::InitData;
explicit WalletBase(State state) : SmartContract(std::move(state)) {
}
static td::Ref<WalletT> create(State state) {
return td::Ref<WalletT>(true, std::move(state));
}
static td::Ref<vm::Cell> get_init_code(int revision) {
return SmartContractCode::get_code(get_code_type(), revision);
};
size_t get_max_gifts_size() const override {
return Traits::max_gifts_size;
}
static SmartContractCode::Type get_code_type() {
return Traits::code_type;
}
static td::optional<td::int32> guess_revision(const vm::Cell::Hash& code_hash) {
for (auto i : ton::SmartContractCode::get_revisions(get_code_type())) {
auto code = SmartContractCode::get_code(get_code_type(), i);
if (code->get_hash() == code_hash) {
return i;
}
}
return {};
}
static td::Ref<WalletT> create(const InitData& init_data, int revision) {
return td::Ref<WalletT>(true, State{get_init_code(revision), WalletT::get_init_data(init_data)});
}
td::Result<td::uint32> get_seqno() const {
return TRY_VM([&]() -> td::Result<td::uint32> {
Answer answer = this->run_get_method("seqno");
if (!answer.success) {
return td::Status::Error("seqno get method failed");
}
return static_cast<td::uint32>(answer.stack.write().pop_long_range(std::numeric_limits<td::uint32>::max()));
}());
}
td::Result<td::uint32> get_wallet_id() const {
return TRY_VM([&]() -> td::Result<td::uint32> {
Answer answer = this->run_get_method("wallet_id");
if (!answer.success) {
return td::Status::Error("seqno get method failed");
}
return static_cast<td::uint32>(answer.stack.write().pop_long_range(std::numeric_limits<td::uint32>::max()));
}());
}
td::Result<td::uint64> get_balance(td::uint64 account_balance, td::uint32 now) const {
return TRY_VM([&]() -> td::Result<td::uint64> {
Answer answer = this->run_get_method(Args().set_method_id("balance").set_balance(account_balance).set_now(now));
if (!answer.success) {
return td::Status::Error("balance get method failed");
}
return static_cast<td::uint64>(answer.stack.write().pop_long());
}());
}
td::Result<td::Ed25519::PublicKey> get_public_key() const override {
return TRY_VM([&]() -> td::Result<td::Ed25519::PublicKey> {
Answer answer = this->run_get_method("get_public_key");
if (!answer.success) {
return td::Status::Error("get_public_key get method failed");
}
auto key_int = answer.stack.write().pop_int();
LOG(ERROR) << key_int->bit_size(false);
td::SecureString bytes(32);
if (!key_int->export_bytes(bytes.as_mutable_slice().ubegin(), bytes.size(), false)) {
return td::Status::Error("not a public key");
}
return td::Ed25519::PublicKey(std::move(bytes));
}());
};
};
struct RestrictedWalletTraits {
struct InitData {
td::SecureString init_key;
td::SecureString main_key;
td::uint32 wallet_id{0};
};
static constexpr unsigned max_message_size = vm::CellString::max_bytes;
static constexpr unsigned max_gifts_size = 4;
static constexpr auto code_type = SmartContractCode::RestrictedWallet;
};
class RestrictedWallet : public WalletBase<RestrictedWallet, RestrictedWalletTraits> {
public:
struct Config {
td::uint32 start_at{0};
std::vector<std::pair<td::int32, td::uint64>> limits;
};
explicit RestrictedWallet(State state) : WalletBase(std::move(state)) {
}
td::Result<Config> get_config() const {
return TRY_VM([this]() -> td::Result<Config> {
auto cs = vm::load_cell_slice(get_state().data);
Config config;
td::Ref<vm::Cell> dict_root;
auto ok = cs.advance(32 + 32 + 256) && cs.fetch_uint_to(32, config.start_at) && cs.fetch_maybe_ref(dict_root);
vm::Dictionary dict(std::move(dict_root), 32);
dict.check_for_each([&](auto cs, auto ptr, auto ptr_bits) {
auto r_seconds = td::narrow_cast_safe<td::int32>(dict.key_as_integer(ptr, true)->to_long());
if (r_seconds.is_error()) {
ok = false;
return ok;
}
td::uint64 value;
ok &= smc::unpack_grams(cs, value);
config.limits.emplace_back(r_seconds.ok(), value);
return ok;
});
if (!ok) {
return td::Status::Error("Can't parse config");
}
std::sort(config.limits.begin(), config.limits.end());
return config;
}());
}
static td::Ref<vm::Cell> get_init_data(const InitData& init_data) {
vm::CellBuilder cb;
cb.store_long(0, 32);
cb.store_long(init_data.wallet_id, 32);
CHECK(init_data.init_key.size() == 32);
CHECK(init_data.main_key.size() == 32);
cb.store_bytes(init_data.init_key.as_slice());
cb.store_bytes(init_data.main_key.as_slice());
return cb.finalize();
}
td::Result<td::Ref<vm::Cell>> get_init_message(const td::Ed25519::PrivateKey& init_private_key,
td::uint32 valid_until, const Config& config) const {
vm::CellBuilder cb;
TRY_RESULT(seqno, get_seqno());
TRY_RESULT(wallet_id, get_wallet_id());
LOG(ERROR) << "seqno: " << seqno << " wallet_id: " << wallet_id;
if (seqno != 0) {
return td::Status::Error("Wallet is already inited");
}
cb.store_long(wallet_id, 32);
cb.store_long(valid_until, 32);
cb.store_long(seqno, 32);
cb.store_long(config.start_at, 32);
vm::Dictionary dict(32);
auto add = [&](td::int32 till, td::uint64 value) {
auto key = dict.integer_key(td::make_refint(till), 32, true);
vm::CellBuilder gcb;
block::tlb::t_Grams.store_integer_value(gcb, td::BigInt256(value));
dict.set_builder(key.bits(), 32, gcb);
};
for (auto limit : config.limits) {
add(limit.first, limit.second);
}
cb.store_maybe_ref(dict.get_root_cell());
auto message_outer = cb.finalize();
auto signature = init_private_key.sign(message_outer->get_hash().as_slice()).move_as_ok();
return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize();
}
td::Result<td::Ref<vm::Cell>> make_a_gift_message(const td::Ed25519::PrivateKey& private_key, td::uint32 valid_until,
td::Span<Gift> gifts) const override {
CHECK(gifts.size() <= Traits::max_gifts_size);
vm::CellBuilder cb;
TRY_RESULT(seqno, get_seqno());
TRY_RESULT(wallet_id, get_wallet_id());
if (seqno == 0) {
return td::Status::Error("Wallet is not inited yet");
}
cb.store_long(wallet_id, 32);
cb.store_long(valid_until, 32);
cb.store_long(seqno, 32);
for (auto& gift : gifts) {
td::int32 send_mode = 3;
if (gift.gramms == -1) {
send_mode += 128;
}
cb.store_long(send_mode, 8).store_ref(create_int_message(gift));
}
auto message_outer = cb.finalize();
auto signature = private_key.sign(message_outer->get_hash().as_slice()).move_as_ok();
return vm::CellBuilder().store_bytes(signature).append_cellslice(vm::load_cell_slice(message_outer)).finalize();
}
};
} // namespace ton

View File

@ -1542,6 +1542,7 @@ template <class DeserializerT>
class BenchBocDeserializer : public td::Benchmark {
public:
BenchBocDeserializer(std::string name, BenchBocDeserializerConfig config) : name_(std::move(name)), config_(config) {
td::PerfWarningTimer perf("A", 1);
fast_array_ = vm::FastCompactArray(array_size);
td::Random::Xorshift128plus rnd{123};
for (td::uint32 i = 0; i < array_size; i++) {

View File

@ -21,7 +21,9 @@
#include "Ed25519.h"
#include "block/block-auto.h"
#include "block/block.h"
#include "block/block-parse.h"
#include "fift/Fift.h"
#include "fift/words.h"
@ -38,6 +40,7 @@
#include "smc-envelope/WalletV3.h"
#include "smc-envelope/HighloadWallet.h"
#include "smc-envelope/HighloadWalletV2.h"
#include "smc-envelope/PaymentChannel.h"
#include "td/utils/base64.h"
#include "td/utils/crypto.h"
@ -489,6 +492,99 @@ TEST(Tonlib, TestGiver) {
CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == res->get_hash());
}
TEST(Tonlib, RestrictedWallet) {
//auto source_lookup = fift::create_mem_source_lookup(load_source("smartcont/new-restricted-wallet2.fif")).move_as_ok();
//source_lookup
//.write_file("/auto/restricted-wallet2-code.fif", load_source("smartcont/auto/restricted-wallet2-code.fif"))
//.ensure();
//class ZeroOsTime : public fift::OsTime {
//public:
//td::uint32 now() override {
//return 0;
//}
//};
//source_lookup.set_os_time(std::make_unique<ZeroOsTime>());
//auto priv_key = td::Ed25519::generate_private_key().move_as_ok();
//auto pub_key = priv_key.get_public_key().move_as_ok();
//auto pub_key_serialized = block::PublicKey::from_bytes(pub_key.as_octet_string()).move_as_ok().serialize(true);
//std::vector<std::string> args = {"path", pub_key_serialized, std::string("100")};
//auto fift_output = fift::mem_run_fift(std::move(source_lookup), args).move_as_ok();
//ton::RestrictedWallet::InitData init_data;
//td::uint64 x = 100 * 1000000000ull;
//init_data.key = &pub_key;
//init_data.start_at = 0;
//init_data.limits = {{-32768, x}, {92, x * 3 / 4}, {183, x * 1 / 2}, {366, x * 1 / 4}, {548, 0}};
//auto wallet = ton::RestrictedWallet::create(init_data, -1);
//ASSERT_EQ(0u, wallet->get_seqno().move_as_ok());
//CHECK(pub_key.as_octet_string() == wallet->get_public_key().move_as_ok().as_octet_string());
////LOG(ERROR) << wallet->get_balance(x, 60 * 60 * 24 * 400).move_as_ok();
//auto new_wallet_query = fift_output.source_lookup.read_file("rwallet-query.boc").move_as_ok().data;
//auto new_wallet_addr = fift_output.source_lookup.read_file("rwallet.addr").move_as_ok().data;
//auto address = wallet->get_address(-1);
////CHECK(address.addr.as_slice() == td::Slice(new_wallet_addr).substr(0, 32));
//address.bounceable = false;
//auto res = ton::GenericAccount::create_ext_message(address, wallet->get_init_state(),
//wallet->get_init_message(priv_key).move_as_ok());
//LOG(ERROR) << "-------";
//vm::load_cell_slice(res).print_rec(std::cerr);
//LOG(ERROR) << "-------";
//vm::load_cell_slice(vm::std_boc_deserialize(new_wallet_query).move_as_ok()).print_rec(std::cerr);
//CHECK(vm::std_boc_deserialize(new_wallet_query).move_as_ok()->get_hash() == res->get_hash());
//auto dest = block::StdAddress::parse("Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX").move_as_ok();
//fift_output.source_lookup.write_file("/main.fif", load_source("smartcont/wallet-v2.fif")).ensure();
//fift_output.source_lookup.write_file("rwallet.pk", priv_key.as_octet_string().as_slice()).ensure();
//fift_output = fift::mem_run_fift(
//std::move(fift_output.source_lookup),
//{"aba", "rwallet", "-C", "TESTv2", "Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX", "0", "321"})
//.move_as_ok();
//auto wallet_query = fift_output.source_lookup.read_file("wallet-query.boc").move_as_ok().data;
//ton::TestWallet::Gift gift;
//gift.destination = dest;
//gift.message = "TESTv2";
//gift.gramms = 321000000000ll;
////CHECK(priv_key.get_public_key().ok().as_octet_string() == wallet->get_public_key().ok().as_octet_string());
//auto gift_message = ton::GenericAccount::create_ext_message(
//address, {}, wallet->make_a_gift_message(priv_key, 60, {gift}).move_as_ok());
//LOG(ERROR) << "-------";
//vm::load_cell_slice(gift_message).print_rec(std::cerr);
//LOG(ERROR) << "-------";
//vm::load_cell_slice(vm::std_boc_deserialize(wallet_query).move_as_ok()).print_rec(std::cerr);
//CHECK(vm::std_boc_deserialize(wallet_query).move_as_ok()->get_hash() == gift_message->get_hash());
}
TEST(Tonlib, RestrictedWallet3) {
auto init_priv_key = td::Ed25519::generate_private_key().move_as_ok();
auto init_pub_key = init_priv_key.get_public_key().move_as_ok();
auto priv_key = td::Ed25519::generate_private_key().move_as_ok();
auto pub_key = priv_key.get_public_key().move_as_ok();
ton::RestrictedWallet::InitData init_data;
init_data.init_key = init_pub_key.as_octet_string();
init_data.main_key = pub_key.as_octet_string();
init_data.wallet_id = 123;
auto wallet = ton::RestrictedWallet::create(init_data, -1);
auto address = wallet->get_address();
td::uint64 x = 100 * 1000000000ull;
ton::RestrictedWallet::Config config;
config.start_at = 1;
config.limits = {{-32768, x}, {92, x * 3 / 4}, {183, x * 1 / 2}, {366, x * 1 / 4}, {548, 0}};
CHECK(wallet.write().send_external_message(wallet->get_init_message(init_priv_key, 10, config).move_as_ok()).success);
CHECK(wallet->get_seqno().move_as_ok() == 1);
ton::WalletInterface::Gift gift;
gift.destination = address;
gift.message = "hello";
CHECK(wallet.write().send_external_message(wallet->make_a_gift_message(priv_key, 10, {gift}).move_as_ok()).success);
CHECK(wallet->get_seqno().move_as_ok() == 2);
}
class SimpleWallet : public ton::SmartContract {
public:
SimpleWallet(State state) : SmartContract(std::move(state)) {
@ -1286,3 +1382,397 @@ TEST(Smartcont, DnsManual) {
// TODO: rethink semantic of creating an empty dictionary
do_dns_test(CheckedDns(true, true));
}
using namespace ton::pchan;
template <class T>
struct ValidateState {
T& self() {
return static_cast<T&>(*this);
}
void init(td::Ref<vm::Cell> state) {
state_ = state;
block::gen::ChanData::Record data_rec;
if (!tlb::unpack_cell(state, data_rec)) {
on_fatal_error(td::Status::Error("Expected Data"));
return;
}
if (!tlb::unpack_cell(data_rec.state, self().rec)) {
on_fatal_error(td::Status::Error("Expected StatePayout"));
return;
}
CHECK(self().rec.A.not_null());
}
T& expect_grams(td::Ref<vm::CellSlice> cs, td::uint64 expected, td::Slice name) {
if (has_fatal_error_) {
return self();
}
td::RefInt256 got;
CHECK(cs.not_null());
CHECK(block::tlb::t_Grams.as_integer_to(cs, got));
if (got->cmp(expected) != 0) {
on_error(td::Status::Error(PSLICE() << name << ": expected " << expected << ", got " << got->to_dec_string()));
}
return self();
}
template <class S>
T& expect_eq(S a, S expected, td::Slice name) {
if (has_fatal_error_) {
return self();
}
if (!(a == expected)) {
on_error(td::Status::Error(PSLICE() << name << ": expected " << expected << ", got " << a));
}
return self();
}
td::Status finish() {
if (errors_.empty()) {
return td::Status::OK();
}
std::stringstream ss;
block::gen::t_ChanData.print_ref(ss, state_);
td::StringBuilder sb;
for (auto& error : errors_) {
sb << error << "\n";
}
sb << ss.str();
return td::Status::Error(sb.as_cslice());
}
void on_fatal_error(td::Status error) {
CHECK(!has_fatal_error_);
has_fatal_error_ = true;
on_error(std::move(error));
}
void on_error(td::Status error) {
CHECK(error.is_error());
errors_.push_back(std::move(error));
}
public:
td::Ref<vm::Cell> state_;
bool has_fatal_error_{false};
std::vector<td::Status> errors_;
};
struct ValidateStatePayout : public ValidateState<ValidateStatePayout> {
ValidateStatePayout& expect_A(td::uint64 a) {
expect_grams(rec.A, a, "A");
return *this;
}
ValidateStatePayout& expect_B(td::uint64 b) {
expect_grams(rec.B, b, "B");
return *this;
}
ValidateStatePayout(td::Ref<vm::Cell> state) {
init(std::move(state));
}
block::gen::ChanState::Record_chan_state_payout rec;
};
struct ValidateStateInit : public ValidateState<ValidateStateInit> {
ValidateStateInit& expect_A(td::uint64 a) {
expect_grams(rec.A, a, "A");
return *this;
}
ValidateStateInit& expect_B(td::uint64 b) {
expect_grams(rec.B, b, "B");
return *this;
}
ValidateStateInit& expect_min_A(td::uint64 a) {
expect_grams(rec.min_A, a, "min_A");
return *this;
}
ValidateStateInit& expect_min_B(td::uint64 b) {
expect_grams(rec.min_B, b, "min_B");
return *this;
}
ValidateStateInit& expect_expire_at(td::uint32 b) {
expect_eq(rec.expire_at, b, "expire_at");
return *this;
}
ValidateStateInit& expect_signed_A(bool x) {
expect_eq(rec.signed_A, x, "signed_A");
return *this;
}
ValidateStateInit& expect_signed_B(bool x) {
expect_eq(rec.signed_B, x, "signed_B");
return *this;
}
ValidateStateInit(td::Ref<vm::Cell> state) {
init(std::move(state));
}
block::gen::ChanState::Record_chan_state_init rec;
};
struct ValidateStateClose : public ValidateState<ValidateStateClose> {
ValidateStateClose& expect_A(td::uint64 a) {
expect_grams(rec.A, a, "A");
return *this;
}
ValidateStateClose& expect_B(td::uint64 b) {
expect_grams(rec.B, b, "B");
return *this;
}
ValidateStateClose& expect_promise_A(td::uint64 a) {
expect_grams(rec.promise_A, a, "promise_A");
return *this;
}
ValidateStateClose& expect_promise_B(td::uint64 b) {
expect_grams(rec.promise_B, b, "promise_B");
return *this;
}
ValidateStateClose& expect_expire_at(td::uint32 b) {
expect_eq(rec.expire_at, b, "expire_at");
return *this;
}
ValidateStateClose& expect_signed_A(bool x) {
expect_eq(rec.signed_A, x, "signed_A");
return *this;
}
ValidateStateClose& expect_signed_B(bool x) {
expect_eq(rec.signed_B, x, "signed_B");
return *this;
}
ValidateStateClose(td::Ref<vm::Cell> state) {
init(std::move(state));
}
block::gen::ChanState::Record_chan_state_close rec;
};
// config$_ initTimeout:int exitTimeout:int a_key:int256 b_key:int256 a_addr b_addr channel_id:int256 = Config;
TEST(Smarcont, Channel) {
auto code = ton::SmartContractCode::get_code(ton::SmartContractCode::PaymentChannel);
Config config;
auto a_pkey = td::Ed25519::generate_private_key().move_as_ok();
auto b_pkey = td::Ed25519::generate_private_key().move_as_ok();
config.init_timeout = 20;
config.close_timeout = 40;
auto dest = block::StdAddress::parse("Ef9Tj6fMJP+OqhAdhKXxq36DL+HYSzCc3+9O6UNzqsgPfYFX").move_as_ok();
config.a_addr = dest;
config.b_addr = dest;
config.a_key = a_pkey.get_public_key().ok().as_octet_string();
config.b_key = b_pkey.get_public_key().ok().as_octet_string();
config.channel_id = 123;
Data data;
data.config = config.serialize();
data.state = data.init_state();
auto data_cell = data.serialize();
auto channel = ton::SmartContract::create(ton::SmartContract::State{code, data_cell});
ValidateStateInit(channel->get_state().data)
.expect_A(0)
.expect_B(0)
.expect_min_A(0)
.expect_min_B(0)
.expect_signed_A(false)
.expect_signed_B(false)
.expect_expire_at(0)
.finish()
.ensure();
enum err {
ok = 0,
wrong_a_signature = 31,
wrong_b_signature,
msg_value_too_small,
replay_protection,
no_timeout,
expected_init,
expected_close,
no_promise_signature,
wrong_channel_id
};
#define expect_code(description, expected_code, e) \
{ \
auto res = e; \
LOG_IF(FATAL, expected_code != res.code) << " res.code=" << res.code << " " << description << "\n" << #e; \
}
#define expect_ok(description, e) expect_code(description, 0, e)
expect_code("Trying to invoke a timeout while channel is empty", no_timeout,
channel.write().send_external_message(MsgTimeoutBuilder().finalize(),
ton::SmartContract::Args().set_now(1000000)));
expect_code("External init message with no signatures", replay_protection,
channel.write().send_external_message(MsgInitBuilder().channel_id(config.channel_id).finalize()));
expect_code("Internal init message with not enough value", msg_value_too_small,
channel.write().send_internal_message(
MsgInitBuilder().channel_id(config.channel_id).inc_A(1000).min_B(2000).with_a_key(&a_pkey).finalize(),
ton::SmartContract::Args().set_amount(100)));
expect_code(
"Internal init message with wrong channel_id", wrong_channel_id,
channel.write().send_internal_message(MsgInitBuilder().inc_A(1000).min_B(2000).with_a_key(&a_pkey).finalize(),
ton::SmartContract::Args().set_amount(1000)));
expect_ok("A init with (inc_A = 1000, min_A = 1, min_B = 2000)",
channel.write().send_internal_message(MsgInitBuilder()
.channel_id(config.channel_id)
.inc_A(1000)
.min_A(1)
.min_B(2000)
.with_a_key(&a_pkey)
.finalize(),
ton::SmartContract::Args().set_amount(1000)));
ValidateStateInit(channel->get_state().data)
.expect_A(1000)
.expect_B(0)
.expect_min_A(1)
.expect_min_B(2000)
.expect_signed_A(true)
.expect_signed_B(false)
.expect_expire_at(config.init_timeout)
.finish()
.ensure();
expect_code("Repeated init of A init with (inc_A = 100, min_B = 5000). Must be ignored", replay_protection,
channel.write().send_internal_message(
MsgInitBuilder().channel_id(config.channel_id).inc_A(100).min_B(5000).with_a_key(&a_pkey).finalize(),
ton::SmartContract::Args().set_amount(1000)));
expect_code(
"Trying to invoke a timeout too early", no_timeout,
channel.write().send_external_message(MsgTimeoutBuilder().finalize(), ton::SmartContract::Args().set_now(0)));
{
auto channel_copy = channel;
expect_ok("Invoke a timeout", channel_copy.write().send_external_message(MsgTimeoutBuilder().finalize(),
ton::SmartContract::Args().set_now(21)));
ValidateStatePayout(channel_copy->get_state().data).expect_A(1000).expect_B(0).finish().ensure();
}
{
auto channel_copy = channel;
expect_ok("B init with inc_B < min_B. Leads to immediate payout",
channel_copy.write().send_internal_message(
MsgInitBuilder().channel_id(config.channel_id).inc_B(1500).with_b_key(&b_pkey).finalize(),
ton::SmartContract::Args().set_amount(1500)));
ValidateStatePayout(channel_copy->get_state().data).expect_A(1000).expect_B(1500).finish().ensure();
}
expect_ok("B init with (inc_B = 2000, min_A = 1, min_A = 1000)",
channel.write().send_internal_message(
MsgInitBuilder().channel_id(config.channel_id).inc_B(2000).min_A(1000).with_b_key(&b_pkey).finalize(),
ton::SmartContract::Args().set_amount(2000)));
ValidateStateClose(channel->get_state().data)
.expect_A(1000)
.expect_B(2000)
.expect_promise_A(0)
.expect_promise_B(0)
.expect_signed_A(false)
.expect_signed_B(false)
.expect_expire_at(0)
.finish()
.ensure();
{
auto channel_copy = channel;
expect_ok("A&B send Promise(1000000, 1000000 + 10) signed by nobody",
channel_copy.write().send_external_message(MsgCloseBuilder()
.signed_promise(SignedPromiseBuilder()
.promise_A(1000000)
.promise_B(1000000 + 10)
.channel_id(config.channel_id)
.finalize())
.with_a_key(&a_pkey)
.with_b_key(&b_pkey)
.finalize(),
ton::SmartContract::Args().set_now(21)));
ValidateStatePayout(channel_copy->get_state().data).expect_A(1000 + 10).expect_B(2000 - 10).finish().ensure();
}
{
auto channel_copy = channel;
expect_ok("A&B send Promise(1000000, 1000000 + 10) signed by A",
channel_copy.write().send_external_message(MsgCloseBuilder()
.signed_promise(SignedPromiseBuilder()
.promise_A(1000000)
.promise_B(1000000 + 10)
.with_key(&a_pkey)
.channel_id(config.channel_id)
.finalize())
.with_a_key(&a_pkey)
.with_b_key(&b_pkey)
.finalize(),
ton::SmartContract::Args().set_now(21)));
ValidateStatePayout(channel_copy->get_state().data).expect_A(1000 + 10).expect_B(2000 - 10).finish().ensure();
}
expect_code(
"A sends Promise(1000000, 0) signed by A", wrong_b_signature,
channel.write().send_external_message(
MsgCloseBuilder()
.signed_promise(
SignedPromiseBuilder().promise_A(1000000).with_key(&a_pkey).channel_id(config.channel_id).finalize())
.with_a_key(&a_pkey)
.finalize(),
ton::SmartContract::Args().set_now(21)));
expect_code(
"B sends Promise(1000000, 0) signed by B", wrong_a_signature,
channel.write().send_external_message(
MsgCloseBuilder()
.signed_promise(
SignedPromiseBuilder().promise_A(1000000).with_key(&b_pkey).channel_id(config.channel_id).finalize())
.with_b_key(&b_pkey)
.finalize(),
ton::SmartContract::Args().set_now(21)));
expect_code("B sends Promise(1000000, 0) signed by A with wrong channel_id", wrong_channel_id,
channel.write().send_external_message(MsgCloseBuilder()
.signed_promise(SignedPromiseBuilder()
.promise_A(1000000)
.with_key(&a_pkey)
.channel_id(config.channel_id + 1)
.finalize())
.with_b_key(&b_pkey)
.finalize(),
ton::SmartContract::Args().set_now(21)));
expect_code(
"B sends unsigned Promise(1000000, 0)", no_promise_signature,
channel.write().send_external_message(
MsgCloseBuilder()
.signed_promise(SignedPromiseBuilder().promise_A(1000000).channel_id(config.channel_id).finalize())
.with_b_key(&b_pkey)
.finalize(),
ton::SmartContract::Args().set_now(21)));
expect_ok(
"B sends Promise(1000000, 0) signed by A",
channel.write().send_external_message(
MsgCloseBuilder()
.signed_promise(
SignedPromiseBuilder().promise_A(1000000).with_key(&a_pkey).channel_id(config.channel_id).finalize())
.with_b_key(&b_pkey)
.finalize(),
ton::SmartContract::Args().set_now(21)));
ValidateStateClose(channel->get_state().data)
.expect_A(1000)
.expect_B(2000)
.expect_promise_A(1000000)
.expect_promise_B(0)
.expect_signed_A(false)
.expect_signed_B(true)
.expect_expire_at(21 + config.close_timeout)
.finish()
.ensure();
expect_ok("B sends Promise(0, 1000000 + 10) signed by A",
channel.write().send_external_message(MsgCloseBuilder()
.signed_promise(SignedPromiseBuilder()
.promise_B(1000000 + 10)
.with_key(&b_pkey)
.channel_id(config.channel_id)
.finalize())
.with_a_key(&a_pkey)
.finalize(),
ton::SmartContract::Args().set_now(21)));
ValidateStatePayout(channel->get_state().data).expect_A(1000 + 10).expect_B(2000 - 10).finish().ensure();
#undef expect_ok
#undef expect_code
}

View File

@ -19,6 +19,7 @@
#include "vm/cells/CellSlice.h"
#include "vm/excno.hpp"
#include "td/utils/bits.h"
#include "td/utils/misc.h"
namespace vm {
@ -719,6 +720,10 @@ bool CellSlice::prefetch_bytes(unsigned char* buffer, unsigned bytes) const {
}
}
bool CellSlice::fetch_bytes(td::MutableSlice slice) {
return fetch_bytes(slice.ubegin(), td::narrow_cast<unsigned>(slice.size()));
}
bool CellSlice::fetch_bytes(unsigned char* buffer, unsigned bytes) {
if (prefetch_bytes(buffer, bytes)) {
advance(bytes * 8);
@ -728,6 +733,10 @@ bool CellSlice::fetch_bytes(unsigned char* buffer, unsigned bytes) {
}
}
bool CellSlice::prefetch_bytes(td::MutableSlice slice) const {
return prefetch_bytes(slice.ubegin(), td::narrow_cast<unsigned>(slice.size()));
}
Ref<Cell> CellSlice::prefetch_ref(unsigned offset) const {
if (offset < size_refs()) {
auto ref_id = refs_st + offset;

View File

@ -218,7 +218,9 @@ class CellSlice : public td::CntObject {
return prefetch_bits_to(buffer.bits(), n);
}
bool fetch_bytes(unsigned char* buffer, unsigned bytes);
bool fetch_bytes(td::MutableSlice slice);
bool prefetch_bytes(unsigned char* buffer, unsigned bytes) const;
bool prefetch_bytes(td::MutableSlice slice) const;
td::BitSlice as_bitslice() const {
return prefetch_bits(size());
}

View File

@ -148,6 +148,11 @@ class MerkleProofCombineFast {
MerkleProofCombineFast(Ref<Cell> a, Ref<Cell> b) : a_(std::move(a)), b_(std::move(b)) {
}
td::Result<Ref<Cell>> run() {
if (a_.is_null()) {
return b_;
} else if (b_.is_null()) {
return a_;
}
TRY_RESULT_ASSIGN(a_, unpack_proof(a_));
TRY_RESULT_ASSIGN(b_, unpack_proof(b_));
TRY_RESULT(res, run_raw());
@ -204,6 +209,11 @@ class MerkleProofCombine {
MerkleProofCombine(Ref<Cell> a, Ref<Cell> b) : a_(std::move(a)), b_(std::move(b)) {
}
td::Result<Ref<Cell>> run() {
if (a_.is_null()) {
return b_;
} else if (b_.is_null()) {
return a_;
}
TRY_RESULT_ASSIGN(a_, unpack_proof(a_));
TRY_RESULT_ASSIGN(b_, unpack_proof(b_));
TRY_RESULT(res, run_raw());
@ -323,6 +333,10 @@ Ref<Cell> MerkleProof::combine(Ref<Cell> a, Ref<Cell> b) {
return res.move_as_ok();
}
td::Result<Ref<Cell>> MerkleProof::combine_status(Ref<Cell> a, Ref<Cell> b) {
return MerkleProofCombine(std::move(a), std::move(b)).run();
}
Ref<Cell> MerkleProof::combine_fast(Ref<Cell> a, Ref<Cell> b) {
auto res = MerkleProofCombineFast(std::move(a), std::move(b)).run();
if (res.is_error()) {
@ -331,6 +345,10 @@ Ref<Cell> MerkleProof::combine_fast(Ref<Cell> a, Ref<Cell> b) {
return res.move_as_ok();
}
td::Result<Ref<Cell>> MerkleProof::combine_fast_status(Ref<Cell> a, Ref<Cell> b) {
return MerkleProofCombineFast(std::move(a), std::move(b)).run();
}
Ref<Cell> MerkleProof::combine_raw(Ref<Cell> a, Ref<Cell> b) {
auto res = MerkleProofCombine(std::move(a), std::move(b)).run_raw();
if (res.is_error()) {

View File

@ -38,7 +38,9 @@ class MerkleProof {
static Ref<Cell> virtualize(Ref<Cell> cell, int virtualization);
static Ref<Cell> combine(Ref<Cell> a, Ref<Cell> b);
static td::Result<Ref<Cell>> combine_status(Ref<Cell> a, Ref<Cell> b);
static Ref<Cell> combine_fast(Ref<Cell> a, Ref<Cell> b);
static td::Result<Ref<Cell>> combine_fast_status(Ref<Cell> a, Ref<Cell> b);
// works with upwrapped proofs
// works fine with cell of non-zero level, but this is not supported (yet?) in MerkeProof special cell

View File

@ -255,15 +255,15 @@ TonDbTransactionImpl::TonDbTransactionImpl(std::shared_ptr<KeyValue> kv) : kv_(s
}
void TonDbTransactionImpl::begin() {
kv_->begin_transaction();
kv_->begin_write_batch();
generation_++;
}
void TonDbTransactionImpl::commit() {
kv_->commit_transaction();
kv_->commit_write_batch();
reader_.reset(kv_->snapshot().release());
}
void TonDbTransactionImpl::abort() {
kv_->abort_transaction();
kv_->abort_write_batch();
}
void TonDbTransactionImpl::clear_cache() {
contracts_ = {};

View File

@ -77,6 +77,13 @@ class VmError {
long long get_arg() const {
return arg;
}
td::Status as_status() const {
return td::Status::Error(td::Slice{get_msg()});
}
template <typename T>
td::Status as_status(T pfx) const {
return td::Status::Error(PSLICE() << pfx << get_msg());
}
};
struct VmNoGas {
@ -90,6 +97,13 @@ struct VmNoGas {
operator VmError() const {
return VmError{Excno::out_of_gas, "out of gas"};
}
td::Status as_status() const {
return td::Status::Error(td::Slice{get_msg()});
}
template <typename T>
td::Status as_status(T pfx) const {
return td::Status::Error(PSLICE() << pfx << get_msg());
}
};
struct VmVirtError {
@ -106,6 +120,13 @@ struct VmVirtError {
operator VmError() const {
return VmError{Excno::virt_err, "prunned branch", virtualization};
}
td::Status as_status() const {
return td::Status::Error(td::Slice{get_msg()});
}
template <typename T>
td::Status as_status(T pfx) const {
return td::Status::Error(PSLICE() << pfx << get_msg());
}
};
struct VmFatal {};
@ -114,12 +135,12 @@ template <class F>
auto try_f(F&& f) noexcept -> decltype(f()) {
try {
return f();
} catch (vm::VmError error) {
return td::Status::Error(PSLICE() << "Got a vm exception: " << error.get_msg());
} catch (vm::VmVirtError error) {
return td::Status::Error(PSLICE() << "Got a vm virtualization exception: " << error.get_msg());
} catch (vm::VmNoGas error) {
return td::Status::Error(PSLICE() << "Got a vm no gas exception: " << error.get_msg());
} catch (vm::VmError& error) {
return error.as_status("Got a vm exception: ");
} catch (vm::VmVirtError& error) {
return error.as_status("Got a vm virtualization exception: ");
} catch (vm::VmNoGas& error) {
return error.as_status("Got a vm no gas exception: ");
}
}

File diff suppressed because it is too large Load Diff

View File

@ -33,6 +33,7 @@
#include "vm/cells.h"
#include "vm/stack.hpp"
#include "block/block.h"
#include "block/mc-config.h"
#include "td/utils/filesystem.h"
using td::Ref;
@ -89,6 +90,9 @@ class TestNode : public td::actor::Actor {
std::unique_ptr<ton::adnl::AdnlExtClient::Callback> make_callback();
using creator_stats_func_t =
std::function<bool(const td::Bits256&, const block::DiscountedCounter&, const block::DiscountedCounter&)>;
struct TransId {
ton::Bits256 acc_addr;
ton::LogicalTime trans_lt;
@ -98,6 +102,71 @@ class TestNode : public td::actor::Actor {
}
};
struct BlockHdrInfo {
ton::BlockIdExt blk_id;
Ref<vm::Cell> proof, virt_blk_root;
int mode;
BlockHdrInfo() : mode(-1) {
}
BlockHdrInfo(const ton::BlockIdExt blk_id_, Ref<vm::Cell> proof_, Ref<vm::Cell> vroot_, int mode_)
: blk_id(blk_id_), proof(std::move(proof_)), virt_blk_root(std::move(vroot_)), mode(mode_) {
}
};
struct ConfigInfo {
std::unique_ptr<block::Config> config;
Ref<vm::Cell> state_proof, config_proof;
ConfigInfo() = default;
ConfigInfo(std::unique_ptr<block::Config> config_, Ref<vm::Cell> state_proof_, Ref<vm::Cell> config_proof_)
: config(std::move(config_)), state_proof(std::move(state_proof_)), config_proof(std::move(config_proof_)) {
}
};
struct CreatorStatsRes {
int mode;
bool complete{false};
td::Bits256 last_key;
Ref<vm::Cell> state_proof, data_proof;
CreatorStatsRes(int mode_ = 0) : mode(mode_) {
last_key.set_zero();
}
CreatorStatsRes(int mode_, const td::Bits256& key_, Ref<vm::Cell> st_proof_ = {}, Ref<vm::Cell> dproof_ = {})
: mode(mode_), last_key(key_), state_proof(std::move(st_proof_)), data_proof(std::move(dproof_)) {
}
};
struct ValidatorLoadInfo {
ton::BlockIdExt blk_id;
Ref<vm::Cell> state_proof, data_proof, virt_root;
std::unique_ptr<block::Config> config;
ton::UnixTime block_created_at{0};
ton::UnixTime valid_since{0};
ton::LogicalTime end_lt{0};
std::unique_ptr<block::ValidatorSet> vset;
std::map<ton::Bits256, int> vset_map;
std::pair<td::int64, td::int64> created_total;
std::vector<std::pair<td::int64, td::int64>> created;
ValidatorLoadInfo(ton::BlockIdExt blkid, Ref<vm::Cell> root, Ref<vm::Cell> root2,
std::unique_ptr<block::Config> cfg = {})
: blk_id(blkid)
, state_proof(std::move(root))
, data_proof(std::move(root2))
, config(std::move(cfg))
, valid_since(0) {
}
bool unpack_vset();
bool store_record(const td::Bits256& key, const block::DiscountedCounter& mc_cnt,
const block::DiscountedCounter& shard_cnt);
bool has_data() const {
return blk_id.is_masterchain_ext() && state_proof.not_null() && data_proof.not_null() && config;
}
td::Status check_header_proof(ton::UnixTime* save_utime = nullptr, ton::LogicalTime* save_lt = nullptr) const;
td::Result<Ref<vm::Cell>> build_proof(int idx, td::Bits256* save_pubkey = nullptr) const;
td::Result<Ref<vm::Cell>> build_producer_info(int idx, td::Bits256* save_pubkey = nullptr) const;
td::Status init_check_proofs();
static td::Result<std::unique_ptr<ValidatorLoadInfo>> preinit_from_producer_info(Ref<vm::Cell> prod_info);
};
void run_init_queries();
char cur() const {
return *parse_ptr_;
@ -144,18 +213,24 @@ class TestNode : public td::actor::Actor {
bool show_dns_record(std::ostream& os, int cat, Ref<vm::Cell> value, bool raw_dump);
bool get_all_shards(std::string filename = "", bool use_last = true, ton::BlockIdExt blkid = {});
void got_all_shards(ton::BlockIdExt blk, td::BufferSlice proof, td::BufferSlice data, std::string filename);
bool get_config_params(ton::BlockIdExt blkid, td::Promise<td::Unit> do_after, int mode = 0, std::string filename = "",
std::vector<int> params = {});
void got_config_params(ton::BlockIdExt req_blkid, ton::BlockIdExt blkid, td::BufferSlice state_proof,
td::BufferSlice cfg_proof, int mode, std::string filename, std::vector<int> params,
td::Promise<td::Unit> do_after);
bool parse_get_config_params(ton::BlockIdExt blkid, int mode = 0, std::string filename = "",
std::vector<int> params = {});
bool get_config_params(ton::BlockIdExt blkid, td::Promise<std::unique_ptr<block::Config>> promise, int mode = 0,
std::string filename = "", std::vector<int> params = {});
bool get_config_params_ext(ton::BlockIdExt blkid, td::Promise<ConfigInfo> promise, int mode = 0,
std::string filename = "", std::vector<int> params = {});
void got_config_params(ton::BlockIdExt req_blkid, int mode, std::string filename, std::vector<int> params,
td::Result<td::BufferSlice> R, td::Promise<ConfigInfo> promise);
bool get_block(ton::BlockIdExt blk, bool dump = false);
void got_block(ton::BlockIdExt blkid, td::BufferSlice data, bool dump);
bool get_state(ton::BlockIdExt blk, bool dump = false);
void got_state(ton::BlockIdExt blkid, ton::RootHash root_hash, ton::FileHash file_hash, td::BufferSlice data,
bool dump);
bool get_block_header(ton::BlockIdExt blk, int mode);
bool lookup_block(ton::ShardIdFull shard, int mode, td::uint64 arg);
bool get_show_block_header(ton::BlockIdExt blk, int mode);
bool get_block_header(ton::BlockIdExt blk, int mode, td::Promise<BlockHdrInfo> promise);
bool lookup_show_block(ton::ShardIdFull shard, int mode, td::uint64 arg);
bool lookup_block(ton::ShardIdFull shard, int mode, td::uint64 arg, td::Promise<BlockHdrInfo>);
void got_block_header_raw(td::BufferSlice res, td::Promise<BlockHdrInfo> promise, ton::BlockIdExt req_blkid = {});
void got_block_header(ton::BlockIdExt blkid, td::BufferSlice data, int mode);
bool show_block_header(ton::BlockIdExt blkid, Ref<vm::Cell> root, int mode);
bool show_state_header(ton::BlockIdExt blkid, Ref<vm::Cell> root, int mode);
@ -177,9 +252,28 @@ class TestNode : public td::actor::Actor {
void got_block_proof(ton::BlockIdExt from, ton::BlockIdExt to, int mode, td::BufferSlice res);
bool get_creator_stats(ton::BlockIdExt blkid, int mode, unsigned req_count, ton::Bits256 start_after,
ton::UnixTime min_utime);
void got_creator_stats(ton::BlockIdExt req_blkid, ton::BlockIdExt blkid, int req_mode, int mode,
td::Bits256 start_after, ton::UnixTime min_utime, td::BufferSlice state_proof,
td::BufferSlice data_proof, int count, int req_count, bool complete);
bool get_creator_stats(ton::BlockIdExt blkid, int mode, unsigned req_count, ton::Bits256 start_after,
ton::UnixTime min_utime, creator_stats_func_t func, td::Promise<td::Bits256> promise);
bool get_creator_stats(ton::BlockIdExt blkid, unsigned req_count, ton::UnixTime min_utime, creator_stats_func_t func,
std::unique_ptr<CreatorStatsRes> state, td::Promise<std::unique_ptr<CreatorStatsRes>> promise);
void got_creator_stats(ton::BlockIdExt req_blkid, ton::BlockIdExt blkid, int mode, ton::UnixTime min_utime,
td::BufferSlice state_proof, td::BufferSlice data_proof, int count, int req_count,
bool complete, creator_stats_func_t func, std::unique_ptr<CreatorStatsRes> state,
td::Promise<std::unique_ptr<CreatorStatsRes>> promise);
bool check_validator_load(int start_time, int end_time, int mode = 0, std::string file_pfx = "");
void continue_check_validator_load(ton::BlockIdExt blkid1, Ref<vm::Cell> root1, ton::BlockIdExt blkid2,
Ref<vm::Cell> root2, int mode = 0, std::string file_pfx = "");
void continue_check_validator_load2(std::unique_ptr<ValidatorLoadInfo> info1,
std::unique_ptr<ValidatorLoadInfo> info2, int mode = 0,
std::string file_pfx = "");
void continue_check_validator_load3(std::unique_ptr<ValidatorLoadInfo> info1,
std::unique_ptr<ValidatorLoadInfo> info2, int mode = 0,
std::string file_pfx = "");
td::Status write_val_create_proof(ValidatorLoadInfo& info1, ValidatorLoadInfo& info2, int idx, bool severe,
std::string file_pfx, int cnt);
bool load_creator_stats(std::unique_ptr<ValidatorLoadInfo> load_to,
td::Promise<std::unique_ptr<ValidatorLoadInfo>> promise, bool need_proofs);
td::Status check_validator_load_proof(std::string filename);
bool cache_cell(Ref<vm::Cell> cell);
bool list_cached_cells() const;
bool dump_cached_cell(td::Slice hash_pfx, td::Slice type_name = {});
@ -222,6 +316,17 @@ class TestNode : public td::actor::Actor {
bool show_new_blkids(bool all = false);
bool complete_blkid(ton::BlockId partial_blkid, ton::BlockIdExt& complete_blkid) const;
td::Promise<td::Unit> trivial_promise();
template <typename T>
td::Promise<T> trivial_promise_of() {
return td::PromiseCreator::lambda([Self = actor_id(this)](td::Result<T> res) {
if (res.is_error()) {
LOG(ERROR) << "error: " << res.move_as_error();
}
});
}
static ton::UnixTime now() {
return static_cast<td::uint32>(td::Clocks::system());
}
static const tlb::TypenameLookup& get_tlb_dict();
public:
@ -292,7 +397,8 @@ class TestNode : public td::actor::Actor {
//td::actor::SchedulerContext::get()->stop();
}
void got_result();
void got_result(td::Result<td::BufferSlice> R, td::Promise<td::BufferSlice> promise);
void after_got_result(bool ok);
bool envelope_send_query(td::BufferSlice query, td::Promise<td::BufferSlice> promise);
void parse_line(td::BufferSlice data);

View File

@ -44,8 +44,24 @@ class GetArg<R (C::*)(Arg) const> {
using type = Arg;
};
template <typename T>
struct GetRet : public GetRet<decltype(&T::operator())> {};
template <class C, class R, class... Arg>
class GetRet<R (C::*)(Arg...)> {
public:
using type = R;
};
template <class C, class R, class... Arg>
class GetRet<R (C::*)(Arg...) const> {
public:
using type = R;
};
template <class T>
using get_arg_t = std::decay_t<typename GetArg<T>::type>;
template <class T>
using get_ret_t = std::decay_t<typename GetRet<T>::type>;
template <class T>
struct DropResult {
@ -131,6 +147,7 @@ constexpr bool is_promise_interface_ptr() {
template <class ValueT, class FunctionT>
class LambdaPromise : public PromiseInterface<ValueT> {
public:
using ArgT = ValueT;
void set_value(ValueT &&value) override {
CHECK(has_lambda_.get());
do_ok(std::move(value));
@ -288,12 +305,6 @@ class Promise {
std::unique_ptr<PromiseInterface<T>> promise_;
};
template <class F>
auto make_promise(F &&f) {
using ValueT = detail::drop_result_t<detail::get_arg_t<F>>;
return Promise<ValueT>(promise_interface_ptr(std::forward<F>(f)));
}
namespace detail {
template <class... ArgsT>
class JoinPromise : public PromiseInterface<Unit> {
@ -331,6 +342,16 @@ class PromiseCreator {
}
};
template <class F>
auto make_promise(F &&f) {
using ValueT = typename decltype(PromiseCreator::lambda(std::move(f)))::ArgT;
return Promise<ValueT>(PromiseCreator::lambda(std::move(f)));
}
template <class T>
auto make_promise(Promise<T> &&f) {
return std::move(f);
}
template <class T = Unit>
class SafePromise {
public:
@ -356,4 +377,145 @@ class SafePromise {
Promise<T> promise_;
Result<T> result_;
};
template <class PromiseT, typename... ArgsT>
class PromiseMerger;
template <class F>
struct SplitPromise {
using PromiseT = decltype(make_promise(std::declval<F>()));
using ArgT = typename PromiseT::ArgT;
template <class S, class T>
static std::pair<Promise<S>, Promise<T>> split(std::pair<S, T>);
template <class... ArgsT>
static std::tuple<Promise<ArgsT>...> split(std::tuple<ArgsT...>);
using SplittedT = decltype(split(std::declval<ArgT>()));
template <class S, class T>
static PromiseMerger<PromiseT, S, T> merger(std::pair<S, T>);
template <class... ArgsT>
static PromiseMerger<PromiseT, ArgsT...> merger(std::tuple<ArgsT...>);
using MergerT = decltype(merger(std::declval<ArgT>()));
};
template <class PromiseT, typename... ArgsT>
class PromiseMerger : public std::enable_shared_from_this<PromiseMerger<PromiseT, ArgsT...>> {
public:
std::tuple<Result<ArgsT>...> args_;
PromiseT promise_;
PromiseMerger(PromiseT promise) : promise_(std::move(promise)) {
}
~PromiseMerger() {
td::Status status;
tuple_for_each(args_, [&status](auto &&arg) {
if (status.is_error()) {
return;
}
if (arg.is_error()) {
status = arg.move_as_error();
}
});
if (status.is_error()) {
promise_.set_error(std::move(status));
return;
}
call_tuple([this](auto &&... args) { promise_.set_value({args.move_as_ok()...}); }, std::move(args_));
}
template <class T>
Promise<typename T::ValueT> make_promise(T &arg) {
return [&arg, self = this->shared_from_this()](auto res) { arg = std::move(res); };
}
template <class R>
auto split() {
return call_tuple([this](auto &&... arg) { return R{this->make_promise(arg)...}; }, std::move(args_));
}
};
template <class F>
auto split_promise(F &&f) {
auto merger = std::make_shared<typename SplitPromise<F>::MergerT>(std::move(f));
return merger->template split<typename SplitPromise<F>::SplittedT>();
}
template <class T>
struct PromiseFuture {
Result<Promise<T>> promise_;
Result<T> result_;
~PromiseFuture() {
if (promise_.is_ok()) {
promise_.move_as_ok().set_result(std::move(result_));
} else {
LOG(ERROR) << "Lost PromiseFuture";
}
}
};
template <class T>
struct Future;
template <class T>
std::pair<Promise<T>, Future<T>> make_promise_future();
template <class T>
struct Future {
Promise<Promise<T>> promise_;
Future(Promise<Promise<T>> promise) : promise_(std::move(promise)) {
}
void finish(Promise<T> promise) {
promise_.set_value(std::move(promise));
}
template <class F>
auto map(F &&f) {
using R = detail::drop_result_t<decltype(f(std::declval<T>()))>;
auto pf = make_promise_future<R>();
promise_.set_value([p = std::move(pf.first), f = std::move(f)](Result<T> res) mutable {
TRY_RESULT_PROMISE(p, x, std::move(res));
p.set_result(f(std::move(x)));
});
return std::move(pf.second);
}
template <class F>
auto fmap(F &&f) {
return flatten(map(std::move(f)));
}
template <class X>
static Future<X> flatten(Future<Future<X>> ff) {
auto pf = make_promise_future<X>();
ff.promise_.set_value([p = std::move(pf.first)](Result<Future<X>> r_f) mutable {
TRY_RESULT_PROMISE(p, f, std::move(r_f));
// Promise<X> p
// Future<X> f
f.promise_.set_value(std::move(p));
});
return std::move(pf.second);
}
};
template <class T>
Future<T> make_future(T &&value) {
return Future<T>([value = std::move(value)](Result<Promise<T>> r_promise) mutable {
if (r_promise.is_ok()) {
r_promise.move_as_ok().set_value(std::move(value));
} else {
LOG(ERROR) << "Lost future";
}
});
}
template <class T>
std::pair<Promise<T>, Future<T>> make_promise_future() {
auto pf = std::make_shared<PromiseFuture<T>>();
Future<T> future([pf](Result<Promise<T>> res) mutable { pf->promise_ = std::move(res); });
Promise<T> promise = [pf = std::move(pf)](Result<T> res) mutable { pf->result_ = std::move(res); };
return std::make_pair(std::move(promise), std::move(future));
}
} // namespace td

View File

@ -100,6 +100,28 @@ void send_closure(ActorIdT &&actor_id, FunctionT function, ArgsT &&... args) {
#endif
template <class ActorIdT, class FunctionT, class... ArgsT, class FunctionClassT = member_function_class_t<FunctionT>,
size_t argument_count = member_function_argument_count<FunctionT>(),
std::enable_if_t<argument_count == sizeof...(ArgsT), bool> with_promise = false>
auto future_send_closure(ActorIdT &&actor_id, FunctionT function, ArgsT &&... args) {
using R = ::td::detail::get_ret_t<std::decay_t<FunctionT>>;
auto pf = make_promise_future<R>();
send_closure(std::forward<ActorIdT>(actor_id), std::move(function), std::forward<ArgsT>(args)...,
std::move(pf.first));
return std::move(pf.second);
}
template <class R, class ActorIdT, class FunctionT, class... ArgsT,
class FunctionClassT = member_function_class_t<FunctionT>,
size_t argument_count = member_function_argument_count<FunctionT>(),
std::enable_if_t<argument_count != sizeof...(ArgsT), bool> with_promise = true>
Future<R> future_send_closure(ActorIdT &&actor_id, FunctionT function, ArgsT &&... args) {
auto pf = make_promise_future<R>();
send_closure(std::forward<ActorIdT>(actor_id), std::move(function), std::forward<ArgsT>(args)...,
std::move(pf.first));
return std::move(pf.second);
}
template <typename ActorIdT, typename FunctionT, typename... ArgsT>
bool send_closure_bool(ActorIdT &&actor_id, FunctionT function, ArgsT &&... args) {
send_closure(std::forward<ActorIdT>(actor_id), function, std::forward<ArgsT>(args)...);

View File

@ -63,6 +63,36 @@ class ActorSignals {
using core::Actor;
using core::SchedulerContext;
using core::SchedulerId;
using core::set_debug;
struct Debug {
public:
Debug() = default;
Debug(std::shared_ptr<core::SchedulerGroupInfo> group_info) : group_info_(std::move(group_info)) {
}
template <class F>
void for_each(F &&f) {
for (auto &scheduler : group_info_->schedulers) {
f(scheduler.io_worker->debug);
for (auto &cpu : scheduler.cpu_workers) {
f(cpu->debug);
}
}
}
void dump() {
for_each([](core::Debug &debug) {
core::DebugInfo info;
debug.read(info);
if (info.is_active) {
LOG(ERROR) << info.name << " " << td::format::as_time(Time::now() - info.start_at);
}
});
}
private:
std::shared_ptr<core::SchedulerGroupInfo> group_info_;
};
class Scheduler {
public:
@ -110,6 +140,10 @@ class Scheduler {
}
}
Debug get_debug() {
return Debug{group_info_};
}
bool run() {
start();
while (schedulers_[0]->run(10)) {

View File

@ -32,6 +32,7 @@ void CpuWorker::run() {
MpmcWaiter::Slot slot;
waiter_.init_slot(slot, thread_id);
auto &debug = dispatcher.get_debug();
while (true) {
SchedulerMessage message;
if (try_pop(message, thread_id)) {
@ -39,6 +40,7 @@ void CpuWorker::run() {
if (!message) {
return;
}
auto lock = debug.start(message->get_name());
ActorExecutor executor(*message, dispatcher, ActorExecutor::Options().with_from_queue());
} else {
waiter_.wait(slot);

View File

@ -19,6 +19,7 @@
#include "td/actor/core/IoWorker.h"
#include "td/actor/core/ActorExecutor.h"
#include "td/actor/core/Scheduler.h"
namespace td {
namespace actor {
@ -42,6 +43,7 @@ bool IoWorker::run_once(double timeout) {
auto &poll = SchedulerContext::get()->get_poll();
#endif
auto &heap = SchedulerContext::get()->get_heap();
auto &debug = SchedulerContext::get()->get_debug();
auto now = Time::now(); // update Time::now_cached()
while (!heap.empty() && heap.top_key() <= now) {
@ -49,6 +51,7 @@ bool IoWorker::run_once(double timeout) {
auto *actor_info = ActorInfo::from_heap_node(heap_node);
auto id = actor_info->unpin();
auto lock = debug.start(actor_info->get_name());
ActorExecutor executor(*actor_info, dispatcher, ActorExecutor::Options().with_has_poll(true));
if (executor.can_send_immediate()) {
executor.send_immediate(ActorSignals::one(ActorSignals::Alarm));
@ -68,6 +71,7 @@ bool IoWorker::run_once(double timeout) {
dispatcher.set_alarm_timestamp(message);
continue;
}
auto lock = debug.start(message->get_name());
ActorExecutor executor(*message, dispatcher, ActorExecutor::Options().with_from_queue().with_has_poll(true));
}
queue_.reader_flush();

View File

@ -25,6 +25,15 @@ namespace td {
namespace actor {
namespace core {
std::atomic<bool> debug;
void set_debug(bool flag) {
debug = flag;
}
bool need_debug() {
return debug.load(std::memory_order_relaxed);
}
Scheduler::Scheduler(std::shared_ptr<SchedulerGroupInfo> scheduler_group_info, SchedulerId id, size_t cpu_threads_count)
: scheduler_group_info_(std::move(scheduler_group_info)), cpu_threads_(cpu_threads_count) {
scheduler_group_info_->active_scheduler_count++;
@ -128,13 +137,14 @@ void Scheduler::do_stop() {
}
Scheduler::ContextImpl::ContextImpl(ActorInfoCreator *creator, SchedulerId scheduler_id, CpuWorkerId cpu_worker_id,
SchedulerGroupInfo *scheduler_group, Poll *poll, KHeap<double> *heap)
SchedulerGroupInfo *scheduler_group, Poll *poll, KHeap<double> *heap, Debug *debug)
: creator_(creator)
, scheduler_id_(scheduler_id)
, cpu_worker_id_(cpu_worker_id)
, scheduler_group_(scheduler_group)
, poll_(poll)
, heap_(heap) {
, heap_(heap)
, debug_(debug) {
}
SchedulerId Scheduler::ContextImpl::get_scheduler_id() const {
@ -184,6 +194,9 @@ KHeap<double> &Scheduler::ContextImpl::get_heap() {
CHECK(has_heap());
return *heap_;
}
Debug &Scheduler::ContextImpl::get_debug() {
return *debug_;
}
void Scheduler::ContextImpl::set_alarm_timestamp(const ActorInfoPtr &actor_info_ptr) {
// Ideas for optimization

View File

@ -31,6 +31,7 @@
#include "td/actor/core/SchedulerId.h"
#include "td/actor/core/SchedulerMessage.h"
#include "td/utils/AtomicRead.h"
#include "td/utils/Closure.h"
#include "td/utils/common.h"
#include "td/utils/format.h"
@ -65,6 +66,54 @@ namespace actor {
namespace core {
class IoWorker;
struct DebugInfo {
bool is_active{false};
double start_at{0};
static constexpr size_t name_size{32};
char name[name_size] = {};
void set_name(td::Slice from) {
from.truncate(name_size - 1);
std::memcpy(name, from.data(), from.size());
name[from.size()] = 0;
}
};
void set_debug(bool flag);
bool need_debug();
struct Debug {
public:
bool is_on() const {
return need_debug();
}
struct Destructor {
void operator()(Debug *info) {
info->info_.lock().value().is_active = false;
}
};
void read(DebugInfo &info) {
info_.read(info);
}
std::unique_ptr<Debug, Destructor> start(td::Slice name) {
if (!is_on()) {
return {};
}
{
auto lock = info_.lock();
auto &value = lock.value();
value.is_active = true;
value.start_at = Time::now();
value.set_name(name);
}
return std::unique_ptr<Debug, Destructor>(this);
}
private:
AtomicRead<DebugInfo> info_;
};
struct WorkerInfo {
enum class Type { Io, Cpu } type{Type::Io};
WorkerInfo() = default;
@ -73,6 +122,7 @@ struct WorkerInfo {
}
ActorInfoCreator actor_info_creator;
CpuWorkerId cpu_worker_id;
Debug debug;
};
template <class T>
@ -195,7 +245,7 @@ class Scheduler {
class ContextImpl : public SchedulerContext {
public:
ContextImpl(ActorInfoCreator *creator, SchedulerId scheduler_id, CpuWorkerId cpu_worker_id,
SchedulerGroupInfo *scheduler_group, Poll *poll, KHeap<double> *heap);
SchedulerGroupInfo *scheduler_group, Poll *poll, KHeap<double> *heap, Debug *debug);
SchedulerId get_scheduler_id() const override;
void add_to_queue(ActorInfoPtr actor_info_ptr, SchedulerId scheduler_id, bool need_poll) override;
@ -208,6 +258,8 @@ class Scheduler {
bool has_heap() override;
KHeap<double> &get_heap() override;
Debug &get_debug() override;
void set_alarm_timestamp(const ActorInfoPtr &actor_info_ptr) override;
bool is_stop_requested() override;
@ -225,6 +277,8 @@ class Scheduler {
Poll *poll_;
KHeap<double> *heap_;
Debug *debug_;
};
template <class F>
@ -234,7 +288,8 @@ class Scheduler {
#endif
bool is_io_worker = worker_info.type == WorkerInfo::Type::Io;
ContextImpl context(&worker_info.actor_info_creator, info_->id, worker_info.cpu_worker_id,
scheduler_group_info_.get(), is_io_worker ? &poll_ : nullptr, is_io_worker ? &heap_ : nullptr);
scheduler_group_info_.get(), is_io_worker ? &poll_ : nullptr, is_io_worker ? &heap_ : nullptr,
&worker_info.debug);
SchedulerContext::Guard guard(&context);
f();
}

View File

@ -37,6 +37,7 @@ class SchedulerDispatcher {
virtual void set_alarm_timestamp(const ActorInfoPtr &actor_info_ptr) = 0;
};
struct Debug;
class SchedulerContext : public Context<SchedulerContext>, public SchedulerDispatcher {
public:
virtual ~SchedulerContext() = default;
@ -55,6 +56,9 @@ class SchedulerContext : public Context<SchedulerContext>, public SchedulerDispa
// Stop all schedulers
virtual bool is_stop_requested() = 0;
virtual void stop() = 0;
// Debug
virtual Debug &get_debug() = 0;
};
} // namespace core
} // namespace actor

View File

@ -675,7 +675,8 @@ TEST(Actor2, actor_function_result) {
public:
A(std::shared_ptr<td::Destructor> watcher) : watcher_(std::move(watcher)) {
}
void on_result(uint32 x, uint32 y) {
void on_result(uint32 x, td::Result<uint32> r_y) {
auto y = r_y.move_as_ok();
LOG_CHECK(x * x == y) << x << " " << y;
if (--cnt_ == 0) {
stop();
@ -683,7 +684,7 @@ TEST(Actor2, actor_function_result) {
}
void start_up() {
b_ = create_actor<B>(ActorOptions().with_name("B"));
cnt_ = 3;
cnt_ = 5;
send_closure(b_, &B::query, 3, [a = std::make_unique<int>(), self = actor_id(this)](td::Result<uint32> y) {
LOG_IF(ERROR, y.is_error()) << y.error();
send_closure(self, &A::on_result, 3, y.ok());
@ -696,6 +697,11 @@ TEST(Actor2, actor_function_result) {
CHECK(!self.empty());
send_closure(self, &A::on_result, 5, y);
});
auto future = future_send_closure(b_, &B::query, 7);
future.finish(td::promise_send_closure(actor_id(this), &A::on_result, 7));
//TODO: deduce Future type (i.e. Future<td::uint32>)
auto future2 = future_send_closure<td::uint32>(b_, &B::query_async, 7);
future2.finish(td::promise_send_closure(actor_id(this), &A::on_result, 7));
}
private:
@ -714,12 +720,12 @@ TEST(Actor2, actor_function_result) {
}
TEST(Actor2, actor_ping_pong) {
auto group_info = std::make_shared<core::SchedulerGroupInfo>(1);
core::Scheduler scheduler{group_info, SchedulerId{0}, 3};
Scheduler scheduler{{3}, Scheduler::Paused};
sb.clear();
scheduler.start();
auto watcher = td::create_shared_destructor([] { SchedulerContext::get()->stop(); });
td::actor::set_debug(true);
for (int i = 0; i < 2000; i++) {
scheduler.run_in_context([watcher] {
class PingPong : public Actor {
@ -781,9 +787,9 @@ TEST(Actor2, actor_ping_pong) {
});
}
watcher.reset();
while (scheduler.run(1000)) {
while (scheduler.run(0.1)) {
//scheduler.get_debug().dump();
}
core::Scheduler::close_scheduler_group(*group_info);
sb.clear();
}

View File

@ -135,6 +135,78 @@ TEST(Actor, safe_promise) {
ASSERT_EQ(res, 3);
}
TEST(Actor, split_promise) {
using td::Promise;
using td::Result;
using td::split_promise;
using td::SplitPromise;
{
td::optional<std::pair<int, double>> x;
auto pair = [&](Result<std::pair<int, double>> res) { x = res.move_as_ok(); };
static_assert(std::is_same<SplitPromise<decltype(pair)>::ArgT, std::pair<int, double>>::value, "A");
static_assert(
std::is_same<SplitPromise<decltype(pair)>::SplittedT, std::pair<Promise<int>, Promise<double>>>::value, "A");
auto splitted = split_promise(pair);
static_assert(std::is_same<decltype(splitted), std::pair<Promise<int>, Promise<double>>>::value, "A");
splitted.first.set_value(1);
splitted.second.set_value(2.0);
CHECK(x.unwrap() == std::make_pair(1, 2.0));
} // namespace td
{
td::optional<std::tuple<int, double, std::string>> x;
auto triple = [&](Result<std::tuple<int, double, std::string>> res) { x = res.move_as_ok(); };
static_assert(std::is_same<SplitPromise<decltype(triple)>::ArgT, std::tuple<int, double, std::string>>::value, "A");
static_assert(std::is_same<SplitPromise<decltype(triple)>::SplittedT,
std::tuple<Promise<int>, Promise<double>, Promise<std::string>>>::value,
"A");
auto splitted = split_promise(triple);
static_assert(
std::is_same<decltype(splitted), std::tuple<Promise<int>, Promise<double>, Promise<std::string>>>::value, "A");
std::get<0>(splitted).set_value(1);
std::get<1>(splitted).set_value(2.0);
std::get<2>(splitted).set_value("hello");
CHECK(x.unwrap() == std::make_tuple(1, 2.0, "hello"));
}
{
int code = 0;
auto pair = [&](Result<std::pair<int, double>> res) {
res.ensure_error();
code = res.error().code();
};
auto splitted = split_promise(td::Promise<std::pair<int, double>>(pair));
splitted.second.set_error(td::Status::Error(123, "123"));
CHECK(code == 0);
splitted.first.set_value(1);
CHECK(code == 123);
}
}
TEST(Actor, promise_future) {
using td::make_promise_future;
{
auto pf = make_promise_future<int>();
td::optional<int> res;
pf.second.map([](int x) { return x * 2; }).map([](int x) { return x + 10; }).map([&](int x) {
res = x;
return td::Unit();
});
CHECK(!res);
pf.first.set_value(6);
ASSERT_EQ(22, res.unwrap());
}
{
LOG(ERROR) << "Second test";
td::optional<int> res;
td::make_future(6)
.map([](int x) { return x * 2; })
.map([](int x) { return x + 10; })
.fmap([&](int x) { return td::make_future(x * 2); })
.finish([&](int x) { res = x; });
ASSERT_EQ(44, res.unwrap());
}
}
TEST(Actor2, actor_lost_promise) {
using namespace td::actor;
using namespace td;
@ -459,7 +531,7 @@ class SampleActor : public Actor {
detail::current_actor<Printer>().print_a();
co_await OnActor(self);
LOG(ERROR) << "exit print_a";
co_return{};
co_return {};
}
task<Unit> print_b() {
auto self = actor_id(this);
@ -468,7 +540,7 @@ class SampleActor : public Actor {
detail::current_actor<Printer>().print_b();
co_await OnActor(self);
LOG(ERROR) << "exit print_b";
co_return{};
co_return {};
}
immediate_task run_coroutine() {

View File

@ -55,6 +55,10 @@ class KeyValue : public KeyValueReader {
virtual Status set(Slice key, Slice value) = 0;
virtual Status erase(Slice key) = 0;
virtual Status begin_write_batch() = 0;
virtual Status commit_write_batch() = 0;
virtual Status abort_write_batch() = 0;
virtual Status begin_transaction() = 0;
virtual Status commit_transaction() = 0;
virtual Status abort_transaction() = 0;
@ -86,6 +90,16 @@ class PrefixedKeyValue : public KeyValue {
return kv_->erase(PSLICE() << prefix_ << key);
}
Status begin_write_batch() override {
return kv_->begin_write_batch();
}
Status commit_write_batch() override {
return kv_->commit_write_batch();
}
Status abort_write_batch() override {
return kv_->abort_write_batch();
}
Status begin_transaction() override {
return kv_->begin_transaction();
}

View File

@ -61,6 +61,15 @@ std::unique_ptr<KeyValueReader> MemoryKeyValue::snapshot() {
std::string MemoryKeyValue::stats() const {
return PSTRING() << "MemoryKeyValueStats{" << tag("get_count", get_count_) << "}";
}
Status MemoryKeyValue::begin_write_batch() {
UNREACHABLE();
}
Status MemoryKeyValue::commit_write_batch() {
UNREACHABLE();
}
Status MemoryKeyValue::abort_write_batch() {
UNREACHABLE();
}
Status MemoryKeyValue::begin_transaction() {
UNREACHABLE();

View File

@ -29,6 +29,10 @@ class MemoryKeyValue : public KeyValue {
Status erase(Slice key) override;
Result<size_t> count(Slice prefix) override;
Status begin_write_batch() override;
Status commit_write_batch() override;
Status abort_write_batch() override;
Status begin_transaction() override;
Status commit_transaction() override;
Status abort_transaction() override;

View File

@ -78,7 +78,18 @@ Result<RocksDb> RocksDb::open(std::string path) {
options.bytes_per_sync = 1 << 20;
options.writable_file_max_buffer_size = 2 << 14;
options.statistics = statistics;
TRY_STATUS(from_rocksdb(rocksdb::OptimisticTransactionDB::Open(options, std::move(path), &db)));
rocksdb::OptimisticTransactionDBOptions occ_options;
occ_options.validate_policy = rocksdb::OccValidationPolicy::kValidateSerial;
rocksdb::ColumnFamilyOptions cf_options(options);
std::vector<rocksdb::ColumnFamilyDescriptor> column_families;
column_families.push_back(rocksdb::ColumnFamilyDescriptor(rocksdb::kDefaultColumnFamilyName, cf_options));
std::vector<rocksdb::ColumnFamilyHandle *> handles;
TRY_STATUS(from_rocksdb(
rocksdb::OptimisticTransactionDB::Open(options, occ_options, std::move(path), column_families, &handles, &db)));
CHECK(handles.size() == 1);
// i can delete the handle since DBImpl is always holding a reference to
// default column family
delete handles[0];
}
return RocksDb(std::shared_ptr<rocksdb::OptimisticTransactionDB>(db), std::move(statistics));
}
@ -161,31 +172,41 @@ Result<size_t> RocksDb::count(Slice prefix) {
return res;
}
Status RocksDb::begin_write_batch() {
CHECK(!transaction_);
write_batch_ = std::make_unique<rocksdb::WriteBatch>();
return Status::OK();
}
Status RocksDb::begin_transaction() {
//write_batch_ = std::make_unique<rocksdb::WriteBatch>();
CHECK(!write_batch_);
rocksdb::WriteOptions options;
options.sync = true;
transaction_.reset(db_->BeginTransaction(options, {}));
return Status::OK();
}
Status RocksDb::commit_transaction() {
//CHECK(write_batch_);
//auto write_batch = std::move(write_batch_);
//rocksdb::WriteOptions options;
//options.sync = true;
//TRY_STATUS(from_rocksdb(db_->Write(options, write_batch.get())));
//return Status::OK();
Status RocksDb::commit_write_batch() {
CHECK(write_batch_);
auto write_batch = std::move(write_batch_);
rocksdb::WriteOptions options;
options.sync = true;
return from_rocksdb(db_->Write(options, write_batch.get()));
}
Status RocksDb::commit_transaction() {
CHECK(transaction_);
auto res = from_rocksdb(transaction_->Commit());
transaction_.reset();
return res;
auto transaction = std::move(transaction_);
return from_rocksdb(transaction->Commit());
}
Status RocksDb::abort_write_batch() {
CHECK(write_batch_);
write_batch_.reset();
return Status::OK();
}
Status RocksDb::abort_transaction() {
//CHECK(write_batch_);
//write_batch_.reset();
CHECK(transaction_);
transaction_.reset();
return Status::OK();

View File

@ -45,6 +45,10 @@ class RocksDb : public KeyValue {
Status erase(Slice key) override;
Result<size_t> count(Slice prefix) override;
Status begin_write_batch() override;
Status commit_write_batch() override;
Status abort_write_batch() override;
Status begin_transaction() override;
Status commit_transaction() override;
Status abort_transaction() override;

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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