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:
Francisco Gindre 2019-12-17 14:12:07 -03:00 committed by GitHub
parent a139ffbdfe
commit 5930fbed6f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 52 additions and 6 deletions

View File

@ -19,6 +19,7 @@ public struct CompactBlockProcessorNotificationKey {
public static let progressHeight = "CompactBlockProcessorNotificationKey.progressHeight"
public static let reorgHeight = "CompactBlockProcessorNotificationKey.reorgHeight"
public static let latestScannedBlockHeight = "CompactBlockProcessorNotificationKey.latestScannedBlockHeight"
public static let rewindHeight = "CompactBlockProcessorNotificationKey.rewindHeight"
}
public extension Notification.Name {
@ -313,9 +314,6 @@ public class CompactBlockProcessor {
// cancel all Tasks
queue.cancelAllOperations()
// notify reorg
NotificationCenter.default.post(name: Notification.Name.blockProcessorHandledReOrg, object: self, userInfo: [CompactBlockProcessorNotificationKey.reorgHeight : height])
// register latest failure
self.lastChainValidationFailure = height
self.consecutiveChainValidationErrors = self.consecutiveChainValidationErrors + 1
@ -328,6 +326,10 @@ public class CompactBlockProcessor {
return
}
// notify reorg
NotificationCenter.default.post(name: Notification.Name.blockProcessorHandledReOrg, object: self, userInfo: [CompactBlockProcessorNotificationKey.reorgHeight : height,
CompactBlockProcessorNotificationKey.rewindHeight : rewindHeight])
do {
try downloader.rewind(to: rewindHeight)
// process next batch

View File

@ -19,6 +19,7 @@ enum TransactionManagerError: Error {
class PersistentTransactionManager: OutboundTransactionManager {
var repository: PendingTransactionRepository
var encoder: TransactionEncoder
var service: LightWalletService
@ -126,6 +127,19 @@ class PersistentTransactionManager: OutboundTransactionManager {
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) {
// TODO: Implement this
}

View File

@ -30,4 +30,6 @@ protocol OutboundTransactionManager {
func cancel(pendingTransaction: PendingTransactionEntity) -> Bool
func allPendingTransactions() throws -> [PendingTransactionEntity]?
func handleReorg(at: BlockHeight) throws
}

View File

@ -100,9 +100,12 @@ public class SDKSynchronizer: Synchronizer {
center.addObserver(self,
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)
center.addObserver(self,
@ -178,10 +181,32 @@ public class SDKSynchronizer: Synchronizer {
selector: #selector(processorTransitionUnknown(_:)),
name: Notification.Name.blockProcessorUnknownTransition,
object: processor)
center.addObserver(self,
selector: #selector(reorgDetected(_:)),
name: Notification.Name.blockProcessorHandledReOrg,
object: processor)
}
// 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) {
guard let userInfo = notification.userInfo,
let progress = userInfo[CompactBlockProcessorNotificationKey.progress] as? Float,

View File

@ -67,8 +67,11 @@ class CompactBlockReorgTests: XCTestCase {
@objc func processorHandledReorg(_ notification: Notification) {
DispatchQueue.main.sync {
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( rewind == 0 || rewind > SAPLING_ACTIVATION_HEIGHT)
XCTAssertTrue( rewind <= reorg )
} else {
XCTFail("CompactBlockProcessor reorg notification is malformed")
}