Merge pull request #1327 from LukasKorba/1325-Log-metrics
the logger has been extended to log the level as well there is only partial match of levels between SDK logger levels, OSLogEntryLogLevel and OSLogType so only debug, info, error are fully matched this is a base for the exporter on client's side Scan & Enhance logs added checkpoints updated every CBP action is measured separately and collects the data, when the sync is done it dumps overview of the run to the logger next run clears out the previous data and starts to collect fresh reports for the run
This commit is contained in:
commit
ad6ac80ee4
29
CHANGELOG.md
29
CHANGELOG.md
|
@ -4,6 +4,35 @@ All notable changes to this library will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this library adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
# Unreleased
|
||||
|
||||
## Changed
|
||||
The `SDKMetrics` logs data using os_log. The public API `enableMetrics()` and `disableMetrics()` no longer exist. All metrics are automatically logged for every sync run. Extraction of the metrics is up to the client/dev - done by using `OSLogStore`.
|
||||
|
||||
## Added
|
||||
|
||||
### [#1325] Log metrics
|
||||
The sync process is measured and detailed metrics are logged for every sync run. The data are logged using os_log so any client can export it. Verbose logs are under `sdkLogs_default` category, `default` level. Sync specific logs use `error` level.
|
||||
|
||||
## Checkpoints
|
||||
|
||||
Mainnet
|
||||
|
||||
````
|
||||
Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2270000.json
|
||||
...
|
||||
Sources/ZcashLightClientKit/Resources/checkpoints/mainnet/2327500.json
|
||||
````
|
||||
|
||||
|
||||
Testnet
|
||||
|
||||
````
|
||||
Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2560000.json
|
||||
...
|
||||
Sources/ZcashLightClientKit/Resources/checkpoints/testnet/2620000.json
|
||||
````
|
||||
|
||||
# 2.0.3 - 2023-10-20
|
||||
|
||||
## Fixed
|
||||
|
|
|
@ -24,16 +24,6 @@ class SyncBlocksViewController: UIViewController {
|
|||
private var queue = DispatchQueue(label: "metrics.queue", qos: .default)
|
||||
private var enhancingStarted = false
|
||||
private var accumulatedMetrics: ProcessorMetrics = .initial
|
||||
private var currentMetric: SDKMetrics.Operation?
|
||||
private var currentMetricName: String {
|
||||
guard let currentMetric else { return "" }
|
||||
switch currentMetric {
|
||||
case .downloadBlocks: return "download: "
|
||||
case .scanBlocks: return "scan: "
|
||||
case .enhancement: return "enhancement: "
|
||||
case .fetchUTXOs: return "fetchUTXOs: "
|
||||
}
|
||||
}
|
||||
|
||||
var cancellables: [AnyCancellable] = []
|
||||
let dateFormatter = DateFormatter()
|
||||
|
@ -88,50 +78,14 @@ class SyncBlocksViewController: UIViewController {
|
|||
"""
|
||||
progressDataLabel.text = progressText
|
||||
|
||||
if let currentMetric {
|
||||
let report = synchronizer.metrics.popBlock(operation: currentMetric)?.last
|
||||
metricLabel.text = currentMetricName + report.debugDescription
|
||||
}
|
||||
|
||||
case .upToDate, .stopped:
|
||||
accumulateMetrics()
|
||||
summaryLabel.text = "enhancement: \(accumulatedMetrics.debugDescription)"
|
||||
overallSummary()
|
||||
|
||||
case .error:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
func accumulateMetrics() {
|
||||
guard let currentMetric else { return }
|
||||
if let reports = synchronizer.metrics.popBlock(operation: currentMetric) {
|
||||
for report in reports {
|
||||
accumulatedMetrics = .accumulate(accumulatedMetrics, current: report)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func overallSummary() {
|
||||
let cumulativeSummary = synchronizer.metrics.cumulativeSummary()
|
||||
|
||||
let downloadedBlocksReport = cumulativeSummary.downloadedBlocksReport ?? SDKMetrics.ReportSummary.zero
|
||||
let scannedBlocksReport = cumulativeSummary.scannedBlocksReport ?? SDKMetrics.ReportSummary.zero
|
||||
let enhancementReport = cumulativeSummary.enhancementReport ?? SDKMetrics.ReportSummary.zero
|
||||
let fetchUTXOsReport = cumulativeSummary.fetchUTXOsReport ?? SDKMetrics.ReportSummary.zero
|
||||
let totalSyncReport = cumulativeSummary.totalSyncReport ?? SDKMetrics.ReportSummary.zero
|
||||
|
||||
metricLabel.text =
|
||||
"""
|
||||
Summary:
|
||||
downloadedBlocks: min: \(downloadedBlocksReport.minTime) max: \(downloadedBlocksReport.maxTime) avg: \(downloadedBlocksReport.avgTime)
|
||||
scannedBlocks: min: \(scannedBlocksReport.minTime) max: \(scannedBlocksReport.maxTime) avg: \(scannedBlocksReport.avgTime)
|
||||
enhancement: min: \(enhancementReport.minTime) max: \(enhancementReport.maxTime) avg: \(enhancementReport.avgTime)
|
||||
fetchUTXOs: min: \(fetchUTXOsReport.minTime) max: \(fetchUTXOsReport.maxTime) avg: \(fetchUTXOsReport.avgTime)
|
||||
totalSync: min: \(totalSyncReport.minTime) max: \(totalSyncReport.maxTime) avg: \(totalSyncReport.avgTime)
|
||||
"""
|
||||
}
|
||||
|
||||
@IBAction func startStop() {
|
||||
Task { @MainActor in
|
||||
await doStartStop()
|
||||
|
@ -156,7 +110,6 @@ class SyncBlocksViewController: UIViewController {
|
|||
}
|
||||
}
|
||||
|
||||
synchronizer.metrics.enableMetrics()
|
||||
try await synchronizer.start()
|
||||
updateUI()
|
||||
} catch {
|
||||
|
@ -165,7 +118,6 @@ class SyncBlocksViewController: UIViewController {
|
|||
}
|
||||
default:
|
||||
synchronizer.stop()
|
||||
synchronizer.metrics.disableMetrics()
|
||||
updateUI()
|
||||
}
|
||||
|
||||
|
@ -253,17 +205,6 @@ struct ProcessorMetrics {
|
|||
measuredCount: 0
|
||||
)
|
||||
|
||||
static func accumulate(_ prev: ProcessorMetrics, current: SDKMetrics.BlockMetricReport) -> Self {
|
||||
.init(
|
||||
minHeight: prev.minHeight,
|
||||
maxHeight: prev.maxHeight,
|
||||
maxDuration: prev.maxDuration,
|
||||
minDuration: prev.minDuration,
|
||||
cumulativeDuration: prev.cumulativeDuration + current.duration,
|
||||
measuredCount: prev.measuredCount + 1
|
||||
)
|
||||
}
|
||||
|
||||
static func compareDuration(
|
||||
_ prev: (TimeInterval, CompactBlockRange),
|
||||
_ current: (TimeInterval, CompactBlockRange),
|
||||
|
@ -300,16 +241,6 @@ extension CompactBlockRange {
|
|||
}
|
||||
}
|
||||
|
||||
extension SDKMetrics.BlockMetricReport: CustomDebugStringConvertible {
|
||||
public var debugDescription: String {
|
||||
"""
|
||||
BlockMetric:
|
||||
batchSize: \(self.batchSize)
|
||||
duration: \(self.duration)
|
||||
"""
|
||||
}
|
||||
}
|
||||
|
||||
// MARK: Wipe
|
||||
|
||||
extension SyncBlocksViewController {
|
||||
|
|
|
@ -88,6 +88,8 @@ extension EnhanceAction: Action {
|
|||
if let transactions {
|
||||
await didUpdate(.foundTransactions(transactions, enhanceRange))
|
||||
}
|
||||
} else {
|
||||
logger.sync("Action called but skipped for not enough blocks scanned from the last time.")
|
||||
}
|
||||
|
||||
return await decideWhatToDoNext(context: context, lastScannedHeight: lastScannedHeight)
|
||||
|
|
|
@ -11,11 +11,13 @@ final class ProcessSuggestedScanRangesAction {
|
|||
let rustBackend: ZcashRustBackendWelding
|
||||
let service: LightWalletService
|
||||
let logger: Logger
|
||||
let metrics: SDKMetrics
|
||||
|
||||
init(container: DIContainer) {
|
||||
service = container.resolve(LightWalletService.self)
|
||||
rustBackend = container.resolve(ZcashRustBackendWelding.self)
|
||||
logger = container.resolve(Logger.self)
|
||||
metrics = container.resolve(SDKMetrics.self)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,10 +25,17 @@ extension ProcessSuggestedScanRangesAction: Action {
|
|||
var removeBlocksCacheWhenFailed: Bool { false }
|
||||
|
||||
func run(with context: ActionContext, didUpdate: @escaping (CompactBlockProcessor.Event) async -> Void) async throws -> ActionContext {
|
||||
logger.info("Getting the suggested scan ranges from the wallet database.")
|
||||
logger.debug("Getting the suggested scan ranges from the wallet database.")
|
||||
let scanRanges = try await rustBackend.suggestScanRanges()
|
||||
|
||||
logger.sync("CALL suggestScanRanges \(scanRanges)")
|
||||
|
||||
for scanRange in scanRanges {
|
||||
metrics.actionDetail("range \(scanRange.priority) \(scanRange.range)", for: .processSuggestedScanRanges)
|
||||
}
|
||||
|
||||
if let firstRange = scanRanges.first {
|
||||
logger.sync("PROCESSING range \(firstRange.priority) \(firstRange.range)")
|
||||
let rangeStartExclusive = firstRange.range.lowerBound - 1
|
||||
let rangeEndInclusive = firstRange.range.upperBound - 1
|
||||
|
||||
|
|
|
@ -50,7 +50,8 @@ extension ScanAction: Action {
|
|||
let batchRange = batchRangeStart...batchRangeEnd
|
||||
|
||||
logger.debug("Starting scan blocks with range: \(batchRange.lowerBound)...\(batchRange.upperBound)")
|
||||
|
||||
logger.sync("Starting scan blocks with range \(batchRange.lowerBound)...\(batchRange.upperBound)")
|
||||
|
||||
do {
|
||||
try await blockScanner.scanBlocks(at: batchRange) { [weak self] lastScannedHeight, increment in
|
||||
let processedHeight = await context.processedHeight
|
||||
|
|
|
@ -25,7 +25,7 @@ final class UpdateChainTipAction {
|
|||
func updateChainTip(_ context: ActionContext, time: TimeInterval) async throws {
|
||||
let latestBlockHeight = try await service.latestBlockHeight()
|
||||
|
||||
logger.info("Latest block height is \(latestBlockHeight)")
|
||||
logger.debug("Latest block height is \(latestBlockHeight)")
|
||||
try await rustBackend.updateChainTip(height: Int32(latestBlockHeight))
|
||||
await context.update(lastChainTipUpdateTime: time)
|
||||
await latestBlocksDataProvider.update(latestBlockHeight)
|
||||
|
|
|
@ -28,7 +28,7 @@ extension UpdateSubtreeRootsAction: Action {
|
|||
var request = GetSubtreeRootsArg()
|
||||
request.shieldedProtocol = .sapling
|
||||
|
||||
logger.info("Attempt to get subtree roots, this may fail because lightwalletd may not support Spend before Sync.")
|
||||
logger.debug("Attempt to get subtree roots, this may fail because lightwalletd may not support Spend before Sync.")
|
||||
let stream = service.getSubtreeRoots(request)
|
||||
|
||||
var roots: [SubtreeRoot] = []
|
||||
|
@ -41,7 +41,7 @@ extension UpdateSubtreeRootsAction: Action {
|
|||
throw ZcashError.serviceSubtreeRootsStreamFailed(LightWalletServiceError.timeOut)
|
||||
}
|
||||
|
||||
logger.info("Sapling tree has \(roots.count) subtrees")
|
||||
logger.debug("Sapling tree has \(roots.count) subtrees")
|
||||
do {
|
||||
try await rustBackend.putSaplingSubtreeRoots(startIndex: UInt64(request.startIndex), roots: roots)
|
||||
|
||||
|
|
|
@ -478,6 +478,7 @@ extension CompactBlockProcessor {
|
|||
// swiftlint:disable:next cyclomatic_complexity
|
||||
private func run() async {
|
||||
logger.debug("Starting run")
|
||||
metrics.cbpStart()
|
||||
await resetContext()
|
||||
|
||||
while true {
|
||||
|
@ -513,6 +514,7 @@ extension CompactBlockProcessor {
|
|||
try Task.checkCancellation()
|
||||
|
||||
// Execute action.
|
||||
metrics.actionStart(state)
|
||||
context = try await action.run(with: context) { [weak self] event in
|
||||
await self?.send(event: event)
|
||||
if let progressChanged = await self?.compactBlockProgress.hasProgressUpdated(event), progressChanged {
|
||||
|
@ -633,6 +635,7 @@ extension CompactBlockProcessor {
|
|||
|
||||
private func syncFinished() async -> Bool {
|
||||
logger.debug("Sync finished")
|
||||
metrics.logCBPOverviewReport(logger)
|
||||
let latestBlockHeightWhenSyncing = await context.syncControlData.latestBlockHeight
|
||||
let latestBlockHeight = await latestBlocksDataProvider.latestBlockHeight
|
||||
// If `latestBlockHeightWhenSyncing` is 0 then it means that there was nothing to sync in last sync process.
|
||||
|
|
|
@ -196,23 +196,12 @@ actor BlockDownloaderImpl {
|
|||
logger.debug("Downloading blocks in range: \(range.lowerBound)...\(range.upperBound)")
|
||||
|
||||
var startTime = Date()
|
||||
var counter = 0
|
||||
var lastDownloadedBlockHeight = -1
|
||||
|
||||
let pushMetrics: (BlockHeight, Date, Date) -> Void = { [metrics] _, startTime, finishTime in
|
||||
metrics.pushProgressReport(
|
||||
start: startTime,
|
||||
end: finishTime,
|
||||
batchSize: maxBlockBufferSize,
|
||||
operation: .downloadBlocks
|
||||
)
|
||||
}
|
||||
|
||||
for _ in stride(from: range.lowerBound, to: range.upperBound + 1, by: 1) {
|
||||
try Task.checkCancellation()
|
||||
guard let block = try await stream.nextBlock() else { break }
|
||||
|
||||
counter += 1
|
||||
lastDownloadedBlockHeight = block.height
|
||||
|
||||
buffer.append(block)
|
||||
|
@ -222,17 +211,10 @@ actor BlockDownloaderImpl {
|
|||
try await blocksBufferWritten(buffer)
|
||||
buffer.removeAll(keepingCapacity: true)
|
||||
|
||||
pushMetrics(block.height, startTime, finishTime)
|
||||
|
||||
counter = 0
|
||||
startTime = finishTime
|
||||
}
|
||||
}
|
||||
|
||||
if counter > 0 {
|
||||
pushMetrics(lastDownloadedBlockHeight, startTime, Date())
|
||||
}
|
||||
|
||||
try await storage.write(blocks: buffer)
|
||||
try await blocksBufferWritten(buffer)
|
||||
}
|
||||
|
|
|
@ -96,6 +96,7 @@ extension BlockEnhancerImpl: BlockEnhancer {
|
|||
|
||||
guard !transactions.isEmpty else {
|
||||
logger.debug("no transactions detected on range: \(range.lowerBound)...\(range.upperBound)")
|
||||
logger.sync("No transactions detected on range: \(range.lowerBound)...\(range.upperBound)")
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -134,12 +135,10 @@ extension BlockEnhancerImpl: BlockEnhancer {
|
|||
}
|
||||
}
|
||||
|
||||
metrics.pushProgressReport(
|
||||
start: startTime,
|
||||
end: Date(),
|
||||
batchSize: range.count,
|
||||
operation: .enhancement
|
||||
)
|
||||
let endTime = Date()
|
||||
let logMsg = "Enhanced \(transactions.count) transaction(s) in \(endTime.timeIntervalSince1970 - startTime.timeIntervalSince1970) for range \(range.lowerBound)...\(range.upperBound)"
|
||||
logger.sync(logMsg)
|
||||
metrics.actionDetail(logMsg, for: .enhance)
|
||||
} catch {
|
||||
logger.error("error enhancing transactions! \(error)")
|
||||
throw error
|
||||
|
|
|
@ -85,13 +85,6 @@ extension UTXOFetcherImpl: UTXOFetcher {
|
|||
}
|
||||
}
|
||||
|
||||
metrics.pushProgressReport(
|
||||
start: startTime,
|
||||
end: Date(),
|
||||
batchSize: 1,
|
||||
operation: .fetchUTXOs
|
||||
)
|
||||
|
||||
let result = (inserted: refreshed, skipped: skipped)
|
||||
|
||||
if Task.isCancelled {
|
||||
|
|
|
@ -69,16 +69,10 @@ extension BlockScannerImpl: BlockScanner {
|
|||
if scannedNewBlocks {
|
||||
try await didScan(lastScannedHeight, batchSize)
|
||||
|
||||
metrics.pushProgressReport(
|
||||
start: scanStartTime,
|
||||
end: scanFinishTime,
|
||||
batchSize: Int(batchSize),
|
||||
operation: .scanBlocks
|
||||
)
|
||||
|
||||
let heightCount = lastScannedHeight - previousScannedHeight
|
||||
let seconds = scanFinishTime.timeIntervalSinceReferenceDate - scanStartTime.timeIntervalSinceReferenceDate
|
||||
logger.debug("Scanned \(heightCount) blocks in \(seconds) seconds")
|
||||
logger.sync("Scanned \(heightCount) blocks in \(seconds) seconds")
|
||||
}
|
||||
|
||||
await Task.yield()
|
||||
|
|
|
@ -7,252 +7,131 @@
|
|||
|
||||
import Foundation
|
||||
|
||||
/// SDK's tool for the measurement of metrics.
|
||||
/// The barebone API of the `SDKMetrics` is all about turning it on/off, pushing new reports in and popping RAW data out.
|
||||
/// The processing of data is either left to the user of `SDKMetrics` or anybody can take an advantage of extension APIs
|
||||
/// providing useful structs and reports.
|
||||
///
|
||||
/// Usage:
|
||||
/// The `SDKMetrics` API has been designed so it has the lowest impact possible on the SDK itself.
|
||||
/// Reporting of the metrics is already in place but ignored until `enableMetrics()` is called. Once turned on, the data is collected
|
||||
/// and cumulated to the in memory structural storage until `disableMetrics()` is called.
|
||||
/// `disableMetrics()` also clears out the in memory storage.
|
||||
///
|
||||
/// To collect data and process it there are 2 ways:
|
||||
///
|
||||
/// 1.
|
||||
/// Get RAW data by calling either `popBlock` or `popAllBlockReports`. The post-processing of data is then delegated to the caller.
|
||||
///
|
||||
/// 2.
|
||||
/// Get cumulated data by using an extension APIs. For the summarized collection, call `cumulativeSummary()`.
|
||||
/// Sometimes, typically when you want to run several iterations, the `cumulateReportsAndStartNewSet()` automatically computes
|
||||
/// cumulativeSummary, stores it and starts to collect a new set. All summaries can be either processed by a caller,
|
||||
/// accessing the collection `cumulativeSummaries` directly or values can be merged into one final summary by calling `summarizedCumulativeReports()`.
|
||||
///
|
||||
/// We encourage you to check`SDKMetricsTests` and other tests in the Test/PerformanceTests/ folder.
|
||||
public class SDKMetrics {
|
||||
public struct BlockMetricReport: Equatable {
|
||||
public let batchSize: Int
|
||||
public let startTime: TimeInterval
|
||||
public let endTime: TimeInterval
|
||||
public var duration: TimeInterval { endTime - startTime }
|
||||
}
|
||||
|
||||
public enum Operation {
|
||||
case downloadBlocks
|
||||
case scanBlocks
|
||||
case enhancement
|
||||
case fetchUTXOs
|
||||
}
|
||||
|
||||
public struct SyncReport: Equatable {
|
||||
public let startTime: TimeInterval
|
||||
public let endTime: TimeInterval
|
||||
public var duration: TimeInterval { endTime - startTime }
|
||||
}
|
||||
|
||||
public var cumulativeSummaries: [CumulativeSummary] = []
|
||||
public var syncReport: SyncReport?
|
||||
var isEnabled = false
|
||||
var reports: [Operation: [BlockMetricReport]] = [:]
|
||||
protocol SDKMetrics {
|
||||
func cbpStart()
|
||||
func actionStart(_ action: CBPState)
|
||||
func actionDetail(_ detail: String, `for` action: CBPState)
|
||||
func actionStop()
|
||||
func logCBPOverviewReport(_ logger: Logger)
|
||||
}
|
||||
|
||||
final class SDKMetricsImpl: SDKMetrics {
|
||||
public struct CBPStateMetricReport: Equatable {
|
||||
static let zero = Self(runs: 0, startTime: 0, cummulativeTime: 0, maxTime: 0, avgTime: 0)
|
||||
|
||||
var runs: Int
|
||||
var startTime: TimeInterval
|
||||
var cummulativeTime: TimeInterval
|
||||
var minTime: TimeInterval = .infinity
|
||||
var maxTime: TimeInterval
|
||||
var avgTime: TimeInterval
|
||||
|
||||
var details: [String] = []
|
||||
}
|
||||
|
||||
// Compact Block Processor Metrics
|
||||
var syncs = 0
|
||||
var cbpStartTime: TimeInterval = 0
|
||||
var cbpOverview: [CBPState: CBPStateMetricReport] = [:]
|
||||
var lastActionInRun: CBPState?
|
||||
|
||||
public init() { }
|
||||
|
||||
/// `SDKMetrics` is disabled by default. Any pushed data are simply ignored until `enableMetrics()` is called.
|
||||
public func enableMetrics() {
|
||||
isEnabled = true
|
||||
|
||||
func cbpStart() {
|
||||
syncs += 1
|
||||
cbpStartTime = Date().timeIntervalSince1970
|
||||
|
||||
// reset of previous values
|
||||
cbpOverview.removeAll()
|
||||
}
|
||||
|
||||
func actionStart(_ action: CBPState) {
|
||||
actionStop()
|
||||
|
||||
lastActionInRun = action
|
||||
|
||||
var report = CBPStateMetricReport.zero
|
||||
|
||||
if let reportFound = cbpOverview[action] {
|
||||
report = reportFound
|
||||
}
|
||||
|
||||
report.runs += 1
|
||||
report.startTime = Date().timeIntervalSince1970
|
||||
|
||||
cbpOverview[action] = report
|
||||
}
|
||||
|
||||
public func disableMetrics() {
|
||||
isEnabled = false
|
||||
clearAll()
|
||||
}
|
||||
|
||||
/// `SDKMetrics` focuses deeply on sync process and metrics related to it. By default there are reports around
|
||||
/// block operations like download, validate, etc. This method pushes data on a stack for the specific operation.
|
||||
func pushProgressReport(
|
||||
start: Date,
|
||||
end: Date,
|
||||
batchSize: Int,
|
||||
operation: Operation
|
||||
) {
|
||||
guard isEnabled else { return }
|
||||
|
||||
let blockMetricReport = BlockMetricReport(
|
||||
batchSize: batchSize,
|
||||
startTime: start.timeIntervalSinceReferenceDate,
|
||||
endTime: end.timeIntervalSinceReferenceDate
|
||||
)
|
||||
|
||||
guard reports[operation] != nil else {
|
||||
reports[operation] = [blockMetricReport]
|
||||
func actionDetail(_ detail: String, `for` action: CBPState) {
|
||||
guard var report = cbpOverview[action] else {
|
||||
return
|
||||
}
|
||||
|
||||
reports[operation]?.append(blockMetricReport)
|
||||
report.details.append(detail)
|
||||
|
||||
cbpOverview[action] = report
|
||||
}
|
||||
|
||||
/// Block synchronisation consists of operations but the whole process is measured also, represented by
|
||||
/// different struct `SyncReport`, missing specifics for the operations like batch size, etc.
|
||||
/// Used for the total syncing time report in the first place.
|
||||
func pushSyncReport(
|
||||
start: Date,
|
||||
end: Date
|
||||
) {
|
||||
guard isEnabled else { return }
|
||||
|
||||
let syncReport = SyncReport(
|
||||
startTime: start.timeIntervalSinceReferenceDate,
|
||||
endTime: end.timeIntervalSinceReferenceDate
|
||||
)
|
||||
|
||||
self.syncReport = syncReport
|
||||
}
|
||||
|
||||
/// A method allowing users of the `SDKMetrics` to pop the RAW data out of the system. For the specific `operation`
|
||||
/// with option to either leave data in the storage or flushing it out and start the next batch of collecting new ones.
|
||||
public func popBlock(operation: Operation, flush: Bool = false) -> [BlockMetricReport]? {
|
||||
defer {
|
||||
if flush { clearReport(operation) }
|
||||
func actionStop() {
|
||||
guard let lastActionInRun else {
|
||||
return
|
||||
}
|
||||
|
||||
return reports[operation]
|
||||
}
|
||||
|
||||
/// A method allowing users of the `SDKMetrics` to pop the RAW data out of the system. This time for all measured operations
|
||||
/// with option to either leave data in the storage or flushing it out and start the next batch of collecting new ones.
|
||||
public func popAllBlockReports(flush: Bool = false) -> [Operation: [BlockMetricReport]] {
|
||||
defer {
|
||||
if flush { clearAllBlockReports() }
|
||||
guard var report = cbpOverview[lastActionInRun] else {
|
||||
return
|
||||
}
|
||||
|
||||
let endTime = Date().timeIntervalSince1970
|
||||
let runTime = endTime - report.startTime
|
||||
|
||||
report.cummulativeTime += runTime
|
||||
|
||||
if runTime < report.minTime {
|
||||
report.minTime = runTime
|
||||
}
|
||||
|
||||
if runTime > report.maxTime {
|
||||
report.maxTime = runTime
|
||||
}
|
||||
|
||||
return reports
|
||||
}
|
||||
|
||||
func clearReport(_ operation: Operation) {
|
||||
reports.removeValue(forKey: operation)
|
||||
}
|
||||
|
||||
func clearAllBlockReports() {
|
||||
reports.removeAll()
|
||||
cumulativeSummaries.removeAll()
|
||||
}
|
||||
|
||||
func clearAll() {
|
||||
clearAllBlockReports()
|
||||
syncReport = nil
|
||||
}
|
||||
}
|
||||
|
||||
/// This extension provides an API that provides the summary and accumulated reports.
|
||||
/// The RAW data can pulled out and be processed without this extension but we
|
||||
/// wanted to provide a way how to get essential summaries right from the SDK.
|
||||
extension SDKMetrics {
|
||||
public struct CumulativeSummary: Equatable {
|
||||
public let downloadedBlocksReport: ReportSummary?
|
||||
public let scannedBlocksReport: ReportSummary?
|
||||
public let enhancementReport: ReportSummary?
|
||||
public let fetchUTXOsReport: ReportSummary?
|
||||
public let totalSyncReport: ReportSummary?
|
||||
}
|
||||
|
||||
public struct ReportSummary: Equatable {
|
||||
public let minTime: TimeInterval
|
||||
public let maxTime: TimeInterval
|
||||
public let avgTime: TimeInterval
|
||||
|
||||
public static let zero = Self(minTime: 0, maxTime: 0, avgTime: 0)
|
||||
}
|
||||
|
||||
/// This method takes all the RAW data and computes a `CumulativeSummary` for every `operation`
|
||||
/// independently. A `ReportSummary` is the result per `operation`, providing min, max and avg times.
|
||||
public func cumulativeSummary() -> CumulativeSummary {
|
||||
let downloadReport = summaryFor(reports: reports[.downloadBlocks])
|
||||
let scanReport = summaryFor(reports: reports[.scanBlocks])
|
||||
let enhancementReport = summaryFor(reports: reports[.enhancement])
|
||||
let fetchUTXOsReport = summaryFor(reports: reports[.fetchUTXOs])
|
||||
var totalSyncReport: ReportSummary?
|
||||
|
||||
if let duration = syncReport?.duration {
|
||||
totalSyncReport = ReportSummary(minTime: duration, maxTime: duration, avgTime: duration)
|
||||
if report.runs > 0 {
|
||||
report.avgTime = report.cummulativeTime / Double(report.runs)
|
||||
}
|
||||
|
||||
return CumulativeSummary(
|
||||
downloadedBlocksReport: downloadReport,
|
||||
scannedBlocksReport: scanReport,
|
||||
enhancementReport: enhancementReport,
|
||||
fetchUTXOsReport: fetchUTXOsReport,
|
||||
totalSyncReport: totalSyncReport
|
||||
)
|
||||
}
|
||||
|
||||
/// This method computes the `CumulativeSummary` for the RAW data already in the system, stores it
|
||||
/// and leave room for collecting new RAW data. Typical use case is when some code is expected to run several times
|
||||
/// and every run is expected to be a new data collection.
|
||||
/// Usage of this API is then typically followed by calling `summarizedCumulativeReports()` which merges all stored
|
||||
/// cumulative reports into one final report.
|
||||
public func cumulateReportsAndStartNewSet() {
|
||||
cumulativeSummaries.append(cumulativeSummary())
|
||||
reports.removeAll()
|
||||
syncReport = nil
|
||||
}
|
||||
|
||||
/// This method takes all `CumulativeSummary` reports and merge them all together, providing
|
||||
/// final `CumulativeSummary` per `operation`, ensuring right min and max values are in the place
|
||||
/// as well as computes final avg time per `operation`.
|
||||
public func summarizedCumulativeReports() -> CumulativeSummary? {
|
||||
var finalSummary: CumulativeSummary?
|
||||
|
||||
cumulativeSummaries.forEach { summary in
|
||||
finalSummary = CumulativeSummary(
|
||||
downloadedBlocksReport: accumulate(left: finalSummary?.downloadedBlocksReport, right: summary.downloadedBlocksReport),
|
||||
scannedBlocksReport: accumulate(left: finalSummary?.scannedBlocksReport, right: summary.scannedBlocksReport),
|
||||
enhancementReport: accumulate(left: finalSummary?.enhancementReport, right: summary.enhancementReport),
|
||||
fetchUTXOsReport: accumulate(left: finalSummary?.fetchUTXOsReport, right: summary.fetchUTXOsReport),
|
||||
totalSyncReport: accumulate(left: finalSummary?.totalSyncReport, right: summary.totalSyncReport)
|
||||
)
|
||||
}
|
||||
|
||||
return finalSummary
|
||||
cbpOverview[lastActionInRun] = report
|
||||
}
|
||||
|
||||
/// Internal helper method that accumulates `ReportSummary` times.
|
||||
func accumulate(left: ReportSummary?, right: ReportSummary?) -> ReportSummary? {
|
||||
guard let left, let right else {
|
||||
if let right {
|
||||
return ReportSummary(
|
||||
minTime: right.minTime,
|
||||
maxTime: right.maxTime,
|
||||
avgTime: right.avgTime
|
||||
)
|
||||
// swiftlint:disable string_concatenation
|
||||
func logCBPOverviewReport(_ logger: Logger) {
|
||||
actionStop()
|
||||
|
||||
var resText = "SYNC (\(syncs)) REPORT\n"
|
||||
|
||||
resText += "finished in: \(Date().timeIntervalSince1970 - cbpStartTime)\n\n"
|
||||
|
||||
for action in cbpOverview {
|
||||
let report = action.value
|
||||
|
||||
resText += """
|
||||
action: \(action.key)
|
||||
runs: \(report.runs)
|
||||
cummulativeTime: \(report.cummulativeTime)
|
||||
minTime: \(report.minTime)
|
||||
maxTime: \(report.maxTime)
|
||||
avgTime: \(report.avgTime)
|
||||
"""
|
||||
|
||||
if !report.details.isEmpty {
|
||||
resText += "\ndetails:\n"
|
||||
|
||||
for detail in report.details {
|
||||
resText += "\t\(detail)\n"
|
||||
}
|
||||
}
|
||||
return nil
|
||||
|
||||
resText += "\n\n"
|
||||
}
|
||||
|
||||
return ReportSummary(
|
||||
minTime: min(left.minTime, right.minTime),
|
||||
maxTime: max(left.maxTime, right.maxTime),
|
||||
avgTime: (left.avgTime + right.avgTime) * 0.5
|
||||
)
|
||||
}
|
||||
|
||||
/// Internal helper method that computes min, max and avg times for the `BlockMetricReport` collection.
|
||||
func summaryFor(reports: [BlockMetricReport]?) -> ReportSummary? {
|
||||
guard let reports, !reports.isEmpty else { return nil }
|
||||
|
||||
var min: TimeInterval = 99999999.0
|
||||
var max: TimeInterval = 0.0
|
||||
var avg: TimeInterval = 0.0
|
||||
|
||||
reports.forEach { report in
|
||||
let duration = report.duration
|
||||
avg += duration
|
||||
if duration > max { max = duration }
|
||||
if duration < min { min = duration }
|
||||
}
|
||||
// reports.count is guarded to never be a zero
|
||||
avg /= TimeInterval(reports.count)
|
||||
|
||||
return ReportSummary(minTime: min, maxTime: max, avgTime: avg)
|
||||
logger.sync(resText)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "main",
|
||||
"height": "2270000",
|
||||
"hash": "0000000000c4314de2a15c56249dfe277ff51e1d6a179a5ae3c01e709d80f07d",
|
||||
"time": 1697958631,
|
||||
"saplingTree": "014c6ab9468d295866f051ee781d91aca0e7542e143680c89f8cb5c9e72a9838720158af6691c73faa4c3afd7efcbbffa362384f1f3f9eef160de6a9c8ac8bd81d391a0000012731c443460effc12a99483ac5d5c9540cf6edc9932bdee163f331df76218c5b000001adfa0efcad0c23520a4e2a6865ecf1fbdae3851d2019340a1caa84c854fa3e0f01229b6ab4de80a3843c039bd50b9687460b5c04c29b4c5d9baef2742bdb816a0c013b67cbdd09009cd311022460c802b77cce63c5e661b4dcd5543ed98cbdccb8660113ea747404c27dbf347dc87f56796d7a7654455cc9f81db36a1da7b2b063661401d9025bc766e91070f70716c15fb3a8f85525c5b082a0bc9ec77cd667e7b07d38015cc47e57f3c7a46b570ec6f664d5e39e67c73bdaee1e006dc32f9d6fbbf2eb2c01090b969f4a6c25a0bcf22b976c7c7a1145320eb0620ff4da3ac1cc6627b2720301f71ed6dee66571dddf2e1cbc239b50cbd1867ac519ccbc491b3a1284f05b860401ca2ef102fb828472bb323b8e334a3f7dd188b4bd043df1f9e5b525b654bf27200001ffdd1a0628296bf274daa5ea13fb57818d678957d49f91ccd69efae0bac249340182ac7ebe2f7e9d8084a38aa8be56b1bd86292babf05f98882a6959cf9d318c110001d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e",
|
||||
"orchardTree": "01b2c39d0a9083b615af23168684321b83b0ffaa65a242f168ffca783520910a34001f01c76b3e598049fe3a88fafc213451d6c6bc4758a52c5620300b9482ebf67caa3201034d2240ab404d03f6231989d1aec297566daafe798a3ce71e61cb6de526aa1200019db5c0c598c9db3e4bf851e29ce058878840d7dad804713a251cf8d8e9d8c50601d4962b21d6711214c867706a8e50b07616e92ee8c867ab3fae5912b86a71c7380001d078599b76eba7ce4b38db395b4ca6c289afb1e139ba0ca4281d33fdc5c23d2701acbca7b28d294abc7cec56fe1ad4e653578e188012d70361700803254f0a8934000001ed0b8645ba722e2aeef2cfff7374a7dba95d92a6438276ddca7c76f6f2a3ae1600012ffae608e1b4d4de4f5ac38068b613d9761ba34d95430e5ec8d058848cc9c0250001dd3e61f3e1d497d923486bd000737ea0ffac885fa481316727d0d8f7fc2138100001cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "main",
|
||||
"height": "2272500",
|
||||
"hash": "000000000101df774d2a45008af97d1cbc62117ce8a2635e8be8432de50c18cd",
|
||||
"time": 1698147530,
|
||||
"saplingTree": "018f896dbdedae2e50c0cce982c8204141730b196f745175a82830ca9a47fedf48017a8a234509e3c64c49b5229d8b6589f6389832dfd7ee95696008442827f5116f1a01f16748b8fa01f368a968b5d6f45aa499540bb566314328d4337ea9bad0d69719000001c698abd29732e8d765df5aa15db8652a347aa6dd9e1592f200d3e5bfe66ad26200000129cee479373bc283fdf83c02b3a019178bfa1f8a34a868092a75022cdf43db3a01095b14c824594a5311d7f29406e99dea20cf53b90a27a12df4bdf8b1281fb5080001b099b710de3efd37a880d4372c44c7388d14fdf4a34ca2d97a6ac1acf33d6817000195690f105db42ae533ed2406bd24155128007fb2fbc80676b4e59a05d7aec52100000143d0958ed592bc13ecf47b2664d9087554b43c69f01d23247d1323006a4d750601ffdd1a0628296bf274daa5ea13fb57818d678957d49f91ccd69efae0bac249340182ac7ebe2f7e9d8084a38aa8be56b1bd86292babf05f98882a6959cf9d318c110001d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e",
|
||||
"orchardTree": "01cdcfa81709e09b8ddbbef40c2953d9222f79a7fd490bb0270fb5182ca3f7ba01001f0199ce56729f4c6e25622d23e77c8f8edbb89e1e65bc042d7b08e0b720d2e3fc1800011eb748c95af57b028618c39fc1ef4d2491578c1a91040224fedc95ec3f6a8f2d018422698954742926542754dc904a3e5b8cb7a1b9411df69fa549496be51e4a1700000000000189a2e7029abe265a7acf4227791eae37c48bb19e21cb2f95d9a87e8914f56e3901ed0b8645ba722e2aeef2cfff7374a7dba95d92a6438276ddca7c76f6f2a3ae1600012ffae608e1b4d4de4f5ac38068b613d9761ba34d95430e5ec8d058848cc9c0250001dd3e61f3e1d497d923486bd000737ea0ffac885fa481316727d0d8f7fc2138100001cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "main",
|
||||
"height": "2275000",
|
||||
"hash": "0000000001a1d06d7297d87f0f7e0e49383b0f10dbe3e22e7a66ab1ba9cebf66",
|
||||
"time": 1698335616,
|
||||
"saplingTree": "015d9ff1fd129cb09cfb3322c4eb284bbb44a30b4027edf52ca5500a18da76382e01251130dcfbc8dbca704428db2fc252d1b63bfe9d8fa9a3a3ff5ad18fec0c266e1a000170b1341313ab46627dce9b636268c1aa457eea90b5ebcf7f0f0a3b8224d32b630000000001435c8a879c5799d05b4467d5de64193196e1793295cb85464e04aec99ef4b77101e508516f5c55360490aabf7dc59642c75dfa08a6a6532e7deb59a427b792360101b09f2068c37a8e89d92735a8672559c77aa4cb5d15d5a84f7c40d30b248dc15c00013bae7327f0e7b60dd2c09cd29e34da76f79da4f1b7593f611e2cc6cb45dcfc090001602f25e80946cd987a4d570593152f72bfe10b054be88436e82bfd9cbbf0cf06000143d0958ed592bc13ecf47b2664d9087554b43c69f01d23247d1323006a4d750601ffdd1a0628296bf274daa5ea13fb57818d678957d49f91ccd69efae0bac249340182ac7ebe2f7e9d8084a38aa8be56b1bd86292babf05f98882a6959cf9d318c110001d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e",
|
||||
"orchardTree": "010d092947e75a832b868d3dc43cd3d6556e5f3647da76165ac9506fb004930830001f015cc156677f54dc339ddd846e6f94232771f460e8e73e90abec57bd5c7f7cfe34000001f536aced4e2e37cf02ed4b6aa6ac8fb7fa6610f0370020e6d1ac10995d2f8928000001cb1efaf88c34aabae5756dfaaf3d03856d70889efbab49b38496c10987a40a3e0001c13878d4402edfd680750b5cf58e79190819928dc7a553e75d8f14763894ca3d0189a2e7029abe265a7acf4227791eae37c48bb19e21cb2f95d9a87e8914f56e3901ed0b8645ba722e2aeef2cfff7374a7dba95d92a6438276ddca7c76f6f2a3ae1600012ffae608e1b4d4de4f5ac38068b613d9761ba34d95430e5ec8d058848cc9c0250001dd3e61f3e1d497d923486bd000737ea0ffac885fa481316727d0d8f7fc2138100001cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "main",
|
||||
"height": "2277500",
|
||||
"hash": "0000000000a4dd1adf30d25e02984056504ff96d76fae62f7085458695a411a2",
|
||||
"time": 1698524343,
|
||||
"saplingTree": "01fc5d5e38cffb57e9dbb61e285b960a1e63f005dc8e7e709fc22014c36088ef3f001a0001de0ed3aa986e08d4e303387490dce97cb7c99e54e02d16256446718935ccb62b000001da1e0dd9a0db7d398862c6cc76b3743b7c33b5a032f3e96644102508c20e473801ad5fef9438eae8dd617eabaa220a1865d6284ae46488b667abe397753c4cd2200000000000000001fb99b6df98e30fbb8d0b50b4d4af9777346c943d2c2718a5df0c531667e6ba530143d0958ed592bc13ecf47b2664d9087554b43c69f01d23247d1323006a4d750601ffdd1a0628296bf274daa5ea13fb57818d678957d49f91ccd69efae0bac249340182ac7ebe2f7e9d8084a38aa8be56b1bd86292babf05f98882a6959cf9d318c110001d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e",
|
||||
"orchardTree": "012d9d285babba1461ab28ef71eb6fc228124379fcc5eeece62a00007c676396150122ef493fe98e3fd2afd981a1c596e1b02837036ef50703860847eb1854bed8351f0000015e25ce67f68c5a9f167148a80d46d897c6ccc05dff8ad80f9f7eb429ff0a3d2b01e2e14918c25cd079460a5088418de57cf996e0b7cee619d1c639ee9ef41c671301cdfa1c1b946fc22d67e3a1a9b0485597625481268119937f4c5816d8eb8fe2390187685cddc66dce2bd8e05e6bb26cbb4dbc57e6d06055012ee17af7956caf132b01ba45e35cc7c794e859b2723b5467389966ed6d2b50d6e487d961d10f8786f41300000000014f4953491ea4a29604f7f1935e3fde4d40851918267a9340aa73d3375b86ed3a012ffae608e1b4d4de4f5ac38068b613d9761ba34d95430e5ec8d058848cc9c0250001dd3e61f3e1d497d923486bd000737ea0ffac885fa481316727d0d8f7fc2138100001cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "main",
|
||||
"height": "2280000",
|
||||
"hash": "000000000140ca5ed3a137186ce43f05f25b260c4655851dfa7bc43ea8a5c72b",
|
||||
"time": 1698712464,
|
||||
"saplingTree": "011e05ce06ba3378e547a8a42b98ebb4f6a2c8e73f05361f1b42be375710ce8237001a0001f53aa634664cd779fd9756c12663f4d0629788dbb16554b3a841845c6ac428180001831cf884b406defb117444655fac7c18afd9730a0b73226309e3e541b4e91908014de6d8ce3075eaa6614391761b0aa7c82000ac7c4824718e906165cb836a0b2e016a754768870d1b80d47f335ae06e841586edc9c1d24a0ffcbd27ac0d682f1a3e01978df5cf6aa9b70cd809ea1c47431dc83ff970819d7975297132870a25044105010f081ad9f6048010f58038c0d072a8a0c58955c6b88b561976d47724eaed313a00018e6514477b4ffa248d092e60b08f25ac7bd83b376f17330062ee12e13d29f56c0001730e5ddc4703c047da1756a95be56bd2e4fd3b8985af48e6b47c5ec98b084c090001fb99b6df98e30fbb8d0b50b4d4af9777346c943d2c2718a5df0c531667e6ba530143d0958ed592bc13ecf47b2664d9087554b43c69f01d23247d1323006a4d750601ffdd1a0628296bf274daa5ea13fb57818d678957d49f91ccd69efae0bac249340182ac7ebe2f7e9d8084a38aa8be56b1bd86292babf05f98882a6959cf9d318c110001d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e",
|
||||
"orchardTree": "0148572bb05a2e3580007a9f0f5472130ab275dc5885d0d5170eee00a46dcff83b001f0001096ffd805bb818893d4b2ebdfed87eb1e54a95f66d9af7057c1ccc9bd27ac6060001c641afdad2ab3db87674559f9e3ae5ddbf4751cbea1a89c4c76bbda291635937010eaa5be52e7d91a8a4965a41a3dfe3bb61195d5d529bc354dcc13c3ccd4ba608000001b34d2602cfdab5c035a7ad3b66943a9ed55500fecf62dee06e305893bd7c671a0181fe0236f1875abc9be3db2a15d90d07b8f47ddd820f2c184cecc4c600eab12d0000014f4953491ea4a29604f7f1935e3fde4d40851918267a9340aa73d3375b86ed3a012ffae608e1b4d4de4f5ac38068b613d9761ba34d95430e5ec8d058848cc9c0250001dd3e61f3e1d497d923486bd000737ea0ffac885fa481316727d0d8f7fc2138100001cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "main",
|
||||
"height": "2282500",
|
||||
"hash": "0000000000dea1da65f7c7fb38ddb0b11a6d29c95e1f304cbd04451201282939",
|
||||
"time": 1698900669,
|
||||
"saplingTree": "01756a28016aca946bedcb35376db79860fb150163df7246e0127af71ff0a0a020001a01cc10c94b8d4cd542d974a8e7dde7e3d7c41e4b94c9a6eb3c43ee936f562d521b01142843d118ac16ca4e6426e9e874c4a922001e86e7a3dc700e532a8af6b1496500013e7baa0b1252773c12796f433bfd2ef548f272a25b3a454f8a76ef7a0af5ed55016300c5c70e2afe30c30a40e762a6f323798bddf2f5986516649b965ea828ac5500000000015445ffff80d5f45be983d38f5e803facecbbc23fb1199dfad3b11cf3c51926180174488dfd0763a57c02b40c1b9b26fb289357b41cec6191ede85b385ba5288e0800019850bfbfe34e5e816c2f73f9fef1a74c6088e93930517a29a4a0e6000d1b510401fb99b6df98e30fbb8d0b50b4d4af9777346c943d2c2718a5df0c531667e6ba530143d0958ed592bc13ecf47b2664d9087554b43c69f01d23247d1323006a4d750601ffdd1a0628296bf274daa5ea13fb57818d678957d49f91ccd69efae0bac249340182ac7ebe2f7e9d8084a38aa8be56b1bd86292babf05f98882a6959cf9d318c110001d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e",
|
||||
"orchardTree": "01030ed7b60fa7f2cd6fc773e57d1ead213ad15134000d6941a39e766bade6232101a9d4c282339305676f8189c920263fa6e42b82a707e93d6496cf9dec25189f1b1f01a71adf481108cbe2d25c907da3a069d0562a24fa83d058e163fa0b670371953c000001f264831f79e88c3ba899764c723a3f9b929b655b7ef046b78a74019ca14c3c34000174b81a801540db38a2ccc7c7a7b2133fe6d6a1a0ea4dad665319ff21c629940d01126f17a1966531b97f22559213db4af212a340a5a5d77f6b2a2ee020fa89632e014af93d6dd233c0d0e8a6d98b73170f15967d4d3f6e842da9ab822c125ac5c5380001c35b0d064585c89bc92632c1698020ab8182daede7217bd50f223124fd717c0100014f4953491ea4a29604f7f1935e3fde4d40851918267a9340aa73d3375b86ed3a012ffae608e1b4d4de4f5ac38068b613d9761ba34d95430e5ec8d058848cc9c0250001dd3e61f3e1d497d923486bd000737ea0ffac885fa481316727d0d8f7fc2138100001cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "main",
|
||||
"height": "2285000",
|
||||
"hash": "0000000001b67658580edf118870e75c4ab5577059685ddcb24d9c92b443382f",
|
||||
"time": 1699089648,
|
||||
"saplingTree": "012d44cc8e1d16e6852fc14639e98736668a7b5d028ab3159c5982940c9299e24f01868f46f3ed9f21ce98d27957a7f33b62c5bc4ebd3451013252c99401097f566e1a01bcc45bef6435eeefc68d237aa2acbe2e95e15997ea1e636fd9210f72ab10ff6d01c56bd1007ac121c76c8d858a74c6f8105e5836faa57fe9341414334ab559c9720186f5b62c921c7053bfde3fff715d8103e1d0bd2a2ddc13ec40dd47c2555df915011489c314b2b11f0e344836151e03a95578552c74f376da2a84022dad6d205d0801c282c5b40f298c8df4f35fb4a256e658b235ddbb0ff5961fc8bf9ded3c5e665f01b70423dee0ff8ef547760df373ae704fd6b659bc23f787bbbdaa3108a6a8661f0001b5d3445be440b9b5e51ca2a79ba867948f5fb528f1647470dad9b2f8edfa702100000000000000000001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e",
|
||||
"orchardTree": "01198d97b218abf3a512b1ea5ea22615af4272955033f20043ebf25fb263803414001f0133af1c23ceeb554627b4647ce8ec33986c39e728071ae4940e1b9f833dfb4e000001e80bc8a0c15b7ae11eb61cef6605c3b4c4a550ccd215b49108a03cb8a566c81e00018a886a9ca0854d493406dc6b48d3ad27e67b27473c5ade974309778862d68f1d01ddc8cd44d9f82d7781860ce7d73b1f843f5ec912a129aa68f6207ac5b0195e0700000000016c3d712fa25ffe81b7565bf0970dd2cef0b14df1ba1719ba1589349a10610b2b014f4953491ea4a29604f7f1935e3fde4d40851918267a9340aa73d3375b86ed3a012ffae608e1b4d4de4f5ac38068b613d9761ba34d95430e5ec8d058848cc9c0250001dd3e61f3e1d497d923486bd000737ea0ffac885fa481316727d0d8f7fc2138100001cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "main",
|
||||
"height": "2287500",
|
||||
"hash": "0000000000757aa79361a32650287f111bc1977c1cf671ce0a139f870e014b0c",
|
||||
"time": 1699277300,
|
||||
"saplingTree": "0149db9481cf9d90582a6d635957c125163626f0c7dedb00f38d460f2f56a29d3e001a01099b44d4f58af7097a0ade2a0e5c0a575b43bdbc740214b8f23e5d48e987db540193bac04c4aad7b9aa65a15791526e7ff12b0a747db79a1062145f6c34554556b0166a1e7b8e5e92f271074957a8671311de5aa262151b60c78df13b4aa57f1a00e01b637e75c296556d34d65e3d960ec98fadfb1f788071a3d59b2056fe629490337015399c88cb4e5242bb76014c7073f65916318fa6aec9f70007be0cc1f322b2e36000103e597a1d37b8bc6569618464753213c48f71801350fa8fe0b5b38f73b83dc280001d2aee58f847d62b7daab3ad6e7b8036bff83cc6cf7761fe257aaea1c98f5fc13011622bf09c438466da1f14f2e94866e486566fd36edc459779d99102426c00e390001e6bf5bd7ec2b3e8c5bd194500babe084eafde1c0d35a2d3cb640dea7d15ed85e000000000001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e",
|
||||
"orchardTree": "013f4e84041192636736575b21268768777a4f3b6f415076dc6f47ebc1bb80643001d84be4fb5cfa5cf4a05ebd02091a254f8c00bc7bc8ba7afdb37b1c392f6f04051f01edd7857eb012cbb1958755fd52919cdcb3787dea8c2a9dd8c660b59f93a8d20701689a937f5e4fa984c43c60ee713dec372ff3eaa1b015249c5382176441b7302b0000014b46453a22fa268cc103f435c9b8af9391312fde360c860151236f630c3dc322018a4eaa3a1fc86c7d4c93132b463c9d3debce2bc9f176eecfa831c6fc5002a827000001c640fc3d8029cb55871f853502c7ac1137cdcf9f9b31746f05f24b5d494d863400016c3d712fa25ffe81b7565bf0970dd2cef0b14df1ba1719ba1589349a10610b2b014f4953491ea4a29604f7f1935e3fde4d40851918267a9340aa73d3375b86ed3a012ffae608e1b4d4de4f5ac38068b613d9761ba34d95430e5ec8d058848cc9c0250001dd3e61f3e1d497d923486bd000737ea0ffac885fa481316727d0d8f7fc2138100001cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "main",
|
||||
"height": "2290000",
|
||||
"hash": "00000000008bb1dde2ae2424eac14ff313cbe98184ff0ce14906a1b598a2e326",
|
||||
"time": 1699465348,
|
||||
"saplingTree": "014dd08e5128109754bc3f9cc5bd5792135851ef1ba944f7f11093cd829ee11f6d01847eec9edfe1b553aaa58e4cccd32af9d7b100bd4bd5877a57410614263c98271a01e973c5e44e96837efe3a1445a3090dd8f068235618de1b3989efaa2da638294400000128457ea2dcd149d9bc6396f889f1c87c330a8137d5226d3d4625235882e66654019fc0c24fc03f6111ad1f4bda55890cdde7ea9051693ed025f7b8caa4375d0532000199d133fef0424c9a384a5821d16802aa160eaac03e2de67f8a29a31828b8812301a715b831a08242e508a5c4da866634910c2315a5ed77f5219401297850247670018629eaa956de2399bb1234d32888cba94d3a8ad8abac3a95724ab09d1657a8320001f769333859670184606e0fb23864a2d55507d6f474d0a3bff3510cfb7b8ba7530001251f21b30ba98b69091a40adac6bda1159f58994df1b1e116d277d6ace9b69220000000001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e",
|
||||
"orchardTree": "0180a6c82f929467c2d84b698b226e7907054d80d0ef87c72b3f7e752555ef8d060113474a6c7bbfdd7b8b80dfe4e1ced724f3afd8022c507bc9fbbb37ba96b4a0001f0000000000010e67ad6365d0b475918235e8cda31a103f821ccad22dcba4ccef93e3c1c0181501f1130e13dd8ca300a64a4c732a4050230278c621a24eb126e5fd13c9ee8e110e000001e8a2e43418c17ecfd14ac24c026ee73f625633b20f723a46df9aad874efef406016c3d712fa25ffe81b7565bf0970dd2cef0b14df1ba1719ba1589349a10610b2b014f4953491ea4a29604f7f1935e3fde4d40851918267a9340aa73d3375b86ed3a012ffae608e1b4d4de4f5ac38068b613d9761ba34d95430e5ec8d058848cc9c0250001dd3e61f3e1d497d923486bd000737ea0ffac885fa481316727d0d8f7fc2138100001cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "main",
|
||||
"height": "2292500",
|
||||
"hash": "00000000014801ec195f78ec263f748937e51d9b438ff02b7b1ee07f2e710a0b",
|
||||
"time": 1699654836,
|
||||
"saplingTree": "01875808c0ce4642dd091aade8d5db6b4927c587efa5ebd0b7457052aa17493a2c001a010e20e6eeb9c9cc2c4b98957158bfce57a8a631c251bd3ab0fd7fd6c13f23482a000001841e41793f8b1b27fd5ca1dea68e6c22b95631405969939c60bd03b258a84f6f0113f375f185305df58801bfcd46d0eb5a6e9ea1c120c07146382960bfb8c053360101924d8f42a1b23bd3e869dd4eac63da1143abdbad6b5c3baec1e3a0d98c786701498d637205c7bbd6b7c88f2c32a89f9c74f32eef493ee616bc18d06ea440dd46018c108fed4b016b60b201ec936fee0598e48c1170a28fccad6a64eab3d628645401a5e6fa13e528a85d8a17f0f63714c4f329a48b30ddab1ea4a2c4db85d1695f6201af6fcef008a3470e0acad2cc76d4ca14091356daa614f64a58a16548e186ab590139006e8a757558976502ee21cc910f297f1f69ebd48082b9ebd396c2a0b04c5c01ea0b9e046b2c7470bee0408fcbd7b49f065fef6c337e37c23ac692fabb28d56501251f21b30ba98b69091a40adac6bda1159f58994df1b1e116d277d6ace9b69220000000001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e",
|
||||
"orchardTree": "01447b429e6388b5ae527a23ef66141f7ffc54b1c90621376da94c758499db3d2f001f01db7ab361b645ff9b5e61d31fb42773da682a045d1a432cf8133f530e4ac6a83e0112cabacd9c5bcb20f397f36b9e309ebb939132d8411413f73d895c3f4444780c0000019c001b2a6aba0e7a68d73ee6298b431b8014fa2269355716157814a9480fde2b00000176fd40208c8b7c179cf701e65c4dba33ac2fd75daee86e7894112caabf3f75310136b8e923cc82844773eb4ea694395cc983f01c8ee6d2e7f379e758a572bcab1f01e8a2e43418c17ecfd14ac24c026ee73f625633b20f723a46df9aad874efef406016c3d712fa25ffe81b7565bf0970dd2cef0b14df1ba1719ba1589349a10610b2b014f4953491ea4a29604f7f1935e3fde4d40851918267a9340aa73d3375b86ed3a012ffae608e1b4d4de4f5ac38068b613d9761ba34d95430e5ec8d058848cc9c0250001dd3e61f3e1d497d923486bd000737ea0ffac885fa481316727d0d8f7fc2138100001cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "main",
|
||||
"height": "2295000",
|
||||
"hash": "00000000001ce059723e23b0d3df9d9fb55a5c4f9373c744b3f20571dc462e50",
|
||||
"time": 1699842627,
|
||||
"saplingTree": "0170240a7369307d304008700051f4715c7b6e31272439149e57b9c3e5964ca34801894d24c2d36e2e2d1d1b687033d5cca5724cd2dadacf7d5c60c730ef319252151a014401ad1c7122d27d0211b0c8ae3262e5618154f59502bc213aaec3746780603e01ed45eed6f820fef18e7a9b28fb127863fbddc64bddab07b75eaa5659e5d62e180001be77df922409f48462e62dee48efd1919cc4519aeb357cb94b3d21eeecd4c42000019a1747e24ea7c6ecaaedfb546c16aeb0658e7f01bbba9060db9ed3dae55e1c1a000170723be8910c6964fc936e9eae09b4af8b733bdcddfde30d3e2d64d6aa1d8d0700018930903c559e12edd7ccddc2121e688db849318458ef04e8f5bb36e9d2b9045a0001d0470322b61e0c802a5a5ea4c4eed59f122fe2699dd082679db5eca34d35bc0a00017f30bab3d865e778bb8423a91092cb8406cd2b56fa2cfd7902bfc3640d6b6b7200000001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e",
|
||||
"orchardTree": "01ad5c9080c6c851baea50e3a6233e518b79026d4ea6907896ecde893149547f30001f018fea81778edf9edaf8118848a5afe66577015dc9a6011344fbb3fcc38a279e2b011f72b50b566081cb11acc23d0a3813424e85b82826b7cecac21860e4d0ccca1201d7ce154714faf6530e987e49becdee999d6d586e2133790d1b5ebf480cbe030d013c52e2d4527af8006bfa08486fb6b623bce8388818f7b912b0fa27d4cdcaf1200001f9e0e127a9952b5a15799e1968b758bf0227e2f62cb684cbb5eaee69d281c511011f1466ece7caaa8db087c09d40937487de8d207804b667932bc346c3dc9ad21e00000000000001d209b8b2dfd0ebb5e08e33d7d2d6ccedd61c67db8ea38cf33a695ab5a80fea1101dd3e61f3e1d497d923486bd000737ea0ffac885fa481316727d0d8f7fc2138100001cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "main",
|
||||
"height": "2297500",
|
||||
"hash": "0000000001157714936cd91baad52b26420f70d878a27f6bab348e2939b5bde5",
|
||||
"time": 1700031969,
|
||||
"saplingTree": "016cd61e5ab0ccd09c6076935098c3e8b9aecee2f9d3790431e1399840346c0e0201223fa371110693364a1c1877cdd219dfba7a134aa56a1ac47d55840d6ce665271a000000000001a9e947dab0b2907f94c8465661721d18140c6ab96fbcebf2b9487e72ec17c6000158a00dea151f1ae1790916fb739bb60d1f29a11d8067a64a1fa9a8c8ebab641300018e553358f32f5810dbb1b10b958583336fe2f331fe931fbfb34aacad78a91b630001a383100ed46da6862bfcd0a7b63d7409922e14c4c932960def290ea7befd50260001dfad84b2c2c9959eef6615f690c8cf687f09c70d21421c319af74b392a4e0c2d017f30bab3d865e778bb8423a91092cb8406cd2b56fa2cfd7902bfc3640d6b6b7200000001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e",
|
||||
"orchardTree": "01d9588c0a6fe7fb1371c16087a92dd9561fa7fa7078f1ed8305ad69db3637f8350131b39bf4d91f7ffd3915c37ef23544d5e0f2a148f4b52a4d7fc24d2dda89d8041f000000000000015f6bcb933aea63ab62194dc732814b029eb366d5ba1196298b57a08f9083993b0171164246e90fb351e6bb66d6b2633ec0ab366e2dfbd25232330512de884dc32e01f5133a9e365dc1fc1d9a4246dd3670c7b70fa0332a288ee8147de062b4022b150000000001d209b8b2dfd0ebb5e08e33d7d2d6ccedd61c67db8ea38cf33a695ab5a80fea1101dd3e61f3e1d497d923486bd000737ea0ffac885fa481316727d0d8f7fc2138100001cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "main",
|
||||
"height": "2300000",
|
||||
"hash": "00000000005ac7bf0554036dac0c5682fe35efe6979899ab3b84e436b44f0271",
|
||||
"time": 1700219650,
|
||||
"saplingTree": "018e44e79d9b50eb410b2b71822dd20cd15ee24b5179d029a3f7b82625efc47d1b01493ed5e5ef4c0925a250b0958e5a5e19a1217b5078d53696127ca74671133b2c1a016a56fc185d21058b1b3c6f9f8d1cf807518aa2ec0b1cf1674e7282ed5c755649012bc9a1ef85304112128c9cb1dadd4cb82061bcb5acc780bb4d8255444050d71201a9e703a68d505d581d2d08f0ace75985c8bd8da07c69412b6b7b24556c45ec4b01d9f91700e846cd65ed188d3104da34c5fdbea9bb33ff395df28efd3e2defbd610000000163a89e43566d3f5c83fe55dc82563460d9f511dbcb24d39b48781e926787873100019d739d626112351a4a29ee7081fb7197170858275ba47de1d89cf03304188e530001a624e057dbe29939e16bfeb1a70d128f9086cb4a13d20e58e759138c2f55f26e01dfad84b2c2c9959eef6615f690c8cf687f09c70d21421c319af74b392a4e0c2d017f30bab3d865e778bb8423a91092cb8406cd2b56fa2cfd7902bfc3640d6b6b7200000001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e",
|
||||
"orchardTree": "01e350c1c77aa1f7c85653c14d60b3f0898fb83cdc82340dcb894971dc7cfe1b11001f0000014f114d152411d917850c82ad7b88ff867fdf2d19ecb2a962efc492a4af59b832016c103fedd793dd79bfe0f8ca501da680eb5f4fcea41a175dbd6ae460af516e01000175ebb11b11dff6d75d3b1204616a3c1b88bd321c598bb0ff3209052df7e52f030101b760210f1f8a2c29b2d0048598b2d61152cfd798eaa2137ca1ff5ee745383b000130c3ceff4eea725a232d82f23f6f66ea3ca90758d67dc74407f68eb753c0f72e0116ee4da8c900e170c2dcbfbc660feb1fe8670f9ecd3e0b71b2958791196a2b1800000001d209b8b2dfd0ebb5e08e33d7d2d6ccedd61c67db8ea38cf33a695ab5a80fea1101dd3e61f3e1d497d923486bd000737ea0ffac885fa481316727d0d8f7fc2138100001cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "main",
|
||||
"height": "2302500",
|
||||
"hash": "000000000049a89a8d41f023b486cb6d1548f2a2d64f908a9fbf76e61dacd302",
|
||||
"time": 1700408079,
|
||||
"saplingTree": "01b3ac4e9b0e9c2010ccf8f81694c1fa43771fb84630c94b41328138c03cb3c048001a0000000001425472a998fba107d7736e4116a21082412a42a1e3b61d0ff7c1764d52e2d546000188c0404b7d872b78ee9918fd04281d55222afb1b175ad8350eb393046871f43c00000001deed527701b75f6c950b9a7128c98faa3702a38c857b7696da8f11f67e5a3d0001a624e057dbe29939e16bfeb1a70d128f9086cb4a13d20e58e759138c2f55f26e01dfad84b2c2c9959eef6615f690c8cf687f09c70d21421c319af74b392a4e0c2d017f30bab3d865e778bb8423a91092cb8406cd2b56fa2cfd7902bfc3640d6b6b7200000001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e",
|
||||
"orchardTree": "01734870baf0efee72311408efab0909c9ad8b7ccaa99c1d37c75fa712a12caf120152075aef8984c941a53bcfffa7c4ef275e3102ba0301078b12ccc84786f647001f000001e3e9c4ff8237d1d9fa922f4d354820042925544f925572f22db6d13c08c01c2c00016d457bc303eb23978ef1832c16ae9ca515f0351c2b8b067a835902239e1f9d1901caa032d62f3164041df40ebaa71e1867abcc1c9e073db8cc0b0db3a8c5d4c71c00016432489114896a710d08486188b0c6d78bd9c1fc304ea923ef8fff3ffc04493c00000183411dfe5993abe0abdd925c9ec970859a893a8ad8f3e07e08cd23f185b3f613000001d209b8b2dfd0ebb5e08e33d7d2d6ccedd61c67db8ea38cf33a695ab5a80fea1101dd3e61f3e1d497d923486bd000737ea0ffac885fa481316727d0d8f7fc2138100001cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "main",
|
||||
"height": "2305000",
|
||||
"hash": "000000000079e81f8f4a7a7488a6cde3ac7042bf862264e73c614e18021fb634",
|
||||
"time": 1700596777,
|
||||
"saplingTree": "018b04b540c4a829001b9d9d2482fb225308527158c76ea4cbb0238c3b991db511001a000000000001a937d02e58bfe7c4b694ae884244af95aa79e2cd7cd947b1dad3100ebb2cb62d00000001a007a3456464c26d2d533b6b2493a5f1be94714f7c3fa071802b0ed2175b255801deed527701b75f6c950b9a7128c98faa3702a38c857b7696da8f11f67e5a3d0001a624e057dbe29939e16bfeb1a70d128f9086cb4a13d20e58e759138c2f55f26e01dfad84b2c2c9959eef6615f690c8cf687f09c70d21421c319af74b392a4e0c2d017f30bab3d865e778bb8423a91092cb8406cd2b56fa2cfd7902bfc3640d6b6b7200000001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e",
|
||||
"orchardTree": "01c153f59f2dde1049aabede4a2daee3a755f3b6cf394218341dd5c0e3b16df42a01376e4e22cb0ad65701983457eb4a8fc04cc3c92d378642063d2829d74f6bcd071f01f975ad6414124bfc33a98058f6284af7b3da356098a99b9976aeef4b41c2ea0c017da8eda542a9417023dd63717897bfa86470d14f3ca76f6cdb0c9c6309ff49160177c04cc8c4b25f9bfebb5c3ff80c977fe489139d41e67cafc3da11f2924f0e3501aa8759f042284547e9ff050cf4988037c24dd722d910e20f495bd12e2b12b2190000000000011ba71e24c0a4b3b4d3d7aeacc9fdb005e3e8fdd2a0ac4e208b9f8038846fa5230183411dfe5993abe0abdd925c9ec970859a893a8ad8f3e07e08cd23f185b3f613000001d209b8b2dfd0ebb5e08e33d7d2d6ccedd61c67db8ea38cf33a695ab5a80fea1101dd3e61f3e1d497d923486bd000737ea0ffac885fa481316727d0d8f7fc2138100001cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "main",
|
||||
"height": "2307500",
|
||||
"hash": "0000000000c314fa9e022058471f7c22f373049213d66197fd2caff804331c5c",
|
||||
"time": 1700784785,
|
||||
"saplingTree": "01fa707068382e014cab53bd955811594ae0b8cb3ccab46d4ed2a67929f08d1c46001a000001895183b4862b58cde910c5d6fba0e46e1f20f15366c29e486e1d76895ad176410115e6ef7132a9cadce028659efc457848ed2e492c8497fe394ff8b05d78300715000001283d2c3554b31ba3fdc549548013737d2c13c2fcfc75ad151c92b0823d0e1c4e00000000000000017c650876ee17479db2e11cb5385df4fc60ae8f8cc8d501fa5dc07a4438e5863a000001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e",
|
||||
"orchardTree": "012d3af44ebbce403cbaa3dea8646db860d162620ebc757b828152fac7081fca2c0120149eb5e3ced3ecdf5d2a3e72b17104c62e22afb27884894c11c458d874d6371f00000000000174032ed7794d8fd255551f745978ece0903911d92365d3c17e9b613c32418b3301cd138752ced84e3842b622d56eecd4b56f4b39833760f06f2865186d5b64301201daa313d6b302574f10607d76cd93e712d8dfe12cd944046b02733568e0e012040157269ec6a22f08eea74d8932bcdd0ba7bad60727d5f3173ef3087e5c20f3a201011ba71e24c0a4b3b4d3d7aeacc9fdb005e3e8fdd2a0ac4e208b9f8038846fa5230183411dfe5993abe0abdd925c9ec970859a893a8ad8f3e07e08cd23f185b3f613000001d209b8b2dfd0ebb5e08e33d7d2d6ccedd61c67db8ea38cf33a695ab5a80fea1101dd3e61f3e1d497d923486bd000737ea0ffac885fa481316727d0d8f7fc2138100001cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "main",
|
||||
"height": "2310000",
|
||||
"hash": "00000000003e3be289375a8f4f5ed390b827ae52805a69e69c82adc47a7f638a",
|
||||
"time": 1700973210,
|
||||
"saplingTree": "01fe557c21a5d4647046e902864638d20362ccc7a9ef56852483c86bcd20007e0c01fd001f566d9fbd40a11b7f99836983715c36b423d6f7180b2a3da6bc637c22041a015f3279b196009a204d57b38723428f41b694cd5d952e10141c72ad98d4dad14f000001e4096c60afd06552a95caa4d1fdb47ef90671eff98279690d5f8eca38b05e0380001da52ca490452d5e8039117ad108ef6625eb20b0a3ed1543493f221e38830ab0d019dcbbe67345d0ebb294e6f4407495d05d5d7d67bdc05d5c72cc4cb626d38005a0194e6b1b0e859aaa6f334f26787b892653ac7419a99775cefc91a431d94ba8c1801e3295c2b4c5162269534ee66fd4d1883d8c2e28cad49310fe947b7b82dd077010000000000017c650876ee17479db2e11cb5385df4fc60ae8f8cc8d501fa5dc07a4438e5863a000001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e",
|
||||
"orchardTree": "01a19219bf964863f94659fbf191ecfaf0c10b07226bb933b5051f6d3908fcf026001f01ff9aeb5da2f0ef643823ce28d831290c9d5f441572593f796dc83f8234f7b9010000017a308fd5603ddb44d460d70e14677b040a08c965a685e2b33e14b530cd6f38270168674fefed9b39ff94de72c22d2bfcdddbc329b4b8464582da502f329267a81301616a84ebf0c29c75d3fdb8bbd3c3c33b6aeee66610ed586aedb11964a2f6da2501533e6416d9ed2a110da9349a52c00022888e54f83858c467538c7f8f5aaac2300001ba23e71929daee1f2746bba3eaf121060d94b58d474053fbf0a6753b7fbce53d0000015e077957f78fdaaa3c9d109d71a37e2eb7b1eabba42e0aa4aa93531189193b280001d209b8b2dfd0ebb5e08e33d7d2d6ccedd61c67db8ea38cf33a695ab5a80fea1101dd3e61f3e1d497d923486bd000737ea0ffac885fa481316727d0d8f7fc2138100001cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "main",
|
||||
"height": "2312500",
|
||||
"hash": "0000000000dba0ccbb7e962de6055896a33ee8a215334e765752225f57f25319",
|
||||
"time": 1701161717,
|
||||
"saplingTree": "011b047b029b0223d01a7d07cd5f8f0b66819877dfc005225784dc6e6f006d913a019caeb7e6ada760c4ee6534767e1b67300962caf78c75abba6706e7381efa4c031a00000000018c889ec59634e2526bff2eca96f846c9fb9b228e125c6710ca241190a45a5c080000013f61a08e8d9843a60979fe1c6deb77da4a99fce0bcd7dd1624dfa98c446e194001b6867d2c3c104e649df401daa8373c7b1b2c8d54ca293b779e125b551fefa83f0148a553a0ebae8a53b6ccd8515cba62e04889039e168769eb1ccd11366822ae6900000000017c650876ee17479db2e11cb5385df4fc60ae8f8cc8d501fa5dc07a4438e5863a000001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e",
|
||||
"orchardTree": "013e4e8b363581ffbc0a51253ab7c4477b2dede771041dde0fa8560b276f7b481f01d05925ddf7371123f63750935dbffc1083d075e782c479cb804a1ce47ebe07101f011b96f60d6c8c8d6de77f81ae356258bbe4f340c479b9899b55efe815d55a8b3d012b46aec45ecdc454af7d98c7e4e106323e3acd1977174348622bae9d1c2a0e1f000000018cb71ceec37ab14e37db4dec90cae5a29ee5f8e4786522d2144a6c97df0dfe13010e345504f193f43cc185a88d5f1c4b276eff7949f2510b9d38a868f4082c1a370175a1b7b6ebfed2c594767d94bb9f7f5bd9807b64b145ff8d39a237a6cc37b20500018fd27921b6dc15e1e964b30359db1a479820a41e31e5455eeeca0b6d589aab2300015e077957f78fdaaa3c9d109d71a37e2eb7b1eabba42e0aa4aa93531189193b280001d209b8b2dfd0ebb5e08e33d7d2d6ccedd61c67db8ea38cf33a695ab5a80fea1101dd3e61f3e1d497d923486bd000737ea0ffac885fa481316727d0d8f7fc2138100001cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "main",
|
||||
"height": "2315000",
|
||||
"hash": "000000000063c80bca59d74e55af35f6b59c01687d31f26a2b3729b93f98ad28",
|
||||
"time": 1701350630,
|
||||
"saplingTree": "01d2519e2c14a29555b9cde40649861b5b0e41342a9dbbdab934b58deea51e326801305bbab496d6038dde6346b4a75f3445209e2727ed96d2f1543b468e523ddf171a0126e16076b2d31d91029d8f11d048530feb242d3907291d612cbff71a2c679b4100012d780487df8dc25aed2aa828a195ac1edfe1a86bdc72d51906334d6ffacf5c390001afbe47945030efce4743e3172c2ede9d3160d0e40d3135c04b0e5bff46a5d32700011181d08141fb96403667db5c5856900cb88efbc0d24f0e38ebfae0c70cb1cd160196327ceab6a4189834bf78a47b558e5eab9610cb7377b07f1c82adddc9f0a33301873041160a4a52846ec91f092822480495858022fbb6f7640070745a5575dd5900010eb533eb674f2ac390ccb7684976b2428b83b8b39e6c43d2f3a43d10eb1bdc3b000000017c650876ee17479db2e11cb5385df4fc60ae8f8cc8d501fa5dc07a4438e5863a000001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e",
|
||||
"orchardTree": "0141714549d5769047a201cd12210be13b3cbf373e7e86833719c635addda31a3d01bb6b35e170a530aef78b9ef55ae310fe7877f2a204ef0538c132fde36ae6b5221f0001e3f7122d8c022a6de32e3c08fed433c0591ec14529a5cfa98e7ffa93c6c0c6070001d748a193374b9fb63fa75cde9b338aba2c78a913e2c39e92f55f67b3d805173a000000000000018ad9c06ecaeac466f69d27b659ff3fdf5287171b7df71f822cc167be9e51333b015e077957f78fdaaa3c9d109d71a37e2eb7b1eabba42e0aa4aa93531189193b280001d209b8b2dfd0ebb5e08e33d7d2d6ccedd61c67db8ea38cf33a695ab5a80fea1101dd3e61f3e1d497d923486bd000737ea0ffac885fa481316727d0d8f7fc2138100001cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "main",
|
||||
"height": "2317500",
|
||||
"hash": "00000000000d3facec8102c21e27b796085e726f22a6b4404af403f2e06246ce",
|
||||
"time": 1701538290,
|
||||
"saplingTree": "0155db89249b69a58c85c01a4e7c8469ac09ba32bc7f51b25679919e6a7eea911d013d8af6af82a1d198d18332485cd6eebb6e0f74a2c9eabcdf235bf6aedb495d181a00000000011fd68f39cba1dfa0a91a698f5263918918a17cdab23f591b75791ab6bea1531d011958f866e2712bef4e8e0d2c848ac354a357473f77e6d22c94ebc7e2a5b4653a000186fe98e6681eb57ec026a3900596c4cd45f866d5bdce901d6605dbee33b23d020126d05908fadda6bbb60afbb88a504da766b9d75da5aafa4ad5c92e341535ff6f010d99ab6d6e3b3983376306f6e72ab14814473041a883f9922c92fe7b07726d48010eb533eb674f2ac390ccb7684976b2428b83b8b39e6c43d2f3a43d10eb1bdc3b000000017c650876ee17479db2e11cb5385df4fc60ae8f8cc8d501fa5dc07a4438e5863a000001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e",
|
||||
"orchardTree": "018478a5883a7f71da8680c223850112fd44301b4e3b56adcdc114c1cea7287a160165732310afaadfd3f4ef6757723826e0085dfd8019e3437d6badd5000db34e1c1f0001bb3758c39242b6c84af5189d705d43551d16737fe72bb67815af188fe6e8b12801d77b815c0b254e0b9ed4ef38667cc24b38a28d1d63a466b432fe7ca15117491b0001563137aeadc46a1380214ce6d8a97660c7dc73b76f56efaf8ec1dedb40f1610b017e14bb5c438d09ccbb47ed8b7c72949398aa3a35f2eb9d0d9917f8dea93a9938013952475147995c96e33405e74d84870b8b76e7095098de777a07df80876a6c290001619564c35b4a3b4ef1f091097f6238c6a9780a3176461afd91a81ac745bdb22000018ad9c06ecaeac466f69d27b659ff3fdf5287171b7df71f822cc167be9e51333b015e077957f78fdaaa3c9d109d71a37e2eb7b1eabba42e0aa4aa93531189193b280001d209b8b2dfd0ebb5e08e33d7d2d6ccedd61c67db8ea38cf33a695ab5a80fea1101dd3e61f3e1d497d923486bd000737ea0ffac885fa481316727d0d8f7fc2138100001cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "main",
|
||||
"height": "2320000",
|
||||
"hash": "00000000003b4ec49e08e7e5bc9d2050a7d8643af4b5eb31d9ee7028612a880f",
|
||||
"time": 1701727742,
|
||||
"saplingTree": "01602ab6d2b8b67fbf0e58e9ee8a0b345b39ded68dfc7d62af19907ec9fc4f222d012cf650868b19f72ae52101641c5747365a443ab711e50a25eeb6b472bfac77181a014b0f98696ad26bfe5926936fa8c5de8bb7416cd0dc2301bbc3585ae6c5e1115d017e6af85204c809fcec6dc5602ad709bac1edc817ca70020e69d5b93a987bc42d0000000111a06a46a8a80840c4a4c2f815a29da482223039dce8edbdb6689d7701fa072301af4f78eccdda9fd76545db911714ea06ce7c3c6ae4bd0282433d7f60111d40400001b99a5fc8f04fadae650d5ba9a8c2b3dc2118b201443633bba3094c614a21875d00000186fa0a21fa4284d9c240f071b5af78077c52eaa3dfe191b54419e864544bdf290000017c650876ee17479db2e11cb5385df4fc60ae8f8cc8d501fa5dc07a4438e5863a000001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e",
|
||||
"orchardTree": "018431396c97957db93241404a0639bc60ba09a9f2975a2ca24acd59b97c20671901f413e556f6c3e1dfc94e4d72238e85954c46990db1477d6de37af96e518e8c3a1f0117fb0fb518825c203982a62d18a47d951b7bc05e745804db3088ae4bff83df0501f24ad76de4127a948767a3c951b2f7669872b30060f74f277d9dd639ad91373d0145909b77966da09903cf6f9f3775767fd22bd2684d0d5481bc722547c84baf2801e20bbd57e4abb340e725c3e45b11e2586d973cfd8936281ad040551903d10810000001760daff686b68989fa82b07113710706f3c53210f60564c80f05c3650b183733016902d64f2c463444a48030b6f0296dd78195d0ee061d94d76a2708aed4c88235000190b29301c40d674af3e16086be3da02dc7fdd0e41608f224a5189efc4cbea418018ad9c06ecaeac466f69d27b659ff3fdf5287171b7df71f822cc167be9e51333b015e077957f78fdaaa3c9d109d71a37e2eb7b1eabba42e0aa4aa93531189193b280001d209b8b2dfd0ebb5e08e33d7d2d6ccedd61c67db8ea38cf33a695ab5a80fea1101dd3e61f3e1d497d923486bd000737ea0ffac885fa481316727d0d8f7fc2138100001cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "main",
|
||||
"height": "2322500",
|
||||
"hash": "0000000000e8340a4a923642702f5f4c93c1057072f07c4426db57a71b2b05c7",
|
||||
"time": 1701915899,
|
||||
"saplingTree": "0130543b861c21671bbfcc2c928df965785d5a3fab1426b73879c0022e6bb79d5a01328a4783881ecd0011aeef5f70d7496a4bc425c94993d3b228cd6c3690ccf4381a0001a93de2cf2543f2fd6f2504e814922c8c9f7b162a1fd4fec6158fd4eff35e95690192dd47b9a4a2c6e72ceb7540848118de3bfd86641538cd84f770aff8ab792d31011144e221bed09d3de04ab1dfa21951d2e606a60432364f820b505b14fa85704c000001a32c4d21a961c0bcef8fc7508948492e8acab31cdd7bb55cded3776c54a67420000154c177cdf9db3c72580ed4490a1aa1cc46e1f360de6a5bee1d3453259781d955018eb775530e562c46105b272abf6a21413fbcfcac2ea017a78bce6499d1b89936000186fa0a21fa4284d9c240f071b5af78077c52eaa3dfe191b54419e864544bdf290000017c650876ee17479db2e11cb5385df4fc60ae8f8cc8d501fa5dc07a4438e5863a000001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e",
|
||||
"orchardTree": "01fa516ebcda12379171d2afdc1f59f3180b8ce2ad6c38391b51c0ed4cf7139a3b001f01a08e4fa9dd9def0e83cbd58125938fb596bac3477fd89b36ab821bc33d8cab000195d6948704e2a64a15df1ac9813b0347a662fa40bcd18215a7efc2cae78d9c0d0147cd40f30d1b0bbcffb7e475d9a68c3a16bd8628224545f181d9f2f63a0fce37010938aaf4fb08768aff4a340648326e59d88dc7d0a24c2e562a73b548db6436010001979070a88699d4f746386d66f275107fa863e2805c8a324e4ec026be634dc42101cb98c8c4d66301b255da9cfb983eb45ccc23a73c7ae15850e014929ebd5805300000000000015b40eb9abd1efbc71e5f3b718a42650c8a9353438a71c4362de2f6d98280c72801d209b8b2dfd0ebb5e08e33d7d2d6ccedd61c67db8ea38cf33a695ab5a80fea1101dd3e61f3e1d497d923486bd000737ea0ffac885fa481316727d0d8f7fc2138100001cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "main",
|
||||
"height": "2325000",
|
||||
"hash": "00000000013f351b47fa9808602c2ca9a9eadee3db250ace3aff6f94a05a2c79",
|
||||
"time": 1702104343,
|
||||
"saplingTree": "019f014bc938bf342e8fa962df405dfc7f70383ae75a3d84f3d8387b8cfc8fd7040195152d1cd4f6c864e8ae0d72b08e2dda2dcd5e9edaaddc2f5dbb7178b289ad4a1a0109f5e959a861537894bc48e6dde123b710df1d9b3ce0964bd654e57830d17d0a0001f4a87eb83b5988dc58f83e6c0ba540b4bb0dc40e224e88f35fb6cd62b2535c4d0000010d2534d167fbe412ef86ec222ed59380bc162550ce21200c123aefa79d43805a01526b97e22e9413bf11b37c0604401a0d8b693e1b7bb1cc0233cda3a88d4bf24701fe0232c88a5c2333f9523137ef0abff30aba4fc8cf4f9c6cdf82842f67309017000001c8d2d57c894732aa88c35fe01438b84eb067a83f0d09315b76494f27bbab57340186fa0a21fa4284d9c240f071b5af78077c52eaa3dfe191b54419e864544bdf290000017c650876ee17479db2e11cb5385df4fc60ae8f8cc8d501fa5dc07a4438e5863a000001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e",
|
||||
"orchardTree": "017f448390aeadd1125100d98fbde772e11d0e91c4860e90e1c0803332cf6f602b0112392d87b5a97ed3e9afb41c7d9b07aaa8e11b623f8e7f61f1d5bf230582a82f1f00017d420d9ad88166b9134bb6cf4d62c3a37d5d268c0b84c3c7f3f3eb34a51cbb3f017fec6d32a455c17cef710dea502f916540006d9a80216982e3b147b1f8f3773c01e2daae27ecf43e2b0105b0affe5e263674e936cbaa9964a0f95f09b1f89cd12b000187535510b6168d9f9e2750143cd792334c4d8c0f712bfb336232f55ba65d261b0001c0db8fd4e6e85447ec9f5568a6fff28abb7b97b5557cf877a45c50cc5bafd20f0120bd425a02ca0ae137d2cb86c65e59f3f49bec197893b17028ca66069ff68c0d000000015b40eb9abd1efbc71e5f3b718a42650c8a9353438a71c4362de2f6d98280c72801d209b8b2dfd0ebb5e08e33d7d2d6ccedd61c67db8ea38cf33a695ab5a80fea1101dd3e61f3e1d497d923486bd000737ea0ffac885fa481316727d0d8f7fc2138100001cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "main",
|
||||
"height": "2327500",
|
||||
"hash": "0000000000503558ba505b993ee8e7edac7005ffbf55eaded600679639de2b77",
|
||||
"time": 1702292824,
|
||||
"saplingTree": "011c3d10e821079fc295396929ebe3c3dbb027fa0fa049810743adc7a545ca3f1d001a0154e610d47a9580cc8aa570d00f495fed79aaa04be1f5a7e5b0f374fa2a79c7300001d0372bcf02021e31a46589204ddaaf1c3be0c1ab6afea383d268d74b0f60fd010000000142ebd53d9f0b8e01fda3799a041c8349b6d78f6fcd6a273f3ab9142137fbdc0d0000010c381362b8a0a10e2d2fc13e91f593cf74735f774893771e666f7852afb1f24001c8d2d57c894732aa88c35fe01438b84eb067a83f0d09315b76494f27bbab57340186fa0a21fa4284d9c240f071b5af78077c52eaa3dfe191b54419e864544bdf290000017c650876ee17479db2e11cb5385df4fc60ae8f8cc8d501fa5dc07a4438e5863a000001d1b36bbba8e6e1be8f09baf2b829bafc4ccd89ad25fb730d2b8a995b60fc3a6701d8ccea507421a590ed38116b834189cdc22421b4764179fa4e364803dfa66d56018cf6f5034a55fed59d1d832620916a43c756d2379e50ef9440fc4c3e7a29aa2300011619f99023a69bb647eab2d2aa1a73c3673c74bb033c3c4930eacda19e6fd93b0000000160272b134ca494b602137d89e528c751c06d3ef4a87a45f33af343c15060cc1e",
|
||||
"orchardTree": "016de0af325e9c46845ba0cc7229d632adc8babf9c3ceb435b73357459c493802201bc556afc0fead4f8ad877abc405019e3e7f22c243066ee91b5839d2494183f001f00016b3996001480b04b641a77e7b76fd0ffe8f111d7cd0c758c33150fb0568c2a1e01f9ebca20fe1e4ef33048f8bddc2e49c8ce7df0dde189d9e9b1ba6ef3ec4d520901de6f9d9db1541b2814aebb15085157c559780f993429a0c506aff765a582ea2501faec26b888c102aa510c66b2df1043aebfae92242e6099a66de3c5f42d31842a018fe817250be193132dfa50e005276d8211acb83e7bcb9947b3e15fb43abe281a0000011da8ae6166aa439a84eefa793cc964d33ac07b03084931c64686b2b64a00023101b93071dc309561911691da735ded9daccadffe308fd65dcada374934bfe8ce1e0000015b40eb9abd1efbc71e5f3b718a42650c8a9353438a71c4362de2f6d98280c72801d209b8b2dfd0ebb5e08e33d7d2d6ccedd61c67db8ea38cf33a695ab5a80fea1101dd3e61f3e1d497d923486bd000737ea0ffac885fa481316727d0d8f7fc2138100001cf3bf92f69798e68555548afcce1648add1fb2548d64fa9a1ec22a3e26e7890101e637281deb58dff0c44ba13149b784a95da1b493005efd057e6f4ac20ef5d81d000001cc2dcaa338b312112db04b435a706d63244dd435238f0aa1e9e1598d35470810012dcc4273c8a0ed2337ecf7879380a07e7d427c7f9d82e538002bd1442978402c01daf63debf5b40df902dae98dadc029f281474d190cddecef1b10653248a234150001e2bca6a8d987d668defba89dc082196a922634ed88e065c669e526bb8815ee1b000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "test",
|
||||
"height": "2560000",
|
||||
"hash": "001a1df8cd5e636cda82900c4030432017f7aeda9ca60f6c706164732e70fae7",
|
||||
"time": 1698006275,
|
||||
"saplingTree": "01b76b5c4c6b1b0797856ce48b060d0c0a32fc9c0873d7bd2df2dc666d634062680010000001e64e209c74e6a23211d48d472f10a90313fd3705afecac8518067299e684460f0001ba113fafff85ecfb63f2b8fefeefd176abad18b332fcbe4617cc377c59259d3f0129e08af8189ff4018093656da1e36422aef2b323ad17c90c1ae06a520fe7935901ece1ab9533870d5cac32a7d18da872b63a200fd7a6f56cea365bb8d64ec34758000001229c65dcabf0d220239a425d9716c9d4ee45201f7e300f7cdc3e02e26af8504f00015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39",
|
||||
"orchardTree": "0171b2ffcff2bbb7770d8418363b6128491f7b12fd854fa346b696f5a7a187f60c0141b23d5a87625c5c9dfa7689863e97f5bd36b4093f64fee7c28785e7b26f770b1f00018fa3c7f5144f59625d9c2e8113eac210378aa5667faa574ed9033fe2d6f2540e0165ac84927cd3d5fe6889b21711cb332888f2162f1dbe7cf92af562655c8f97030193bbae6d58db67a04ce81d81c529c3eba51215aee8ee356c73aaadab16c662250001f2f62617a99c5ba23fb9842b8816583e7bd124085a538a747857f30226512827000137f6061a5a492f13652bef0cbc396afd913a57bc7f877517ed7b08ca92acd83e000178a68d439d45f555fd4fe08eeeab4545091b53d73da118b6084042c4a4c44d3a00013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "test",
|
||||
"height": "2570000",
|
||||
"hash": "002f16febc8fe7cf17ed80593d364d639f5702bf780a91a81642bc195693667c",
|
||||
"time": 1698650731,
|
||||
"saplingTree": "01172469ecffe90fd74caecb72e0a8238a0c487c230adc637ef6496dca9da3336701b090011eefc4d43d9b5946d8a2aebcd1dc76c797ab059e51ebcee8aa2c6850141000000000016326709119d4f2bbad47d29b693881cc7c057c1b8776923ce1c3a4317df5444300000155a83a21de7d49c5a147fa4f27e249f8ff599a2e2094ab8a86fa94e0e57606150001229c65dcabf0d220239a425d9716c9d4ee45201f7e300f7cdc3e02e26af8504f00015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39",
|
||||
"orchardTree": "0193e602be1e5746f0978be494185934e884d0b11c32d601faf88402b2db809f1c015563e319e9e02e7f712e3cf62958a2f6bd1c5f2088796852263f2d561b5a76191f01dbb0162fba11b68d21c5aec3c9df5d29ff36b8e762022d1f61eb1e27427ff6290001d48ad801c0fc9a914533c8e67a74a7a53e7674f2c42b88c93f5dfbe7a1095c3700011ccdc25ac8b1322de8eb13af092cc33d2031b0da53ee35b07a9c944f8631c71801f2f62617a99c5ba23fb9842b8816583e7bd124085a538a747857f30226512827000137f6061a5a492f13652bef0cbc396afd913a57bc7f877517ed7b08ca92acd83e000178a68d439d45f555fd4fe08eeeab4545091b53d73da118b6084042c4a4c44d3a00013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "test",
|
||||
"height": "2580000",
|
||||
"hash": "000d717569dcdac9458704cec46eb7fc67ca105a20f6dbbf9b21f0b2a0a87910",
|
||||
"time": 1699309684,
|
||||
"saplingTree": "0151e8b859755bfb7cc5a1169da36a21c2f15f0d86c650c12f579cb0d47df1a46c00100001478acbd016a7669a43f925fda51709c64e9a62a4d8308cc927da3ccdfbd0344600000000015b4d7c5fdbd3fc65c60ba67e3c26a5647422620dd326ba2b2aa0b2874aafc93a0155a83a21de7d49c5a147fa4f27e249f8ff599a2e2094ab8a86fa94e0e57606150001229c65dcabf0d220239a425d9716c9d4ee45201f7e300f7cdc3e02e26af8504f00015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39",
|
||||
"orchardTree": "0153a87f57b6f0117fa5e66f907e43072d174b0dacf7c3579f6e131fd5ef26030401fa13e543f0b9cb6979eeb22dafffffb21384edf3ce061d5695ee4ff50bbc43221f0000000001681015d288503e6d4cca7cb633620cf23988d46109d0dad198361957ecfad60c00000001cf1d91dab12c89f04b9f6a2f650544c23cb24c7fc8c9b7d10830e9c1623cff370178a68d439d45f555fd4fe08eeeab4545091b53d73da118b6084042c4a4c44d3a00013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "test",
|
||||
"height": "2590000",
|
||||
"hash": "000e025af081ac1d5deac23db13a487a740c1dd11c8edbe101da87491c59987e",
|
||||
"time": 1699964905,
|
||||
"saplingTree": "01c48ab439dfa3023a4072c099ac0d135964ed439c196c76abad469010394c823701a2ebeb57c8b6a5dc11700c5b1b76b5844a1851c43fef162bb05dbceaf39666271001c8e3520813916a8e29b50daad01a379d4bbd4c263d2d4d06bf98e3e7e0cc065901d900f8dc9a7040f29eb91ef95e2f0e063f03c967d6d9dd7daa82aaa71576584501a17c8ee954beeaea9e9dcec6c7971a002fbdd60eadcf6b371dfe5d611041d35b000001a8d2ffd9468989e0bc26d93d585f10414c2f1fa66cf6a9c52f5e40852ca86510015b4d7c5fdbd3fc65c60ba67e3c26a5647422620dd326ba2b2aa0b2874aafc93a0155a83a21de7d49c5a147fa4f27e249f8ff599a2e2094ab8a86fa94e0e57606150001229c65dcabf0d220239a425d9716c9d4ee45201f7e300f7cdc3e02e26af8504f00015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39",
|
||||
"orchardTree": "0141df0ebfcd40cfc60b565c5d07136853e2125a0fde00181dceceef0d8c5ee538001f000000000001a14e51730f1aac956ca02793a6c274f1bca3a81d368f0ba52ecf14f13b947b3d00012bbdeb10ce1ba239547d675779168586ceab869467a8a36642e0af67ce1bca3301cf1d91dab12c89f04b9f6a2f650544c23cb24c7fc8c9b7d10830e9c1623cff370178a68d439d45f555fd4fe08eeeab4545091b53d73da118b6084042c4a4c44d3a00013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "test",
|
||||
"height": "2600000",
|
||||
"hash": "000b9ecc8bd155dedcf64d27771fc79b8b753fef0d8b0a2557eea1c17b0d8fe6",
|
||||
"time": 1700583151,
|
||||
"saplingTree": "015ef8ae4e19192fa4762fd53de41e1697e437578938a3617fe09f7a0c94195d55001001c4d0edf589adab2f1c85b1475be801abd5d577cb7767910dbbc8235bdddb5560011eba92d6993e1041fa1351631967a03717bd0f676a03f3844ebdce72d37cb15f0001aa4ed78e33a85295032fd575083045c3fc6f1b9869bde4043b56aa1a48b338040000000001285d51c9dd96d971a1c15b2af0693eff7b8002261be896f3b5b1965b2707642801229c65dcabf0d220239a425d9716c9d4ee45201f7e300f7cdc3e02e26af8504f00015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39",
|
||||
"orchardTree": "014eb099e4cb8492169d35249d01aadf61efe9f81f54cee545ba16df72319ca42c001f0001a5322460edfd5a6e300456e17093f2c8ce43e022b5c73731c76b1f51afaf882e01aef6f9da0bb0ebaf9e0d8bdccfa66822e82d88c723ebcd215120056e6a1d6a20000001a14e51730f1aac956ca02793a6c274f1bca3a81d368f0ba52ecf14f13b947b3d00012bbdeb10ce1ba239547d675779168586ceab869467a8a36642e0af67ce1bca3301cf1d91dab12c89f04b9f6a2f650544c23cb24c7fc8c9b7d10830e9c1623cff370178a68d439d45f555fd4fe08eeeab4545091b53d73da118b6084042c4a4c44d3a00013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "test",
|
||||
"height": "2610000",
|
||||
"hash": "001ec0dab50faa76f18e6f909e47fab55b1c4ca8d3e71ecb2ade5b4cbd0e5fe7",
|
||||
"time": 1701164195,
|
||||
"saplingTree": "01dbd359d688208f96a23db7bebb62419f2625e84ad5618866bec96a6343fb564801d54a95397777b995acad192de3e701774cb37b4ccf8725f0f44fd310c75271421001f9170bd1bf292c3e6c18f3e71754965b617b12616a91774b71855405ef74f243000000015f6141cef1b8f811dc43dd394607d67d27082c6f275bd40fe756cd93dd858938010ebe26176758357ad3cdd4b31d1762471121764549f55f456aeb12b337e4936c015288f7d7d6224034bb962401accd8299c2a060bb99d5ed93501fecb93066bf6a0001285d51c9dd96d971a1c15b2af0693eff7b8002261be896f3b5b1965b2707642801229c65dcabf0d220239a425d9716c9d4ee45201f7e300f7cdc3e02e26af8504f00015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39",
|
||||
"orchardTree": "01a7c5f84910c8cf8415e8b44f63c50db5e2d07aea69fba45ff1a2a66bb5f174010192bc02fd7ebf7fc163e3032222ca582f03031ebfa1716f9809df2aa6451652151f01dc80cc2741d5a3da94b52b6b41ecebb92e7cb7f6d19af5b5b60fdb4b6d59a50a019e9ac8749a2eabf9337fb776d780dfa285cbf54c05536711a46003b7bad84019018754c572f9305fb4eabcb9cfa58b6604d9606315c8a9c6d92a5aa234e15a113201a351dd9405e1391682d06a221bc814b3a4f4a937228507e2308a4e34e6c8922d0001a14e51730f1aac956ca02793a6c274f1bca3a81d368f0ba52ecf14f13b947b3d00012bbdeb10ce1ba239547d675779168586ceab869467a8a36642e0af67ce1bca3301cf1d91dab12c89f04b9f6a2f650544c23cb24c7fc8c9b7d10830e9c1623cff370178a68d439d45f555fd4fe08eeeab4545091b53d73da118b6084042c4a4c44d3a00013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000"
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
{
|
||||
"network": "test",
|
||||
"height": "2620000",
|
||||
"hash": "00139f229081782e5f0238fa02da7b0dd7516a0c6c8f7e3bc1940f841146f32b",
|
||||
"time": 1701773766,
|
||||
"saplingTree": "01ee7aef1afcb5615f849cf17167b39c323ca5acac80885697ff0dff0f41f0df6f01d986b6586d59fe113e2fbdf6f03c4e1199c22c3f0b2dff8e3ed544958487b26510000142a19e30b4b3f16d9f1002df18f3b05e7215f050a8b1b749a47f33e5ed9f9e6100000161ae6a6536a4928b9fe11aedb223fe58893f9c337d6c7b0e41cc82509cd780580132f6625f5568d5c36d820f3b5cdf7b9e20b6faa4ba27cc418f5828ab60ec6c2d0175a85ed94a7f19985be0ff968e0b1fa0cb25c1792c4990ab5e6b30d5cfa57f30013aeaf03c22ca18f70275bf195ba855cffad97f3d2d40d1aa991711f2dbf4d52501285d51c9dd96d971a1c15b2af0693eff7b8002261be896f3b5b1965b2707642801229c65dcabf0d220239a425d9716c9d4ee45201f7e300f7cdc3e02e26af8504f00015f6c95e75f4601a0a31670a7deb970fc8988c611685161d2e1629d0a1a0ebd07015f8b9205e0514fa235d75c150b87e23866b882b39786852d1ab42aab11d31a4a0117ddeb3a5f8d2f6b2d0a07f28f01ab25e03a05a9319275bb86d72fcaef6fc01501f08f39275112dd8905b854170b7f247cf2df18454d4fa94e6e4f9320cca05f24011f8322ef806eb2430dc4a7a41c1b344bea5be946efc7b4349c1c9edb14ff9d39",
|
||||
"orchardTree": "0173c10050c6cb1d64e5a4552e922cad6387a76cf4385ae8c7ca212d99267eb62e01e925514d92ef5f27f19f6314d00bda50fdaeac24ad7cce8542d91f25f5d523141f0000016219cc01738c79fdadb026ab4f36f02a6a9bebec9e89ff96ea6d58cc24893f060001c9851bb75b703e0d30a1cb1c6073989fc85114ff6e43c5464ad33c789c054a1701a14e51730f1aac956ca02793a6c274f1bca3a81d368f0ba52ecf14f13b947b3d00012bbdeb10ce1ba239547d675779168586ceab869467a8a36642e0af67ce1bca3301cf1d91dab12c89f04b9f6a2f650544c23cb24c7fc8c9b7d10830e9c1623cff370178a68d439d45f555fd4fe08eeeab4545091b53d73da118b6084042c4a4c44d3a00013bf8b923e4187754e85175748d9cce4824a6787e4258977b5bfe1ba59012c032000001f3bbdc62260c4fca5c84bf3487246d4542da48eeeec8ec40c1029b6908eef83c00000000000000000000000000000000"
|
||||
}
|
|
@ -106,9 +106,6 @@ public protocol Synchronizer: AnyObject {
|
|||
/// This stream is backed by `PassthroughSubject`. Check `SynchronizerEvent` to see which events may be emitted.
|
||||
var eventStream: AnyPublisher<SynchronizerEvent, Never> { get }
|
||||
|
||||
/// An object that when enabled collects mertrics from the synchronizer
|
||||
var metrics: SDKMetrics { get }
|
||||
|
||||
/// Initialize the wallet. The ZIP-32 seed bytes can optionally be passed to perform
|
||||
/// database migrations. most of the times the seed won't be needed. If they do and are
|
||||
/// not provided this will fail with `InitializationResult.seedRequired`. It could
|
||||
|
|
|
@ -79,7 +79,7 @@ enum Dependencies {
|
|||
}
|
||||
|
||||
container.register(type: SDKMetrics.self, isSingleton: true) { _ in
|
||||
SDKMetrics()
|
||||
SDKMetricsImpl()
|
||||
}
|
||||
|
||||
container.register(type: LatestBlocksDataProvider.self, isSingleton: true) { di in
|
||||
|
|
|
@ -22,7 +22,7 @@ public class SDKSynchronizer: Synchronizer {
|
|||
private let eventSubject = PassthroughSubject<SynchronizerEvent, Never>()
|
||||
public var eventStream: AnyPublisher<SynchronizerEvent, Never> { eventSubject.eraseToAnyPublisher() }
|
||||
|
||||
public let metrics: SDKMetrics
|
||||
let metrics: SDKMetrics
|
||||
public let logger: Logger
|
||||
|
||||
// Don't read this variable directly. Use `status` instead. And don't update this variable directly use `updateStatus()` methods instead.
|
||||
|
@ -44,7 +44,6 @@ public class SDKSynchronizer: Synchronizer {
|
|||
private let syncSessionIDGenerator: SyncSessionIDGenerator
|
||||
private let syncSession: SyncSession
|
||||
private let syncSessionTicker: SessionTicker
|
||||
private var syncStartDate: Date?
|
||||
let latestBlocksDataProvider: LatestBlocksDataProvider
|
||||
|
||||
/// Creates an SDKSynchronizer instance
|
||||
|
@ -104,6 +103,7 @@ public class SDKSynchronizer: Synchronizer {
|
|||
|
||||
func updateStatus(_ newValue: InternalSyncStatus, updateExternalStatus: Bool = true) async {
|
||||
let oldValue = await underlyingStatus.update(newValue)
|
||||
logger.info("Synchronizer's status updated from \(oldValue) to \(newValue)")
|
||||
await notify(oldStatus: oldValue, newStatus: newValue, updateExternalStatus: updateExternalStatus)
|
||||
}
|
||||
|
||||
|
@ -165,7 +165,6 @@ public class SDKSynchronizer: Synchronizer {
|
|||
|
||||
case .stopped, .synced, .disconnected, .error:
|
||||
await updateStatus(.syncing(0))
|
||||
syncStartDate = Date()
|
||||
await blockProcessor.start(retry: retry)
|
||||
}
|
||||
}
|
||||
|
@ -244,13 +243,6 @@ public class SDKSynchronizer: Synchronizer {
|
|||
await latestBlocksDataProvider.updateScannedData()
|
||||
|
||||
await updateStatus(.synced)
|
||||
|
||||
if let syncStartDate {
|
||||
metrics.pushSyncReport(
|
||||
start: syncStartDate,
|
||||
end: Date()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func foundTransactions(transactions: [ZcashTransaction.Overview], in range: CompactBlockRange) {
|
||||
|
|
|
@ -16,6 +16,7 @@ public protocol Logger {
|
|||
func event(_ message: String, file: StaticString, function: StaticString, line: Int)
|
||||
func warn(_ message: String, file: StaticString, function: StaticString, line: Int)
|
||||
func error(_ message: String, file: StaticString, function: StaticString, line: Int)
|
||||
func sync(_ message: String, file: StaticString, function: StaticString, line: Int)
|
||||
}
|
||||
|
||||
extension Logger {
|
||||
|
@ -34,6 +35,9 @@ extension Logger {
|
|||
func error(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
|
||||
error(message, file: file, function: function, line: line)
|
||||
}
|
||||
func sync(_ message: String, file: StaticString = #file, function: StaticString = #function, line: Int = #line) {
|
||||
sync(message, file: file, function: function, line: line)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -45,4 +49,5 @@ struct NullLogger: Logger {
|
|||
func event(_ message: String, file: StaticString, function: StaticString, line: Int) {}
|
||||
func warn(_ message: String, file: StaticString, function: StaticString, line: Int) {}
|
||||
func error(_ message: String, file: StaticString, function: StaticString, line: Int) {}
|
||||
func sync(_ message: String, file: StaticString, function: StaticString, line: Int) {}
|
||||
}
|
||||
|
|
|
@ -23,7 +23,11 @@ public class OSLogger: Logger {
|
|||
|
||||
var level: LogLevel
|
||||
|
||||
public init(logLevel: LogLevel, category: String = "logs", alias: ZcashSynchronizerAlias? = nil) {
|
||||
public init(
|
||||
logLevel: LogLevel,
|
||||
category: String = "sdkLogs",
|
||||
alias: ZcashSynchronizerAlias? = nil
|
||||
) {
|
||||
self.alias = alias
|
||||
self.level = logLevel
|
||||
if let bundleName = Bundle.main.bundleIdentifier {
|
||||
|
@ -42,37 +46,14 @@ public class OSLogger: Logger {
|
|||
line: Int = #line
|
||||
) {
|
||||
guard level.rawValue == LogLevel.debug.rawValue else { return }
|
||||
log(level: "DEBUG 🐞", message: message, file: file, function: function, line: line)
|
||||
}
|
||||
|
||||
public func error(
|
||||
_ message: String,
|
||||
file: StaticString = #file,
|
||||
function: StaticString = #function,
|
||||
line: Int = #line
|
||||
) {
|
||||
guard level.rawValue <= LogLevel.error.rawValue else { return }
|
||||
log(level: "ERROR 💥", message: message, file: file, function: function, line: line)
|
||||
}
|
||||
|
||||
public func warn(
|
||||
_ message: String,
|
||||
file: StaticString = #file,
|
||||
function: StaticString = #function,
|
||||
line: Int = #line
|
||||
) {
|
||||
guard level.rawValue <= LogLevel.warning.rawValue else { return }
|
||||
log(level: "WARNING ⚠️", message: message, file: file, function: function, line: line)
|
||||
}
|
||||
|
||||
public func event(
|
||||
_ message: String,
|
||||
file: StaticString = #file,
|
||||
function: StaticString = #function,
|
||||
line: Int = #line
|
||||
) {
|
||||
guard level.rawValue <= LogLevel.event.rawValue else { return }
|
||||
log(level: "EVENT ⏱", message: message, file: file, function: function, line: line)
|
||||
log(
|
||||
level: "DEBUG 🐞",
|
||||
logType: .debug,
|
||||
message: message,
|
||||
file: file,
|
||||
function: function,
|
||||
line: line
|
||||
)
|
||||
}
|
||||
|
||||
public func info(
|
||||
|
@ -82,11 +63,86 @@ public class OSLogger: Logger {
|
|||
line: Int = #line
|
||||
) {
|
||||
guard level.rawValue <= LogLevel.info.rawValue else { return }
|
||||
log(level: "INFO ℹ️", message: message, file: file, function: function, line: line)
|
||||
log(
|
||||
level: "INFO ℹ️",
|
||||
logType: .info,
|
||||
message: message,
|
||||
file: file,
|
||||
function: function,
|
||||
line: line
|
||||
)
|
||||
}
|
||||
|
||||
public func event(
|
||||
_ message: String,
|
||||
file: StaticString = #file,
|
||||
function: StaticString = #function,
|
||||
line: Int = #line
|
||||
) {
|
||||
guard level.rawValue <= LogLevel.event.rawValue else { return }
|
||||
log(
|
||||
level: "EVENT ⏱",
|
||||
logType: .default,
|
||||
message: message,
|
||||
file: file,
|
||||
function: function,
|
||||
line: line
|
||||
)
|
||||
}
|
||||
|
||||
public func warn(
|
||||
_ message: String,
|
||||
file: StaticString = #file,
|
||||
function: StaticString = #function,
|
||||
line: Int = #line
|
||||
) {
|
||||
guard level.rawValue <= LogLevel.warning.rawValue else { return }
|
||||
log(
|
||||
level: "WARNING ⚠️",
|
||||
logType: .default,
|
||||
message: message,
|
||||
file: file,
|
||||
function: function,
|
||||
line: line
|
||||
)
|
||||
}
|
||||
|
||||
public func error(
|
||||
_ message: String,
|
||||
file: StaticString = #file,
|
||||
function: StaticString = #function,
|
||||
line: Int = #line
|
||||
) {
|
||||
guard level.rawValue <= LogLevel.error.rawValue else { return }
|
||||
log(
|
||||
level: "ERROR 💥",
|
||||
logType: .error,
|
||||
message: message,
|
||||
file: file,
|
||||
function: function,
|
||||
line: line
|
||||
)
|
||||
}
|
||||
|
||||
public func sync(
|
||||
_ message: String,
|
||||
file: StaticString = #file,
|
||||
function: StaticString = #function,
|
||||
line: Int = #line
|
||||
) {
|
||||
log(
|
||||
level: "SYNC_METRIC",
|
||||
logType: .info,
|
||||
message: message,
|
||||
file: file,
|
||||
function: function,
|
||||
line: line
|
||||
)
|
||||
}
|
||||
|
||||
private func log(
|
||||
level: String,
|
||||
logType: OSLogType,
|
||||
message: String,
|
||||
file: StaticString = #file,
|
||||
function: StaticString = #function,
|
||||
|
@ -99,6 +155,7 @@ public class OSLogger: Logger {
|
|||
os_log(
|
||||
"[%{public}@] %{public}@ - %{public}@ - Line: %{public}d -> %{public}@",
|
||||
log: oslog,
|
||||
type: logType,
|
||||
level,
|
||||
fileName,
|
||||
String(describing: function),
|
||||
|
|
|
@ -106,7 +106,7 @@ class BlockStreamingTest: ZcashTestCase {
|
|||
service: service,
|
||||
downloaderService: BlockDownloaderServiceImpl(service: service, storage: storage),
|
||||
storage: storage,
|
||||
metrics: SDKMetrics(),
|
||||
metrics: SDKMetricsImpl(),
|
||||
logger: logger
|
||||
)
|
||||
mockContainer.mock(type: BlockDownloader.self, isSingleton: true) { _ in blockDownloader }
|
||||
|
|
|
@ -62,7 +62,7 @@ class CheckpointSourceTests: XCTestCase {
|
|||
|
||||
let birthday = source.birthday(for: 1520000)
|
||||
|
||||
|
||||
// swiftlint:disable line_length
|
||||
let expected = Checkpoint(
|
||||
height: 1520000,
|
||||
hash: "0014a50344a6a43b02421286f6db15dad50cea54f3f0858f044ad0f1b845c395",
|
||||
|
|
|
@ -332,11 +332,15 @@ final class EnhanceActionTests: ZcashTestCase {
|
|||
private func setupAction(
|
||||
_ blockEnhancerMock: BlockEnhancerMock = BlockEnhancerMock(),
|
||||
_ transactionRepositoryMock: TransactionRepositoryMock = TransactionRepositoryMock(),
|
||||
_ loggerMock: LoggerMock = LoggerMock()
|
||||
_ loggerMock: LoggerMock = LoggerMock(),
|
||||
_ sdkMetricsMock: SDKMetricsMock = SDKMetricsMock()
|
||||
) -> EnhanceAction {
|
||||
mockContainer.mock(type: BlockEnhancer.self, isSingleton: true) { _ in blockEnhancerMock }
|
||||
mockContainer.mock(type: TransactionRepository.self, isSingleton: true) { _ in transactionRepositoryMock }
|
||||
mockContainer.mock(type: Logger.self, isSingleton: true) { _ in loggerMock }
|
||||
mockContainer.mock(type: SDKMetrics.self, isSingleton: true) { _ in sdkMetricsMock }
|
||||
|
||||
loggerMock.syncFileFunctionLineClosure = { _, _, _, _ in }
|
||||
|
||||
let config: CompactBlockProcessor.Configuration = .standard(
|
||||
for: ZcashNetworkBuilder.network(for: .testnet), walletBirthday: 0
|
||||
|
|
|
@ -27,7 +27,8 @@ final class ProcessSuggestedScanRangesActionTests: ZcashTestCase {
|
|||
func testProcessSuggestedScanRangesAction_EmptyScanRanges() async throws {
|
||||
let loggerMock = LoggerMock()
|
||||
|
||||
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
|
||||
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
||||
loggerMock.syncFileFunctionLineClosure = { _, _, _, _ in }
|
||||
|
||||
let tupple = setupAction(loggerMock)
|
||||
await tupple.rustBackendMock.setSuggestScanRangesClosure({ [] })
|
||||
|
@ -39,11 +40,6 @@ final class ProcessSuggestedScanRangesActionTests: ZcashTestCase {
|
|||
|
||||
let nextContext = try await processSuggestedScanRangesActionAction.run(with: context) { _ in }
|
||||
|
||||
XCTAssertFalse(
|
||||
loggerMock.debugFileFunctionLineCalled,
|
||||
"logger.debug() is not expected to be called."
|
||||
)
|
||||
|
||||
let acResult = nextContext.checkStateIs(.finished)
|
||||
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||
} catch {
|
||||
|
@ -56,8 +52,13 @@ final class ProcessSuggestedScanRangesActionTests: ZcashTestCase {
|
|||
|
||||
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
|
||||
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
||||
loggerMock.syncFileFunctionLineClosure = { _, _, _, _ in }
|
||||
|
||||
let tupple = setupAction(loggerMock)
|
||||
let sdkMetricsMock = SDKMetricsMock()
|
||||
|
||||
sdkMetricsMock.actionDetailForClosure = { _, _ in }
|
||||
|
||||
let tupple = setupAction(loggerMock, sdkMetricsMock)
|
||||
await tupple.rustBackendMock.setSuggestScanRangesClosure({ [
|
||||
ScanRange(range: 0..<10, priority: .chainTip)
|
||||
] })
|
||||
|
@ -83,7 +84,7 @@ final class ProcessSuggestedScanRangesActionTests: ZcashTestCase {
|
|||
let enhancedValue = nextContextMock.updateLastEnhancedHeightReceivedLastEnhancedHeight
|
||||
XCTAssertNil(
|
||||
enhancedValue,
|
||||
"context.update(updateLastEnhancedHeight:) is expected to reset the value to nil but received \(enhancedValue)"
|
||||
"context.update(updateLastEnhancedHeight:) is expected to reset the value to nil but received \(String(describing: enhancedValue))"
|
||||
)
|
||||
} else {
|
||||
XCTFail("`nextContext` is not the ActionContextMock")
|
||||
|
@ -94,10 +95,10 @@ final class ProcessSuggestedScanRangesActionTests: ZcashTestCase {
|
|||
"logger.debug() is not expected to be called."
|
||||
)
|
||||
|
||||
if let infoArguments = loggerMock.infoFileFunctionLineReceivedArguments {
|
||||
XCTAssertFalse(infoArguments.message.contains("Setting the total range for Spend before Sync to"))
|
||||
if let syncArguments = loggerMock.syncFileFunctionLineReceivedArguments {
|
||||
XCTAssertFalse(syncArguments.message.contains("Setting the total range for Spend before Sync to"))
|
||||
} else {
|
||||
XCTFail("`infoArguments` unavailable.")
|
||||
XCTFail("`syncArguments` unavailable.")
|
||||
}
|
||||
|
||||
let acResult = nextContext.checkStateIs(.download)
|
||||
|
@ -109,7 +110,8 @@ final class ProcessSuggestedScanRangesActionTests: ZcashTestCase {
|
|||
|
||||
// swiftlint:disable large_tuple
|
||||
private func setupAction(
|
||||
_ loggerMock: LoggerMock = LoggerMock()
|
||||
_ loggerMock: LoggerMock = LoggerMock(),
|
||||
_ sdkMetricsMock: SDKMetricsMock = SDKMetricsMock()
|
||||
) -> (
|
||||
action: ProcessSuggestedScanRangesAction,
|
||||
serviceMock: LightWalletServiceMock,
|
||||
|
@ -138,6 +140,7 @@ final class ProcessSuggestedScanRangesActionTests: ZcashTestCase {
|
|||
mockContainer.mock(type: ZcashRustBackendWelding.self, isSingleton: true) { _ in rustBackendMock }
|
||||
mockContainer.mock(type: LightWalletService.self, isSingleton: true) { _ in serviceMock }
|
||||
mockContainer.mock(type: Logger.self, isSingleton: true) { _ in loggerMock }
|
||||
mockContainer.mock(type: SDKMetrics.self, isSingleton: true) { _ in sdkMetricsMock }
|
||||
|
||||
return (
|
||||
action: ProcessSuggestedScanRangesAction(container: mockContainer),
|
||||
|
|
|
@ -15,6 +15,7 @@ final class ScanActionTests: ZcashTestCase {
|
|||
let loggerMock = LoggerMock()
|
||||
|
||||
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
||||
loggerMock.syncFileFunctionLineClosure = { _, _, _, _ in }
|
||||
|
||||
let scanAction = setupAction(blockScannerMock, loggerMock)
|
||||
|
||||
|
@ -85,6 +86,7 @@ final class ScanActionTests: ZcashTestCase {
|
|||
let loggerMock = LoggerMock()
|
||||
|
||||
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
||||
loggerMock.syncFileFunctionLineClosure = { _, _, _, _ in }
|
||||
|
||||
let scanAction = setupAction(blockScannerMock, loggerMock)
|
||||
let syncContext = ActionContextMock.default()
|
||||
|
@ -109,6 +111,7 @@ final class ScanActionTests: ZcashTestCase {
|
|||
let loggerMock = LoggerMock()
|
||||
|
||||
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
||||
loggerMock.syncFileFunctionLineClosure = { _, _, _, _ in }
|
||||
|
||||
let scanAction = setupAction(blockScannerMock, loggerMock)
|
||||
let syncContext = ActionContextMock.default()
|
||||
|
|
|
@ -29,7 +29,7 @@ final class UpdateChainTipActionTests: ZcashTestCase {
|
|||
let blockDownloaderMock = BlockDownloaderMock()
|
||||
let latestBlocksDataProvider = LatestBlocksDataProviderMock()
|
||||
|
||||
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
|
||||
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
||||
blockDownloaderMock.stopDownloadClosure = { }
|
||||
latestBlocksDataProvider.updateClosure = { _ in }
|
||||
|
||||
|
@ -57,7 +57,7 @@ final class UpdateChainTipActionTests: ZcashTestCase {
|
|||
let blockDownloaderMock = BlockDownloaderMock()
|
||||
let latestBlocksDataProvider = LatestBlocksDataProviderMock()
|
||||
|
||||
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
|
||||
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
||||
blockDownloaderMock.stopDownloadClosure = { }
|
||||
latestBlocksDataProvider.updateClosure = { _ in }
|
||||
|
||||
|
|
|
@ -27,7 +27,7 @@ final class UpdateSubtreeRootsActionTests: ZcashTestCase {
|
|||
func testUpdateSubtreeRootsAction_getSubtreeRootsTimeout() async throws {
|
||||
let loggerMock = LoggerMock()
|
||||
|
||||
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
|
||||
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
||||
|
||||
let tupple = setupAction(loggerMock)
|
||||
let updateSubtreeRootsActionAction = tupple.action
|
||||
|
@ -59,7 +59,6 @@ final class UpdateSubtreeRootsActionTests: ZcashTestCase {
|
|||
func testUpdateSubtreeRootsAction_RootsAvailablePutRootsSuccess() async throws {
|
||||
let loggerMock = LoggerMock()
|
||||
|
||||
loggerMock.infoFileFunctionLineClosure = { _, _, _, _ in }
|
||||
loggerMock.debugFileFunctionLineClosure = { _, _, _, _ in }
|
||||
|
||||
let tupple = setupAction(loggerMock)
|
||||
|
@ -77,8 +76,6 @@ final class UpdateSubtreeRootsActionTests: ZcashTestCase {
|
|||
|
||||
let nextContext = try await updateSubtreeRootsActionAction.run(with: context) { _ in }
|
||||
|
||||
XCTAssertFalse(loggerMock.debugFileFunctionLineCalled, "logger.debug() is not expected to be called.")
|
||||
|
||||
let acResult = nextContext.checkStateIs(.updateChainTip)
|
||||
XCTAssertTrue(acResult == .true, "Check of state failed with '\(acResult)'")
|
||||
} catch {
|
||||
|
|
|
@ -1,152 +0,0 @@
|
|||
//
|
||||
// SDKMetricsTests.swift
|
||||
//
|
||||
//
|
||||
// Created by Lukáš Korba on 19.12.2022.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import ZcashLightClientKit
|
||||
|
||||
final class SDKMetricsTests: XCTestCase {
|
||||
func testPushDownloadBlocksReport() throws {
|
||||
let metrics = SDKMetrics()
|
||||
metrics.enableMetrics()
|
||||
|
||||
metrics.pushProgressReport(
|
||||
start: Date(timeIntervalSinceReferenceDate: 0.0),
|
||||
end: Date(timeIntervalSinceReferenceDate: 1.0),
|
||||
batchSize: 10,
|
||||
operation: .downloadBlocks
|
||||
)
|
||||
|
||||
XCTAssertTrue(metrics.popBlock(operation: .downloadBlocks)?.count == 1)
|
||||
|
||||
if let reports = metrics.reports[.downloadBlocks], let report = reports.first {
|
||||
XCTAssertEqual(report, SDKMetrics.BlockMetricReport.placeholderA)
|
||||
} else {
|
||||
XCTFail("Not expected to fail.")
|
||||
}
|
||||
|
||||
metrics.disableMetrics()
|
||||
}
|
||||
|
||||
func testPopDownloadBlocksReport() throws {
|
||||
let metrics = SDKMetrics()
|
||||
metrics.enableMetrics()
|
||||
|
||||
metrics.reports[.downloadBlocks] = [SDKMetrics.BlockMetricReport.placeholderA]
|
||||
|
||||
if let reports = metrics.popBlock(operation: .downloadBlocks), let report = reports.first {
|
||||
XCTAssertEqual(report, SDKMetrics.BlockMetricReport.placeholderA)
|
||||
} else {
|
||||
XCTFail("Not expected to fail.")
|
||||
}
|
||||
|
||||
metrics.disableMetrics()
|
||||
}
|
||||
|
||||
func testCumulativeSummary() throws {
|
||||
let metrics = SDKMetrics()
|
||||
metrics.enableMetrics()
|
||||
|
||||
metrics.reports[.downloadBlocks] = [SDKMetrics.BlockMetricReport.placeholderA]
|
||||
|
||||
let summary = SDKMetrics.CumulativeSummary(
|
||||
downloadedBlocksReport: SDKMetrics.ReportSummary(minTime: 1.0, maxTime: 1.0, avgTime: 1.0),
|
||||
scannedBlocksReport: nil,
|
||||
enhancementReport: nil,
|
||||
fetchUTXOsReport: nil,
|
||||
totalSyncReport: nil
|
||||
)
|
||||
|
||||
XCTAssertEqual(summary, metrics.cumulativeSummary())
|
||||
|
||||
metrics.disableMetrics()
|
||||
}
|
||||
|
||||
func testCumulateAndStartNewSet() throws {
|
||||
let metrics = SDKMetrics()
|
||||
metrics.enableMetrics()
|
||||
|
||||
metrics.reports[.downloadBlocks] = [SDKMetrics.BlockMetricReport.placeholderA]
|
||||
metrics.cumulateReportsAndStartNewSet()
|
||||
|
||||
metrics.reports[.downloadBlocks] = [SDKMetrics.BlockMetricReport.placeholderA]
|
||||
metrics.cumulateReportsAndStartNewSet()
|
||||
|
||||
metrics.reports[.downloadBlocks] = [SDKMetrics.BlockMetricReport.placeholderA]
|
||||
metrics.cumulateReportsAndStartNewSet()
|
||||
|
||||
XCTAssertTrue(metrics.cumulativeSummaries.count == 3)
|
||||
|
||||
let summary = SDKMetrics.CumulativeSummary(
|
||||
downloadedBlocksReport: SDKMetrics.ReportSummary(minTime: 1.0, maxTime: 1.0, avgTime: 1.0),
|
||||
scannedBlocksReport: nil,
|
||||
enhancementReport: nil,
|
||||
fetchUTXOsReport: nil,
|
||||
totalSyncReport: nil
|
||||
)
|
||||
let summaries = [summary, summary, summary]
|
||||
|
||||
XCTAssertEqual(summaries, metrics.cumulativeSummaries)
|
||||
|
||||
metrics.disableMetrics()
|
||||
}
|
||||
|
||||
func testCumulativeSummaryMinMaxAvg() throws {
|
||||
let metrics = SDKMetrics()
|
||||
metrics.enableMetrics()
|
||||
|
||||
metrics.reports[.downloadBlocks] = [SDKMetrics.BlockMetricReport.placeholderA, SDKMetrics.BlockMetricReport.placeholderB]
|
||||
|
||||
let summary = SDKMetrics.CumulativeSummary(
|
||||
downloadedBlocksReport: SDKMetrics.ReportSummary(minTime: 1.0, maxTime: 6.0, avgTime: 3.5),
|
||||
scannedBlocksReport: nil,
|
||||
enhancementReport: nil,
|
||||
fetchUTXOsReport: nil,
|
||||
totalSyncReport: nil
|
||||
)
|
||||
|
||||
XCTAssertEqual(summary, metrics.cumulativeSummary())
|
||||
|
||||
metrics.disableMetrics()
|
||||
}
|
||||
|
||||
func testSummarizedCumulativeReports() throws {
|
||||
let metrics = SDKMetrics()
|
||||
metrics.enableMetrics()
|
||||
|
||||
metrics.reports[.downloadBlocks] = [SDKMetrics.BlockMetricReport.placeholderA]
|
||||
metrics.cumulateReportsAndStartNewSet()
|
||||
|
||||
metrics.reports[.downloadBlocks] = [SDKMetrics.BlockMetricReport.placeholderB]
|
||||
metrics.cumulateReportsAndStartNewSet()
|
||||
|
||||
let summary = SDKMetrics.CumulativeSummary(
|
||||
downloadedBlocksReport: SDKMetrics.ReportSummary(minTime: 1.0, maxTime: 6.0, avgTime: 3.5),
|
||||
scannedBlocksReport: nil,
|
||||
enhancementReport: nil,
|
||||
fetchUTXOsReport: nil,
|
||||
totalSyncReport: nil
|
||||
)
|
||||
|
||||
XCTAssertEqual(metrics.summarizedCumulativeReports(), summary)
|
||||
|
||||
metrics.disableMetrics()
|
||||
}
|
||||
}
|
||||
|
||||
extension SDKMetrics.BlockMetricReport {
|
||||
static let placeholderA = Self(
|
||||
batchSize: 10,
|
||||
startTime: Date(timeIntervalSinceReferenceDate: 0.0).timeIntervalSinceReferenceDate,
|
||||
endTime: Date(timeIntervalSinceReferenceDate: 1.0).timeIntervalSinceReferenceDate
|
||||
)
|
||||
|
||||
static let placeholderB = Self(
|
||||
batchSize: 10,
|
||||
startTime: Date(timeIntervalSinceReferenceDate: 0.0).timeIntervalSinceReferenceDate,
|
||||
endTime: Date(timeIntervalSinceReferenceDate: 6.0).timeIntervalSinceReferenceDate
|
||||
)
|
||||
}
|
|
@ -80,7 +80,6 @@ class SynchronizerTests: ZcashTestCase {
|
|||
|
||||
guard let synchronizer else { fatalError("Synchronizer not initialized.") }
|
||||
|
||||
synchronizer.metrics.enableMetrics()
|
||||
_ = try await synchronizer.prepare(with: seedBytes, walletBirthday: birthday, for: .existingWallet)
|
||||
|
||||
let syncSyncedExpectation = XCTestExpectation(description: "synchronizerSynced Expectation")
|
||||
|
@ -93,28 +92,6 @@ class SynchronizerTests: ZcashTestCase {
|
|||
try await synchronizer.start()
|
||||
|
||||
await fulfillment(of: [syncSyncedExpectation], timeout: 100)
|
||||
|
||||
synchronizer.metrics.cumulateReportsAndStartNewSet()
|
||||
}
|
||||
|
||||
if let cumulativeSummary = synchronizer?.metrics.summarizedCumulativeReports() {
|
||||
let downloadedBlocksReport = cumulativeSummary.downloadedBlocksReport ?? .zero
|
||||
let scannedBlocksReport = cumulativeSummary.scannedBlocksReport ?? .zero
|
||||
let enhancementReport = cumulativeSummary.enhancementReport ?? .zero
|
||||
let fetchUTXOsReport = cumulativeSummary.fetchUTXOsReport ?? .zero
|
||||
let totalSyncReport = cumulativeSummary.totalSyncReport ?? .zero
|
||||
|
||||
let downloadedBlockAVGTime = downloadedBlocksReport.avgTime
|
||||
LoggerProxy.debug("""
|
||||
testHundredBlocksSync() SUMMARY min max avg REPORT:
|
||||
downloadedBlocksTimes: min: \(downloadedBlocksReport.minTime) max: \(downloadedBlocksReport.maxTime) avg: \(downloadedBlockAVGTime)
|
||||
scannedBlocksTimes: min: \(scannedBlocksReport.minTime) max: \(scannedBlocksReport.maxTime) avg: \(scannedBlocksReport.avgTime)
|
||||
enhancementTimes: min: \(enhancementReport.minTime) max: \(enhancementReport.maxTime) avg: \(enhancementReport.avgTime)
|
||||
fetchUTXOsTimes: min: \(fetchUTXOsReport.minTime) max: \(fetchUTXOsReport.maxTime) avg: \(fetchUTXOsReport.avgTime)
|
||||
totalSyncTimes: min: \(totalSyncReport.minTime) max: \(totalSyncReport.maxTime) avg: \(totalSyncReport.avgTime)
|
||||
""")
|
||||
}
|
||||
|
||||
synchronizer?.metrics.disableMetrics()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,5 +28,6 @@ extension TransactionRepository { }
|
|||
extension UTXOFetcher { }
|
||||
extension ZcashFileManager { }
|
||||
extension ZcashRustBackendWelding { }
|
||||
extension SDKMetrics { }
|
||||
|
||||
// sourcery:end:
|
||||
|
|
|
@ -1110,6 +1110,100 @@ class LoggerMock: Logger {
|
|||
errorFileFunctionLineClosure!(message, file, function, line)
|
||||
}
|
||||
|
||||
// MARK: - sync
|
||||
|
||||
var syncFileFunctionLineCallsCount = 0
|
||||
var syncFileFunctionLineCalled: Bool {
|
||||
return syncFileFunctionLineCallsCount > 0
|
||||
}
|
||||
var syncFileFunctionLineReceivedArguments: (message: String, file: StaticString, function: StaticString, line: Int)?
|
||||
var syncFileFunctionLineClosure: ((String, StaticString, StaticString, Int) -> Void)?
|
||||
|
||||
func sync(_ message: String, file: StaticString, function: StaticString, line: Int) {
|
||||
syncFileFunctionLineCallsCount += 1
|
||||
syncFileFunctionLineReceivedArguments = (message: message, file: file, function: function, line: line)
|
||||
syncFileFunctionLineClosure!(message, file, function, line)
|
||||
}
|
||||
|
||||
}
|
||||
class SDKMetricsMock: SDKMetrics {
|
||||
|
||||
|
||||
init(
|
||||
) {
|
||||
}
|
||||
|
||||
// MARK: - cbpStart
|
||||
|
||||
var cbpStartCallsCount = 0
|
||||
var cbpStartCalled: Bool {
|
||||
return cbpStartCallsCount > 0
|
||||
}
|
||||
var cbpStartClosure: (() -> Void)?
|
||||
|
||||
func cbpStart() {
|
||||
cbpStartCallsCount += 1
|
||||
cbpStartClosure!()
|
||||
}
|
||||
|
||||
// MARK: - actionStart
|
||||
|
||||
var actionStartCallsCount = 0
|
||||
var actionStartCalled: Bool {
|
||||
return actionStartCallsCount > 0
|
||||
}
|
||||
var actionStartReceivedAction: CBPState?
|
||||
var actionStartClosure: ((CBPState) -> Void)?
|
||||
|
||||
func actionStart(_ action: CBPState) {
|
||||
actionStartCallsCount += 1
|
||||
actionStartReceivedAction = action
|
||||
actionStartClosure!(action)
|
||||
}
|
||||
|
||||
// MARK: - actionDetail
|
||||
|
||||
var actionDetailForCallsCount = 0
|
||||
var actionDetailForCalled: Bool {
|
||||
return actionDetailForCallsCount > 0
|
||||
}
|
||||
var actionDetailForReceivedArguments: (detail: String, action: CBPState)?
|
||||
var actionDetailForClosure: ((String, CBPState) -> Void)?
|
||||
|
||||
func actionDetail(_ detail: String, `for` action: CBPState) {
|
||||
actionDetailForCallsCount += 1
|
||||
actionDetailForReceivedArguments = (detail: detail, action: action)
|
||||
actionDetailForClosure!(detail, action)
|
||||
}
|
||||
|
||||
// MARK: - actionStop
|
||||
|
||||
var actionStopCallsCount = 0
|
||||
var actionStopCalled: Bool {
|
||||
return actionStopCallsCount > 0
|
||||
}
|
||||
var actionStopClosure: (() -> Void)?
|
||||
|
||||
func actionStop() {
|
||||
actionStopCallsCount += 1
|
||||
actionStopClosure!()
|
||||
}
|
||||
|
||||
// MARK: - logCBPOverviewReport
|
||||
|
||||
var logCBPOverviewReportCallsCount = 0
|
||||
var logCBPOverviewReportCalled: Bool {
|
||||
return logCBPOverviewReportCallsCount > 0
|
||||
}
|
||||
var logCBPOverviewReportReceivedLogger: Logger?
|
||||
var logCBPOverviewReportClosure: ((Logger) -> Void)?
|
||||
|
||||
func logCBPOverviewReport(_ logger: Logger) {
|
||||
logCBPOverviewReportCallsCount += 1
|
||||
logCBPOverviewReportReceivedLogger = logger
|
||||
logCBPOverviewReportClosure!(logger)
|
||||
}
|
||||
|
||||
}
|
||||
class SaplingParametersHandlerMock: SaplingParametersHandler {
|
||||
|
||||
|
@ -1162,10 +1256,6 @@ class SynchronizerMock: Synchronizer {
|
|||
get { return underlyingEventStream }
|
||||
}
|
||||
var underlyingEventStream: AnyPublisher<SynchronizerEvent, Never>!
|
||||
var metrics: SDKMetrics {
|
||||
get { return underlyingMetrics }
|
||||
}
|
||||
var underlyingMetrics: SDKMetrics!
|
||||
var transactions: [ZcashTransaction.Overview] {
|
||||
get async { return underlyingTransactions }
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue