Outbound transaction reorg handling proposal (#48)
* Outbound transaction reorg handling proposal * Add rewind distance * include rewind on reorg notification, use it to refresh pending transactions
This commit is contained in:
parent
a139ffbdfe
commit
5930fbed6f
|
@ -19,6 +19,7 @@ public struct CompactBlockProcessorNotificationKey {
|
||||||
public static let progressHeight = "CompactBlockProcessorNotificationKey.progressHeight"
|
public static let progressHeight = "CompactBlockProcessorNotificationKey.progressHeight"
|
||||||
public static let reorgHeight = "CompactBlockProcessorNotificationKey.reorgHeight"
|
public static let reorgHeight = "CompactBlockProcessorNotificationKey.reorgHeight"
|
||||||
public static let latestScannedBlockHeight = "CompactBlockProcessorNotificationKey.latestScannedBlockHeight"
|
public static let latestScannedBlockHeight = "CompactBlockProcessorNotificationKey.latestScannedBlockHeight"
|
||||||
|
public static let rewindHeight = "CompactBlockProcessorNotificationKey.rewindHeight"
|
||||||
}
|
}
|
||||||
|
|
||||||
public extension Notification.Name {
|
public extension Notification.Name {
|
||||||
|
@ -313,9 +314,6 @@ public class CompactBlockProcessor {
|
||||||
// cancel all Tasks
|
// cancel all Tasks
|
||||||
queue.cancelAllOperations()
|
queue.cancelAllOperations()
|
||||||
|
|
||||||
// notify reorg
|
|
||||||
NotificationCenter.default.post(name: Notification.Name.blockProcessorHandledReOrg, object: self, userInfo: [CompactBlockProcessorNotificationKey.reorgHeight : height])
|
|
||||||
|
|
||||||
// register latest failure
|
// register latest failure
|
||||||
self.lastChainValidationFailure = height
|
self.lastChainValidationFailure = height
|
||||||
self.consecutiveChainValidationErrors = self.consecutiveChainValidationErrors + 1
|
self.consecutiveChainValidationErrors = self.consecutiveChainValidationErrors + 1
|
||||||
|
@ -328,6 +326,10 @@ public class CompactBlockProcessor {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// notify reorg
|
||||||
|
NotificationCenter.default.post(name: Notification.Name.blockProcessorHandledReOrg, object: self, userInfo: [CompactBlockProcessorNotificationKey.reorgHeight : height,
|
||||||
|
CompactBlockProcessorNotificationKey.rewindHeight : rewindHeight])
|
||||||
|
|
||||||
do {
|
do {
|
||||||
try downloader.rewind(to: rewindHeight)
|
try downloader.rewind(to: rewindHeight)
|
||||||
// process next batch
|
// process next batch
|
||||||
|
|
|
@ -19,6 +19,7 @@ enum TransactionManagerError: Error {
|
||||||
|
|
||||||
class PersistentTransactionManager: OutboundTransactionManager {
|
class PersistentTransactionManager: OutboundTransactionManager {
|
||||||
|
|
||||||
|
|
||||||
var repository: PendingTransactionRepository
|
var repository: PendingTransactionRepository
|
||||||
var encoder: TransactionEncoder
|
var encoder: TransactionEncoder
|
||||||
var service: LightWalletService
|
var service: LightWalletService
|
||||||
|
@ -126,6 +127,19 @@ class PersistentTransactionManager: OutboundTransactionManager {
|
||||||
return tx
|
return tx
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleReorg(at height: BlockHeight) throws {
|
||||||
|
guard let affectedTxs = try self.allPendingTransactions()?.filter({ $0.minedHeight >= height }) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try affectedTxs.map { (tx) -> PendingTransactionEntity in
|
||||||
|
var updatedTx = tx
|
||||||
|
updatedTx.minedHeight = -1
|
||||||
|
return updatedTx
|
||||||
|
} .forEach({ try self.repository.update($0) })
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
func monitorChanges(byId: Int, observer: Any) {
|
func monitorChanges(byId: Int, observer: Any) {
|
||||||
// TODO: Implement this
|
// TODO: Implement this
|
||||||
}
|
}
|
||||||
|
|
|
@ -30,4 +30,6 @@ protocol OutboundTransactionManager {
|
||||||
func cancel(pendingTransaction: PendingTransactionEntity) -> Bool
|
func cancel(pendingTransaction: PendingTransactionEntity) -> Bool
|
||||||
|
|
||||||
func allPendingTransactions() throws -> [PendingTransactionEntity]?
|
func allPendingTransactions() throws -> [PendingTransactionEntity]?
|
||||||
|
|
||||||
|
func handleReorg(at: BlockHeight) throws
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,9 +100,12 @@ public class SDKSynchronizer: Synchronizer {
|
||||||
|
|
||||||
center.addObserver(self,
|
center.addObserver(self,
|
||||||
selector: #selector(applicationDidBecomeActive(_:)),
|
selector: #selector(applicationDidBecomeActive(_:)),
|
||||||
name: UIApplication.didBecomeActiveNotification, object: nil)
|
name: UIApplication.didBecomeActiveNotification,
|
||||||
|
object: nil)
|
||||||
|
|
||||||
center.addObserver(self, selector: #selector(applicationWillTerminate(_:)), name: UIApplication.willTerminateNotification,
|
center.addObserver(self,
|
||||||
|
selector: #selector(applicationWillTerminate(_:)),
|
||||||
|
name: UIApplication.willTerminateNotification,
|
||||||
object: nil)
|
object: nil)
|
||||||
|
|
||||||
center.addObserver(self,
|
center.addObserver(self,
|
||||||
|
@ -178,10 +181,32 @@ public class SDKSynchronizer: Synchronizer {
|
||||||
selector: #selector(processorTransitionUnknown(_:)),
|
selector: #selector(processorTransitionUnknown(_:)),
|
||||||
name: Notification.Name.blockProcessorUnknownTransition,
|
name: Notification.Name.blockProcessorUnknownTransition,
|
||||||
object: processor)
|
object: processor)
|
||||||
|
|
||||||
|
center.addObserver(self,
|
||||||
|
selector: #selector(reorgDetected(_:)),
|
||||||
|
name: Notification.Name.blockProcessorHandledReOrg,
|
||||||
|
object: processor)
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// MARK: Block Processor notifications
|
// MARK: Block Processor notifications
|
||||||
|
|
||||||
|
@objc func reorgDetected(_ notification: Notification) {
|
||||||
|
guard let userInfo = notification.userInfo,
|
||||||
|
let progress = userInfo[CompactBlockProcessorNotificationKey.reorgHeight] as? BlockHeight,
|
||||||
|
let rewindHeight = userInfo[CompactBlockProcessorNotificationKey.rewindHeight] as? BlockHeight else {
|
||||||
|
print("error processing reorg notification")
|
||||||
|
return }
|
||||||
|
|
||||||
|
print("handling reorg at: \(progress) with rewind height: \(rewindHeight)")
|
||||||
|
do {
|
||||||
|
try transactionManager.handleReorg(at: rewindHeight)
|
||||||
|
} catch {
|
||||||
|
print("error handling reorg: \(error)") // TODO: handle and propagate Error
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@objc func processorUpdated(_ notification: Notification) {
|
@objc func processorUpdated(_ notification: Notification) {
|
||||||
guard let userInfo = notification.userInfo,
|
guard let userInfo = notification.userInfo,
|
||||||
let progress = userInfo[CompactBlockProcessorNotificationKey.progress] as? Float,
|
let progress = userInfo[CompactBlockProcessorNotificationKey.progress] as? Float,
|
||||||
|
|
|
@ -67,8 +67,11 @@ class CompactBlockReorgTests: XCTestCase {
|
||||||
@objc func processorHandledReorg(_ notification: Notification) {
|
@objc func processorHandledReorg(_ notification: Notification) {
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
XCTAssertNotNil(notification.userInfo)
|
XCTAssertNotNil(notification.userInfo)
|
||||||
if let reorg = notification.userInfo?[CompactBlockProcessorNotificationKey.reorgHeight] as? BlockHeight {
|
if let reorg = notification.userInfo?[CompactBlockProcessorNotificationKey.reorgHeight] as? BlockHeight,
|
||||||
|
let rewind = notification.userInfo?[CompactBlockProcessorNotificationKey.rewindHeight] as? BlockHeight {
|
||||||
XCTAssertTrue( reorg == 0 || reorg > SAPLING_ACTIVATION_HEIGHT)
|
XCTAssertTrue( reorg == 0 || reorg > SAPLING_ACTIVATION_HEIGHT)
|
||||||
|
XCTAssertTrue( rewind == 0 || rewind > SAPLING_ACTIVATION_HEIGHT)
|
||||||
|
XCTAssertTrue( rewind <= reorg )
|
||||||
} else {
|
} else {
|
||||||
XCTFail("CompactBlockProcessor reorg notification is malformed")
|
XCTFail("CompactBlockProcessor reorg notification is malformed")
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue