Skip to content

Commit

Permalink
[Electric-Coin-Company#1452] TX Resubmission-the wallet has to period…
Browse files Browse the repository at this point in the history
…ically resubmit unmined transactions

- functional version is done
  • Loading branch information
LukasKorba committed Jun 25, 2024
1 parent d9a706d commit adf84b5
Show file tree
Hide file tree
Showing 10 changed files with 131 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,21 @@ enum DemoAppConfig {
let seed: [UInt8]
}

static let host = ZcashSDK.isMainnet ? "mainnet.lightwalletd.com" : "lightwalletd.testnet.electriccoin.co"
static let port: Int = 9067
static let host = ZcashSDK.isMainnet ? "zec.rocks" : "lightwalletd.testnet.electriccoin.co"
static let port: Int = 443

static let defaultBirthdayHeight: BlockHeight = ZcashSDK.isMainnet ? 935000 : 1386000
// static let defaultBirthdayHeight: BlockHeight = ZcashSDK.isMainnet ? 935000 : 1386000
static let defaultBirthdayHeight: BlockHeight = ZcashSDK.isMainnet ? 2200000 : 2200000

// static let defaultSeed = try! Mnemonic.deterministicSeedBytes(from: """
// wish puppy smile loan doll curve hole maze file ginger hair nose key relax knife witness cannon grab despair throw review deal slush frame
// """)

// static let defaultSeed = try! Mnemonic.deterministicSeedBytes(from: """
// live combine flight accident slow soda mind bright absent bid hen shy decade biology amazing mix enlist ensure biology rhythm snap duty soap armor
// """)
static let defaultSeed = try! Mnemonic.deterministicSeedBytes(from: """
live combine flight accident slow soda mind bright absent bid hen shy decade biology amazing mix enlist ensure biology rhythm snap duty soap armor
humble fall vacuum supreme charge type raw wedding soft need goat kitchen dragon lunch history soup lazy capital aisle credit turn cake disagree auction
""")

static let otherSynchronizers: [SynchronizerInitData] = [
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class SendViewController: UIViewController {
}

func toggleSendButton() async {
sendButton.isEnabled = await isFormValid()
sendButton.isEnabled = true//await isFormValid()
}

func maxFundsOn() {
Expand All @@ -135,6 +135,7 @@ class SendViewController: UIViewController {
func isFormValid() async -> Bool {
switch synchronizer.latestState.syncStatus {
case .upToDate:
return true
let isBalanceValid = await self.isBalanceValid()
let isAmountValid = await self.isAmountValid()
return isBalanceValid && isAmountValid && isRecipientValid()
Expand Down Expand Up @@ -177,6 +178,12 @@ class SendViewController: UIViewController {
}

@IBAction func send(_ sender: Any) {
//addressLabel.text = "zs1feq24yjsq0p94tem9g5zvpnnpk587hlu27pwmskkuucx5gt0tleulad7tp2snqkeh9urxal078k"
addressTextField.text = "zs1feq24yjsq0p94tem9g5zvpnnpk587hlu27pwmskkuucx5gt0tleulad7tp2snqkeh9urxal078k"
//amountLabel.text = "0,00000001"
amountTextField.text = "0,00000001"
memoField.text = "test for ressubmission"

Task { @MainActor in
guard await isFormValid() else {
loggerProxy.warn("WARNING: Form is invalid")
Expand Down Expand Up @@ -247,6 +254,7 @@ class SendViewController: UIViewController {
KRProgressHUD.dismiss()
loggerProxy.info("transaction created: \(pendingTransaction)")
} catch {
KRProgressHUD.dismiss()
fail(error)
loggerProxy.error("SEND FAILED: \(error)")
}
Expand Down
1 change: 1 addition & 0 deletions Sources/ZcashLightClientKit/Block/Actions/Action.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ enum CBPState: CaseIterable {
case fetchUTXO
case handleSaplingParams
case clearCache
case txResubmission
case finished
case failed
case stopped
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ final class EnhanceAction {
if lastScannedHeight >= latestBlockHeight {
await context.update(state: .clearCache)
} else {
await context.update(state: .updateChainTip)
await context.update(state: .txResubmission)
}

return context
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ extension ProcessSuggestedScanRangesAction: Action {

await context.update(state: .download)
} else {
await context.update(state: .finished)
await context.update(state: .txResubmission)
}

return context
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//
// TxResubmissionAction.swift
//
//
// Created by Lukas Korba on 06-17-2024.
//

import Foundation

final class TxResubmissionAction {
private enum Constants {
static let thresholdToTrigger = TimeInterval(300.0)
}

var latestResolvedTime: TimeInterval = 0
let transactionRepository: TransactionRepository
let transactionEncoder: TransactionEncoder
let logger: Logger

init(container: DIContainer) {
transactionRepository = container.resolve(TransactionRepository.self)
transactionEncoder = container.resolve(TransactionEncoder.self)
logger = container.resolve(Logger.self)
}
}

extension TxResubmissionAction: Action {
var removeBlocksCacheWhenFailed: Bool { true }

func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
let latestBlockHeight = await context.syncControlData.latestBlockHeight

// find all candidates for the resubmission
do {
let transactions = try await transactionRepository.findForResubmission(upTo: latestBlockHeight)

// no candidates, update the time and continue with the next action
if transactions.isEmpty {
latestResolvedTime = Date().timeIntervalSince1970
} else {
let now = Date().timeIntervalSince1970
let diff = now - latestResolvedTime

// the last time resubmission was triggered is more than 5 minutes ago so try again
if diff > Constants.thresholdToTrigger {
// resubmission
do {
for transaction in transactions {
let encodedTransaction = try transaction.encodedTransaction()

try await transactionEncoder.submit(transaction: encodedTransaction)
logger.info("TxResubmissionAction trying to resubmit transaction")
}
} catch {
logger.error("TxResubmissionAction failed to resubmit candidates")
}

latestResolvedTime = Date().timeIntervalSince1970
}
}
} catch {
logger.error("TxResubmissionAction failed to find candidates")
}

if await context.prevState == .enhance {
await context.update(state: .updateChainTip)
} else {
await context.update(state: .finished)
}
return context
}

func stop() async { }
}
4 changes: 4 additions & 0 deletions Sources/ZcashLightClientKit/Block/CompactBlockProcessor.swift
Original file line number Diff line number Diff line change
Expand Up @@ -228,6 +228,8 @@ actor CompactBlockProcessor {
action = SaplingParamsAction(container: container)
case .clearCache:
action = ClearCacheAction(container: container)
case .txResubmission:
action = TxResubmissionAction(container: container)
case .finished, .failed, .stopped, .idle:
return nil
}
Expand Down Expand Up @@ -667,6 +669,8 @@ extension CompactBlockProcessor {
break
case .stopped:
break
case .txResubmission:
break
}
}

Expand Down
13 changes: 13 additions & 0 deletions Sources/ZcashLightClientKit/DAO/TransactionDao.swift
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,19 @@ class TransactionSQLDAO: TransactionRepository {
return try await execute(query) { try ZcashTransaction.Overview(row: $0) }
}

func findForResubmission(upTo: BlockHeight) async throws -> [ZcashTransaction.Overview] {
let query = transactionsView
.order((ZcashTransaction.Overview.Column.minedHeight ?? BlockHeight.max).desc)
.filter(
ZcashTransaction.Overview.Column.minedHeight == nil &&
ZcashTransaction.Overview.Column.expiryHeight > upTo
)
.filterQueryFor(kind: .sent)
.limit(Int.max)

return try await execute(query) { try ZcashTransaction.Overview(row: $0) }
}

func findReceived(offset: Int, limit: Int) async throws -> [ZcashTransaction.Overview] {
let query = transactionsView
.filterQueryFor(kind: .received)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ protocol TransactionRepository {
func findPendingTransactions(latestHeight: BlockHeight, offset: Int, limit: Int) async throws -> [ZcashTransaction.Overview]
func findReceived(offset: Int, limit: Int) async throws -> [ZcashTransaction.Overview]
func findSent(offset: Int, limit: Int) async throws -> [ZcashTransaction.Overview]
func findForResubmission(upTo: BlockHeight) async throws -> [ZcashTransaction.Overview]
// sourcery: mockedName="findMemosForRawID"
func findMemos(for rawID: Data) async throws -> [Memo]
// sourcery: mockedName="findMemosForZcashTransaction"
Expand Down
19 changes: 19 additions & 0 deletions Sources/ZcashLightClientKit/Synchronizer/Dependencies.swift
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,25 @@ enum Dependencies {
container.register(type: ZcashFileManager.self, isSingleton: true) { _ in
FileManager.default
}

container.register(type: TransactionEncoder.self, isSingleton: true) { di in
let service = di.resolve(LightWalletService.self)
let logger = di.resolve(Logger.self)
let transactionRepository = di.resolve(TransactionRepository.self)
let rustBackend = di.resolve(ZcashRustBackendWelding.self)

return WalletTransactionEncoder(
rustBackend: rustBackend,
dataDb: urls.dataDbURL,
fsBlockDbRoot: urls.fsBlockDbRoot,
service: service,
repository: transactionRepository,
outputParams: urls.outputParamsURL,
spendParams: urls.spendParamsURL,
networkType: networkType,
logger: logger
)
}
}

static func setupCompactBlockProcessor(
Expand Down

0 comments on commit adf84b5

Please sign in to comment.