[
  {
    "path": ".github/workflows/pull-request-notify.yml",
    "content": "name: Notify PR events\n\non:\n  pull_request:\n\njobs:\n  build:\n    runs-on: ubuntu-latest\n\n    steps:\n      - uses: actions/checkout@v2\n      - name: Notify to Telegrem\n        uses: appleboy/telegram-action@master\n        with:\n          to: ${{secrets.TELEGRAM_TO}}\n          token: ${{secrets.TELEGRAM_TOKEN}}\n          disable_web_page_preview: true\n          message: |\n            ${{ github.event.pull_request.html_url }}\n\n            Title: ${{github.event.pull_request.title}}\n            Author: ${{github.actor}}\n"
  },
  {
    "path": ".gitignore",
    "content": "*.iml\n.gradle\n.idea\n.DS_Store\n/build\n/captures\n/local.properties\n.externalNativeBuild\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"dashkit/cpp/dashj-bls/bls-signatures\"]\n\tpath = dashkit/cpp/dashj-bls/bls-signatures\n\turl = https://github.com/Chia-Network/bls-signatures\n"
  },
  {
    "path": "CONTRIBUTING.md",
    "content": "## Implementing support for a new coin for external developers\n\nSupport for coin is implemented as a separate module that depends on the module `bitcoincore`. This repository contains modules for supporting coins like `Bitcoin`, `BitcoinCash` and `Dash`. Support for a new coin should be implemented in the owners repository.\n\n### Structure of module\n\nThe module depends on the `bitcoincore`. This dependency can be added via JitPack repository. \n\nIn the main `build.gradle` add the JitPack repository:\n\n```\nrepositories {\n    maven { url 'https://jitpack.io' }\n}\n```\n\nAdd the following dependency to module `build.gradle` file:\n\n```\ndependencies {\n    implementation 'com.github.horizontalsystems.bitcoin-kit-android:bitcoincore:${version}'\n}\n```\n\nIt implements `AbstractKit` and `Network` interfaces (abstract classes). \n\nCustomizing can be done in 2 places:\n\n1. Via `BitcoinCoreBuilder` when building `BitcoinCore`\n2. Via `BitcoinCore`\n\nThere are multiple places that can be customized. See the modules [`bitcoinkit`](bitcoinkit), [`bitcoincashkit`](bitcoincashkit) and [`dashkit`](dashkit) for reference. If you need a new extension point please [add an issue](https://github.com/horizontalsystems/bitcoin-kit-android/issues/new).\n\nWhen the module is released let us know about it. We will review it and decide whether to add it to Unstoppable wallet app.\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2018 Horizontal Systems\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# BitcoinKit\n\n`bitcoin-kit-android` is a Bitcoin wallet toolkit implemented in Kotlin. It consists of following libraries:\n\n- `bitcoincore` is a core library that implements a full Simplified Payment Verification (`SPV`) client in `Kotlin`. It implements Bitcoin `P2P Protocol` and can be extended to be a client of other Bitcoin forks like BitcoinCash, Litecoin, etc. \n- `bitcoinkit` extends **bitcoincore**, makes it usable with `Bitcoin` network.\n- `bitcoincashkit` extends **bitcoincore**, makes it usable with `BitcoinCash(ABC)` network.\n- `litecoinkit` extends **bitcoincore**, makes it usable with `Litecoin` network.\n- `dashkit` extends **bitcoincore**, makes it usable with `Dash` network.\n- `hodler` is a plugin for `bitcoincore`, that makes it possible to lock certain amount of coins until some time in the future. \n\nBeing an SPV client, **bitcoincore** downloads and validates all the block headers, inclusion of transactions in the blocks, integrity and immutability of transactions as described in the Bitcoin whitepaper or delegates validation to the extensions that implement the forks of Bitcoin.\n\n## Core Features\n\n- [x] Bitcoin P2P Protocol implementation in Kotlin.\n- [x] Full SPV implementation for fast mobile performance with account security and privacy in mind\n- [x] `P2PK`, `P2PKH`, `P2SH-P2WPKH`, `P2WPKH` outputs support.\n- [x] Restoring with mnemonic seed. (Generated from private seed phrase)\n- [x] Restoring with [BIP32](https://github.com/bitcoin/bips/blob/master/bip-0032.mediawiki) extended public key. (This becomes a `Watch account` unable to spend funds)\n- [x] Quick initial restore over node API. (optional)\n- [x] Handling transaction (Replacement)/(Double spend)/(Failure by expiration)\n- [x] Optimized UTXO selection when spending coins.\n- [x] [BIP69](https://github.com/bitcoin/bips/blob/master/bip-0069.mediawiki) or simple shuffle output ordering. (configurable)\n- [x] [BIP21](https://github.com/bitcoin/bips/blob/master/bip-0021.mediawiki) URI schemes with payment address, amount, label and other parameters\n\n\n# bitcoinkit\n\n## Usage\n\n### Initialization\n\nFirst, you need an instance of *BitcoinKit* class. You can initialize it with Mnemonic seed or BIP32 extended key (private or public). To generate seed from mnemonic seed phrase you can use [HdWalletKit](https://github.com/horizontalsystems/hd-wallet-kit-android) to convert a word list to a seed.\n\n```kotlin\nval words = listOf(\"mnemonic\", \"phrase\", \"words\")\nval passphrase: String = \"\"\n        \nval seed = Mnemonic().toSeed(words, passphrase)\n```\n\nThen you can pass a seed to initialize an instance of *BitcoinKit*\n\n```kotlin\nval context = Application()\n\nval bitcoinKit = BitcoinKit(\n    context = context,\n    seed = seed,\n    walletId = \"unique_wallet_id\",\n    syncMode = BitcoinCore.SyncMode.Api(),\n    networkType = NetworkType.MainNet,\n    confirmationsThreshold = 6,\n    purpose = HDWallet.Purpose.BIP84\n)\n```\n\n#### `purpose`\n\n*bitcoinkit* supports `BIP44`, `BIP49` and `BIP84` wallets. They have different derivation paths, so you need to specify this on kit initialization.\n\n\n#### `syncMode`\n\n*bitcoinkit* pulls all historical transactions of given account from bitcoin peers according to SPV protocol. This process may take several hours as it needs to download every block header with some transactions to find transactions concerning the accounts addresses. In order to speed up the initial blockchain scan, *bitcoincore* has some optimization options:\n\n- It doesn't download blocks added before the [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) was implemented by wallets, because there were no transactions concerning addresses generated by BIP44 wallets.\n\n- If you set **API()** or **NewWallet()** to *syncMode* parameter, it first requests from an **API**(currently [Blockchain.com](https://blockchain.info)) the hashes of the blocks where there are transactions we need. Then, it downloads those blocks from the bitcoin peers. This reduces the initial synchronization time to several minutes. This also carries some risks that makes it possible for a middle-man attacker to learn about the addresses requested from your IP address. But your funds are totally safe.\n\nIf you set **Full()** to *syncMode*, then only decentralized peers are used. Once the initial blockchain scan is completed, the remaining synchronization works with decentralized peers only for all *syncMode*s.\n\n#### Additional parameters:\n- `networkType`: Mainnet or Testnet\n- `confirmationsThreshold`: Minimum number of confirmations required for an unspent output to be available for use (*default: 6*)\n\n#### Initializing with HD extended key\n\nYou can initialize `BitcoinKit` using BIP32 Extended Private/Public Key as follows:\n\n```kotlin\nval extendedKey = HDExtendedKey(\"xprvA1BgyAq84AiAsrMm6DKqwCXDwxLBXq76dpUfuNXNziGMzDxYLjE9AkuYBAQTpt6aJu4nFYamh6BbrRkys5fJcxGd7qixNrpVpPBxui9oYyF\")\n\nval bitcoinKit = BitcoinKit(\n    context = context,\n    extendedKey = extendedKey,\n    walletId = \"unique_wallet_id\",\n    syncMode = BitcoinCore.SyncMode.Api(),\n    networkType = NetworkType.MainNet,\n    confirmationsThreshold = 6\n)\n```\n\nIf you restore with a public extended key, then you only will be able to watch the wallet. You won't be able to send any transactions. This is how the **watch account** feature is implemented.\n\n### Starting and Stopping\n\n*BitcoinKit* requires to be started with `start` command. It will be in synced state as long as it is possible. You can call `stop` to stop it\n\n```kotlin\nbitcoinKit.start()\nbitcoinKit.stop()\n```\n\n### Getting wallet data\n\n#### Balance\n\nBalance is provided in `Satoshis`:\n\n```kotlin\nval balance = bitcoinKit.balance\n\nprintln(balance.spendable)\nprintln(balance.unspendable)\n```\n\nUnspendable balance is non-zero if you have UTXO that is currently not spendable due to some custom unlock script. These custom scripts can be implemented as a plugin, like **Hodler**\n\n#### Last Block Info\n\n```kotlin\nval blockInfo = bitcoinKit.lastBlockInfo ?: return\n\nprintln(blockInfo.headerHash)\nprintln(blockInfo.height)\nprintln(blockInfo.timestamp)\n```\n\n#### Receive Address\n\nGet an address which you can receive coins to. Receive address is changed each time after you actually get some coins in that address\n\n```kotlin\nbitcoinKit.receiveAddress()   // \"mgv1KTzGZby57K5EngZVaPdPtphPmEWjiS\"\n```\n\n#### Transactions\n\nYou can get your transactions using `transactions(fromUid: String? = null, type: TransactionFilterType? = null, limit: Int? = null)` method of the *BitcoinKit* instance. It returns *Single<List<TransactionInfo>>*. You'll need to subscribe and get transactions asynchronously. See [RX Single Observers](http://reactivex.io/RxJava/3.x/javadoc/io/reactivex/rxjava3/core/Single.html) for more info.\n\n\n```kotlin\nval disposables = CompositeDisposable()\n\nbitcoinKit.transactions().subscribe { transactionInfos ->\n    for (transactionInfo in transactionInfos) {\n        println(\"Uid: ${transactionInfo.uid}\")\n        println(\"Hash: ${transactionInfo.transactionHash}\")\n    }\n}.let {\n    disposables.add(it)\n}\n```\n\n- `fromUid` and `limit` parameters can be used for pagination. \n- `type` parameter enables to filter transactions by coins flow. You can pass *incoming* OR *outgoing* to get filtered transactions\n\n\n#### TransactionInfo\n\nA sample dump:\n\n```kotlin\n// transactionInfo = {TransactionInfo}\n//    amount = 13114\n//    blockHeight = 740024\n//    conflictingTxHash = null\n//    fee = null\n//    inputs = {ArrayList} size = 1\n//      0 = {TransactionInputInfo}\n//          address = \"16s6q8dAgLbDT3szEc4nvTh81deRCBtEa1\"\n//          mine = false\n//          value = null\n//    outputs = {ArrayList}  size = 2\n//      0 = {TransactionOutputInfo}\n//          address = \"bc1qsg9ul383f8pespcvc8u3katl6gnsr7sjyfe3pc\"\n//          changeOutput = false\n//          mine = true\n//          pluginData = null\n//          pluginDataString = null\n//          pluginId = null\n//          value = 13114\n//      1 = {TransactionOutputInfo}\n//          address = \"16VCm8mYhHE3EiELi8GiYEqAjnPu1TSgAV\"\n//          changeOutput = false\n//          mine = false\n//          pluginData = null\n//          pluginDataString = null\n//          pluginId = null\n//          value = 1422\n//    status = {TransactionStatus} RELAYED\n//    timestamp = 1654766137\n//    transactionHash = \"cadf99db1e145dcfadfa2bc3eacb94831eb6c53d376f4f873aa4ac017b8c7f8f\"\n//    transactionIndex = 2760\n//    type = {TransactionType} Incoming\n//    uid = \"75934663-3c84-4b38-9b6d-810d3433de17\"\n```\n\n`uid`\n\nA local unique ID\n\n`type` \n\n- *Incoming*\n- *Outgoing*\n- *SentToSelf*\n\n`status`\n\n- *NEW* -> transaction is in mempool\n- *RELAYED* -> transaction is in block\n- *INVALID* -> transaction is not included in block due to an error OR replaced by another one (RBF).\n\n\n### Sending BTC\n\n\n```kotlin\nbitcoinKit.send(address = \"36k1UofZ2iP2NYax9znDCsksajfKeKLLMJ\", value = 100000000, feeRate = 10, sortType = TransactionDataSortType.Bip69)\n```\n\nThis first validates a given address and amount, creates new transaction, then sends it over the peers network. If there's any error with given address/amount or network, it raises an exception.\n\n#### Validate address\n\n```kotlin\nbitcoinKit.validateAddress(address = \"mrjQyzbX9SiJxRC2mQhT4LvxFEmt9KEeRY\")\n```\n\n#### Evaluate fee\n\n```kotlin\nbitcoinKit.fee(address = \"36k1UofZ2iP2NYax9znDCsksajfKeKLLMJ\", value = 100000000, feeRate = 10)\n```\n\n\n### Parsing BIP21 URI\n\nYou can use `parsePaymentAddress` method to parse a BIP21 URI:\n\n```kotlin\nbitcoinKit.parsePaymentAddress(\"bitcoin:175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W?amount=50&label=Luke-Jr&message=Donation%20for%20project%20xyz\")\n\n\n// ▿ BitcoinPaymentData\n//   - address : \"175tWpb8K1S7NmH4Zx6rewF9WQrcZv245W\"\n//   - version : null\n//   - amount : 50.0\n//   - label : \"Luke-Jr\"\n//   - message : \"Donation for project xyz\"\n//   - parameters : null\n```\n\n### Subscribing to BitcoinKit data\n\nBalance, transactions, last blocks synced and kit state are available in real-time. `BitcoinKit.Listener` interface must be implemented and set to *BitcoinKit* instance to receive that.\n\n```kotlin\nclass Manager(val bitcoinKit: BitcoinKit) : BitcoinKit.Listener {\n    \n    init {\n        bitcoinKit.listener = this\n    }\n\n    override fun onBalanceUpdate(balance: BalanceInfo) {\n    }\n\n    override fun onLastBlockInfoUpdate(blockInfo: BlockInfo) {\n    }\n\n    override fun onKitStateUpdate(state: BitcoinCore.KitState) {\n    }\n\n    override fun onTransactionsUpdate(inserted: List<TransactionInfo>, updated: List<TransactionInfo>) {\n    }\n\n    override fun onTransactionsDelete(hashes: List<String>) {\n    }\n\n}\n```\n\n# bitcoincashkit\n\n\n## Features\n\n- [x] `Base58` and `Bech32`\n- [x] Validation of BCH hard forks\n- [x] `ASERT`, `DAA`, `EDA` validations\n\n\n## Usage \n\nBecause BitcoinCash is a fork of Bitcoin, the usage of this library does not differ much from `bitcoinkit`. So we only describe some differences between them.\n\n### Initialization\n\nAll BitcoinCash wallets use default [BIP44](https://github.com/bitcoin/bips/blob/master/bip-0044.mediawiki) derivation path where *coinType* is `145` according to [SLIP44](https://github.com/satoshilabs/slips/blob/master/slip-0044.md). But since it's a fork of Bitcoin, `0` coinType also can be restored.\n\n```kotlin\nval context = Application()\nval seed = Mnemonic().toSeed(listOf(\"mnemonic\", \"phrase\", \"words\"), \"\")\n\nval bitcoinCashKit = BitcoinCashKit(\n        context = context,\n        seed = seed,\n        walletId = \"unique_wallet_id\",\n        syncMode = BitcoinCore.SyncMode.Api(),\n        networkType = NetworkType.MainNet(MainNetBitcoinCash.CoinType.Type145),\n        confirmationsThreshold = 6\n)\n```\n\n# litecoinkit\n\nUsage identical to `bitcoinkit`\n\n# dashkit\n\n## Features\n\n- [x] Instant send\n- [x] LLMQ lock, Masternodes validation\n\n## Usage\n\n### Initialization\n\n```kotlin\nval context = Application()\nval seed = Mnemonic().toSeed(listOf(\"mnemonic\", \"phrase\", \"words\"), \"\")\n\nval dashKit = DashKit(\n        context = context,\n        seed = seed,\n        walletId = \"unique_wallet_id\",\n        syncMode = BitcoinCore.SyncMode.Api(),\n        networkType = NetworkType.MainNet,\n        confirmationsThreshold = 6\n)\n```\n\n### DashTransactionInfo\n\nDash has some transactions marked `instant`. So, instead of `TransactionInfo` object *DashKit* works with `DashTransactionInfo` that has that field and a respective `DashKit.Listener` listener class.\n\n\n# hodler\n\n`hodler` is a plugin to `bitcoincore`, that makes it possible to lock bitcoins until some time in the future. It relies on [CHECKSEQUENCEVERIFY](https://github.com/bitcoin/bips/blob/master/bip-0112.mediawiki) and [Relative time-locks](https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki). It may be used with other forks of Bitcoin that support them. `UnstooppableWallet` opts in this plugin and enables it for Bitcoin as an experimental feature.\n\n## How it works\n\nTo lock funds we create P2SH output where redeem script has `OP_CSV` OpCode that ensures that the input has a proper Sequence Number(`nSequence`) field and that it enables a relative time-lock. \n\nIn [this](https://blockstream.info/tx/1cd11e80d04c82d098f19badb153ea12ec84cda408daaadc566cc129f967a435?input:1&expand) sample transaction the second input unlocks such an output. It has a signature, public key and the following redeem script in its scriptSig:\n\n`OP_PUSHBYTES_3 070040 OP_CSV OP_DROP OP_DUP OP_HASH160 OP_PUSHBYTES_20 853316620ed93e4ade18f8218f9aa15dc36c768e OP_EQUALVERIFY OP_CHECKSIG`\n\n- `OP_PUSHBYTES_3 070040 OP_CSV OP_DROP` part ensures that needed amount of time is passed. Specifically `07` part of `070040` bytes says that it's locked for 1 hour. See [here](https://github.com/horizontalsystems/bitcoin-kit-android/blob/master/hodler/src/main/kotlin/io/horizontalsystems/hodler/LockTimeInterval.kt) and [here](https://github.com/bitcoin/bips/blob/master/bip-0068.mediawiki) for how it's evaluated.\n- `OP_DUP OP_HASH160 OP_PUSHBYTES_20 853316620ed93e4ade18f8218f9aa15dc36c768e OP_EQUALVERIFY OP_CHECKSIG` part is the same locking script as of `P2PKH` output, that ensures the spender is the owner of the private key matching the public key hashed to `853316620ed93e4ade18f8218f9aa15dc36c768e`.\n\n### Detection of incoming time-locked funds\n\nWhen you have such an `P2SH` output, you only have an address and a hash of a redeem script in the output. If you are not aware of incoming time-locked funds in advance, there's no way you can detect that a particular output is yours. For this reason, we add an extra `OP_RETURN` output beside that `P2SH` output as a hint. That output tells us \n\n- ID of the plugin (1 byte): `bitcoincore` can handle multiple plugins like this one.\n- Time-lock period (2 bytes)\n- Hash of the receiver's public key (20 bytes)\n\nFor example, [this](https://blockstream.info/tx/bdc3e995100269c8813f291dd9ea5489d8a17bd163002f70b5abbe05b5dccbd3?expand) is a *hint* output for the input above. It has following data:\n\n`OP_RETURN OP_PUSHNUM_1 OP_PUSHBYTES_2 0700 OP_PUSHBYTES_20 853316620ed93e4ade18f8218f9aa15dc36c768e` \n\n\n## Limitations\n\n### Locked time periods\n\nThis plugin can lock coins for `1 hour`, `1 month`, `half a year` and `1 year`. This is a limitation arising from the need of restoring those outputs using Simplified Payment Verification (SPV) `Bloom Filters`. Since each lock time generates different `P2SH` addresses, it wouldn't be possible to restore those outputs without knowing the exact lock time period in advance. So we generate 4 different addresses for each public key and use them in the bloom filters.\n\n### BTC amount\n\nWe allow maximum 0.5 BTC to be locked. We assume that's an acceptable amount to be locked if done unintentionally.\n\n\n## Prerequisites\n* JDK >= 1.8\n* Android 6 (minSdkVersion 23) or greater\n\n## Installation\nAdd the JitPack to module build.gradle\n```\nrepositories {\n    maven { url 'https://jitpack.io' }\n}\n```\nAdd the following dependency to your build.gradle file:\n```\ndependencies {\n    implementation 'com.github.horizontalsystems:bitcoin-kit-android:master-SNAPSHOT'\n}\n```\n\n## Example App\n\nAll features of the library are used in example project. It can be referred as a starting point for usage of the library.\n* [Example App](https://github.com/horizontalsystems/bitcoin-kit-android/tree/master/app)\n\n## Dependencies\n* [HDWalletKit](https://github.com/horizontalsystems/hd-wallet-kit-android) - HD Wallet related features, mnemonic phrase \n\n## Contributing\n\n[Contributing](CONTRIBUTING.md)\n\n## License\n\nThe `bitcoin-kit-android` is open source and available under the terms of the [MIT License](https://github.com/horizontalsystems/bitcoin-kit-android/blob/master/LICENSE)\n"
  },
  {
    "path": "app/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "app/build.gradle",
    "content": "plugins {\n    id 'com.android.application'\n    id 'org.jetbrains.kotlin.android'\n}\n\nandroid {\n    namespace 'io.horizontalsystems.bitcoinkit.demo'\n    compileSdk 34\n\n    defaultConfig {\n        applicationId \"io.horizontalsystems.bitcoinkit.demo\"\n        minSdkVersion 23\n        targetSdkVersion 34\n        versionCode 1\n        versionName \"0.3.0\"\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_17\n        targetCompatibility JavaVersion.VERSION_17\n    }\n    kotlinOptions { jvmTarget = '17' }\n    packagingOptions {\n        resources {\n            pickFirsts += ['META-INF/atomicfu.kotlin_module']\n        }\n    }\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version\"\n    implementation 'androidx.appcompat:appcompat:1.6.1'\n    implementation 'androidx.constraintlayout:constraintlayout:2.1.4'\n    implementation 'com.google.android.material:material:1.10.0'\n\n    implementation 'io.reactivex.rxjava2:rxjava:2.2.19'\n    implementation 'io.reactivex.rxjava2:rxandroid:2.1.1'\n\n    // ViewModel and LiveData\n    implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'\n\n    //LeakCanary\n    debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.10'\n\n    testImplementation 'junit:junit:4.13.2'\n    androidTestImplementation 'androidx.test.ext:junit:1.1.5'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'\n\n    implementation project(':bitcoinkit')\n    implementation project(':dashkit')\n    implementation project(':bitcoincashkit')\n    implementation project(':litecoinkit')\n    implementation project(':ecashkit')\n}\n"
  },
  {
    "path": "app/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "app/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <uses-permission android:name=\"android.permission.INTERNET\" />\n\n    <application\n        android:name=\".App\"\n        android:allowBackup=\"false\"\n        android:icon=\"@mipmap/ic_launcher\"\n        android:label=\"@string/app_name\"\n        android:roundIcon=\"@mipmap/ic_launcher_round\"\n        android:supportsRtl=\"true\"\n        android:theme=\"@style/AppTheme\">\n        <activity\n            android:name=\".MainActivity\"\n            android:exported=\"true\">\n            <intent-filter>\n                <action android:name=\"android.intent.action.MAIN\" />\n\n                <category android:name=\"android.intent.category.LAUNCHER\" />\n            </intent-filter>\n        </activity>\n    </application>\n\n</manifest>"
  },
  {
    "path": "app/src/main/java/io/horizontalsystems/bitcoinkit/demo/App.kt",
    "content": "package io.horizontalsystems.bitcoinkit.demo\n\nimport android.app.Application\n\nclass App : Application() {\n\n    override fun onCreate() {\n        super.onCreate()\n\n        instance = this\n    }\n\n    companion object {\n        lateinit var instance: App\n            private set\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/io/horizontalsystems/bitcoinkit/demo/BalanceFragment.kt",
    "content": "package io.horizontalsystems.bitcoinkit.demo\n\nimport androidx.lifecycle.Observer\nimport android.os.Bundle\nimport androidx.fragment.app.Fragment\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.Button\nimport android.widget.TextView\nimport androidx.appcompat.app.AlertDialog\nimport androidx.lifecycle.ViewModelProvider\nimport io.horizontalsystems.bitcoincore.BitcoinCore\nimport java.text.SimpleDateFormat\nimport java.util.*\n\nclass BalanceFragment : Fragment() {\n\n    lateinit var viewModel: MainViewModel\n    lateinit var networkName: TextView\n    lateinit var balanceValue: TextView\n    lateinit var balanceUnspendableValue: TextView\n    lateinit var lastBlockDateValue: TextView\n    lateinit var lastBlockValue: TextView\n    lateinit var stateValue: TextView\n    lateinit var startButton: Button\n    lateinit var clearButton: Button\n    lateinit var buttonDebug: Button\n    lateinit var buttonStatus: Button\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        viewModel = activity?.let { ViewModelProvider(it).get(MainViewModel::class.java) } ?: return\n\n        viewModel.balance.observe(this, Observer { balance ->\n            when (balance) {\n                null -> {\n                    balanceValue.text = \"\"\n                    balanceUnspendableValue.text = \"\"\n                }\n                else -> {\n                    balanceValue.text = NumberFormatHelper.cryptoAmountFormat.format(balance.spendable / 100_000_000.0)\n                    balanceUnspendableValue.text = NumberFormatHelper.cryptoAmountFormat.format((balance.unspendableTimeLocked + balance.unspendableNotRelayed) / 100_000_000.0)\n                }\n            }\n        })\n\n        val dateFormat = SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\", Locale.US)\n        viewModel.lastBlock.observe(this, Observer {\n            it?.let { blockInfo ->\n                lastBlockValue.text = blockInfo.height.toString()\n\n                val strDate = dateFormat.format(Date(blockInfo.timestamp * 1000))\n                lastBlockDateValue.text = strDate\n            }\n        })\n\n        viewModel.state.observe(this, Observer { state ->\n            when (state) {\n                is BitcoinCore.KitState.Synced -> {\n                    stateValue.text = \"synced\"\n                }\n                is BitcoinCore.KitState.ApiSyncing -> {\n                    stateValue.text = \"api syncing ${state.transactions} txs\"\n                }\n                is BitcoinCore.KitState.Syncing -> {\n                    stateValue.text = \"syncing ${\"%.3f\".format(state.progress)}\"\n                }\n                is BitcoinCore.KitState.NotSynced -> {\n                    stateValue.text = \"not synced ${state.exception.javaClass.simpleName}\"\n                }\n            }\n        })\n\n        viewModel.status.observe(this, Observer {\n            when (it) {\n                MainViewModel.State.STARTED -> {\n                    startButton.isEnabled = false\n                }\n                else -> {\n                    startButton.isEnabled = true\n                }\n            }\n        })\n\n        viewModel.statusInfo.observe(this, Observer { statusInfo ->\n            activity?.let {\n                val dialog = AlertDialog.Builder(it)\n                        .setMessage(formatMapToString(statusInfo))\n                        .setTitle(\"Status Info\")\n                        .create()\n                dialog.show()\n            }\n        })\n    }\n\n    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {\n        return inflater.inflate(R.layout.fragment_balance, container, false)\n    }\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n\n        networkName = view.findViewById(R.id.networkName)\n        networkName.text = viewModel.networkName\n\n        balanceValue = view.findViewById(R.id.balanceValue)\n        balanceUnspendableValue = view.findViewById(R.id.balanceUnspendableValue)\n        lastBlockValue = view.findViewById(R.id.lastBlockValue)\n        lastBlockDateValue = view.findViewById(R.id.lastBlockDateValue)\n        stateValue = view.findViewById(R.id.stateValue)\n        startButton = view.findViewById(R.id.buttonStart)\n        clearButton = view.findViewById(R.id.buttonClear)\n        buttonDebug = view.findViewById(R.id.buttonDebug)\n        buttonStatus = view.findViewById(R.id.buttonStatus)\n\n        startButton.setOnClickListener {\n            viewModel.start()\n        }\n\n        clearButton.setOnClickListener {\n            viewModel.clear()\n        }\n\n        buttonDebug.setOnClickListener {\n            viewModel.showDebugInfo()\n        }\n\n        buttonStatus.setOnClickListener {\n            viewModel.showStatusInfo()\n        }\n    }\n\n    @Suppress(\"UNCHECKED_CAST\")\n    private fun formatMapToString(status: Map<String, Any>?, indentation: String = \"\", bullet: String = \"\", level: Int = 0): String? {\n        if (status == null)\n            return null\n\n        val sb = StringBuilder()\n        status.toList().forEach { (key, value) ->\n            val title = \"$indentation$bullet$key\"\n            when (value) {\n                is Map<*, *> -> {\n                    val formattedValue = formatMapToString(value as? Map<String, Any>, \"\\t\\t$indentation\", \" - \", level + 1)\n                    sb.append(\"$title:\\n$formattedValue${if (level < 2) \"\\n\" else \"\"}\")\n                }\n                else -> {\n                    sb.appendln(\"$title: $value\")\n                }\n            }\n        }\n\n        val statusString = sb.trimEnd()\n\n        return if (statusString.isEmpty()) \"\" else \"$statusString\\n\"\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/io/horizontalsystems/bitcoinkit/demo/FeePriority.kt",
    "content": "package io.horizontalsystems.bitcoinkit.demo\n\nsealed class FeePriority(val feeRate: Int) {\n    object Low : FeePriority(5)\n    object Medium : FeePriority(10)\n    object High : FeePriority(15)\n}\n"
  },
  {
    "path": "app/src/main/java/io/horizontalsystems/bitcoinkit/demo/MainActivity.kt",
    "content": "package io.horizontalsystems.bitcoinkit.demo\n\nimport android.os.Bundle\nimport com.google.android.material.bottomnavigation.BottomNavigationView\nimport androidx.fragment.app.Fragment\nimport androidx.appcompat.app.AppCompatActivity\nimport android.view.MenuItem\nimport androidx.lifecycle.ViewModelProvider\n\nclass MainActivity : AppCompatActivity(), BottomNavigationView.OnNavigationItemSelectedListener {\n\n    private val balanceFragment = BalanceFragment()\n    private val transactionsFragment = TransactionsFragment()\n    private val sendReceiveFragment = SendReceiveFragment()\n    private val fm = supportFragmentManager\n    private var active: Fragment = balanceFragment\n\n    lateinit var viewModel: MainViewModel\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n        setContentView(R.layout.activity_main)\n\n        val navigation = findViewById<BottomNavigationView>(R.id.navigation)\n        navigation.setOnNavigationItemSelectedListener(this)\n\n        fm.beginTransaction().add(R.id.fragment_container, sendReceiveFragment, \"3\").hide(sendReceiveFragment).commit()\n        fm.beginTransaction().add(R.id.fragment_container, transactionsFragment, \"2\").hide(transactionsFragment).commit()\n        fm.beginTransaction().add(R.id.fragment_container, balanceFragment, \"1\").commit()\n\n        viewModel = ViewModelProvider(this).get(MainViewModel::class.java)\n        viewModel.init()\n    }\n\n    override fun onNavigationItemSelected(item: MenuItem): Boolean {\n        val fragment = when (item.itemId) {\n            R.id.navigation_home -> balanceFragment\n            R.id.navigation_transactions -> transactionsFragment\n            R.id.navigation_send_receive -> sendReceiveFragment\n            else -> null\n        }\n\n        if (fragment != null) {\n            supportFragmentManager\n                    .beginTransaction()\n                    .hide(active)\n                    .show(fragment)\n                    .commit()\n\n            active = fragment\n\n            return true\n        }\n\n        return false\n    }\n\n}\n"
  },
  {
    "path": "app/src/main/java/io/horizontalsystems/bitcoinkit/demo/MainViewModel.kt",
    "content": "package io.horizontalsystems.bitcoinkit.demo\n\nimport androidx.lifecycle.MutableLiveData\nimport androidx.lifecycle.ViewModel\nimport io.horizontalsystems.bitcoincore.BitcoinCore\nimport io.horizontalsystems.bitcoincore.BitcoinCore.KitState\nimport io.horizontalsystems.bitcoincore.core.IPluginData\nimport io.horizontalsystems.bitcoincore.exceptions.AddressFormatException\nimport io.horizontalsystems.bitcoincore.managers.SendValueErrors\nimport io.horizontalsystems.bitcoincore.models.BalanceInfo\nimport io.horizontalsystems.bitcoincore.models.BitcoinSendInfo\nimport io.horizontalsystems.bitcoincore.models.BlockInfo\nimport io.horizontalsystems.bitcoincore.models.TransactionDataSortType\nimport io.horizontalsystems.bitcoincore.models.TransactionFilterType\nimport io.horizontalsystems.bitcoincore.models.TransactionInfo\nimport io.horizontalsystems.bitcoincore.storage.UtxoFilters\nimport io.horizontalsystems.bitcoinkit.BitcoinKit\nimport io.horizontalsystems.hdwalletkit.HDWallet.Purpose\nimport io.horizontalsystems.hodler.HodlerData\nimport io.horizontalsystems.hodler.HodlerPlugin\nimport io.horizontalsystems.hodler.LockTimeInterval\nimport io.reactivex.disposables.CompositeDisposable\n\nclass MainViewModel : ViewModel(), BitcoinKit.Listener {\n\n    enum class State {\n        STARTED, STOPPED\n    }\n\n    private var transactionFilterType: TransactionFilterType? = null\n    val types = listOf(null) + TransactionFilterType.values()\n\n    val transactions = MutableLiveData<List<TransactionInfo>>()\n    val balance = MutableLiveData<BalanceInfo>()\n    val lastBlock = MutableLiveData<BlockInfo>()\n    val state = MutableLiveData<KitState>()\n    val status = MutableLiveData<State>()\n    val transactionRaw = MutableLiveData<String>()\n    val statusInfo = MutableLiveData<Map<String, Any>>()\n    lateinit var networkName: String\n    private val disposables = CompositeDisposable()\n\n    private var started = false\n        set(value) {\n            field = value\n            status.value = (if (value) State.STARTED else State.STOPPED)\n        }\n\n    private lateinit var bitcoinKit: BitcoinKit\n\n    private val walletId = \"MyWallet\"\n    private val networkType = BitcoinKit.NetworkType.MainNet\n    private val syncMode = BitcoinCore.SyncMode.Api()\n    private val purpose = Purpose.BIP44\n\n    fun init() {\n        //TODO create unique seed phrase,perhaps using shared preferences?\n        val words = \"used ugly meat glad balance divorce inner artwork hire invest already piano\".split(\" \")\n        val passphrase = \"\"\n\n        bitcoinKit = BitcoinKit(App.instance, words, passphrase, walletId, networkType, syncMode = syncMode, purpose = purpose)\n\n        bitcoinKit.listener = this\n\n        networkName = bitcoinKit.networkName\n        balance.value = bitcoinKit.balance\n\n        lastBlock.value = bitcoinKit.lastBlockInfo\n        state.value = bitcoinKit.syncState\n\n        started = false\n    }\n\n    fun start() {\n        if (started) return\n        started = true\n\n        bitcoinKit.start()\n    }\n\n    fun clear() {\n        bitcoinKit.stop()\n        BitcoinKit.clear(App.instance, networkType, walletId)\n\n        init()\n    }\n\n\n    fun showDebugInfo() {\n        bitcoinKit.showDebugInfo()\n    }\n\n    fun showStatusInfo() {\n        statusInfo.postValue(bitcoinKit.statusInfo())\n    }\n\n    //\n    // BitcoinKit Listener implementations\n    //\n    override fun onTransactionsUpdate(inserted: List<TransactionInfo>, updated: List<TransactionInfo>) {\n        setTransactionFilterType(transactionFilterType)\n    }\n\n    override fun onTransactionsDelete(hashes: List<String>) {\n    }\n\n    override fun onBalanceUpdate(balance: BalanceInfo) {\n        this.balance.postValue(balance)\n    }\n\n    override fun onLastBlockInfoUpdate(blockInfo: BlockInfo) {\n        this.lastBlock.postValue(blockInfo)\n    }\n\n    override fun onKitStateUpdate(state: KitState) {\n        this.state.postValue(state)\n    }\n\n    val receiveAddressLiveData = MutableLiveData<String>()\n    val feeLiveData = MutableLiveData<Long>()\n    val errorLiveData = MutableLiveData<String>()\n    val addressLiveData = MutableLiveData<String>()\n    val amountLiveData = MutableLiveData<Long>()\n\n    var amount: Long? = null\n        set(value) {\n            field = value\n            updateFee()\n        }\n\n    var address: String? = null\n        set(value) {\n            field = value\n            updateFee()\n        }\n\n    var feePriority: FeePriority = FeePriority.Medium\n        set(value) {\n            field = value\n            updateFee()\n        }\n\n    var timeLockInterval: LockTimeInterval? = null\n        set(value) {\n            field = value\n            updateFee()\n        }\n\n    fun onReceiveClick() {\n        receiveAddressLiveData.value = bitcoinKit.receiveAddress()\n    }\n\n    fun onSendClick() {\n        when {\n            address.isNullOrBlank() -> {\n                errorLiveData.value = \"Send address cannot be blank\"\n            }\n            amount == null -> {\n                errorLiveData.value = \"Send amount cannot be blank\"\n            }\n            else -> {\n                try {\n                    val transaction = bitcoinKit.send(\n                        address!!,\n                        null,\n                        amount!!,\n                        feeRate = feePriority.feeRate,\n                        sortType = TransactionDataSortType.Shuffle,\n                        pluginData = getPluginData(),\n                        rbfEnabled = true,\n                        changeToFirstInput = false,\n                        filters = UtxoFilters()\n                    )\n\n                    amountLiveData.value = null\n                    feeLiveData.value = null\n                    addressLiveData.value = null\n                    errorLiveData.value = \"Transaction sent ${transaction.header.serializedTxInfo}\"\n                } catch (e: Exception) {\n                    errorLiveData.value = when (e) {\n                        is SendValueErrors.InsufficientUnspentOutputs,\n                        is SendValueErrors.EmptyOutputs -> \"Insufficient balance\"\n                        is AddressFormatException -> \"Could not Format Address\"\n                        else -> e.message ?: \"Failed to send transaction (${e.javaClass.name})\"\n                    }\n\n                }\n            }\n        }\n    }\n\n    fun onMaxClick() {\n        try {\n            amountLiveData.value = bitcoinKit.maximumSpendableValue(\n                address,\n                null,\n                feePriority.feeRate,\n                null,\n                getPluginData(),\n                false,\n                UtxoFilters()\n            )\n        } catch (e: Exception) {\n            amountLiveData.value = 0\n            errorLiveData.value = when (e) {\n\n                is SendValueErrors.Dust,\n                is SendValueErrors.EmptyOutputs -> \"You need at least ${e.message} satoshis to make an transaction\"\n                is AddressFormatException -> \"Could not Format Address\"\n                else -> e.message ?: \"Maximum could not be calculated\"\n            }\n        }\n    }\n\n    private fun updateFee() {\n        try {\n            feeLiveData.value = amount?.let {\n                fee(it, address).fee\n            }\n        } catch (e: Exception) {\n            errorLiveData.value = e.message ?: e.javaClass.simpleName\n        }\n    }\n\n    private fun fee(value: Long, address: String? = null): BitcoinSendInfo {\n        return bitcoinKit.sendInfo(\n            value,\n            address,\n            null,\n            feeRate = feePriority.feeRate,\n            unspentOutputs = null,\n            pluginData = getPluginData(),\n            changeToFirstInput = false,\n            filters = UtxoFilters()\n        )\n    }\n\n    private fun getPluginData(): MutableMap<Byte, IPluginData> {\n        val pluginData = mutableMapOf<Byte, IPluginData>()\n        timeLockInterval?.let {\n            pluginData[HodlerPlugin.id] = HodlerData(it)\n        }\n        return pluginData\n    }\n\n    fun onRawTransactionClick(transactionHash: String) {\n        transactionRaw.postValue(bitcoinKit.getRawTransaction(transactionHash))\n    }\n\n    fun setTransactionFilterType(transactionFilterType: TransactionFilterType?) {\n        this.transactionFilterType = transactionFilterType\n\n        bitcoinKit.transactions(type = transactionFilterType).subscribe { txList: List<TransactionInfo> ->\n            transactions.postValue(txList)\n        }.let {\n            disposables.add(it)\n        }\n    }\n}\n"
  },
  {
    "path": "app/src/main/java/io/horizontalsystems/bitcoinkit/demo/NumberFormatHelper.kt",
    "content": "package io.horizontalsystems.bitcoinkit.demo\n\nimport java.text.NumberFormat\n\nobject NumberFormatHelper {\n\n    val fiatAmountFormat: NumberFormat\n        get() {\n            val numberFormat = NumberFormat.getInstance()\n            numberFormat.maximumFractionDigits = 2\n            numberFormat.minimumFractionDigits = 2\n            return numberFormat\n        }\n\n    val cryptoAmountFormat: NumberFormat\n        get() {\n            val numberFormat = NumberFormat.getInstance()\n            numberFormat.maximumFractionDigits = 12\n            numberFormat.minimumFractionDigits = 2\n            return numberFormat\n        }\n\n}\n"
  },
  {
    "path": "app/src/main/java/io/horizontalsystems/bitcoinkit/demo/SendReceiveFragment.kt",
    "content": "package io.horizontalsystems.bitcoinkit.demo\n\nimport android.os.Bundle\nimport android.text.Editable\nimport android.text.TextWatcher\nimport android.view.Gravity\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.AdapterView\nimport android.widget.ArrayAdapter\nimport android.widget.Button\nimport android.widget.EditText\nimport android.widget.RadioGroup\nimport android.widget.Spinner\nimport android.widget.TextView\nimport android.widget.Toast\nimport androidx.fragment.app.Fragment\nimport androidx.lifecycle.Observer\nimport androidx.lifecycle.ViewModelProvider\nimport io.horizontalsystems.hodler.LockTimeInterval\n\nclass SendReceiveFragment : Fragment() {\n    private lateinit var viewModel: MainViewModel\n\n    lateinit var receiveAddressText: TextView\n    lateinit var receiveAddressButton: Button\n    lateinit var sendAmount: EditText\n    lateinit var sendAddress: EditText\n    lateinit var txFeeValue: TextView\n    lateinit var sendButton: Button\n    lateinit var maxButton: Button\n    lateinit var radioGroup: RadioGroup\n    lateinit var lockTimePeriodValue: Spinner\n\n    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {\n        return inflater.inflate(R.layout.fragment_send_receive, container, false)\n    }\n\n    override fun onActivityCreated(savedInstanceState: Bundle?) {\n        super.onActivityCreated(savedInstanceState)\n\n        viewModel = activity?.let { ViewModelProvider(it).get(MainViewModel::class.java) } ?: return\n\n        viewModel.receiveAddressLiveData.observe(viewLifecycleOwner, Observer {\n            receiveAddressText.text = it\n        })\n\n        viewModel.amountLiveData.observe(viewLifecycleOwner, Observer {\n            sendAmount.setText(it?.toString())\n        })\n\n        viewModel.addressLiveData.observe(viewLifecycleOwner, Observer {\n            sendAddress.setText(it)\n        })\n\n        viewModel.feeLiveData.observe(viewLifecycleOwner, Observer {\n            txFeeValue.text = it?.toString()\n        })\n\n        viewModel.errorLiveData.observe(viewLifecycleOwner, Observer {\n            //TODO TOASTS NO PREVIOUS OUTPUT SCRIPT CHANGE THE SEED?\n            val toast = Toast.makeText(context, \"$it\", Toast.LENGTH_LONG)\n\n            toast.setGravity(Gravity.CENTER, 0, 0)\n            toast.show()\n        })\n\n    }\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n\n        receiveAddressText = view.findViewById(R.id.receiveAddressText)\n        receiveAddressButton = view.findViewById(R.id.receiveAddressButton)\n        sendAmount = view.findViewById(R.id.sendAmount)\n        sendAddress = view.findViewById(R.id.sendAddress)\n        txFeeValue = view.findViewById(R.id.txFeeValue)\n        sendButton = view.findViewById(R.id.sendButton)\n        maxButton = view.findViewById(R.id.maxButton)\n        radioGroup = view.findViewById(R.id.radioGroup)\n        lockTimePeriodValue = view.findViewById(R.id.lockTimePeriodValue)\n\n        receiveAddressButton.setOnClickListener {\n            viewModel.onReceiveClick()\n        }\n\n        sendButton.setOnClickListener {\n            viewModel.onSendClick()\n        }\n\n        maxButton.setOnClickListener {\n\n\n            viewModel.onMaxClick()\n\n        }\n\n        sendAmount.addTextChangedListener(SimpleTextWatcher {\n            viewModel.amount = try {\n                sendAmount.text?.toString()?.toLong()\n            } catch (e: NumberFormatException) {\n                null\n            }\n        })\n\n        sendAddress.addTextChangedListener(SimpleTextWatcher {\n            viewModel.address = sendAddress.text.toString()\n        })\n\n        radioGroup.setOnCheckedChangeListener { _, checkedId ->\n            val feePriority = when (checkedId) {\n                R.id.radioLow -> FeePriority.Low\n                R.id.radioMedium -> FeePriority.Medium\n                R.id.radioHigh -> FeePriority.High\n                else -> throw Exception(\"Undefined priority\")\n            }\n            viewModel.feePriority = feePriority\n        }\n\n\n        lockTimePeriodValue.adapter = ArrayAdapter(view.context, android.R.layout.simple_spinner_dropdown_item, LockTimeIntervalOption.getIntervals())\n        lockTimePeriodValue.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {\n            override fun onNothingSelected(parent: AdapterView<*>?) = Unit\n\n            override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {\n                viewModel.timeLockInterval = (parent?.getItemAtPosition(position) as LockTimeIntervalOption).interval\n            }\n        }\n    }\n}\n\nclass LockTimeIntervalOption(val interval: LockTimeInterval? = null) {\n    override fun toString(): String {\n        return interval?.name ?: \"OFF\"\n    }\n\n    companion object {\n        fun getIntervals(): Array<LockTimeIntervalOption> {\n            return arrayOf(LockTimeIntervalOption()) + LockTimeInterval.values().map { LockTimeIntervalOption(it) }\n        }\n    }\n}\n\nclass SimpleTextWatcher(private val callback: (s: Editable?) -> Unit) : TextWatcher {\n    override fun afterTextChanged(s: Editable?) {\n        callback.invoke(s)\n    }\n\n    override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) = Unit\n    override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) = Unit\n}\n"
  },
  {
    "path": "app/src/main/java/io/horizontalsystems/bitcoinkit/demo/TransactionsFragment.kt",
    "content": "package io.horizontalsystems.bitcoinkit.demo\n\nimport android.annotation.SuppressLint\nimport android.graphics.Color\nimport android.os.Bundle\nimport android.view.LayoutInflater\nimport android.view.View\nimport android.view.ViewGroup\nimport android.widget.Button\nimport android.widget.TextView\nimport androidx.appcompat.app.AlertDialog\nimport androidx.fragment.app.Fragment\nimport androidx.lifecycle.Observer\nimport androidx.lifecycle.ViewModelProvider\nimport androidx.recyclerview.widget.LinearLayoutManager\nimport androidx.recyclerview.widget.RecyclerView\nimport com.google.android.material.tabs.TabLayout\nimport io.horizontalsystems.bitcoincore.models.TransactionInfo\nimport io.horizontalsystems.bitcoincore.models.TransactionInputInfo\nimport io.horizontalsystems.bitcoincore.models.TransactionOutputInfo\nimport io.horizontalsystems.dashkit.models.DashTransactionInfo\nimport io.horizontalsystems.hodler.HodlerOutputData\nimport io.horizontalsystems.hodler.HodlerPlugin\nimport java.text.DateFormat\nimport java.util.Date\nimport java.util.Locale\n\nclass TransactionsFragment : Fragment(), ViewHolderTransaction.Listener {\n\n    private lateinit var viewModel: MainViewModel\n    private lateinit var transactionsRecyclerView: RecyclerView\n    private val transactionsAdapter = TransactionsAdapter(this)\n\n    lateinit var tabs: TabLayout\n\n    override fun onCreate(savedInstanceState: Bundle?) {\n        super.onCreate(savedInstanceState)\n\n        viewModel = activity?.let { ViewModelProvider(it).get(MainViewModel::class.java) } ?: return\n\n        viewModel.transactions.observe(this, Observer {\n            it?.let { transactions ->\n                transactionsAdapter.items = transactions\n                transactionsAdapter.notifyDataSetChanged()\n            }\n        })\n\n        viewModel.transactionRaw.observe(this, Observer { transactionHex ->\n            activity?.let {\n                val dialog = AlertDialog.Builder(it)\n                        .setMessage(transactionHex)\n                        .setTitle(\"Transaction HEX\")\n                        .create()\n\n                dialog.show()\n            }\n        })\n    }\n\n    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {\n        return inflater.inflate(R.layout.fragment_transactions, container, false)\n    }\n\n    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {\n        super.onViewCreated(view, savedInstanceState)\n\n        tabs = view.findViewById(R.id.tabs)\n\n        transactionsRecyclerView = view.findViewById(R.id.transactions)\n        transactionsRecyclerView.adapter = transactionsAdapter\n        transactionsRecyclerView.layoutManager = LinearLayoutManager(context)\n\n        viewModel.types.forEach {\n            val text = tabs.newTab()\n                .setText(it?.name ?: \"All\")\n                .setId(it?.ordinal ?: 100)\n\n            tabs.addTab(text)\n        }\n\n        tabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {\n            override fun onTabSelected(tab: TabLayout.Tab) {\n                viewModel.setTransactionFilterType(viewModel.types.find {\n                    it?.ordinal == tab.id\n                })\n            }\n\n            override fun onTabUnselected(tab: TabLayout.Tab?) = Unit\n            override fun onTabReselected(tab: TabLayout.Tab?) = Unit\n        })\n\n        viewModel.setTransactionFilterType(null)\n    }\n\n    override fun onClickRawTransaction(transactionHash: String) {\n        viewModel.onRawTransactionClick(transactionHash)\n    }\n}\n\nclass TransactionsAdapter(private val listener: ViewHolderTransaction.Listener) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {\n    var items = listOf<TransactionInfo>()\n\n    override fun getItemCount() = items.size\n\n    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =\n            ViewHolderTransaction(LayoutInflater.from(parent.context).inflate(R.layout.view_holder_transaction, parent, false), listener)\n\n    override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {\n        when (holder) {\n            is ViewHolderTransaction -> holder.bind(items[position], itemCount - position)\n        }\n    }\n}\n\nclass ViewHolderTransaction(val containerView: View, private val listener: Listener) : RecyclerView.ViewHolder(containerView) {\n\n    interface Listener {\n        fun onClickRawTransaction(transactionHash: String)\n    }\n\n    private val summary = containerView.findViewById<TextView>(R.id.summary)!!\n    private val buttonRaw = containerView.findViewById<Button>(R.id.buttonRaw)!!\n\n    @SuppressLint(\"SetTextI18n\")\n    fun bind(transactionInfo: TransactionInfo, index: Int) {\n        buttonRaw.setOnClickListener {\n            listener.onClickRawTransaction(transactionInfo.transactionHash)\n        }\n\n        containerView.setBackgroundColor(if (index % 2 == 0) Color.parseColor(\"#dddddd\") else Color.TRANSPARENT)\n\n        val txAmount = transactionInfo.amount\n        val amount = NumberFormatHelper.cryptoAmountFormat.format(txAmount / 100_000_000.0)\n        val fee = transactionInfo.fee?.let {\n            NumberFormatHelper.cryptoAmountFormat.format(it / 100_000_000.0)\n        } ?: \"n/a\"\n\n        var text = \"#$index\"\n        text += \"\\nStatus: ${transactionInfo.status.name}, ${transactionInfo.type.name}\"\n        if (transactionInfo is DashTransactionInfo) {\n            text += \"\\nInstant: ${transactionInfo.instantTx.toString().toUpperCase(Locale.getDefault())}\"\n        }\n\n        text += \"\\nInputs: ${mapInputs(transactionInfo.inputs)}\" +\n                \"\\nOutputs: ${mapOutputs(transactionInfo.outputs)}\" +\n                \"\\nAmount: $amount\" +\n                \"\\nFee: $fee\" +\n                \"\\nTx hash: ${transactionInfo.transactionHash}\" +\n                \"\\nTx index: ${transactionInfo.transactionIndex}\" +\n                \"\\nBlock: ${transactionInfo.blockHeight}\" +\n                \"\\nTimestamp: ${transactionInfo.timestamp}\" +\n                \"\\nDate: ${formatDate(transactionInfo.timestamp)}\" +\n                \"\\nConflicting tx hash: ${transactionInfo.conflictingTxHash}\"\n\n        summary.text = text\n        summary.setOnClickListener {\n            println(transactionInfo)\n            println(text)\n        }\n    }\n\n    private fun mapOutputs(list: List<TransactionOutputInfo>): String {\n        return list.joinToString(\"\") {\n            val sb = StringBuilder()\n            sb.append(\"\\n- address: ${it.address}\")\n            sb.append(\"\\n  value: ${it.value}\")\n            sb.append(\"\\n  mine: ${it.mine}\")\n            sb.append(\"\\n  change: ${it.changeOutput}\")\n            sb.append(\"\\n  memo: ${it.memo}\")\n\n            if (it.pluginId == HodlerPlugin.id && it.pluginData != null) {\n                (it.pluginData as? HodlerOutputData)?.let { hodlerData ->\n                    val lockTimeInterval = hodlerData.lockTimeInterval\n\n                    hodlerData.approxUnlockTime?.let { lockedUntilApprox ->\n                        sb.append(\"\\n  * Locked: ${lockTimeInterval.name}, approx until ${formatDate(lockedUntilApprox)}\")\n                    }\n\n                    sb.append(\"\\n  * Address: ${hodlerData.addressString}\")\n                    sb.append(\"\\n  * Value: ${it.value}\")\n                }\n            }\n            sb.toString()\n        }\n    }\n\n    private fun mapInputs(list: List<TransactionInputInfo>): String {\n        return list.joinToString(\"\") {\n            val sb = StringBuilder()\n            sb.append(\"\\n- address: ${it.address}\")\n            sb.append(\"\\n  value: ${it.value}\")\n            sb.append(\"\\n  mine: ${it.mine}\")\n\n            sb.toString()\n        }\n    }\n\n    private fun formatDate(timestamp: Long): String {\n        return DateFormat.getInstance().format(Date(timestamp * 1000))\n    }\n}\n"
  },
  {
    "path": "app/src/main/res/drawable/ic_launcher_background.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n    <path\n        android:fillColor=\"#26A69A\"\n        android:pathData=\"M0,0h108v108h-108z\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M9,0L9,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,0L19,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,0L29,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,0L39,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,0L49,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,0L59,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,0L69,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,0L79,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M89,0L89,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M99,0L99,108\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,9L108,9\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,19L108,19\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,29L108,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,39L108,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,49L108,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,59L108,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,69L108,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,79L108,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,89L108,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M0,99L108,99\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,29L89,29\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,39L89,39\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,49L89,49\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,59L89,59\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,69L89,69\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M19,79L89,79\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M29,19L29,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M39,19L39,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M49,19L49,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M59,19L59,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M69,19L69,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n    <path\n        android:fillColor=\"#00000000\"\n        android:pathData=\"M79,19L79,89\"\n        android:strokeColor=\"#33FFFFFF\"\n        android:strokeWidth=\"0.8\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/drawable-v24/ic_launcher_foreground.xml",
    "content": "<vector xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:aapt=\"http://schemas.android.com/aapt\"\n    android:width=\"108dp\"\n    android:height=\"108dp\"\n    android:viewportHeight=\"108\"\n    android:viewportWidth=\"108\">\n    <path\n        android:fillType=\"evenOdd\"\n        android:pathData=\"M32,64C32,64 38.39,52.99 44.13,50.95C51.37,48.37 70.14,49.57 70.14,49.57L108.26,87.69L108,109.01L75.97,107.97L32,64Z\"\n        android:strokeColor=\"#00000000\"\n        android:strokeWidth=\"1\">\n        <aapt:attr name=\"android:fillColor\">\n            <gradient\n                android:endX=\"78.5885\"\n                android:endY=\"90.9159\"\n                android:startX=\"48.7653\"\n                android:startY=\"61.0927\"\n                android:type=\"linear\">\n                <item\n                    android:color=\"#44000000\"\n                    android:offset=\"0.0\" />\n                <item\n                    android:color=\"#00000000\"\n                    android:offset=\"1.0\" />\n            </gradient>\n        </aapt:attr>\n    </path>\n    <path\n        android:fillColor=\"#FFFFFF\"\n        android:fillType=\"nonZero\"\n        android:pathData=\"M66.94,46.02L66.94,46.02C72.44,50.07 76,56.61 76,64L32,64C32,56.61 35.56,50.11 40.98,46.06L36.18,41.19C35.45,40.45 35.45,39.3 36.18,38.56C36.91,37.81 38.05,37.81 38.78,38.56L44.25,44.05C47.18,42.57 50.48,41.71 54,41.71C57.48,41.71 60.78,42.57 63.68,44.05L69.11,38.56C69.84,37.81 70.98,37.81 71.71,38.56C72.44,39.3 72.44,40.45 71.71,41.19L66.94,46.02ZM62.94,56.92C64.08,56.92 65,56.01 65,54.88C65,53.76 64.08,52.85 62.94,52.85C61.8,52.85 60.88,53.76 60.88,54.88C60.88,56.01 61.8,56.92 62.94,56.92ZM45.06,56.92C46.2,56.92 47.13,56.01 47.13,54.88C47.13,53.76 46.2,52.85 45.06,52.85C43.92,52.85 43,53.76 43,54.88C43,56.01 43.92,56.92 45.06,56.92Z\"\n        android:strokeColor=\"#00000000\"\n        android:strokeWidth=\"1\" />\n</vector>\n"
  },
  {
    "path": "app/src/main/res/layout/activity_main.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout\n    android:id=\"@+id/container\"\n    xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    >\n\n    <FrameLayout\n        android:id=\"@+id/fragment_container\"\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:layout_marginBottom=\"56dp\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        />\n\n    <com.google.android.material.bottomnavigation.BottomNavigationView\n        android:id=\"@+id/navigation\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintLeft_toLeftOf=\"parent\"\n        app:layout_constraintRight_toRightOf=\"parent\"\n        android:background=\"@android:color/white\"\n        app:menu=\"@menu/navigation\"\n        />\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/fragment_balance.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@android:color/white\"\n    android:padding=\"8dp\">\n\n    <TextView\n        android:id=\"@+id/networkTitle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"Network:\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Large\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <TextView\n        android:id=\"@+id/networkName\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"16dp\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Large\"\n        app:layout_constraintStart_toEndOf=\"@+id/networkTitle\"\n        app:layout_constraintTop_toTopOf=\"parent\"\n        tools:text=\"Testnet\" />\n\n    <TextView\n        android:id=\"@+id/balanceTitle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"8dp\"\n        android:text=\"Balance:\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Large\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/networkTitle\" />\n\n    <TextView\n        android:id=\"@+id/balanceValue\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"16dp\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Large\"\n        app:layout_constraintStart_toEndOf=\"@+id/balanceTitle\"\n        app:layout_constraintTop_toTopOf=\"@+id/balanceTitle\"\n        tools:text=\"100.0\" />\n\n    <TextView\n        android:id=\"@+id/balanceUnspendableTitle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"8dp\"\n        android:text=\"Balance Unspendable:\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Large\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/balanceTitle\" />\n\n    <TextView\n        android:id=\"@+id/balanceUnspendableValue\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"16dp\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Large\"\n        app:layout_constraintStart_toEndOf=\"@+id/balanceUnspendableTitle\"\n        app:layout_constraintTop_toTopOf=\"@+id/balanceUnspendableTitle\"\n        tools:text=\"100.0\" />\n\n    <TextView\n        android:id=\"@+id/stateTitle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"8dp\"\n        android:text=\"State:\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Large\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/balanceUnspendableTitle\" />\n\n    <TextView\n        android:id=\"@+id/stateValue\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"16dp\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Large\"\n        app:layout_constraintStart_toEndOf=\"@+id/stateTitle\"\n        app:layout_constraintTop_toTopOf=\"@+id/stateTitle\"\n        tools:text=\"syncing\" />\n\n    <TextView\n        android:id=\"@+id/lastBlockTitle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"8dp\"\n        android:text=\"Last Block:\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Large\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/stateTitle\" />\n\n    <TextView\n        android:id=\"@+id/lastBlockValue\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"16dp\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Large\"\n        app:layout_constraintStart_toEndOf=\"@+id/lastBlockTitle\"\n        app:layout_constraintTop_toTopOf=\"@+id/lastBlockTitle\"\n        tools:text=\"123000\" />\n\n    <TextView\n        android:id=\"@+id/lastBlockDateTitle\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"8dp\"\n        android:text=\"Until:\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Large\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/lastBlockTitle\" />\n\n    <TextView\n        android:id=\"@+id/lastBlockDateValue\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginStart=\"16dp\"\n        android:textAppearance=\"@style/TextAppearance.AppCompat.Large\"\n        app:layout_constraintStart_toEndOf=\"@+id/lastBlockDateTitle\"\n        app:layout_constraintTop_toTopOf=\"@+id/lastBlockDateTitle\"\n        tools:text=\"2019-01-01 12:12:12\" />\n\n    <Button\n        android:id=\"@+id/buttonStart\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"24dp\"\n        android:text=\"Start\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/lastBlockDateTitle\" />\n\n    <Button\n        android:id=\"@+id/buttonDebug\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"24dp\"\n        android:layout_marginEnd=\"28dp\"\n        android:text=\"Debug info\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/lastBlockDateTitle\" />\n\n    <Button\n        android:id=\"@+id/buttonClear\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"24dp\"\n        android:text=\"Clear\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@+id/buttonStart\" />\n\n    <Button\n        android:id=\"@+id/buttonStatus\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:layout_marginTop=\"24dp\"\n        android:text=\"Status\"\n        app:layout_constraintEnd_toEndOf=\"@+id/buttonDebug\"\n        app:layout_constraintTop_toBottomOf=\"@+id/buttonDebug\" />\n\n</androidx.constraintlayout.widget.ConstraintLayout>\n"
  },
  {
    "path": "app/src/main/res/layout/fragment_send_receive.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.core.widget.NestedScrollView xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\"\n    android:background=\"@android:color/white\"\n    android:scrollbars=\"vertical\">\n\n    <androidx.constraintlayout.widget.ConstraintLayout\n        android:layout_width=\"match_parent\"\n        android:layout_height=\"match_parent\"\n        android:padding=\"8dp\">\n\n        <TextView\n            android:id=\"@+id/receiveAddressText\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"8dp\"\n            android:layout_marginTop=\"8dp\"\n            android:layout_marginEnd=\"8dp\"\n            android:scrollbarAlwaysDrawHorizontalTrack=\"true\"\n            android:text=\"Click the button below to get address\"\n            android:textIsSelectable=\"true\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"parent\" />\n\n        <Button\n            android:id=\"@+id/receiveAddressButton\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginTop=\"36dp\"\n            android:layout_marginEnd=\"20dp\"\n            android:text=\"Receive\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"parent\" />\n\n        <View\n            android:id=\"@+id/divider\"\n            android:layout_width=\"368dp\"\n            android:layout_height=\"1dp\"\n            android:layout_marginStart=\"8dp\"\n            android:layout_marginTop=\"8dp\"\n            android:layout_marginEnd=\"8dp\"\n            android:background=\"?android:attr/listDivider\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@+id/receiveAddressButton\" />\n\n        <com.google.android.material.textfield.TextInputLayout\n            android:id=\"@+id/sendAddressLayout\"\n            android:layout_width=\"match_parent\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"8dp\"\n            android:layout_marginTop=\"8dp\"\n            android:layout_marginEnd=\"8dp\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@+id/divider\">\n\n            <EditText\n                android:id=\"@+id/sendAddress\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"wrap_content\"\n                android:hint=\"Address\"\n                android:inputType=\"text\" />\n\n        </com.google.android.material.textfield.TextInputLayout>\n\n        <com.google.android.material.textfield.TextInputLayout\n            android:id=\"@+id/sendAmountLayout\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"8dp\"\n            android:layout_marginTop=\"8dp\"\n            app:layout_constraintEnd_toStartOf=\"@+id/maxButton\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@+id/sendAddressLayout\">\n\n            <EditText\n                android:id=\"@+id/sendAmount\"\n                android:layout_width=\"match_parent\"\n                android:layout_height=\"match_parent\"\n                android:hint=\"Amount (Satoshi)\"\n                android:inputType=\"numberDecimal\" />\n\n        </com.google.android.material.textfield.TextInputLayout>\n\n        <Button\n            android:id=\"@+id/maxButton\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"8dp\"\n            android:layout_marginEnd=\"8dp\"\n            android:text=\"Max Available\"\n            app:layout_constraintBottom_toBottomOf=\"@+id/sendAmountLayout\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintStart_toEndOf=\"@+id/sendAmountLayout\"\n            app:layout_constraintTop_toTopOf=\"@+id/sendAmountLayout\" />\n\n        <TextView\n            android:id=\"@+id/txFeeTitle\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"8dp\"\n            android:layout_marginTop=\"10dp\"\n            android:text=\"Fee (Satoshi):\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@+id/maxButton\" />\n\n        <TextView\n            android:id=\"@+id/txFeeValue\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"8dp\"\n            app:layout_constraintStart_toEndOf=\"@+id/txFeeTitle\"\n            app:layout_constraintTop_toTopOf=\"@+id/txFeeTitle\"\n            tools:text=\"328\" />\n\n        <TextView\n            android:id=\"@+id/feePriorityTitle\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"8dp\"\n            android:layout_marginTop=\"16dp\"\n            android:text=\"Fee priority:\"\n            app:layout_constraintEnd_toStartOf=\"@+id/radioGroup\"\n            app:layout_constraintHorizontal_bias=\"0.5\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@+id/txFeeTitle\" />\n\n        <RadioGroup\n            android:id=\"@+id/radioGroup\"\n            android:layout_width=\"0dp\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"16dp\"\n            android:orientation=\"horizontal\"\n            app:layout_constraintBottom_toBottomOf=\"@+id/feePriorityTitle\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintHorizontal_bias=\"0.5\"\n            app:layout_constraintStart_toEndOf=\"@+id/feePriorityTitle\"\n            app:layout_constraintTop_toTopOf=\"@+id/feePriorityTitle\">\n\n            <RadioButton\n                android:id=\"@+id/radioLow\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1\"\n\n                android:text=\"Low\" />\n\n            <RadioButton\n                android:id=\"@+id/radioMedium\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1\"\n                android:checked=\"true\"\n                android:text=\"Meduim\" />\n\n            <RadioButton\n                android:id=\"@+id/radioHigh\"\n                android:layout_width=\"wrap_content\"\n                android:layout_height=\"wrap_content\"\n                android:layout_weight=\"1\"\n                android:text=\"High\" />\n\n        </RadioGroup>\n\n        <TextView\n            android:id=\"@+id/lockTimeIntervalTitle\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"8dp\"\n            android:layout_marginTop=\"24dp\"\n            android:text=\"Lock for period\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@+id/feePriorityTitle\" />\n\n        <Spinner\n            android:id=\"@+id/lockTimePeriodValue\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginEnd=\"8dp\"\n            app:layout_constraintBottom_toBottomOf=\"@+id/lockTimeIntervalTitle\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintTop_toTopOf=\"@+id/lockTimeIntervalTitle\" />\n\n        <Button\n            android:id=\"@+id/sendButton\"\n            android:layout_width=\"wrap_content\"\n            android:layout_height=\"wrap_content\"\n            android:layout_marginStart=\"8dp\"\n            android:layout_marginTop=\"24dp\"\n            android:layout_marginEnd=\"8dp\"\n            android:text=\"Send\"\n            app:layout_constraintEnd_toEndOf=\"parent\"\n            app:layout_constraintStart_toStartOf=\"parent\"\n            app:layout_constraintTop_toBottomOf=\"@+id/lockTimePeriodValue\" />\n\n\n    </androidx.constraintlayout.widget.ConstraintLayout>\n</androidx.core.widget.NestedScrollView>\n"
  },
  {
    "path": "app/src/main/res/layout/fragment_transactions.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    android:background=\"@android:color/white\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"match_parent\">\n\n    <com.google.android.material.tabs.TabLayout\n        android:id=\"@+id/tabs\"\n        app:tabMode=\"auto\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <androidx.recyclerview.widget.RecyclerView\n        android:id=\"@+id/transactions\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"0dp\"\n        app:layout_constraintBottom_toBottomOf=\"parent\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/tabs\" />\n\n</androidx.constraintlayout.widget.ConstraintLayout>"
  },
  {
    "path": "app/src/main/res/layout/view_holder_transaction.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n    xmlns:app=\"http://schemas.android.com/apk/res-auto\"\n    xmlns:tools=\"http://schemas.android.com/tools\"\n    android:layout_width=\"match_parent\"\n    android:layout_height=\"wrap_content\"\n    android:padding=\"8dp\">\n\n    <TextView\n        android:id=\"@+id/summary\"\n        android:layout_width=\"0dp\"\n        android:layout_height=\"wrap_content\"\n        android:textIsSelectable=\"true\"\n        tools:text=\"Summary\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintStart_toStartOf=\"parent\"\n        app:layout_constraintTop_toTopOf=\"parent\" />\n\n    <Button\n        android:id=\"@+id/buttonRaw\"\n        android:layout_width=\"wrap_content\"\n        android:layout_height=\"wrap_content\"\n        android:text=\"Raw\"\n        app:layout_constraintEnd_toEndOf=\"parent\"\n        app:layout_constraintTop_toBottomOf=\"@id/summary\"\n        style=\"@style/Widget.MaterialComponents.Button.TextButton\"\n        />\n\n</androidx.constraintlayout.widget.ConstraintLayout>\n"
  },
  {
    "path": "app/src/main/res/menu/navigation.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<menu xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <item\n        android:id=\"@+id/navigation_home\"\n        android:title=\"Balance\" />\n\n    <item\n        android:id=\"@+id/navigation_transactions\"\n        android:title=\"Transactions\" />\n\n    <item\n        android:id=\"@+id/navigation_send_receive\"\n        android:title=\"Send/Receive\" />\n\n</menu>"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<adaptive-icon xmlns:android=\"http://schemas.android.com/apk/res/android\">\n    <background android:drawable=\"@drawable/ic_launcher_background\" />\n    <foreground android:drawable=\"@drawable/ic_launcher_foreground\" />\n</adaptive-icon>"
  },
  {
    "path": "app/src/main/res/values/colors.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorPrimary\">#3F51B5</color>\n    <color name=\"colorPrimaryDark\">#303F9F</color>\n    <color name=\"colorAccent\">#FF4081</color>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">BitcoinKit</string>\n</resources>\n"
  },
  {
    "path": "app/src/main/res/values/styles.xml",
    "content": "<resources>\n\n    <!-- Base application theme. -->\n    <style name=\"AppTheme\" parent=\"Theme.MaterialComponents.Light.DarkActionBar\">\n    </style>\n\n</resources>\n"
  },
  {
    "path": "bitcoincashkit/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "bitcoincashkit/build.gradle",
    "content": "plugins {\n    id 'com.android.library'\n    id 'kotlin-android'\n    id 'kotlin-kapt'\n    id 'maven-publish'\n}\n\nafterEvaluate {\n    publishing {\n        publications {\n            release(MavenPublication) {\n                from components.release\n            }\n        }\n    }\n}\n\nandroid {\n    namespace 'io.horizontalsystems.bitcoincashkit'\n    compileSdk 34\n\n    defaultConfig {\n        minSdkVersion 23\n        targetSdkVersion 34\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    sourceSets {\n        main.java.srcDirs += 'src/main/kotlin'\n        test.java.srcDirs += 'src/test/kotlin'\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_17\n        targetCompatibility JavaVersion.VERSION_17\n    }\n    kotlinOptions { jvmTarget = '17' }\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version\"\n    implementation 'androidx.annotation:annotation:1.1.0'\n\n    // Room\n    implementation 'androidx.room:room-runtime:2.5.0'\n    implementation 'androidx.room:room-rxjava2:2.5.0'\n    kapt 'androidx.room:room-compiler:2.5.0'\n\n    api project(':bitcoincore')\n\n    // Test helpers\n    testImplementation 'junit:junit:4.13.2'\n    testImplementation 'org.junit.jupiter:junit-jupiter:5.6.1'\n    testImplementation 'org.mockito:mockito-core:3.3.3'\n    testImplementation 'com.nhaarman:mockito-kotlin-kt1.1:1.6.0'\n    testImplementation 'org.powermock:powermock-api-mockito2:2.0.7'\n    testImplementation 'org.powermock:powermock-module-junit4:2.0.7'\n\n    // Spek\n    testImplementation \"org.spekframework.spek2:spek-dsl-jvm:2.0.9\"\n    testRuntimeOnly \"org.spekframework.spek2:spek-runner-junit5:2.0.9\"\n    testRuntimeOnly \"org.jetbrains.kotlin:kotlin-reflect:$kotlin_version\"\n\n    // Android Instrumentation Test\n    androidTestImplementation 'androidx.test.ext:junit:1.1.1'\n    androidTestImplementation 'com.linkedin.dexmaker:dexmaker-mockito-inline:2.28.3'\n    androidTestImplementation 'com.nhaarman:mockito-kotlin-kt1.1:1.6.0'\n}\n"
  },
  {
    "path": "bitcoincashkit/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "bitcoincashkit/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" />\n"
  },
  {
    "path": "bitcoincashkit/src/main/kotlin/io/horizontalsystems/bitcoincash/BitcoinCashKit.kt",
    "content": "package io.horizontalsystems.bitcoincash\n\nimport android.content.Context\nimport android.database.sqlite.SQLiteDatabase\nimport io.horizontalsystems.bitcoincash.blocks.BitcoinCashBlockValidatorHelper\nimport io.horizontalsystems.bitcoincash.blocks.validators.AsertValidator\nimport io.horizontalsystems.bitcoincash.blocks.validators.DAAValidator\nimport io.horizontalsystems.bitcoincash.blocks.validators.EDAValidator\nimport io.horizontalsystems.bitcoincash.blocks.validators.ForkValidator\nimport io.horizontalsystems.bitcoincore.AbstractKit\nimport io.horizontalsystems.bitcoincore.BitcoinCore\nimport io.horizontalsystems.bitcoincore.BitcoinCore.SyncMode\nimport io.horizontalsystems.bitcoincore.BitcoinCoreBuilder\nimport io.horizontalsystems.bitcoincore.apisync.BlockchainComApi\nimport io.horizontalsystems.bitcoincore.apisync.HsBlockHashFetcher\nimport io.horizontalsystems.bitcoincore.apisync.blockchair.BlockchairApi\nimport io.horizontalsystems.bitcoincore.apisync.blockchair.BlockchairBlockHashFetcher\nimport io.horizontalsystems.bitcoincore.apisync.blockchair.BlockchairTransactionProvider\nimport io.horizontalsystems.bitcoincore.blocks.BlockMedianTimeHelper\nimport io.horizontalsystems.bitcoincore.blocks.validators.BlockValidatorChain\nimport io.horizontalsystems.bitcoincore.blocks.validators.BlockValidatorSet\nimport io.horizontalsystems.bitcoincore.blocks.validators.LegacyDifficultyAdjustmentValidator\nimport io.horizontalsystems.bitcoincore.blocks.validators.ProofOfWorkValidator\nimport io.horizontalsystems.bitcoincore.core.IApiTransactionProvider\nimport io.horizontalsystems.bitcoincore.extensions.toReversedByteArray\nimport io.horizontalsystems.bitcoincore.managers.ApiSyncStateManager\nimport io.horizontalsystems.bitcoincore.managers.Bip44RestoreKeyConverter\nimport io.horizontalsystems.bitcoincore.managers.BlockchairCashRestoreKeyConverter\nimport io.horizontalsystems.bitcoincore.models.Address\nimport io.horizontalsystems.bitcoincore.models.Checkpoint\nimport io.horizontalsystems.bitcoincore.models.WatchAddressPublicKey\nimport io.horizontalsystems.bitcoincore.network.Network\nimport io.horizontalsystems.bitcoincore.storage.CoreDatabase\nimport io.horizontalsystems.bitcoincore.storage.Storage\nimport io.horizontalsystems.bitcoincore.utils.AddressConverterChain\nimport io.horizontalsystems.bitcoincore.utils.Base58AddressConverter\nimport io.horizontalsystems.bitcoincore.utils.CashAddressConverter\nimport io.horizontalsystems.bitcoincore.utils.PaymentAddressParser\nimport io.horizontalsystems.hdwalletkit.HDExtendedKey\nimport io.horizontalsystems.hdwalletkit.HDWallet.Purpose\nimport io.horizontalsystems.hdwalletkit.Mnemonic\n\nclass BitcoinCashKit : AbstractKit {\n    sealed class NetworkType {\n        class MainNet(val coinType: MainNetBitcoinCash.CoinType) : NetworkType()\n        object TestNet : NetworkType()\n\n        val description: String\n            get() = when (this) {\n                is MainNet -> {\n                    when (coinType) {\n                        MainNetBitcoinCash.CoinType.Type0 -> \"mainNet\" // back compatibility for database file name in old NetworkType\n                        MainNetBitcoinCash.CoinType.Type145 -> \"mainNet-145\"\n                    }\n                }\n\n                is TestNet -> \"testNet\"\n            }\n    }\n\n    interface Listener : BitcoinCore.Listener\n\n    override var bitcoinCore: BitcoinCore\n    override var network: Network\n\n    var listener: Listener? = null\n        set(value) {\n            field = value\n            bitcoinCore.listener = value\n        }\n\n    constructor(\n        context: Context,\n        words: List<String>,\n        passphrase: String,\n        walletId: String,\n        networkType: NetworkType = defaultNetworkType,\n        peerSize: Int = defaultPeerSize,\n        syncMode: SyncMode = defaultSyncMode,\n        confirmationsThreshold: Int = defaultConfirmationsThreshold\n    ) : this(context, Mnemonic().toSeed(words, passphrase), walletId, networkType, peerSize, syncMode, confirmationsThreshold)\n\n    constructor(\n        context: Context,\n        seed: ByteArray,\n        walletId: String,\n        networkType: NetworkType = defaultNetworkType,\n        peerSize: Int = defaultPeerSize,\n        syncMode: SyncMode = defaultSyncMode,\n        confirmationsThreshold: Int = defaultConfirmationsThreshold\n    ) : this(context, HDExtendedKey(seed, Purpose.BIP44), walletId, networkType, peerSize, syncMode, confirmationsThreshold)\n\n    /**\n     * @constructor Creates and initializes the BitcoinKit\n     * @param context The Android context\n     * @param extendedKey HDExtendedKey that contains HDKey and version\n     * @param walletId an arbitrary ID of type String.\n     * @param networkType The network type. The default is MainNet.\n     * @param peerSize The # of peer-nodes required. The default is 10 peers.\n     * @param syncMode How the kit syncs with the blockchain. The default is SyncMode.Api().\n     * @param confirmationsThreshold How many confirmations required to be considered confirmed. The default is 6 confirmations.\n     */\n    constructor(\n        context: Context,\n        extendedKey: HDExtendedKey,\n        walletId: String,\n        networkType: NetworkType = defaultNetworkType,\n        peerSize: Int = defaultPeerSize,\n        syncMode: SyncMode = defaultSyncMode,\n        confirmationsThreshold: Int = defaultConfirmationsThreshold\n    ) {\n        network = network(networkType)\n\n        bitcoinCore = bitcoinCore(\n            context = context,\n            extendedKey = extendedKey,\n            watchAddressPublicKey = null,\n            networkType = networkType,\n            network = network,\n            walletId = walletId,\n            syncMode = syncMode,\n            peerSize = peerSize,\n            confirmationsThreshold = confirmationsThreshold\n        )\n    }\n\n    /**\n     * @constructor Creates and initializes the BitcoinKit\n     * @param context The Android context\n     * @param watchAddress address for watching in read-only mode\n     * @param walletId an arbitrary ID of type String.\n     * @param networkType The network type. The default is MainNet.\n     * @param peerSize The # of peer-nodes required. The default is 10 peers.\n     * @param syncMode How the kit syncs with the blockchain. The default is SyncMode.Api().\n     * @param confirmationsThreshold How many confirmations required to be considered confirmed. The default is 6 confirmations.\n     */\n    constructor(\n        context: Context,\n        watchAddress: String,\n        walletId: String,\n        networkType: NetworkType = defaultNetworkType,\n        peerSize: Int = defaultPeerSize,\n        syncMode: SyncMode = defaultSyncMode,\n        confirmationsThreshold: Int = defaultConfirmationsThreshold\n    ) {\n        network = network(networkType)\n\n        val address = parseAddress(watchAddress, network)\n        val watchAddressPublicKey = WatchAddressPublicKey(address.lockingScriptPayload, address.scriptType)\n\n        bitcoinCore = bitcoinCore(\n            context = context,\n            extendedKey = null,\n            watchAddressPublicKey = watchAddressPublicKey,\n            networkType = networkType,\n            network = network,\n            walletId = walletId,\n            syncMode = syncMode,\n            peerSize = peerSize,\n            confirmationsThreshold = confirmationsThreshold\n        )\n    }\n\n    private fun bitcoinCore(\n        context: Context,\n        extendedKey: HDExtendedKey?,\n        watchAddressPublicKey: WatchAddressPublicKey?,\n        networkType: NetworkType,\n        network: Network,\n        walletId: String,\n        syncMode: SyncMode,\n        peerSize: Int,\n        confirmationsThreshold: Int\n    ): BitcoinCore {\n        val database = CoreDatabase.getInstance(context, getDatabaseName(networkType, walletId, syncMode))\n        val storage = Storage(database)\n        val checkpoint = Checkpoint.resolveCheckpoint(syncMode, network, storage)\n        val apiSyncStateManager = ApiSyncStateManager(storage, network.syncableFromApi && syncMode !is SyncMode.Full)\n        val apiTransactionProvider = apiTransactionProvider(networkType, syncMode)\n        val paymentAddressParser = PaymentAddressParser(\"bitcoincash\", removeScheme = false)\n        val blockValidatorSet = blockValidatorSet(networkType, storage)\n\n        val bitcoinCore = BitcoinCoreBuilder()\n            .setContext(context)\n            .setExtendedKey(extendedKey)\n            .setWatchAddressPublicKey(watchAddressPublicKey)\n            .setPurpose(Purpose.BIP44)\n            .setNetwork(network)\n            .setCheckpoint(checkpoint)\n            .setPaymentAddressParser(paymentAddressParser)\n            .setPeerSize(peerSize)\n            .setSyncMode(syncMode)\n            .setConfirmationThreshold(confirmationsThreshold)\n            .setStorage(storage)\n            .setApiTransactionProvider(apiTransactionProvider)\n            .setApiSyncStateManager(apiSyncStateManager)\n            .setBlockValidator(blockValidatorSet)\n            .build()\n\n        //  extending bitcoinCore\n\n        val bech32 = CashAddressConverter(network.addressSegwitHrp)\n        bitcoinCore.prependAddressConverter(bech32)\n\n        val restoreKeyConverter = if (syncMode is SyncMode.Blockchair) {\n            BlockchairCashRestoreKeyConverter(bech32)\n        } else {\n            val base58 = Base58AddressConverter(network.addressVersion, network.addressScriptVersion)\n            Bip44RestoreKeyConverter(base58)\n        }\n        bitcoinCore.addRestoreKeyConverter(restoreKeyConverter)\n\n        return bitcoinCore\n    }\n\n    private fun parseAddress(address: String, network: Network): Address {\n        val addressConverter = AddressConverterChain().apply {\n            prependConverter(CashAddressConverter(network.addressSegwitHrp))\n            prependConverter(Base58AddressConverter(network.addressVersion, network.addressScriptVersion))\n        }\n        return addressConverter.convert(address)\n    }\n\n    private fun blockValidatorSet(\n        networkType: NetworkType,\n        storage: Storage\n    ): BlockValidatorSet {\n        val blockValidatorSet = BlockValidatorSet()\n        blockValidatorSet.addBlockValidator(ProofOfWorkValidator())\n\n        val blockValidatorChain = BlockValidatorChain()\n        if (networkType is NetworkType.MainNet) {\n            val blockHelper = BitcoinCashBlockValidatorHelper(storage)\n\n            val daaValidator = DAAValidator(targetSpacing, blockHelper)\n            val asertValidator = AsertValidator()\n\n            blockValidatorChain.add(ForkValidator(bchnChainForkHeight, bchnChainForkBlockHash, asertValidator))\n            blockValidatorChain.add(asertValidator)\n\n            blockValidatorChain.add(ForkValidator(svForkHeight, abcForkBlockHash, daaValidator))\n            blockValidatorChain.add(daaValidator)\n\n            blockValidatorChain.add(LegacyDifficultyAdjustmentValidator(blockHelper, heightInterval, targetTimespan, maxTargetBits))\n            blockValidatorChain.add(EDAValidator(maxTargetBits, blockHelper, BlockMedianTimeHelper(storage)))\n        }\n\n        blockValidatorSet.addBlockValidator(blockValidatorChain)\n        return blockValidatorSet\n    }\n\n    private fun apiTransactionProvider(\n        networkType: NetworkType,\n        syncMode: SyncMode\n    ): IApiTransactionProvider {\n        val blockchairApi = BlockchairApi(network.blockchairChainId)\n        val blockchairBlockHashFetcher = BlockchairBlockHashFetcher(blockchairApi)\n        return when (networkType) {\n            is NetworkType.MainNet -> {\n                if (syncMode is SyncMode.Blockchair) {\n                    BlockchairTransactionProvider(blockchairApi, blockchairBlockHashFetcher)\n                } else {\n                    BlockchainComApi(\"https://api.haskoin.com/bch/blockchain\", blockchairBlockHashFetcher)\n                }\n            }\n\n            NetworkType.TestNet -> {\n                BlockchainComApi(\n                    transactionApiUrl = \"https://api.haskoin.com/bchtest/blockchain\",\n                    blockHashFetcher = HsBlockHashFetcher(\"https://api.blocksdecoded.com/v1/blockchains/bitcoin-cash\")\n                )\n            }\n        }\n    }\n\n    companion object {\n        const val maxTargetBits: Long = 0x1d00ffff              // Maximum difficulty\n        const val targetSpacing = 10 * 60                       // 10 minutes per block.\n        const val targetTimespan: Long = 14 * 24 * 60 * 60      // 2 weeks per difficulty cycle, on average.\n        var heightInterval = targetTimespan / targetSpacing     // 2016 blocks\n\n        const val svForkHeight = 556767                         // 2018 November 14\n        const val bchnChainForkHeight = 661648                  // 2020 November 15, 14:13 GMT\n\n        val abcForkBlockHash = \"0000000000000000004626ff6e3b936941d341c5932ece4357eeccac44e6d56c\".toReversedByteArray()\n        val bchnChainForkBlockHash = \"0000000000000000029e471c41818d24b8b74c911071c4ef0b4a0509f9b5a8ce\".toReversedByteArray()\n\n        val defaultNetworkType: NetworkType = NetworkType.MainNet(MainNetBitcoinCash.CoinType.Type145)\n        val defaultSyncMode: SyncMode = SyncMode.Api()\n        const val defaultPeerSize: Int = 10\n        const val defaultConfirmationsThreshold: Int = 6\n\n        private fun getDatabaseName(networkType: NetworkType, walletId: String, syncMode: SyncMode): String =\n            \"BitcoinCash-${networkType.description}-$walletId-${syncMode.javaClass.simpleName}\"\n\n        fun clear(context: Context, networkType: NetworkType, walletId: String) {\n            for (syncMode in listOf(SyncMode.Api(), SyncMode.Full(), SyncMode.Blockchair())) {\n                try {\n                    SQLiteDatabase.deleteDatabase(context.getDatabasePath(getDatabaseName(networkType, walletId, syncMode)))\n                } catch (ex: Exception) {\n                    continue\n                }\n            }\n        }\n\n        private fun network(networkType: NetworkType) = when (networkType) {\n            is NetworkType.MainNet -> MainNetBitcoinCash(networkType.coinType)\n            NetworkType.TestNet -> TestNetBitcoinCash()\n        }\n\n        private fun addressConverter(network: Network): AddressConverterChain {\n            val addressConverter = AddressConverterChain()\n\n            val bech32 = CashAddressConverter(network.addressSegwitHrp)\n            addressConverter.prependConverter(bech32)\n\n            return addressConverter\n        }\n\n        fun firstAddress(\n            seed: ByteArray,\n            networkType: NetworkType,\n        ): Address {\n            return BitcoinCore.firstAddress(\n                seed,\n                Purpose.BIP44,\n                network(networkType),\n                addressConverter(network(networkType))\n            )\n        }\n\n        fun firstAddress(\n            extendedKey: HDExtendedKey,\n            networkType: NetworkType,\n        ): Address {\n            return BitcoinCore.firstAddress(\n                extendedKey,\n                Purpose.BIP44,\n                network(networkType),\n                addressConverter(network(networkType))\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincashkit/src/main/kotlin/io/horizontalsystems/bitcoincash/MainNetBitcoinCash.kt",
    "content": "package io.horizontalsystems.bitcoincash\n\nimport io.horizontalsystems.bitcoincore.network.Network\nimport io.horizontalsystems.bitcoincore.transactions.scripts.Sighash\nimport kotlin.experimental.or\n\nclass MainNetBitcoinCash(coinType: CoinType = CoinType.Type145) : Network() {\n\n    override var port: Int = 8333\n\n    override var magic: Long = 0xe8f3e1e3L\n    override var bip32HeaderPub: Int = 0x0488b21e\n    override var bip32HeaderPriv: Int = 0x0488ade4\n    override var addressVersion: Int = 0\n    override var addressSegwitHrp: String = \"bitcoincash\"\n    override var addressScriptVersion: Int = 5\n    override var coinType: Int = coinType.value\n    override val blockchairChainId: String = \"bitcoin-cash\"\n\n    override val maxBlockSize = 32 * 1024 * 1024\n    override val dustRelayTxFee = 1000 // https://github.com/Bitcoin-ABC/bitcoin-abc/blob/master/src/policy/policy.h#L78\n    override val sigHashForked = true\n    override val sigHashValue = Sighash.FORKID or Sighash.ALL\n\n    override var dnsSeeds = listOf(\n        \"seed.bch.loping.net\",                      // Loping.net Seeder\n        \"bch.bitjson.com\",                          // Bitjson (Jason Dreyzehner)\n        \"bchseed.c3-soft.com\",                      // c3-soft\n        \"seed.flowee.cash\",                         // Flowee The Hub\n        \"btccash-seeder.bitcoinunlimited.info\"      // Bitcoin Unlimited\n    )\n\n    enum class CoinType(val value: Int) {\n        Type0(0),\n        Type145(145)\n    }\n}\n"
  },
  {
    "path": "bitcoincashkit/src/main/kotlin/io/horizontalsystems/bitcoincash/TestNetBitcoinCash.kt",
    "content": "package io.horizontalsystems.bitcoincash\n\nimport io.horizontalsystems.bitcoincore.network.Network\nimport io.horizontalsystems.bitcoincore.transactions.scripts.Sighash\nimport kotlin.experimental.or\n\nclass TestNetBitcoinCash : Network() {\n\n    override var port: Int = 18333\n\n    override var magic: Long = 0xf4f3e5f4\n    override var bip32HeaderPub: Int = 0x043587cf\n    override var bip32HeaderPriv: Int = 0x04358394\n    override var addressVersion: Int = 111\n    override var addressSegwitHrp: String = \"bchtest\"\n    override var addressScriptVersion: Int = 196\n    override var coinType: Int = 1\n    override val blockchairChainId: String = \"\"\n\n    override val maxBlockSize = 32 * 1024 * 1024\n    override val dustRelayTxFee = 1000 // https://github.com/Bitcoin-ABC/bitcoin-abc/blob/master/src/policy/policy.h#L78\n    override val sigHashForked = true\n    override val sigHashValue = Sighash.FORKID or Sighash.ALL\n\n    override var dnsSeeds = listOf(\n            \"testnet-seed.bitcoinabc.org\",          // Bitcoin ABC seeder\n            \"testnet-seed-abc.bitcoinforks.org\",    // bitcoinforks seeders\n            \"testnet-seed.bitprim.org\",             // Bitprim\n            \"testnet-seed.deadalnix.me\",            // Amaury SÉCHET\n            \"testnet-seeder.criptolayer.net\"        // criptolayer.net\n    )\n}\n"
  },
  {
    "path": "bitcoincashkit/src/main/kotlin/io/horizontalsystems/bitcoincash/blocks/AsertAlgorithm.java",
    "content": "package io.horizontalsystems.bitcoincash.blocks;\n\nimport java.math.BigInteger;\n\npublic class AsertAlgorithm {\n    public static final BigInteger TARGET_SPACING_BIGINT = BigInteger.valueOf(10L * 60L);  // 10 minutes per block.\n    public static final int MAX_BITS = 0x1d00ffff;\n    public static final String MAX_BITS_STRING = \"1d00ffff\";\n    public static final BigInteger MAX_TARGET = Utils.decodeCompactBits(MAX_BITS);\n\n    /**\n     * Compute aserti-2d DAA target\n     */\n    public static BigInteger computeAsertTarget(BigInteger refTarget, BigInteger referenceBlockAncestorTime, BigInteger referenceBlockHeight,\n                                                BigInteger evalBlockTime, BigInteger evalBlockHeight) {\n        Utils.checkState(evalBlockHeight.compareTo(referenceBlockHeight) >= 0, \"\");\n\n        BigInteger heightDiff = evalBlockHeight.subtract(referenceBlockHeight);\n        //referenceBlockAncestorTime is the timestamp of the ancestor of the anchor block\n        BigInteger timeDiff = evalBlockTime.subtract(referenceBlockAncestorTime);\n        //used by asert. two days in seconds.\n        BigInteger halfLife = BigInteger.valueOf(2L * 24L * 60L * 60L);\n        BigInteger rbits = BigInteger.valueOf(16L);\n        BigInteger radix = BigInteger.ONE.shiftLeft(rbits.intValue());\n\n        BigInteger target = refTarget;\n        BigInteger exponent;\n        BigInteger heightDiffWithOffset = heightDiff.add(BigInteger.ONE);\n        BigInteger targetHeightOffsetMultiple = TARGET_SPACING_BIGINT.multiply(heightDiffWithOffset);\n        exponent = timeDiff.subtract(targetHeightOffsetMultiple);\n        exponent = exponent.shiftLeft(rbits.intValue());\n        exponent = exponent.divide(halfLife);\n        BigInteger numShifts = exponent.shiftRight(rbits.intValue());\n        exponent = exponent.subtract(numShifts.shiftLeft(rbits.intValue()));\n\n        BigInteger factor = BigInteger.valueOf(195766423245049L).multiply(exponent);\n        factor = factor.add(BigInteger.valueOf(971821376L).multiply(exponent.pow(2)));\n        factor = factor.add(BigInteger.valueOf(5127L).multiply(exponent.pow(3)));\n        factor = factor.add(BigInteger.valueOf(2L).pow(47));\n        factor = factor.shiftRight(48);\n        target = target.multiply(radix.add(factor));\n\n        if(numShifts.compareTo(BigInteger.ZERO) < 0) {\n            target = target.shiftRight(-numShifts.intValue());\n        } else {\n            target = target.shiftLeft(numShifts.intValue());\n        }\n\n        target = target.shiftRight(16);\n\n        if(target.equals(BigInteger.ZERO)) {\n            return BigInteger.valueOf(Utils.encodeCompactBits(BigInteger.ONE));\n        }\n        if(target.compareTo(MAX_TARGET) > 0) {\n            return new BigInteger(MAX_BITS_STRING, 16);\n        }\n\n        return BigInteger.valueOf(Utils.encodeCompactBits(target));\n    }\n\n    public static BigInteger computeAsertTarget(int referenceBlockBits, BigInteger referenceBlockAncestorTime, BigInteger referenceBlockHeight,\n                                                BigInteger evalBlockTime, BigInteger evalBlockHeight) {\n        BigInteger refTarget = Utils.decodeCompactBits(referenceBlockBits);\n        return computeAsertTarget(refTarget, referenceBlockAncestorTime, referenceBlockHeight, evalBlockTime, evalBlockHeight);\n    }\n}\n"
  },
  {
    "path": "bitcoincashkit/src/main/kotlin/io/horizontalsystems/bitcoincash/blocks/BitcoinCashBlockValidatorHelper.kt",
    "content": "package io.horizontalsystems.bitcoincash.blocks\n\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.managers.BlockValidatorHelper\nimport io.horizontalsystems.bitcoincore.models.Block\n\nclass BitcoinCashBlockValidatorHelper(storage: IStorage) : BlockValidatorHelper(storage) {\n\n    //  Get median of last 3 blocks based on timestamp\n    fun getSuitableBlock(blocks: MutableList<Block>): Block {\n\n        if (blocks[0].timestamp > blocks[2].timestamp) {\n            blocks.swap(0, 2)\n        }\n\n        if (blocks[0].timestamp > blocks[1].timestamp) {\n            blocks.swap(0, 1)\n        }\n\n        if (blocks[1].timestamp > blocks[2].timestamp) {\n            blocks.swap(1, 2)\n        }\n\n        return blocks[1]\n    }\n\n    private fun MutableList<Block>.swap(index1: Int, index2: Int) {\n        val tmp = this[index1]\n        this[index1] = this[index2]\n        this[index2] = tmp\n    }\n}\n"
  },
  {
    "path": "bitcoincashkit/src/main/kotlin/io/horizontalsystems/bitcoincash/blocks/Utils.java",
    "content": "package io.horizontalsystems.bitcoincash.blocks;\n\nimport java.math.BigInteger;\n\n/*\nExtracted from bitcoincashj\n */\npublic class Utils {\n    /**\n     * <p>The \"compact\" format is a representation of a whole number N using an unsigned 32 bit number similar to a\n     * floating point format. The most significant 8 bits are the unsigned exponent of base 256. This exponent can\n     * be thought of as \"number of bytes of N\". The lower 23 bits are the mantissa. Bit number 24 (0x800000) represents\n     * the sign of N. Therefore, N = (-1^sign) * mantissa * 256^(exponent-3).</p>\n     *\n     * <p>Satoshi's original implementation used BN_bn2mpi() and BN_mpi2bn(). MPI uses the most significant bit of the\n     * first byte as sign. Thus 0x1234560000 is compact 0x05123456 and 0xc0de000000 is compact 0x0600c0de. Compact\n     * 0x05c0de00 would be -0x40de000000.</p>\n     *\n     * <p>Bitcoin only uses this \"compact\" format for encoding difficulty targets, which are unsigned 256bit quantities.\n     * Thus, all the complexities of the sign bit and using base 256 are probably an implementation accident.</p>\n     */\n    public static BigInteger decodeCompactBits(long compact) {\n        int size = ((int) (compact >> 24)) & 0xFF;\n        byte[] bytes = new byte[4 + size];\n        bytes[3] = (byte) size;\n        if (size >= 1) bytes[4] = (byte) ((compact >> 16) & 0xFF);\n        if (size >= 2) bytes[5] = (byte) ((compact >> 8) & 0xFF);\n        if (size >= 3) bytes[6] = (byte) (compact & 0xFF);\n        return decodeMPI(bytes, true);\n    }\n\n    /**\n     * @see Utils#decodeCompactBits(long)\n     */\n    public static long encodeCompactBits(BigInteger value) {\n        long result;\n        int size = value.toByteArray().length;\n        if (size <= 3)\n            result = value.longValue() << 8 * (3 - size);\n        else\n            result = value.shiftRight(8 * (size - 3)).longValue();\n        // The 0x00800000 bit denotes the sign.\n        // Thus, if it is already set, divide the mantissa by 256 and increase the exponent.\n        if ((result & 0x00800000L) != 0) {\n            result >>= 8;\n            size++;\n        }\n        result |= size << 24;\n        result |= value.signum() == -1 ? 0x00800000 : 0;\n        return result;\n    }\n\n    public static BigInteger decodeMPI(byte[] mpi, boolean hasLength) {\n        byte[] buf;\n        if (hasLength) {\n            int length = (int) readUint32BE(mpi, 0);\n            buf = new byte[length];\n            System.arraycopy(mpi, 4, buf, 0, length);\n        } else\n            buf = mpi;\n        if (buf.length == 0)\n            return BigInteger.ZERO;\n        boolean isNegative = (buf[0] & 0x80) == 0x80;\n        if (isNegative)\n            buf[0] &= 0x7f;\n        BigInteger result = new BigInteger(buf);\n        return isNegative ? result.negate() : result;\n    }\n\n    public static long readUint32BE(byte[] bytes, int offset) {\n        return ((bytes[offset] & 0xffl) << 24) |\n                ((bytes[offset + 1] & 0xffl) << 16) |\n                ((bytes[offset + 2] & 0xffl) << 8) |\n                (bytes[offset + 3] & 0xffl);\n    }\n\n    public static void checkState(final boolean expression, String message) {\n        if (!expression) {\n            throw new IllegalStateException(message);\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincashkit/src/main/kotlin/io/horizontalsystems/bitcoincash/blocks/validators/AsertValidator.kt",
    "content": "package io.horizontalsystems.bitcoincash.blocks.validators\n\nimport io.horizontalsystems.bitcoincash.blocks.AsertAlgorithm\nimport io.horizontalsystems.bitcoincore.blocks.validators.BlockValidatorException\nimport io.horizontalsystems.bitcoincore.blocks.validators.IBlockChainedValidator\nimport io.horizontalsystems.bitcoincore.models.Block\n\nclass AsertValidator : IBlockChainedValidator {\n    private val anchorBlockHeight = 661647\n    private val anchorParentBlockTime = 1605447844.toBigInteger() // 2020 November 15, 14:13 GMT\n    private val anchorBlockBits = 0x1804dafe\n\n    override fun isBlockValidatable(block: Block, previousBlock: Block): Boolean {\n        return previousBlock.height >= anchorBlockHeight\n    }\n\n    override fun validate(block: Block, previousBlock: Block) {\n        val bits = AsertAlgorithm.computeAsertTarget(anchorBlockBits, anchorParentBlockTime, anchorBlockHeight.toBigInteger(), previousBlock.timestamp.toBigInteger(), previousBlock.height.toBigInteger())\n        if (bits != block.bits.toBigInteger()) {\n            throw BlockValidatorException.NotEqualBits()\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincashkit/src/main/kotlin/io/horizontalsystems/bitcoincash/blocks/validators/DAAValidator.kt",
    "content": "package io.horizontalsystems.bitcoincash.blocks.validators\n\nimport io.horizontalsystems.bitcoincash.blocks.BitcoinCashBlockValidatorHelper\nimport io.horizontalsystems.bitcoincore.blocks.validators.BlockValidatorException\nimport io.horizontalsystems.bitcoincore.blocks.validators.IBlockChainedValidator\nimport io.horizontalsystems.bitcoincore.crypto.CompactBits\nimport io.horizontalsystems.bitcoincore.models.Block\n\nclass DAAValidator(\n        private val targetSpacing: Int,\n        private val blockValidatorHelper: BitcoinCashBlockValidatorHelper\n) : IBlockChainedValidator {\n    private val largestHash = 1.toBigInteger() shl 256\n    private val consensusDaaForkHeight = 504031 // 11/13/2017 @ 8:58pm (UTC)\n\n    override fun isBlockValidatable(block: Block, previousBlock: Block): Boolean {\n        return previousBlock.height >= consensusDaaForkHeight\n    }\n\n    override fun validate(block: Block, previousBlock: Block) {\n        val chunk = blockValidatorHelper.getPreviousChunk(previousBlock.height, 146)\n        validateDAA(block, chunk)\n    }\n\n    private fun validateDAA(candidate: Block, chunk: List<Block>) {\n        val firstSuitable = blockValidatorHelper.getSuitableBlock(mutableListOf(chunk[0], chunk[1], chunk[2]))\n        val lastSuitable = blockValidatorHelper.getSuitableBlock(mutableListOf(chunk[chunk.size - 3], chunk[chunk.size - 2], chunk[chunk.size - 1]))\n\n        val heightInterval = lastSuitable.height - firstSuitable.height\n\n        var actualTimespan = lastSuitable.timestamp - firstSuitable.timestamp\n        if (actualTimespan > 288 * targetSpacing)\n            actualTimespan = 288 * targetSpacing.toLong()\n        if (actualTimespan < 72 * targetSpacing)\n            actualTimespan = 72 * targetSpacing.toLong()\n\n        val lastSuitableIndex = chunk.indexOf(lastSuitable)\n        val blocks = chunk.slice((lastSuitableIndex - heightInterval + 1) until lastSuitableIndex).toMutableList()\n\n        var chainWork = 0.toBigInteger()\n        blocks += lastSuitable\n        blocks.forEach {\n            val target = CompactBits.decode(it.bits)\n            chainWork += largestHash / (target + 1.toBigInteger())\n        }\n\n        chainWork = chainWork * targetSpacing.toBigInteger() / actualTimespan.toBigInteger()\n\n        val target = largestHash / chainWork - 1.toBigInteger()\n        val bits = CompactBits.encode(target)\n        if (bits != candidate.bits) {\n            throw BlockValidatorException.NotEqualBits()\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincashkit/src/main/kotlin/io/horizontalsystems/bitcoincash/blocks/validators/EDAValidator.kt",
    "content": "package io.horizontalsystems.bitcoincash.blocks.validators\n\nimport io.horizontalsystems.bitcoincash.blocks.BitcoinCashBlockValidatorHelper\nimport io.horizontalsystems.bitcoincore.blocks.BlockMedianTimeHelper\nimport io.horizontalsystems.bitcoincore.blocks.validators.BlockValidatorException\nimport io.horizontalsystems.bitcoincore.blocks.validators.IBlockChainedValidator\nimport io.horizontalsystems.bitcoincore.crypto.CompactBits\nimport io.horizontalsystems.bitcoincore.models.Block\n\n// Emergency Difficulty Adjustment\nclass EDAValidator(\n        private val maxTargetBits: Long,\n        private val blockValidatorHelper: BitcoinCashBlockValidatorHelper,\n        private val blockMedianTimeHelper: BlockMedianTimeHelper\n) : IBlockChainedValidator {\n\n    override fun isBlockValidatable(block: Block, previousBlock: Block): Boolean {\n        return true\n    }\n\n    override fun validate(block: Block, previousBlock: Block) {\n        if (previousBlock.bits == maxTargetBits) {\n            if (block.bits != maxTargetBits) {\n                throw BlockValidatorException.NotEqualBits()\n            }\n\n            return\n        }\n\n        val cursorBlock = checkNotNull(blockValidatorHelper.getPrevious(previousBlock, 6)) {\n            throw BlockValidatorException.NoPreviousBlock()\n        }\n\n        val mpt6blocks = medianTimePast(previousBlock) - medianTimePast(cursorBlock)\n        if (mpt6blocks >= 12 * 3600) {\n            val decodedBits = CompactBits.decode(previousBlock.bits)\n            val pow = decodedBits + (decodedBits shr 2)\n            var powBits = CompactBits.encode(pow)\n            if (powBits > maxTargetBits)\n                powBits = maxTargetBits\n            if (powBits != block.bits) {\n                throw BlockValidatorException.NotEqualBits()\n            }\n        } else if (previousBlock.bits != block.bits) {\n            throw BlockValidatorException.NotEqualBits()\n        }\n    }\n\n    private fun medianTimePast(block: Block): Long {\n        return blockMedianTimeHelper.medianTimePast(block) ?: block.height.toLong()\n    }\n}\n"
  },
  {
    "path": "bitcoincashkit/src/main/kotlin/io/horizontalsystems/bitcoincash/blocks/validators/ForkValidator.kt",
    "content": "package io.horizontalsystems.bitcoincash.blocks.validators\n\nimport io.horizontalsystems.bitcoincore.blocks.validators.BlockValidatorException\nimport io.horizontalsystems.bitcoincore.blocks.validators.IBlockChainedValidator\nimport io.horizontalsystems.bitcoincore.blocks.validators.IBlockValidator\nimport io.horizontalsystems.bitcoincore.models.Block\n\nclass ForkValidator(\n        private val forkHeight: Int,\n        private val expectedBlockHash: ByteArray,\n        private val concreteBlockValidator: IBlockValidator\n) : IBlockChainedValidator {\n\n    override fun isBlockValidatable(block: Block, previousBlock: Block): Boolean {\n        return block.height == forkHeight\n    }\n\n    override fun validate(block: Block, previousBlock: Block) {\n        if (!block.headerHash.contentEquals(expectedBlockHash)) {\n            throw BlockValidatorException.WrongBlockHash(expectedBlockHash, block.headerHash)\n        }\n\n        concreteBlockValidator.validate(block, previousBlock)\n    }\n}\n"
  },
  {
    "path": "bitcoincashkit/src/main/resources/MainNetBitcoinCash-bip44.checkpoint",
    "content": "02000000ba3f2b4208ec0495b2e3743465cae2b44d8f1c778b44cf6b0000000000000000d287e52e8045c060c1cee47d1cc7559c7b8ab8db580539fb55fc579a998ea14efe0e50538c9d001926c0c180a08504003f72e59e0db5b38e5210369dc2fb4831ab1e81f3b5dbec3d0000000000000000\n0200000088c03613a752946272147792ee524af4acbffc3c1a70204b00000000000000005b679afd22900abbf4221db5401ee867c99264e13f754777ccaf1fd92f1fa4fb3a0d5053aab3001931afde449f850400ba3f2b4208ec0495b2e3743465cae2b44d8f1c778b44cf6b0000000000000000\n02000000de6076c4ebaf8a99d1aed6600b702499921d606e5b7df60500000000000000005aedac698f670b8c787f2339575bd4466e558adc6e0f6e8e0373005618c672820a0c5053aab300196bf4edac9e85040088c03613a752946272147792ee524af4acbffc3c1a70204b0000000000000000\n02000000f308b5ad14372c12cd08c584596e0790a7fed9f2846b8e170000000000000000a69485840909da4d1236721b721f2638a7d1c716f085f099cc21c705bdeef8494f085053aab300190efbed749d850400de6076c4ebaf8a99d1aed6600b702499921d606e5b7df6050000000000000000\n02000000ebb127579225c0b7c9556364ee197eff8d84c2c474cbe0090000000000000000bf62b8c209d82909d5883e8095e8cbabab5545ad29839b25c973944fd161ed44a5055053aab30019058e37689c850400f308b5ad14372c12cd08c584596e0790a7fed9f2846b8e170000000000000000\n02000000a8ed0256dd77435126cffa9d9d6550046a45ff63643c492200000000000000002546a265189fd9caf283386a90dc53a15d7a9caf8daff4b4b741851bb8a6421173015053aab30019effe96599b850400ebb127579225c0b7c9556364ee197eff8d84c2c474cbe0090000000000000000\n0200000012a3a9812e4b2f1311576e9abf834e7b247c139453da7b4300000000000000003a54f400aee6cef8fc999858ca83ea110b2a71da91c53061e736dc0f8fe5438fca005053aab3001951fab8bd9a850400a8ed0256dd77435126cffa9d9d6550046a45ff63643c49220000000000000000\n020000001bdf3c4a0aaf48ad2b6fe321f9ca2687fc77b7dcffe0c54b00000000000000005e1932b8b416ffb592c6c6a900016eb76a5ff1ef264823a8dbea2a5506ae5aa472ff4f53aab3001951c856699985040012a3a9812e4b2f1311576e9abf834e7b247c139453da7b430000000000000000\n0200000045c081445f0c886c0e9c06100a032f899a0ca02981c7242c00000000000000007404f4844f9ea3bf199e9b0879359a682442805f36df07f07705a52e818479dc57fc4f53aab30019b4de0dde988504001bdf3c4a0aaf48ad2b6fe321f9ca2687fc77b7dcffe0c54b0000000000000000\n02000000edeafbbb92af050ce4fdff9b2e42c7c7e09a1fb7b3420e81000000000000000061b816aeadc6d8060be33252375f320f73f6b2c7385b517525574dde8bfd1770e2fa4f53aab30019a29707c59785040045c081445f0c886c0e9c06100a032f899a0ca02981c7242c0000000000000000\n02000000223f307aadd3b7f79b72f224a5b5b5df31722cec5f48c51e000000000000000047ea9aa89e6ecc12e573daa8efb6727cfeda897455bbc0a6b2f69c2d209f4b71f5f94f53aab3001907fad61a96850400edeafbbb92af050ce4fdff9b2e42c7c7e09a1fb7b3420e810000000000000000\n02000000b09566bca15f31a35345a5bc315b2957b3efdd75bc7c4c6400000000000000006f5d1c5a30362de6e2bcb95aab111149a77cba7f9f72c925a82b0348500d217748f64f53aab30019f6a067c695850400223f307aadd3b7f79b72f224a5b5b5df31722cec5f48c51e0000000000000000\n020000003209dff479cfc22384c25fe241f907e24ba8e29e103d3c390000000000000000c0ebec8579d6a66721537ec2425c7083b9da1ed863585725b7680b1f6d2072dce1f34f53aab30019365408bc94850400b09566bca15f31a35345a5bc315b2957b3efdd75bc7c4c640000000000000000\n02000000e589a04528b496bb1bd163424583ec8c3a3eebd64decbcaa000000000000000092c84273d629837a781f62df4ff1e98735bae5e69d6680421d3d106a501add60d5f24f53aab300190d2711dc938504003209dff479cfc22384c25fe241f907e24ba8e29e103d3c390000000000000000\n0200000031ad2c1506d203293dd6348d5f54ff5b14629186933f58620000000000000000227058a7137843a516996ce7ce984beab4fd5e9099709bdbb1bc98f3517c3c56e3ee4f53aab3001992a4e3f792850400e589a04528b496bb1bd163424583ec8c3a3eebd64decbcaa0000000000000000\n02000000bc7498364b22f646653e7753fdd49013e51bc7c5be5b8a4e0000000000000000d98447ac6d93c629fc81af9a49a074d7db60b90c5790fce9990f9114a493194affed4f53aab30019d81bf0829185040031ad2c1506d203293dd6348d5f54ff5b14629186933f58620000000000000000\n02000000910bd6e05b76fb5ed472b76c07a28313da789d778ad2e57700000000000000008380c76e3c4b8f48c2965c4c4c16653e85e99329780b92dc12bea667f041e3164aed4f53aab30019aa7b9ff690850400bc7498364b22f646653e7753fdd49013e51bc7c5be5b8a4e0000000000000000\n02000000ba34128aadc2e73a6d9bbe328cd660b517752fa57ee411600000000000000000f1042e7234a288d31ef80f8893481272bcd6eea0fa513dfaebf3ac5a92f37e7c12ec4f53aab300190c5f41688f850400910bd6e05b76fb5ed472b76c07a28313da789d778ad2e5770000000000000000\n02000000bd6fd95daaccf77098024073cc5dededffad394ae025844b00000000000000005d77b6ee34ad3a5363f58cfafb015e3d72efd8e918cde7029345b385f30d16513ae94f53aab30019bb8afec88e850400ba34128aadc2e73a6d9bbe328cd660b517752fa57ee411600000000000000000\n02000000ae7c78592678f6ba5065bd9a0662271013134a89434fd8a900000000000000004759f0afc2f484916fb6a082338323be7c2cd2803b76abc375153df5d808733966e74f53aab300195dcfb6778d850400bd6fd95daaccf77098024073cc5dededffad394ae025844b0000000000000000\n020000009ee688465fe1174c750089d5195f8348cd439e60c930c74a000000000000000006a33880503c7f14c144f037f2ac1819b27160bde2722c995e39be1218d3ecc79ee54f53aab30019a193cd438c850400ae7c78592678f6ba5065bd9a0662271013134a89434fd8a90000000000000000\n02000000e077c2bab49992492d535b1b60549fc97593011717c03580000000000000000065abe8c39e5bf51cf5d87373f6366b87e1974802dd801f1af5516f2cd8bfbddce5e34f53aab30019e34ea8c38b8504009ee688465fe1174c750089d5195f8348cd439e60c930c74a0000000000000000\n020000007a8cb9e2ea2f7fcc1d0195ddb2175965bf45f2812cacea3c0000000000000000304629e4aa6d3c307a719db55ee29bb073048b003e759118a3acbbe02497ca5848df4f53aab30019dbf02e988a850400e077c2bab49992492d535b1b60549fc97593011717c035800000000000000000\n02000000578569a85a532af78d9f2bb448a439c376e916782b212d0f00000000000000006f504b91547c7fd4cf0a99b3ac4fdcee30a779cce3c099819d43be85b838bd5168dd4f53aab30019c54e259b898504007a8cb9e2ea2f7fcc1d0195ddb2175965bf45f2812cacea3c0000000000000000\n02000000f0f00b72042032823151b265c854cced592cf73d8638b94c0000000000000000a34029fd1f9dfe44947178ae0d4491289ddacf75989b06b90d0142eb3d86536c2fd94f53aab30019f08e0c4688850400578569a85a532af78d9f2bb448a439c376e916782b212d0f0000000000000000\n0200000038375a7969c498dab5b388afbc1c3d374bd9210425f4c074000000000000000008b8a73fbb942d2e35f7faaacbe76aa63193b9c36a2f39d0698b1e23a924363333d64f53aab30019a07c2e2b87850400f0f00b72042032823151b265c854cced592cf73d8638b94c0000000000000000\n0200000011355cd4abdbce81ff0583e7317f35329acbf14a57214b85000000000000000070702c32c6a515e0b167e642db4409f537b5addd63c2e41630635aae8499456fb0d54f53aab30019f3b8fbf48685040038375a7969c498dab5b388afbc1c3d374bd9210425f4c0740000000000000000\n02000000a091e9e5205df3c26a27b8499c26b7bfd1e75b7bdb8522a60000000000000000c4d57b53d1ee807ffee287f0a11fe1b24c3324442863ddae9036a8951558d4acf6d34f53aab300194cb573318585040011355cd4abdbce81ff0583e7317f35329acbf14a57214b850000000000000000\n020000007ac6087351f59c8730e7109cef79a2118d4e9788cb09732e0000000000000000b09c72103aa7e6cb81faef18027c57e700aa33a9f02b95013b605605763d02b2bdce4f53aab300190e0c313e84850400a091e9e5205df3c26a27b8499c26b7bfd1e75b7bdb8522a60000000000000000\n02000000543a88c9ead76ff8c031d558b6214f37d2258a057288a98200000000000000004a7420c2953accf8630c75fca3821ade63aafc1d99b476aeb78f1f6c7044044f33cd4f53aab30019cfdad2b6838504007ac6087351f59c8730e7109cef79a2118d4e9788cb09732e0000000000000000\n02000000c281b9672067629d19873f9e50b52e5c6d713f1535cdc6770000000000000000ece1a30937fe7a6478df3283167980b6440a36bfc79fd5ed876aec9a718527d4a1cc4f53aab30019b5a9e54c82850400543a88c9ead76ff8c031d558b6214f37d2258a057288a9820000000000000000\n02000000148699aa20a44895895985d5e3d9f31f0e59e626540b7d840000000000000000dcad236ea06fd34e30ae519f477dc084bf75552f85578e0a8b9c6cc804dcab8e08c94f53aab3001997fd95e881850400c281b9672067629d19873f9e50b52e5c6d713f1535cdc6770000000000000000\n020000008b4317c07870ee8b16e1c279a0a9405e539249d12032b02a00000000000000000b9b5d0f7351b5bc05f2bdc9557963d80a50660dc3aec57fb6fe2432b7ce4b5dacc84f53aab30019486b48f680850400148699aa20a44895895985d5e3d9f31f0e59e626540b7d840000000000000000\n02000000a35cbb341f4dc369daf1db4aa6c7b6deb906f21431fade65000000000000000064014a614d54ad4f58430a53336480ca53fee63b3a24032708c7ca41887f92b07dc84f53aab30019770e1ccb7f8504008b4317c07870ee8b16e1c279a0a9405e539249d12032b02a0000000000000000\n020000000f217937f5acb6f5ee6b1046391fc8dafc7d14f45b543b190000000000000000bc3dadbb38272b5516159c21f3254d4535b9de7e45d142f932295953b953e9788ec54f53aab300197d5dd9e07e850400a35cbb341f4dc369daf1db4aa6c7b6deb906f21431fade650000000000000000\n020000003dfecf95a637d583ed7d83e60c337cce28184b369c8055190000000000000000ed988bc76a1ace4d5af85c915b6b6247fcadb679daeba41e871852899a646ced13c54f53aab3001913012e107d8504000f217937f5acb6f5ee6b1046391fc8dafc7d14f45b543b190000000000000000\n0200000042feba1f0689217040e87a2d5c8003f62054ca70526020870000000000000000854c2a9323d372c80ccd60b8fe93bbf1335e5596dae48aa9a8e85f988c6ab68607c54f53aab30019625b285a7c8504003dfecf95a637d583ed7d83e60c337cce28184b369c8055190000000000000000\n0200000006eed96748eb886c2dd80a66929b6d436d88f8178ce89ab00000000000000000e76ca180a82080b3a12ab75ff1349fb5291ff66e3e061c3bcd2af0937a32cf4c55c24f53aab300190711fa907b85040042feba1f0689217040e87a2d5c8003f62054ca70526020870000000000000000\n0200000060f84899b9dc1829026b42c8a869c3f3cbfe48f44c5fe5740000000000000000f19c0030b60d269e1c1eb2c8c5663b5d848e3e3a8cdb95c40e6cc807cb29aab855bd4f53aab30019cd5a218a7a85040006eed96748eb886c2dd80a66929b6d436d88f8178ce89ab00000000000000000\n020000003874f8b15e3878126e2ab6ada58c4ccb5e9f7ae66006b78700000000000000001a9e63d440c243638fa43d841a6556244b071c51400eaa35f58ab897052bdf5cc6bd4f53aab300195338dba17985040060f84899b9dc1829026b42c8a869c3f3cbfe48f44c5fe5740000000000000000\n0200000048833a11adfe8ca520774d04c30b9c1145d0a0ef7e9d392d0000000000000000f80f0c720479c629e2d8ded2e1835d4871d6efe6489e80a2c7b6b599dd5faa4b35bb4f53aab3001927672991788504003874f8b15e3878126e2ab6ada58c4ccb5e9f7ae66006b7870000000000000000\n0200000027ab9802f9fca4a65bb4ed509f475ec85dced21f5d0337920000000000000000f91e0f223667c1872506d8a658e24d5374b0cc802b2fa552d2fc68f72343aa2e28ba4f53aab300191894e0a87785040048833a11adfe8ca520774d04c30b9c1145d0a0ef7e9d392d0000000000000000\n020000009afe656e0f5922610f0ce98ef4c28e0787674a55189b3619000000000000000039886ac4637bfb353647625edf6b0a6726fa8879c72abb5505022c0fef7bec3c21b94f53aab300197ace04de7685040027ab9802f9fca4a65bb4ed509f475ec85dced21f5d0337920000000000000000\n020000009dd8612854be8ca84cd2cd26de8b43cf5339428a261aed8800000000000000003668922c235558f89ad6a4f0e8b5a20e29aebfdf693df11b302fe523729af35dcfb34f53aab30019337ed7ea758504009afe656e0f5922610f0ce98ef4c28e0787674a55189b36190000000000000000\n02000000b93523be114558c5d172c44d9d1c3f420c9dd4e49d1467a7000000000000000069d1718c45b6cf2fcb253cd14efc6690b5d9afd89f7246daf0e3be736579a94ac4b14f53aab30019024a2f9e748504009dd8612854be8ca84cd2cd26de8b43cf5339428a261aed880000000000000000\n0200000075cbd526a5327ec10cd9bceae74892b180d27a689f153682000000000000000056e857744cc3fb9ac15423b9d23daf69603975ee83876e020fefd946ed6e7bc75bb14f53aab30019ef2968c073850400b93523be114558c5d172c44d9d1c3f420c9dd4e49d1467a70000000000000000\n02000000cb0eddbea32985fc9ee1be87ec23ece886358219536adf440000000000000000653d36f8ffc0a89d2e12358b580cd6e1b0156389fd2d27b54d4107bb9d858f325cb04f53aab300191177f5287285040075cbd526a5327ec10cd9bceae74892b180d27a689f1536820000000000000000\n0200000017fa90299ce9d234cf73c46cb8c4e7480454ec9167b4bd50000000000000000047b38999f9af6fee382fb1b098fe469fc0ab777ab1bc7a73b14bde7673932688deaf4f53aab300194a3cc36671850400cb0eddbea32985fc9ee1be87ec23ece886358219536adf440000000000000000\n020000003268f352f71040e8a15f9c8062d3d5005715436536ba293d00000000000000009e3f8bc6691440d64a681c0dfc9ec2fd459797d7e3e6259362d656daa7374628b8ac4f53aab300197987a3617085040017fa90299ce9d234cf73c46cb8c4e7480454ec9167b4bd500000000000000000\n02000000d2a652ae1b7e57e072f5e3743136dd46ada4d073a3a06039000000000000000079e27bc7faaa03a719d1491ad6dad19ba0b0cb17f443de7c461247c3f47ec45982ab4f53aab30019e804deb86f8504003268f352f71040e8a15f9c8062d3d5005715436536ba293d0000000000000000\n020000006ce2aba8861ec16c546d23fa8af538ce6112f109a9f412360000000000000000497e72f1c1fad86b7e35e230c11e76f635e5c6e809223423f478c39b685b0c615da94f53aab30019f29795776e850400d2a652ae1b7e57e072f5e3743136dd46ada4d073a3a060390000000000000000\n02000000cd27f3686735c946a32f079fea0aff1dc7f8dfe9ada6ecb00000000000000000dd504f552b7f71b6685a8d2dbff92d7c2a62344ce87b801ba4f3c274ca547f7851a94f53aab300194ddb49a06d8504006ce2aba8861ec16c546d23fa8af538ce6112f109a9f412360000000000000000\n0200000089b96fd7b5009cbfdd98322574e1c160f89a1cb7a9bc7fb30000000000000000e3c124151985dd04e2c32ba5e241abd5b22ffd479869259246e29c26a3a9164809a54f53aab30019e177d7866c850400cd27f3686735c946a32f079fea0aff1dc7f8dfe9ada6ecb00000000000000000\n02000000cc8480971555c86209a87f72f51f407ca4dad13fa00079020000000000000000d1ecb901afaaf70e7725f25b444bae44071b6219d50a9c940a556091577a35a50ea44f53aab30019aca3f9ce6b85040089b96fd7b5009cbfdd98322574e1c160f89a1cb7a9bc7fb30000000000000000\n020000004b5b861369e67a4d87657dfffe85253f23fe13815f8e07080000000000000000850ac13a6bf15fe700a8da6f0ec852039a6630a4d474b1b1929db160086ebd1edfa14f53aab30019ce0868a96a850400cc8480971555c86209a87f72f51f407ca4dad13fa00079020000000000000000\n0200000026ba9cc1ff8180ffabee37b7f7682e2dbe38a21762edf53e0000000000000000887b0db8b15a3275165919cdc23ebcfc46b758ce96ecd1ac8c7c8c93a9ec445b88a04f53aab3001973a47edc698504004b5b861369e67a4d87657dfffe85253f23fe13815f8e07080000000000000000\n0200000090bcbfd0a472558833ce464368e939768752e568a93c343500000000000000000716a874daf6c450b277db0baf34463aebb14a4e0e79270e21d4e6c583d33ef0129f4f53aab30019ed605ce76885040026ba9cc1ff8180ffabee37b7f7682e2dbe38a21762edf53e0000000000000000\n020000005f9c242c6b61d0782af687009b48ef873961c8190a052d310000000000000000ed13b2a231aa9fad7154f558202dd7d3ea40105f6d8a1a351043e5a130e8fb2f889e4f53aab30019d8a34f2e6785040090bcbfd0a472558833ce464368e939768752e568a93c34350000000000000000\n02000000be15ccaa2406d7ce2190b4afcb338c49fa3faaa5b6fcd7060000000000000000b2ac4861093a0d319985089c50739d4e8d3e54d0adfc16b4e60f200bcaf8ea43d59d4f53aab30019cdf6810c668504005f9c242c6b61d0782af687009b48ef873961c8190a052d310000000000000000\n02000000c77835534d0a487d00de30f7b40370c958eaa50f1a21089d0000000000000000c26a86958b0dbebe7f7f91e51c0f59486864214b56a1a9c01628a3dad832ca88e09c4f53aab300195365730965850400be15ccaa2406d7ce2190b4afcb338c49fa3faaa5b6fcd7060000000000000000\n02000000429c00b13583ac149843442aa129cf2d44ce396f518e186b00000000000000002796c110ead15a0a3b6b6e84048c821d0edc94a1aaf9eaf08d1ddd6c285927fa4d9b4f53aab30019d8a2f7ff64850400c77835534d0a487d00de30f7b40370c958eaa50f1a21089d0000000000000000\n0200000057415e3cf946d26fad4a69de1832a90937a361a9e8e2b94700000000000000003979bdd7c50be7a00c0d5af930dc11fd21bff44342c09a4d959f60a268f38b5838964f53aab30019c13a8d5663850400429c00b13583ac149843442aa129cf2d44ce396f518e186b0000000000000000\n02000000a1423eae0bbdf430c71516721db80190448f1a9a97e2e2600000000000000000c594080c3ca9c1b1d960025ee20da0e9530cec194c811f728e61bfdb74c308f907944f53aab30019d57d9c066285040057415e3cf946d26fad4a69de1832a90937a361a9e8e2b9470000000000000000\n02000000233b174e68901ebfae050b981ae24d40390d61d26cb7ff720000000000000000b09410313b3c343d16240637ec98993f5c266d1d30a82801a874499b68605bb673934f53aab3001970c6192861850400a1423eae0bbdf430c71516721db80190448f1a9a97e2e2600000000000000000\n02000000f490c3382fac48e779350c67e10072983f5faae76cebaa960000000000000000fb3ed4c465d037d7e0f320702eb52586e95ed052c241266e5a00f04d8d46e013f3924f53aab3001994e10afb60850400233b174e68901ebfae050b981ae24d40390d61d26cb7ff720000000000000000\n02000000a72f696a171bd2c58c7e3eea3ff05be15808d7d9d6860e7b0000000000000000ef70e9f7a49418e55b471de2c416f5d42bc8053972c21b5071e28363bc7085c128904f53aab3001990682ba15f850400f490c3382fac48e779350c67e10072983f5faae76cebaa960000000000000000\n02000000a3f72083fa8cfc8883ef1fb9cf33e719db6c03046f323d6a0000000000000000b213c8c36e257ac08c71f1c11c4b3bbb8e278e3f4f6d96413660a75736fa9ff2018e4f53aab3001925b485255e850400a72f696a171bd2c58c7e3eea3ff05be15808d7d9d6860e7b0000000000000000\n02000000ff3728a8389cd1c1a2ca8a2e825fc1803fd41fca1b41712400000000000000001010056c33aa93d7fe00344d33a90d8c250aaa8cbb7d8a197135a8392fe75c441e8d4f53aab300194839ccd85d850400a3f72083fa8cfc8883ef1fb9cf33e719db6c03046f323d6a0000000000000000\n02000000e1e4f9bfff2512284a9fc44bd35cd2fe9e3deb6d179903660000000000000000f0ae9d62b2e6489d07ab442123e79ebaf87016edf7165f667a1386d1af28bd277d894f53aab300197e4bb89b5c850400ff3728a8389cd1c1a2ca8a2e825fc1803fd41fca1b4171240000000000000000\n020000006b6b77ad6ca3ca34259d6788fdadeef1547a0fa84d731845000000000000000024dcb54597ce76f1d34053d1173092e1a66959c5fc46ddd61e6d1cef2336ac7e27874f53aab3001903c4d70f5b850400e1e4f9bfff2512284a9fc44bd35cd2fe9e3deb6d179903660000000000000000\n020000000df888a842be963a78d897be945d3b6b13c337bd3d99645c0000000000000000d695710e85c2d259b073efc5505d8a33d3065044fe2420263fafd7e165f0accd89874f53aab30019b192482a5a8504006b6b77ad6ca3ca34259d6788fdadeef1547a0fa84d7318450000000000000000\n020000002d420e24ba7ec1fea33a8c317c0f5fc3cb601e28398d219d000000000000000060b3be45558b170a3c5bba932a058f4dab252a2f679021613713eeb80779629386874f53aab3001998924e3f598504000df888a842be963a78d897be945d3b6b13c337bd3d99645c0000000000000000\n02000000764b23552e51c6cfda870eac15f990dac9413932015cc066000000000000000036d520c80b481c1ab56e93466cc6afe2aea1724744ef1bbd7ce09f5f0050a9555e874f53aab300192c47769e588504002d420e24ba7ec1fea33a8c317c0f5fc3cb601e28398d219d0000000000000000\n020000000d1805f9cd06b1a95d282eb025d5df977d95fd48ae6b6d1d0000000000000000beb93f892f198a8a68eec93fe96895c212e78b7f395e1dfeb878a59bbe9562702e864f53aab30019b0cc06b457850400764b23552e51c6cfda870eac15f990dac9413932015cc0660000000000000000\n02000000e3690d5b97db4837340c4b868fa67c9ef0fbd455c05676060000000000000000cd037bce20c7fec31ac599c5ae9b8f585d2cfb722dff5cc6e2a564335f558c1ce2834f53aab300194cfd7f34568504000d1805f9cd06b1a95d282eb025d5df977d95fd48ae6b6d1d0000000000000000\n020000002f7007a253d1c19df4ba90e727bc2074b5a86d19055128730000000000000000fb4a40c6ebe74bdb5ce8bf10360d041995bfc5ded3e9fe775b8b2ee0df2751e3df824f53aab300199dc17fb655850400e3690d5b97db4837340c4b868fa67c9ef0fbd455c05676060000000000000000\n0200000065ce6aee748fa5cf93e4f58a5e27efbf36e808ffdf7f89b100000000000000003918448585c6c551ef5cc3718aa05920832799ab2139e7b1bb27a1952b38f6ba4f814f53aab30019cf84687f548504002f7007a253d1c19df4ba90e727bc2074b5a86d19055128730000000000000000\n0200000050f5d898a6863e578b33cdbf4e0a450ddb5974e6e7cc87a10000000000000000c9bd0e56b8d6ae7ed84cba97b154d14a225b57d494865a398b4cd7df3ae0887893804f53aab300192ca5a1ba5385040065ce6aee748fa5cf93e4f58a5e27efbf36e808ffdf7f89b10000000000000000\n020000001885ce9ecedd1ad9a984a99cb0892e05e76ce4f9a4b0959100000000000000009f99224474cf0aafb9c896ffc8ad292efb26138ff8d3d205401111bdf093c70aed7d4f53aab30019322dad085285040050f5d898a6863e578b33cdbf4e0a450ddb5974e6e7cc87a10000000000000000\n020000003dc4683a79dbebfae6fae6dab9485332c2b4e3104582ee2800000000000000007585606dc0e260afcf37207053d9d20a227052b9c4c08250e365892147aa6704097d4f53aab30019d05817a7518504001885ce9ecedd1ad9a984a99cb0892e05e76ce4f9a4b095910000000000000000\n02000000d4aa5b768011c87fb2988beefc1dfc3c0c27b5445d4ae8060000000000000000b4c2433ae9881920487f975725beabd79399cdb6acd6a57933e9e1dec0261dc5117f4f53aab300191240ee49508504003dc4683a79dbebfae6fae6dab9485332c2b4e3104582ee280000000000000000\n020000001a593e49d585a5987f78c5b141d74238f953dcb25ce2eb680000000000000000bd54cff7a4d185ccad60eb31cf7a9b83256ec0742ebc6e2682f1ada5977a22e69d794f53aab3001916a2d12e4f850400d4aa5b768011c87fb2988beefc1dfc3c0c27b5445d4ae8060000000000000000\n02000000bee844fc7a75db08426459904d2a223bd3c80252ebd18c1f0000000000000000adb779bbc3e7af5f724290c5e102a4d8eae315f8d0b5de6240fdbe7372fdbbed69794f53aab30019d603a6674e8504001a593e49d585a5987f78c5b141d74238f953dcb25ce2eb680000000000000000\n02000000caef89dccbfd7e8a6e14ef44f43f8c16201fb8cbb72a797c0000000000000000770552d3ffa346313864ad81af4e179bb2c6e502a51f34db4c30edb3847de8d441774f53aab30019a642fc8b4d850400bee844fc7a75db08426459904d2a223bd3c80252ebd18c1f0000000000000000\n0200000033a8f19953f1c6b6ece262e4d26a9a262cb598e852cef99a0000000000000000bdf4f023f783249f866adc74e8b633022f1afe71e15f47c4ad03d20bdf79ab9f0a774f53aab30019eb8b93754c850400caef89dccbfd7e8a6e14ef44f43f8c16201fb8cbb72a797c0000000000000000\n0200000034f5b6c08a27e092aeb4adb5370c016cc79a88b09412db4500000000000000003ac289bfdec3d121cc8f2e31404d3c2da154238dbcd424a71cc007b9eff13c3e99754f53aab300195bcd03924b85040033a8f19953f1c6b6ece262e4d26a9a262cb598e852cef99a0000000000000000\n020000005b6639ac9d2483341c991d6c999e415961502bc5c31a168a000000000000000026fc89c24402c97a8cdc70d202014b3f02585b7fedb514a5e4db6a7fa81cd5d9b7744f53aab300198130df8d4a85040034f5b6c08a27e092aeb4adb5370c016cc79a88b09412db450000000000000000\n02000000a64a8d644080e215b4007f64acf883c1877a4e912c83e69e0000000000000000b589f128dd342e7d2acdb1c9471dcbe0c7b9e184ac232b8d3cc4a00bff8042d2766f4f53aab300194ee5562b498504005b6639ac9d2483341c991d6c999e415961502bc5c31a168a0000000000000000\n020000003d0fe23a5aa47fae62dede20794661fba6943c9fe25764a30000000000000000e264374685cafd7b6e4aa6ec65fee7d5f2f2bed349fbaa2b6529823df816bcc1016f4f53aab30019bb976a1048850400a64a8d644080e215b4007f64acf883c1877a4e912c83e69e0000000000000000\n02000000d6daa7600fbeb2e44e30962670ec38573dc4f96e7bda02950000000000000000cb95ae12c3e77829e9eb65f184997ff269a246ef3adf77629b4c467236e20624e86e4f53aab30019be628613478504003d0fe23a5aa47fae62dede20794661fba6943c9fe25764a30000000000000000\n0200000029ed27f5cc527daacf035aa17c6814fd936d9ddf9723d03200000000000000008f836090c6d0b4bb8c3e0d8e79717018e8b9bf0d3613ead3fc003120a82e10324d6e4f53aab30019481e0bc146850400d6daa7600fbeb2e44e30962670ec38573dc4f96e7bda02950000000000000000\n020000009d85a7a41cf63d3a14e0c02b22df37afdf4dafd68578a26a0000000000000000bdeceb9a02027fafd09ce755e1f5f87b31a0bbf27106c6adc990ea40dfeef9e54f694f53aab300196640e5564585040029ed27f5cc527daacf035aa17c6814fd936d9ddf9723d0320000000000000000\n02000000a5cbf81c4d5d3e89669ca406fd9b8fdde99c63e67a7759aa00000000000000007d2107d5f8603e077561f0e9b5eac5936e3919ed12d13afdc3e4eecb9e22c4b2ba694f53aab3001963041b86448504009d85a7a41cf63d3a14e0c02b22df37afdf4dafd68578a26a0000000000000000\n02000000606ded4b084da4f712e562b081408b28584c0ebc05f7bd210000000000000000f4ef310e4649682bc300d2e53d137dc7c2a78c89c6b312d68ba7b2a8a170ddab05694f53aab30019c8dbb3e543850400a5cbf81c4d5d3e89669ca406fd9b8fdde99c63e67a7759aa0000000000000000\n020000002e8da06450c35d020ab2a4d9e510ee89fca196e77f12f0560000000000000000184797be67e17ad2e9fe4eb5d6cb2dd6cb593e28cac759f0048ecd1f1a80600b87694f53aab30019b8a802a842850400606ded4b084da4f712e562b081408b28584c0ebc05f7bd210000000000000000\n02000000ccc36e54810b1412ced7a9a00b37223074c6080ea0dd189c00000000000000000b0cced8fe8361aa7f219649df4daffc0044a4f48e002e17ca6624596401acd0e9664f53aab3001935ee7e1f418504002e8da06450c35d020ab2a4d9e510ee89fca196e77f12f0560000000000000000\n0200000038393b82255dfeadb0fac022ce3e1783eec78ac504b50d9c0000000000000000cb8eb7cf71aeeeb94ec62e30cfef0eb203a8231e22b1ed1eb2ef263f1e4892a586604f53aab30019cd94b47140850400ccc36e54810b1412ced7a9a00b37223074c6080ea0dd189c0000000000000000\n020000000d0d2b4e81465971b908d12a4213a9298acfa7e5c64552910000000000000000cbc469f068c7b093db78b4560f87aa426de78c2a7a1dc4e2b3d070d9c71b3fe85d5f4f53aab30019a3008f9c3f85040038393b82255dfeadb0fac022ce3e1783eec78ac504b50d9c0000000000000000\n02000000be5a12d77b6f57d35657352ca97e6f63cf7c96418b85c279000000000000000029ed471aa72e61952ec049a6e017feab2719c2759fb57e8deb5a8cb440e2d3fbb15c4f53aab3001958d29efb3e8504000d0d2b4e81465971b908d12a4213a9298acfa7e5c64552910000000000000000\n0200000081727d1330040849bde55ef61312bd3cc56a5fd25c338f1b0000000000000000e0fe553450d2009fb94e859fa5304c6bdf6e1a5ca504bc29ec00c49920e0a8cc665c4f53aab30019af038f383d850400be5a12d77b6f57d35657352ca97e6f63cf7c96418b85c2790000000000000000\n02000000fb0307d0fb53e09602225ac23bbe7ed41956ecb7d24baa1e000000000000000060d1ded77639c5d5d3206fa93382ff97af2d73f50d30200bb186dbcf3fd9d3bff85a4f53aab300198fadf0773c85040081727d1330040849bde55ef61312bd3cc56a5fd25c338f1b0000000000000000\n02000000f22bef36ce9113bcbcfed19855f41b892368be6ee4a303a20000000000000000df62f043351ec709ac1b9c4c5bc803aad1a200e55e32d98b876bd9ffbf5c11ffed5a4f53aab30019f624ac1d3b850400fb0307d0fb53e09602225ac23bbe7ed41956ecb7d24baa1e0000000000000000\n02000000e6fc23bf68a531067151ac200b6c17f31ef36ea75ebf054800000000000000000b4cc592c0f058cd48c0f8deced0b419167481fac90e7dddc03666bcd5d0bae77e534f53aab30019101824fd3a850400f22bef36ce9113bcbcfed19855f41b892368be6ee4a303a20000000000000000\n02000000cd4e3a43fcf50fa19ca3ca122bd17741ac6f85a6aa961ab10000000000000000e89391c419414cce2df6c0ae00f5b39d04019a112327942a0187451c80dd6e53ea4f4f53aab3001908489b5439850400e6fc23bf68a531067151ac200b6c17f31ef36ea75ebf05480000000000000000\n020000007102b8fd502967b883062d558bbcb1934de7792aec9413720000000000000000c5676ae74f68105d8d671b3990773a0ec8d6ec3bc663a91c5ae4ea64d6d3347f81494f53aab3001923d85ce638850400cd4e3a43fcf50fa19ca3ca122bd17741ac6f85a6aa961ab10000000000000000\n020000002f15862adb55833c2773f570de340c49db7722809fd3f0940000000000000000b3ed418a2282f811e3d2a90b734f1b639a6ed64a98c01e0ba032ce914ab9b73d73484f53aab30019e2163656378504007102b8fd502967b883062d558bbcb1934de7792aec9413720000000000000000\n02000000101d5109c4b77b74f9fa469d665b68b3dd80d1d84dde19170000000000000000512c5c78a4f53886740ddfcd89f1387d419c0be3f97642465628683c830841f70e484f53aab30019de598b35368504002f15862adb55833c2773f570de340c49db7722809fd3f0940000000000000000\n020000001e6ea1cce073efbfcd5fd45232129a7efeca5c5a43ebe02d00000000000000003b24db33c7b9b256f84c5f934d5f504d885c11b6bd5bb1ce7f14a921992dd274c1434f53aab30019012fb91735850400101d5109c4b77b74f9fa469d665b68b3dd80d1d84dde19170000000000000000\n02000000877999b7a3cb95fa7729a4f5e22109cae34ee4e941c2e7a300000000000000000387ffc351b3167742fd1bf42525d8c33e0c45d5117eec38720a3ac17a21729cef414f53aab3001949b1f27b348504001e6ea1cce073efbfcd5fd45232129a7efeca5c5a43ebe02d0000000000000000\n02000000f70e98f57e4af2a8a7deb52792a250e02d30a68d23d793ad00000000000000003fbf0eb8beca9bd4eed150de6f370b8610579a8d22debfffdbb02f9a183483a319404f53aab3001921d9c2c833850400877999b7a3cb95fa7729a4f5e22109cae34ee4e941c2e7a30000000000000000\n02000000ce7ac68dc3123b45436cef787e839b9eedc7c963da4989ab00000000000000000e246ec6bd3d3321242604f6703e87d59c7642daf414fd5e95e1778a125ec0f542404f53aab30019d8e62d8732850400f70e98f57e4af2a8a7deb52792a250e02d30a68d23d793ad0000000000000000\n0200000062122d2b42728d0b320aabf1732b31b55fad13dd32ccf77700000000000000006258cc658d5427533d85a3252d8778d550fdc15c72a7ade86a16c12459a817bc9e3f4f53aab30019e4f5551131850400ce7ac68dc3123b45436cef787e839b9eedc7c963da4989ab0000000000000000\n020000009423cfc6819c7020bd42ac27e80b1c7c4f730aaea42fd013000000000000000067ba626bd952462953728eb9cb471846cbae71327a36e6a613ab8317995f577e9b3e4f53aab30019e0dcbfac3085040062122d2b42728d0b320aabf1732b31b55fad13dd32ccf7770000000000000000\n02000000ccf2187358b9f1eaaa7fcf58ec2d9fb600348dfa6c2bb8350000000000000000e9b32f7cec2ca0499488cd8159d9e1be2794b20ad5d2b97b5ee9bfdd3a7c320bdd3d4f53aab300196dd6199f2f8504009423cfc6819c7020bd42ac27e80b1c7c4f730aaea42fd0130000000000000000\n0200000065a11cfbe57eb93892b8a6a3c14080c483c2e67ae685f1580000000000000000fe252a834d8b62231f9b01ba6a48227a47fbc22f8007e9728636b11dd6b43409343c4f53aab30019a32afb6e2e850400ccf2187358b9f1eaaa7fcf58ec2d9fb600348dfa6c2bb8350000000000000000\n02000000b04fc8a8dc72a839990c1146a8ab753d96598e1802365a8a000000000000000071bcfd14c366a576b3b05500f9e7052f7704696608272f0b61f86f6c6d469874063b4f53aab30019b5578ed32d85040065a11cfbe57eb93892b8a6a3c14080c483c2e67ae685f1580000000000000000\n02000000c0d2fa1356fecf883d9a905a379d71acaa413368f2f4cb3b0000000000000000400253656db80a89b86b8f685be54be1571a4ea40540773d9101dba015f736d4223a4f53aab30019d88a29842c850400b04fc8a8dc72a839990c1146a8ab753d96598e1802365a8a0000000000000000\n02000000ec1242bd5fe1fdd1b9f3736ba2eae1200cb03235c1d927060000000000000000ad21d25ddca5da312acaf1c50fec67bb18c04f2bc212e64f74c2ce0347375bc9c6384f53aab300199d0e666c2b850400c0d2fa1356fecf883d9a905a379d71acaa413368f2f4cb3b0000000000000000\n0200000044e6a3529e8b18c6449f9e3e1efbda878af990fe5e4aa90500000000000000003174e12d9fa1a7aeae40d2cc8af5b2b31223dc3dbb5ea94ca1cc8bb684b908713f384f53aab30019065bd7292a850400ec1242bd5fe1fdd1b9f3736ba2eae1200cb03235c1d927060000000000000000\n020000004105a5c0ea0853a02d20af3cbd042feafb8dc2f79315522f0000000000000000a1a2dea77a9d186e336ade332fc6b011c83fc44add2d83b4bded99946040aba53a384f53aab300195341aa282985040044e6a3529e8b18c6449f9e3e1efbda878af990fe5e4aa9050000000000000000\n020000002d008795f0acd1e7dcdc5aa76afaba1ba31c271aab32f738000000000000000004dbad9b0b50922c369acd1c1e35bd28bb3deb59f20b2221a7ef78d417e01bd3a3324f53aab30019ac9159d5288504004105a5c0ea0853a02d20af3cbd042feafb8dc2f79315522f0000000000000000\n02000000f8cbbfcbd9a5709910c5f79056ff83e9ffc43e03940a96730000000000000000db2e57c047b670986ac8f7f40992003dd711690f9ba3fe6f0967203e1bf76490e62e4f53aab30019558f91fa278504002d008795f0acd1e7dcdc5aa76afaba1ba31c271aab32f7380000000000000000\n020000001e63ba192d148693cd116c484d602d5b60ae4210c9445d5d0000000000000000185caf72267125f82ea2a8d75690a6250d82739a18f8915a55cbe33a3da598d006284f53aab30019a7ee66ad26850400f8cbbfcbd9a5709910c5f79056ff83e9ffc43e03940a96730000000000000000\n0200000021b148c8fc7f61a8cbab86fa917d3c4c7f6183139b034931000000000000000045e0e292469d9911d8f01d97bb83338f1583625382305441dd9bebf694a80f2cad284f53aab30019c4744f67258504001e63ba192d148693cd116c484d602d5b60ae4210c9445d5d0000000000000000\n020000000248cf85be1d7ba49a2013dde6e2ebe3a8a46e342b16fa3d0000000000000000ee1f75947dff4dea62a76b6b0c60e2cc76cac26a2beacb6c5bc6788828dbbe7008254f53aab30019c9ea1c532485040021b148c8fc7f61a8cbab86fa917d3c4c7f6183139b0349310000000000000000\n0200000022a85507651f1129652a25b54a284d43d1d755ee2d2ce17900000000000000005f25d77d06d7fc6aa3bb0cec575a1b9d68ee120ca6a261e0057240eff8acaa78f4244f53aab30019b82048dc238504000248cf85be1d7ba49a2013dde6e2ebe3a8a46e342b16fa3d0000000000000000\n02000000e1c354e61233452e21c7d7419c9a31fbcb4ccc3c9bbf00600000000000000000a103278c41b8d1e5f9ac5f177005529b1f1ad2d56681ddc03003e07a3223ee1981244f53aab30019a95b4c9c2285040022a85507651f1129652a25b54a284d43d1d755ee2d2ce1790000000000000000\n02000000d44d7f012a71a227c5f500e88e67966b9e0f0e104f2af62d000000000000000046b84388d0c0786b398f7a116e480ed9b945989ae8482a2d5c62b39cb2177fa59f204f53aab3001935ab54c821850400e1c354e61233452e21c7d7419c9a31fbcb4ccc3c9bbf00600000000000000000\n0200000017aad17f4acfdc72e3bbd8e9fc885e3ccef06274e86c5b6a0000000000000000adeb010e3be1306361545949e16dfabad1275361550061801de0227470c383886e1b4f53aab30019b5b7442f20850400d44d7f012a71a227c5f500e88e67966b9e0f0e104f2af62d0000000000000000\n02000000981200022c790a6d2da10be7b2e3d057885c57706f9884100000000000000000a592635e157a7ac121e4ebe71a7e2d8baaa40ea06186997a961de473bcc0e43545194f53aab30019001cdd5a1f85040017aad17f4acfdc72e3bbd8e9fc885e3ccef06274e86c5b6a0000000000000000\n020000008c7c954557748ce467a11742bccf733e5df09aef26cb36080000000000000000677e01646e62e466d8a30870638848f5f2682935047da6ad9b5cbef55af050cdbb174f53aab300198d0c43221e850400981200022c790a6d2da10be7b2e3d057885c57706f9884100000000000000000\n02000000bf4c372f972fbd1beeb682bb5f84169fef89cfa1ff9b322700000000000000006d9a0c989b1532ca9ed7cef4ea8c738822604c276339bad06b828a8fd6ed2ce2f2164f53aab30019923733f91d8504008c7c954557748ce467a11742bccf733e5df09aef26cb36080000000000000000\n0200000054fc78066d71e469c398be1525587527302faab49f81979000000000000000004cc800d9ad02a4180a5ef6e96f13bad059c7a7fd7ae49a3e4c51d4d47963d4cfeb144f53aab30019831103731c850400bf4c372f972fbd1beeb682bb5f84169fef89cfa1ff9b32270000000000000000\n02000000739d7509fc558e59272558711e7560dd3eae023f1a3f484700000000000000008981569b49984136e549bfc2af562057b01e53f3977e472e8bf1152671d6c478d1144f53aab3001930a1a7ab1b85040054fc78066d71e469c398be1525587527302faab49f8197900000000000000000\n02000000a30f864fb3282f0bb533bfaf294fa71ea6d24e9a6a9bfd7d0000000000000000fc56b108ee29b70c7eff51f8ae8a31a76ecfde8cec57e32b90d29336bfe9835330114f53aab30019d28c826c1a850400739d7509fc558e59272558711e7560dd3eae023f1a3f48470000000000000000\n020000003bb767b1cccc63d26553d2ffac3bf8d38648b986e0f7c17f00000000000000004af836f012bf26e6cd03c6d844c7c321aeaadf571f1556fad1fd5f9ffc1c5628e70d4f53aab30019714eb9e219850400a30f864fb3282f0bb533bfaf294fa71ea6d24e9a6a9bfd7d0000000000000000\n0200000039a5d05c2762372ddafba55b2f57406cb3e6c874fc770e950000000000000000327ffc418d5daeec95a0bf5e07eb55171636e62ca9d539302d4fb7b4f79a38ecd90c4f53aab300196d1fd401188504003bb767b1cccc63d26553d2ffac3bf8d38648b986e0f7c17f0000000000000000\n020000003b9343ae2bad5197438df09269076707f328d806f3c0d51c00000000000000009fd2c1de375bdfb2ace14b0236a14bef281fd8d404944053a9fda7a4a376b44f2c0a4f53aab30019237a8cf11785040039a5d05c2762372ddafba55b2f57406cb3e6c874fc770e950000000000000000\n02000000a079768528b9ef1b365b313553337d5fc6e403fb5d3de73100000000000000003992d23c20b895f9fe71994bd03f885c13dff7141117dae130a5f7e3f6c0e45725094f53aab300194843f437168504003b9343ae2bad5197438df09269076707f328d806f3c0d51c0000000000000000\n02000000423eb273b0cf153fbe42b6cfe544f48f1c4991f6fc3562660000000000000000cfc95644d97a0e9e2f04b7139fa3ad891f9fc5ce7fc63ff95cf59430623950e369084f53aab300190c5670c415850400a079768528b9ef1b365b313553337d5fc6e403fb5d3de7310000000000000000\n02000000af28abbb9ed1305d9aaa0f6efeec9021b864480b5e7935a40000000000000000bda0ab7dd66310642e6b39f135f5303f1872e3b3b5d0ce4b1d7423af5e95dd150b004f53aab30019e5b545e214850400423eb273b0cf153fbe42b6cfe544f48f1c4991f6fc3562660000000000000000\n02000000331a74f700fefabfdf1f1093bd36576bb00d7c6d9dd8713a0000000000000000482eecd9a424899a7eb6ae85bc0b9d5519f5fb87739ea6c13f9668a40ef17a39e4fd4e53aab300196e5bce5c13850400af28abbb9ed1305d9aaa0f6efeec9021b864480b5e7935a40000000000000000\n02000000a0310d422e8a7f095ef38b461631a772eb8d1f9dc4bcae3a0000000000000000a6a4469562add71b42286a3033f27d8d027c186b1640735ae206536825b79b0152fd4e53aab300190b4a4cbd12850400331a74f700fefabfdf1f1093bd36576bb00d7c6d9dd8713a0000000000000000\n020000005cfeea1fc4b068f75b30b35edd263a8ffeb90778f6de1c20000000000000000069c107d2d425598fc8352a050d2827b6657e02a0eded7bca667b10910ad64a8f30fd4e53aab300192c5b667611850400a0310d422e8a7f095ef38b461631a772eb8d1f9dc4bcae3a0000000000000000\n02000000c495295ebb9eec9333aceb2c124cae92a3b43c3a4b60a28000000000000000008e189eeab9d432eb27d4a35fc84c538031f2397c8df52d6eb242be1c659c03e89efc4e53aab3001994c92316108504005cfeea1fc4b068f75b30b35edd263a8ffeb90778f6de1c200000000000000000\n02000000ad09610c309bd28cc9c9d036f6ece1797bf6f5be681394790000000000000000ae9e1173bd110c3c9abd91f8a078949e221b3bebbddb0070626aa061056ce29d6ef64e53aab3001963bc9bac0f850400c495295ebb9eec9333aceb2c124cae92a3b43c3a4b60a2800000000000000000\n02000000ebe9e64039e239a5c40498e886b0498d6903a8d364df89200000000000000000a15f0a52681c1bc1c7ad2fa1ef32047d60b3d07ae852fd1ea8b73acca9fb1fea51f64e53aab3001923e35fdc0e850400ad09610c309bd28cc9c9d036f6ece1797bf6f5be681394790000000000000000\n"
  },
  {
    "path": "bitcoincashkit/src/main/resources/MainNetBitcoinCash.checkpoint",
    "content": "0000fa272fd14221bb34078aa748fa05c33ede7b7dbb380e4f6a7e000000000000000000c99855609b8d0e0f8ba9114eaa0885f384fa9c15e436e2ee3cbe0683caa2406f47a3ca694f36011853bdf29aee690e00bbc51b428e139b7f64e4ebb4246a8efc35a8a0d0e761e5000000000000000000\n00405221dc1942796c2587dad65cdcf6948bafbe8995f1acc42c30000000000000000000a96c8d6094e715fba9f028bfac89dc59b29b5d9a289d7b8b9e227afdb7c9ac69ea9fca69ed3601182ee43da7ed690e002fd14221bb34078aa748fa05c33ede7b7dbb380e4f6a7e000000000000000000\n00c0473c32b4969b8e1a6143b1b514e2dddbd2e2fb58fd9a105cd6000000000000000000e5b8d7fc2aeea524281d9d2914aed067e6715f2bf8403250b8340a1aa2994862839fca699d370118f4a584e1ec690e00dc1942796c2587dad65cdcf6948bafbe8995f1acc42c30000000000000000000\n00c0b131bf0267373d7a0cadb668538c35b0d1cc25442a3f3de1c6000000000000000000849b371c00e741ae0c33954e557bb2fca6b7c9894ea177cb77e13384718f275b509fca695338011834785c29eb690e0032b4969b8e1a6143b1b514e2dddbd2e2fb58fd9a105cd6000000000000000000\n00000026228c4f10ff851d148c6fb56f43397b26d7abcdd36c25a200000000000000000011b3a81f152920895460a12c152fd43aaed89ded81e4f624db09159b0b49751b309fca69f9370118a15121b9ea690e00bf0267373d7a0cadb668538c35b0d1cc25442a3f3de1c6000000000000000000\n00000020a0202dd69317d2c6acfb6f0acb3e016f01d7871695018e000000000000000000a95e3317f455d60091f7d1445011645c2cb0d525fe5b2da3d60ef26b3dd7394fbc9bca69d537011855bcd54ae9690e00228c4f10ff851d148c6fb56f43397b26d7abcdd36c25a2000000000000000000\n0080e631c16ebb60b26d2192abcb8857df48a3712d93042bb3002d0000000000000000006cbf8a498a480e2108f4b153d3d53163a48a7d1aae335181fb43ade22dbb44e3f698ca694b3801181211128ae8690e00a0202dd69317d2c6acfb6f0acb3e016f01d7871695018e000000000000000000\n00006020c6024ae2f28440d871a7144b1f9c73c91713c495034414010000000000000000456c57690a19117a7e4c3e64ca0bd173334662e9570d88afa0b43f5fae4fea1c0b98ca69d138011859b6195ee7690e00c16ebb60b26d2192abcb8857df48a3712d93042bb3002d000000000000000000\n00205d2848af7fd5ba80ee7fefa0171f7014c3166549d395b9ffee00000000000000000097197c723ba84ad85ba551aadfdc823bfba4a0fcb79cbd6ce1849e8e8c9a1de45497ca69a538011836b5cd65e6690e00c6024ae2f28440d871a7144b1f9c73c91713c495034414010000000000000000\n000050248940bf978d9e598fac9c4e2fdd90265708b544d0db8f300100000000000000001886825ca108eb33a688bc9416792d739cd8e232fb4213e970e15ede5abb3ae27594ca69f1380118ea33c680e5690e0048af7fd5ba80ee7fefa0171f7014c3166549d395b9ffee000000000000000000\n0040d22537239c2d3d24f83f986abc69ee4919e744fdf4d4373b7e0000000000000000009d2d6a5415880fee4a68f1f47fdd966089c318ee9dcacf8047ff9fa5483769b30a93ca697e38011834898801e4690e008940bf978d9e598fac9c4e2fdd90265708b544d0db8f30010000000000000000\n0020f52a8235f7bb692c937be8cb23bef8b96c8efad4d4a0c5b022000000000000000000dcf379ac866fe6ba637aa9b52891f280ecaa816cec3a7645d27c43c375d760604c8fca69233901181118694be3690e0037239c2d3d24f83f986abc69ee4919e744fdf4d4373b7e000000000000000000\n00208c23b7410977965931ddc9e86f5afa5be117a720cb38bbda2a0100000000000000007ecad4dd77a8138b6d15386cdd7770879df7a34a6dbc5a5ce192e5835f6015aff38eca69553901188fb0e2a4e2690e008235f7bb692c937be8cb23bef8b96c8efad4d4a0c5b022000000000000000000\n00e06d2af3baf8a20d0bcae765f4e73aa3c828916c748d967e9eee00000000000000000060f259564a222a1c9915339709a563db4ce850d805061e870014e6f9d5de57a4368dca69883901180f13464ae1690e00b7410977965931ddc9e86f5afa5be117a720cb38bbda2a010000000000000000\n00200020afd0e74736ab8e06f9a18cd0e1f7d2ce7fa0b9bdcf90b700000000000000000064330a5110c883009456f96b271a180da884c0561069c227b8126e92694949f17d8bca69473a01187b2e2a97e0690e00f3baf8a20d0bcae765f4e73aa3c828916c748d967e9eee000000000000000000\n00e0ff3ff23181cde48b0a35ae8780ece0e2e05f9a30995563317e000000000000000000b39d4b8d8e8216765cc801f4cf74564f8c1558e6dddff8a5cedb0ff9cdd02732748bca69fb3a0118b5cbc453df690e00afd0e74736ab8e06f9a18cd0e1f7d2ce7fa0b9bdcf90b7000000000000000000\n00607f250be62c38c3f2758e032efae3cd5d94dca082916399293a0000000000000000008579ac2b3df2fc8f267eefcbf1f6391f564ac10b935f3a8377b03f9d7a826db1468bca69023b01183ac55657de690e00f23181cde48b0a35ae8780ece0e2e05f9a30995563317e000000000000000000\n0000ff3f750d79a46750f70780092ba9076f74da5484b4e281e628010000000000000000f3350d74d8a7b479552302d49ba08fd6563d68f9b9ca44dff262001a387220430789ca69ef3a01189eacc827dd690e000be62c38c3f2758e032efae3cd5d94dca082916399293a000000000000000000\n00602b21c8d1c8644566ec6662765aabd9c87d541dff4be43efaf1000000000000000000ad6584557336a280e663b65ae4f9971e68b6ca06a91e5d6775502c8c1f43498e6f86ca69613b0118093461e6dc690e00750d79a46750f70780092ba9076f74da5484b4e281e628010000000000000000\n000005207e8a72135e9d70e2b257b0a0ac0000e2ece8f13ee1f006000000000000000000d89a3dd1944f552efe2d92310f28f4b447af9ed212b8a62d373d3b23098d040a7985ca693a3b01189553c608db690e00c8d1c8644566ec6662765aabd9c87d541dff4be43efaf1000000000000000000\n0000ae259b9cf511cbb948d78cfcfbcad37ec8ba2594819369bbd50000000000000000006b6a261537a1b084af8b6ff7a2949e4eec7d24f0c499e433e881e18e5ca162e2a882ca69e83b0118017cee1cda690e007e8a72135e9d70e2b257b0a0ac0000e2ece8f13ee1f006000000000000000000\n004006203c1622b6970ebb9a70c587328da15c00223ba26146c4080100000000000000002e7b85be0412d5378536dc5632b53f36f30f73a4d456be243ee7080673c52d526882ca69a23b01182c749d5cd9690e009b9cf511cbb948d78cfcfbcad37ec8ba2594819369bbd5000000000000000000\n00e00620a58250dd46b5609a11a6bbfa5a5c787c4a99bfc06c4ca0000000000000000000854d911408613c39a8cafe2c050e5f9996ece46764caa4dc97d1bc6960837ee2397fca69d73b01183fd7a558d8690e003c1622b6970ebb9a70c587328da15c00223ba26146c408010000000000000000\n00c0e029c9e2cb65605543ab809b26325413e395aed4619e5e67ff000000000000000000beeb4e4bd78aaf2b18e5cd6c72f220e92582b18d5d1e2e156ff5f6366f097318827dca699b3b01183874f238d7690e00a58250dd46b5609a11a6bbfa5a5c787c4a99bfc06c4ca0000000000000000000\n00e0ff3f947c2d1dd261f1f26840ff1820de5edea1651f9b8ae7be000000000000000000087cdf61d310d52cd2987719e8430c5808e3b219095db7cd77ff6c6de430525d747aca69633b0118bd513ec0d6690e00c9e2cb65605543ab809b26325413e395aed4619e5e67ff000000000000000000\n000000207cd957a77a95abca7c069d33c4d6534bef207a094472a6000000000000000000e6484e1a7ee2bf9c688c760dfe8a9faaa3f40e1f853a570bcfc2fdec69fd2cea7177ca69a73b0118df9cfa8fd5690e00947c2d1dd261f1f26840ff1820de5edea1651f9b8ae7be000000000000000000\n00c0122260be8c5e4eb8e43553f4a66b5fba05ef31aa2e2f49ab35000000000000000000242e4d89d6b8cda569fe7fa511de04c51d4d798a1c1686b32ad9bdad4546ac80ea75ca69673c0118be014ff6d4690e007cd957a77a95abca7c069d33c4d6534bef207a094472a6000000000000000000\n006014311f771ea7a5854d4a5df9c9b1551c667ac5261873c97fc90000000000000000006f09c46d6a008a1fa1703fb4da7bbb849363a26cbe4a17f17f65f5f9da4f03a0df75ca69cd3c01183de05902d3690e0060be8c5e4eb8e43553f4a66b5fba05ef31aa2e2f49ab35000000000000000000\n00200220c475572e0b82ad03e41ea2cd5dc182d04c8cd5e3530d680000000000000000006bbfa7c18951974bdc28ff75d8c3cdf97393f2add46114a4640594001527ff14c174ca695b3d0118723174f1d2690e001f771ea7a5854d4a5df9c9b1551c667ac5261873c97fc9000000000000000000\n00805638f35e7999c74eecbdc60b9424680825689103fc1efa496e0000000000000000000d8c4d50ae7fa40a768505ccd4e8c5672769078b38dfb26123b4283d5e856f0c1974ca69a33d01181ebbd0a1d1690e00c475572e0b82ad03e41ea2cd5dc182d04c8cd5e3530d68000000000000000000\n0080842a77e98bcd2d7361e4bb357f277792bd113b2dabe3e05927000000000000000000faa053042be2305581fc0f95629dfaa5749218cc59250da827c3ec8cd988da059e72ca694f3c01186278761bd0690e00f35e7999c74eecbdc60b9424680825689103fc1efa496e000000000000000000\n00c00620a4544192a355287053f3d6c35c7b4f957f70aac07344a50000000000000000007ece2f73653f69616c1737e7f4e8344208b1a380d2b8173de1aa867aa59cdfe8366cca69903c0118416602d7cf690e0077e98bcd2d7361e4bb357f277792bd113b2dabe3e05927000000000000000000\n008055272737973e90d4c6c3f6b470ce086238c168ff6ba276d4fd00000000000000000086c1c10c7dd257b230329fd32367c0696afd22545648ab0a178f6dae5961a056a66aca69933c011835d6e1d4ce690e00a4544192a355287053f3d6c35c7b4f957f70aac07344a5000000000000000000\n00404d2ad8ed3a951d7b51caaafb6aac80789cd8cfb75de948c7100100000000000000008553e1b0db69265bd492f237bc0d6558cf9ed7d77de16435db78658ca3f2d94b5568ca69eb3c0118cb159d62cd690e002737973e90d4c6c3f6b470ce086238c168ff6ba276d4fd000000000000000000\n00e01320d270276861b0864838ce1f9dd699a6495cbb33ec02ed1a00000000000000000038b1c0c880e96cad09c4d83d1ad13d921b6f1b9bab60f45505d6cfbf7fb05e390f67ca69a53d01188bb8ca50cc690e00d8ed3a951d7b51caaafb6aac80789cd8cfb75de948c710010000000000000000\n0020d22688e92d7b93386f5c0f0f02d56a8c892377c030528dc75b000000000000000000320f9d8c5b34bf2b854019ffed30427e19b284ea43b5ca806260c88a93158850ef66ca693c3e0118409839b1cb690e00d270276861b0864838ce1f9dd699a6495cbb33ec02ed1a000000000000000000\n00000d20825499e1bc8b7dc8c3007319aa5e7921daa4373e1c3087000000000000000000218c7e5194f237f1ca45af3469c8821a32466ba4601c927ec403c27be67196416366ca699b3c01188f9f222cca690e0088e92d7b93386f5c0f0f02d56a8c892377c030528dc75b000000000000000000\n00a00a207df9607dbb25ac5ca7f09a2339110d00715a11e761311a000000000000000000fb9ff542d08ddf36c87f3c0a6abfa49227b2864ea50544d2c5925e23cd6523a30e5fca69de3c01186fdc4f7bc9690e00825499e1bc8b7dc8c3007319aa5e7921daa4373e1c3087000000000000000000\n00407c280166ab240b83e7ee9a0e1e91ecc5faf7bc22850445c109010000000000000000f2081580d42b52da463cb073d9e3915069e0ae1d43590d66b583a838016b6149845dca69993d01180b3a2cf6c8690e007df9607dbb25ac5ca7f09a2339110d00715a11e761311a000000000000000000\n00000032d31f152b4fc0585b7cc7b9560a428603b1491fbcdc7e15010000000000000000805f0aa3fe6721faaa189675ca53e0bbf745aab822b69c0036072bfeb7536463695dca693d3e01185e3861afc7690e000166ab240b83e7ee9a0e1e91ecc5faf7bc22850445c109010000000000000000\n00c00a20d0b5121c7116c832dd946c45683c00901779ef936064c1000000000000000000275c33e4bcde3b60acd5ef292c0659ab9a1cd780e4f8753b578d37581a8ad47b085dca693d3e011866ae790bc6690e00d31f152b4fc0585b7cc7b9560a428603b1491fbcdc7e15010000000000000000\n00403e281ac4fcf0a82ad0bca236dbf0055e7d0c02f0d4fa56ad930000000000000000003a8ec1b9f0a49677ff43c0f3bc2850b950a5e612ca8b5544ef792b618b436db2ac5aca692c3d0118a02a9905c5690e00d0b5121c7116c832dd946c45683c00901779ef936064c1000000000000000000\n0040b425b65671a293c35270171b8882fc35fe9b3b0cb383a0133b010000000000000000b48179d117c56a9e87287de059f305afafde8d57f31fcdac0934bff856f2b5191055ca692d3d0118580eaea4c4690e001ac4fcf0a82ad0bca236dbf0055e7d0c02f0d4fa56ad93000000000000000000\n00200424ebf8fb45d71f7ab784946abad759b846d7b2270c6ea7e30000000000000000008d14a8e50c114190df63ce0c6a980cb1d470788c60ffb10ce0cecd0577fa281cbd52ca69563d011828829c5fc3690e00b65671a293c35270171b8882fc35fe9b3b0cb383a0133b010000000000000000\n00000022940d862db382954844b135e9393017034c6eec9121bccd000000000000000000aa14be8f01ae2cabd46a72e4de1a989ead54c3b8a9cccbcc963d305b85861607e550ca69b53d011897580fa5c2690e00ebf8fb45d71f7ab784946abad759b846d7b2270c6ea7e3000000000000000000\n0000e0208a7d8c042e45c7497ceab01897cb6959a31048236fad2d0000000000000000004aaf2b78fb02228048c1abbdec6a5909986e6296d560cc8e9514c8a881a43d08b04fca69043e011806158bd7c1690e00940d862db382954844b135e9393017034c6eec9121bccd000000000000000000\n0040e2220a0ccbd2f534df96bba87a228d85ac982cbf15103f731d000000000000000000a43935a741d1d1e842bc2675873747450c2d84281b70691e2ab17774f33ae5c3464eca69493c0118ffb854e0c0690e008a7d8c042e45c7497ceab01897cb6959a31048236fad2d000000000000000000\n002000202865e1158705a133ca83a1f66cc5e64c05e8e8200b1c5100000000000000000098ccc5a4010739feca6d39aee4a6ff8a0e45d9b793eb39345dd8e00c59d2f355a246ca690b3a0118681a78d0bf690e000a0ccbd2f534df96bba87a228d85ac982cbf15103f731d000000000000000000\n00e003202563391e02b3cfd63edb8f1aff565a092b6eaf7959c06c0000000000000000004b32b3b28cf7b4c3233c41410f7052cb550b00759e489ac9eff9a174ecd818a5603dca69823a0118348d87f9be690e002865e1158705a133ca83a1f66cc5e64c05e8e8200b1c51000000000000000000\n0000003c844d3286406d0f0e5ad3f0bc0f35693a22659e512ca3fe000000000000000000cc153a28df25a39830c6a3d89b2e498462dbcc9e71d56c1370736d84ac321182793cca690d3b011859d13eb4bd690e002563391e02b3cfd63edb8f1aff565a092b6eaf7959c06c000000000000000000\n00e0b927eb8a652b62b53c93c114928f643bdf37bbf3aadff1eb390100000000000000002be800a73f1b61437d44629a70f7f6c6aa4d9350173f44c98d1fedc47a4a437bce3bca69523b01187ede799ebc690e00844d3286406d0f0e5ad3f0bc0f35693a22659e512ca3fe000000000000000000\n000000346da27058761802db900984ea54de8bc52a64669ba9185c0000000000000000005e5a1790534553e4fcfa41d6ea1485a1d1186b46601ccd0528b055dba651d9be4b3aca69323b0118e7f5c814bb690e00eb8a652b62b53c93c114928f643bdf37bbf3aadff1eb39010000000000000000\n008000206d3bd8160d4070d625315d2823c4821ede63caab02eeee000000000000000000f63f29d6ea0d67be48b0186e30c500ed29a1392bdf07dbebcdad0f5a07f3cf2e8f37ca69e83b01180a94e90cba690e006da27058761802db900984ea54de8bc52a64669ba9185c000000000000000000\n00006020bef0052eb03f2b68e86b36c5ebb28919b230c2a87ded79000000000000000000d811f1f9815756987d3b8626dc38c23e31ef37ff26fe60284ca4e94523e010e66637ca69173c0118633885c4b9690e006d3bd8160d4070d625315d2823c4821ede63caab02eeee000000000000000000\n0020042064e531d38f9b3d84fdb94f667bf906e623aae6b45c6b3401000000000000000084612f7a0aa1b10da4911bf89a5cefbf5c2118e725131dafd9e5b41aeec72319a235ca693b3c0118ee4c27d8b8690e00bef0052eb03f2b68e86b36c5ebb28919b230c2a87ded79000000000000000000\n00000032a7e644a5a4c1716917bd1875def3708bb94189b499a6360100000000000000005a749ebfe3e3a5a6b8141c79cc9cb0715ea459463ef4c87aa9e76a03ead4b462b833ca69dd3b0118affce802b7690e0064e531d38f9b3d84fdb94f667bf906e623aae6b45c6b34010000000000000000\n008001201400a35e86f66678ccef2effdd88125b58f7e6e9686a04000000000000000000255d2a8fe611ae287fde0a0a3563643a1cca63b0b0f1dc86483d50a8cad7bfde4030ca69953a0118211fbebfb6690e00a7e644a5a4c1716917bd1875def3708bb94189b499a636010000000000000000\n00001020b68fee1eff69b26e2b7c707d10216708522f4b45edda090000000000000000007d30bfd8a6c721484bf920a6ed23fd4f2c049b83fd932c06a3d93270f47e9e70f329ca69893a0118c9702d22b5690e001400a35e86f66678ccef2effdd88125b58f7e6e9686a04000000000000000000\n0020c0275daa501e42fd71e847333613c0c403e26e38f33fec1700010000000000000000933a29829daaa79d434b632505d20ca710ee28107b2ae9f973b2ce83b2e2cd957627ca69243b01184484bb17b4690e00b68fee1eff69b26e2b7c707d10216708522f4b45edda09000000000000000000\n00a0c327ecbc1810334ed7036f39c26a27e84f3c9563d93adc311e000000000000000000e78d2f0d19ffa494fda2384911f2a4f255de7e79c9c378a5436e525575b0aaf3fe26ca69d03901187f9a0d7bb3690e005daa501e42fd71e847333613c0c403e26e38f33fec1700010000000000000000\n00000020e871a7b26697a6189b8f6b8ae5a8b1e0b5f6cebc69c6300000000000000000008d8798deb40807821de6083f238c76f479fa9dc475e70e1058bd99d063ef57bc8c20ca69b8370118c9c7170eb2690e00ecbc1810334ed7036f39c26a27e84f3c9563d93adc311e000000000000000000\n00600720e17232fcdf784a22acb5ec6cc89255a3452d5571532c2a010000000000000000a9fedb5c358ecc81a37b8b6791294a87f2dfd7c50116c5b16658c4eb69f9fa9eb517ca69453801180c3337eeb1690e00e871a7b26697a6189b8f6b8ae5a8b1e0b5f6cebc69c630000000000000000000\n002000206f1b66c37e46a09b151bdb6d897c68e3fab2c371a08fe5000000000000000000b7d22ef099f871af52251659ed12b6c6c135c950054e6c8b1d67b8ad2cd868111017ca69de38011833f51a67b0690e00e17232fcdf784a22acb5ec6cc89255a3452d5571532c2a010000000000000000\n0020bf226b96afff8d956e99abed00aaf298508b26008db8ce5d2a000000000000000000c7eb5d8d8579fd379e8ff606e641b386c0622e8ce763bb021a136e527a543a849616ca69023801183e0198dcaf690e006f1b66c37e46a09b151bdb6d897c68e3fab2c371a08fe5000000000000000000\n00e0602441c97c78defddb1d384c2abc905be33b386e033bc8ccb2000000000000000000bcd3a5978e5a8d5982058f4d7f886f8b79886fbae2954df2eb4e7101daebef779111ca695e380118f79e4126ae690e006b96afff8d956e99abed00aaf298508b26008db8ce5d2a000000000000000000\n0080d7297497e379aab2816bc1e1f7599fe76e7cc0183963c5072700000000000000000013ce2596bb23e5aaeaa32922ded4faff1e70f774a2efafcad112666a22daa0865710ca6940360118ce0012d5ad690e0041c97c78defddb1d384c2abc905be33b386e033bc8ccb2000000000000000000\n000000204fc7d2504eaaf9f05d46eb1faf3a3699be630fd2f5dcba000000000000000000217ee68612cca2fedb5dde5ba8e4f56bfabc58fc70db837d4cd4bef8c6e12c9d6207ca69df3601185e6c2d7dac690e007497e379aab2816bc1e1f7599fe76e7cc0183963c50727000000000000000000\n0000003478e1973a51be122418de2d46a4f5be3783cb66e7ad2f900000000000000000005205baed23c8242d416e4a1f38b71549fc27d048885d44ba77bc118eb4c75cabff06ca699136011875443141ab690e004fc7d2504eaaf9f05d46eb1faf3a3699be630fd2f5dcba000000000000000000\n00207325e2a67d664f2475499dbbf38df7ed443a5fac4d0bbd4bbb00000000000000000037acdde0511767f59194aaffc9f3e7687c3d20052ac06581aa5dbae6d87a5dbab103ca69c8350118207c32afaa690e0078e1973a51be122418de2d46a4f5be3783cb66e7ad2f90000000000000000000\n0020152320588b5468bd5fc2754b6bd3d95a847a98aed4aab86443000000000000000000f42a9faf305cee175ee035ab23ac3102fb6ab96431d3a7c2138bf245b1300055e0fec96991340118550285aaa9690e00e2a67d664f2475499dbbf38df7ed443a5fac4d0bbd4bbb000000000000000000\n00406e264d8d8f3a9224e348b826637d56978d7929bae8f0a437cd000000000000000000a29dbac9e9e6643479eac198ddc7f07f67d17957f6c083208e05d82a4827c0f0b4f8c9695334011822e72e42a8690e0020588b5468bd5fc2754b6bd3d95a847a98aed4aab86443000000000000000000\n0000003eee72bef480ac058b4770af7ee544628ee2bb76d2f76611010000000000000000fa190079d15ec14f916cdd64068aaeb9a684faa6cee2048b394ca649472548a497f5c96935330118eaafecb6a7690e004d8d8f3a9224e348b826637d56978d7929bae8f0a437cd000000000000000000\n0040a43fb776eca5021b6441bf01590609fa50a9102c0721cc627a000000000000000000628155c4066d0aff0890ae2f88ced91f307c814e30786b3f0df9d0d353e66cc1b2efc969ca3301185d35ee22a6690e00ee72bef480ac058b4770af7ee544628ee2bb76d2f76611010000000000000000\n00a01820fbb0c97c6a98d3df866be54af0418711c47c62685682ef000000000000000000c896dfd18ea1b95ae1bab9d97b3bae7b0b2e8f31bf037e69beb6242173ba06a336efc96945340118074eca97a5690e00b776eca5021b6441bf01590609fa50a9102c0721cc627a000000000000000000\n00000024c2717b677ac55230c435ea6726c567fd3a826f124ebcaf00000000000000000021d69f08fcc9ec06b7d994c89f47439f30042d26ef8972e944be9d87aa599e3564eec9697f3401186e11f711a4690e00fbb0c97c6a98d3df866be54af0418711c47c62685682ef000000000000000000\n00000028d5abc2080439a604b12de93da57bcb96d70e5c2f83b273000000000000000000d2032ab7fe2b04ee79a6d3db035851da6bb4e5bb837ca691312ddad76303e6b1c1ecc9692f3501182461dbaca3690e00c2717b677ac55230c435ea6726c567fd3a826f124ebcaf000000000000000000\n0000002a3b187aac55e6b61ed07d9bb2e0694be23e312066ac364d00000000000000000076a125fadba6fae9ba71f6e8b45c1b97623eab0439a6a1adcf9e059c6911712e97ecc969b235011853a60081a2690e00d5abc2080439a604b12de93da57bcb96d70e5c2f83b273000000000000000000\n0000003678b7314b624be44c01e6a082f6b341ea2e8377be9c45b4000000000000000000fbee17d187acc0686ca9d6bc882c1c20070f23ac4ef36bbe0af52e8c00d639a3d9ebc96972350118a4a510a9a1690e003b187aac55e6b61ed07d9bb2e0694be23e312066ac364d000000000000000000\n00a03d2865201750790c784bc1e375e44d71874a8973d5c273e235000000000000000000795927e4abe89d27037b78641fb4a344c3289bbef03f65869c306fa559d1b559bde8c9695a3501187f2971f1a0690e0078b7314b624be44c01e6a082f6b341ea2e8377be9c45b4000000000000000000\n0000ff3f45bd9c5fb6dca7721e170611785f4a3618fb73b716eae60000000000000000003b2db82dc761bf5bd6e0e62e4131ea15e1b1c2b7a657e9a45be58ac09456a86015e6c969f8320118bbf83e079f690e0065201750790c784bc1e375e44d71874a8973d5c273e235000000000000000000\n00a00420b0bf2d9d82bfcec8b5f9a449adb702ef43b07b72ccf444000000000000000000599d1723f71eef8ea1cac5fe73a1a0094d46fde1e2e7e87bffbb4d700d3d70ab2fdcc96953330118a77b76ec9e690e0045bd9c5fb6dca7721e170611785f4a3618fb73b716eae6000000000000000000\n00c000208d4dd1744ce7d343180d53fc45a6d739bad0e9a1cce003010000000000000000c987d0b0d6eb630f9a0437405fdffd2993bc0b3e91806f984c595d7221ffdb00fedac969cf3301186aa16c339d690e00b0bf2d9d82bfcec8b5f9a449adb702ef43b07b72ccf444000000000000000000\n00c0a4319e6ce868858bd6819e2e522873f561cac73be9c87c30510000000000000000009526e22c47604263d831f2b02c5611596fea6f87aed38696d34eaf3fa2b13a392ddac9693134011842ce249e9c690e008d4dd1744ce7d343180d53fc45a6d739bad0e9a1cce003010000000000000000\n0020292955f0e2a015ac25716a3ef05db2ce0a3ce9c0305c014ad8000000000000000000bad5acf66518d6d8d4c172b7c197f8e6167991efb8c1dff1a34dbbef88891b9209d9c9692333011882d2a3c79b690e009e6ce868858bd6819e2e522873f561cac73be9c87c3051000000000000000000\n0040ec2b611295edc1175cdf761b4a752d0709089113d9ad619f1f0000000000000000001aec1df0128ccf0848e2b3303770ca1de11ef57b128d3112cbab5a434db3fa4d5ad3c969bc330118415946479a690e0055f0e2a015ac25716a3ef05db2ce0a3ce9c0305c014ad8000000000000000000\n00000032dd3c3e0b441307946ca1481034482ecb8da4d76870ceda0000000000000000004655108df8ccaa71bbeffc34ae2d0f40f50fdd7499e614ce905bf51a229eb030ead2c9696b34011859f304e099690e00611295edc1175cdf761b4a752d0709089113d9ad619f1f000000000000000000\n0000002055f3c8f435bdb9d4c652920d58c5e30a15fcc628c962200000000000000000006131019d4a61576e51147f5d15f17c7d266e3d9520f505f7798b7fd2e11399dfbcd2c969bf3401183c5c70a398690e00dd3c3e0b441307946ca1481034482ecb8da4d76870ceda000000000000000000\n00e00220bcb28b7dab5ed21f2367dd8496906f2244855c60191ee800000000000000000031069b52cc489528fc7ccc2860ced65084eca3937fa22282dbfba5d575a035fb6ed1c9691d3501183e6934c797690e0055f3c8f435bdb9d4c652920d58c5e30a15fcc628c96220000000000000000000\n00201520eba19ac7399a362a484a195c7f6088c8152658111b001d010000000000000000f683a15ff8e584c0489580ba3d170568b9026835e258e21bdc69449ebf8588493ed0c9690535011859c6c83396690e00bcb28b7dab5ed21f2367dd8496906f2244855c60191ee8000000000000000000\n0020fc284f118fb2f21ae0b387a45f2aa33947f27ffd7f16f54023000000000000000000502ef48430f00a20374fb254d2551d2d1a26922babbb37dfec1fe1f3c414ede999cdc96965330118080c32ed95690e00eba19ac7399a362a484a195c7f6088c8152658111b001d010000000000000000\n0000002a59dc25dbde3bdf98e04d558eb8558aec43daf55921e9cb000000000000000000921a0daa5c538f4d2709966affe5c9dafb1569e52d4a528f515d2eac0db1d9051bc6c9696933011813e7893794690e004f118fb2f21ae0b387a45f2aa33947f27ffd7f16f54023000000000000000000\n0000003690f3079ead0e11a10465f65c65b32fee868b694cde0c030100000000000000006abe4166dda1adfc3207d83572adb28e70ab22872b79fd986b04b3d6ba18bd62d1c3c969ef33011885f062b193690e0059dc25dbde3bdf98e04d558eb8558aec43daf55921e9cb000000000000000000\n00603320da95786d27f9764988f0f12461fa45d4822535cac95a0e0000000000000000003e7ed002a46abfac300d1d4eabd170ba1ddce0963271c3ad0d555e90c7b48f2423c3c969c232011831f1a39092690e0090f3079ead0e11a10465f65c65b32fee868b694cde0c03010000000000000000\n00209922989c662610b1c67c35ca3cc4dc2091bfe351dd28e67903010000000000000000303656c085e934687a2076f3d603d09c67a0b02452c95455e86beac09fc43dd00dbdc969f53001183f20a0d591690e00da95786d27f9764988f0f12461fa45d4822535cac95a0e000000000000000000\n004009278d217ea8cb11ad32f56ca9031d91ce979efa17ddbeff6c000000000000000000950a478142d5d26f0d6092986489b5f7f2a7b62290d1ba0866ea545fefaf6015f8b4c969083001183d6c077a90690e00989c662610b1c67c35ca3cc4dc2091bfe351dd28e67903010000000000000000\n00400b2046ec1cf574ed6b9538b552379e8e28d5af9f638f7f40320000000000000000005320793f292fd57696324a8de1c76794255cbef5fbcf51a82a8b173645113b6aa9afc96985300118a5bf81458f690e008d217ea8cb11ad32f56ca9031d91ce979efa17ddbeff6c000000000000000000\n0000003a7cd9e7b4ebc972176b76a38b28fc1945c9bfd9b9a0f8a60000000000000000006820e6e8d6edada9803d3fdf907951fbf67a9355dadb84e181099790773023d4e0aec9696430011849f488b28e690e0046ec1cf574ed6b9538b552379e8e28d5af9f638f7f4032000000000000000000\n0040c52a2706913f4cd37b5169f80a551d2170284ea64eae134c6b00000000000000000010306233656ea1701d7e8a4d03d603c392f8f84fca5fc57f84d4e7a9ac3782da20acc9691e3101183975af808d690e007cd9e7b4ebc972176b76a38b28fc1945c9bfd9b9a0f8a6000000000000000000\n00a09d2549145736a8cc741491d077615d78a216fd0c8b075cecf7000000000000000000c70f1e9feee885ebf10b4a2fa4d0d913c5f55548c192fecdef8c7e5b8b4269481bacc9695a2f0118311021cc8c690e002706913f4cd37b5169f80a551d2170284ea64eae134c6b000000000000000000\n00801c3f97dcac3fa69ce164f4e998612128950e1aec84b2e8664e0000000000000000009dda8283d992bcc827b20a47de72950b801a4ce48ddd0d258b5c883da804e50c1aa4c96905300118b42e96e28b690e0049145736a8cc741491d077615d78a216fd0c8b075cecf7000000000000000000\n00400120a19408ef8355f5a39dbd8d365d316fef47b35039260f16000000000000000000f11a1f7f431dbfbdee643a41ebb34e298ec21def3d9c6d0669a3399096ece6b1e6a3c969bf300118696f50bc8a690e0097dcac3fa69ce164f4e998612128950e1aec84b2e8664e000000000000000000\n00c0a532f14acf5d67a87fba8dff71bb6f025614c9c74ad49b98d500000000000000000058ec3f19b90e4e895a54b27d3f3d54afb22ab931862c1d8e18be8f32effb9ca8e3a3c9696c31011898c2d43f89690e00a19408ef8355f5a39dbd8d365d316fef47b35039260f16000000000000000000\n0000003810119a534645ea403d24fa189ad0073e135927d0fe1d32000000000000000000c740456017c0c62f75466dc989601824e98e79fbc154933ac77e24a0ed9739e4b5a3c969013001183d95858488690e00f14acf5d67a87fba8dff71bb6f025614c9c74ad49b98d5000000000000000000\n0000a72b298d8ee76becbc3c24cbb2ec3c1a88ed2ceccf65c0f3550000000000000000005a18932987b5f75e17c85aebcf54a7ac99a290cb21c1b690829401123d063bc2d49cc969563001187009f82987690e0010119a534645ea403d24fa189ad0073e135927d0fe1d32000000000000000000\n00e09328e46d392662a6e349fca522519d1134882b22e0bf04ce6e00000000000000000082e781a8a6714f61b00293c8404b1fecde515c78c5ee829013d6862eb20648bf8a9bc969322e011819ebb77586690e00298d8ee76becbc3c24cbb2ec3c1a88ed2ceccf65c0f355000000000000000000\n0060a729659e7d24fbe360071dbafe0a10bbed3e05e403cf59717e0000000000000000002cc885078a45b080976deb28507f32173a63ccc7593415e715617c12bc4e094c5092c969982e01183e82180385690e00e46d392662a6e349fca522519d1134882b22e0bf04ce6e000000000000000000\n00800020c78b480e811e0db3deab57f8e22aaa6675db5511f6eb67000000000000000000539a9379269665cbf7df3c0b166dbb7f431bcf3acc9c7abb4ed8538cc051e52e4291c969ee2e0118f95f94d484690e00659e7d24fbe360071dbafe0a10bbed3e05e403cf59717e000000000000000000\n00e0f6317b2a912870d90483f464c9d7f92cdf5ff5a338fc98076300000000000000000092ebf09709c614ffc53839cc67e82eab962e2caf6871cc28c487c52d6383992dfe8fc969e52d01185af165d383690e00c78b480e811e0db3deab57f8e22aaa6675db5511f6eb67000000000000000000\n00000022bb1b22a226eabc79f1f84e3bf1279af0e781bb5e4ab5060000000000000000001d3dba88eec68c923c77b1f03c8b93398170fb15657762ebe328be7a353587d8508ac9698d2e011802c7638b82690e007b2a912870d90483f464c9d7f92cdf5ff5a338fc980763000000000000000000\n00000024ba5b4e885880e993bf1d77a72fc3e0b3049fd1c3a94d5e0000000000000000004a03f8bb387fcdde396c525169920dc9fc0e603f7c090623a414c7a799794c1f178ac9690d2f0118ad22da2a81690e00bb1b22a226eabc79f1f84e3bf1279af0e781bb5e4ab506000000000000000000\n00404a2509698c5eb294921cc464a7527edac22b40f6e2692e4f6d0000000000000000003b8390081848b739a93b8302ab6a9f2ebce0597016bd94d1f100ddf7ce87851d5b89c969ee2e01189904da8880690e00ba5b4e885880e993bf1d77a72fc3e0b3049fd1c3a94d5e000000000000000000\n002068394b8944e32e4b5b3b7e26f71c0852506df4141bae3d3f0a0100000000000000005df1edca40364c9c744f77df634e34917b9be4b216409f695215046f59b25bb39d86c969a22f01180e4209857f690e0009698c5eb294921cc464a7527edac22b40f6e2692e4f6d000000000000000000\n0040a4221226fbf4c794f321267d45c4180bfa2bc601cde474d9c000000000000000000075b080ab409de05c5383d2570cab7e0313a76740c9f11c121ad10dc2d87ca1758886c9694430011802387ab87e690e004b8944e32e4b5b3b7e26f71c0852506df4141bae3d3f0a010000000000000000\n00808b2672ada654d9ecddcaf97cad308defd1b3cacbbc187850c10000000000000000001665b176f598f8779d506385f33debd843bd32c71eef7d57c4bd6fbce9a3f9913886c9693b30011862bda96b7d690e001226fbf4c794f321267d45c4180bfa2bc601cde474d9c0000000000000000000\n00e0772e59c256014e0eb54e812d99167d8d4604aaffe9dd4e2788000000000000000000dd810bee91098cb99151ee3b0acb5ffd1768b9ffced7d481edaa8cefde570739c183c96947300118c8a06f0e7c690e0072ada654d9ecddcaf97cad308defd1b3cacbbc187850c1000000000000000000\n000000280c4fc4038bf0dfdd197158c1bcaaceb57c8dd33373e71201000000000000000013b69ba9273d4d3ac4deaae9d041b0707c43fe43c856a318510cb103a364a2b09481c969ca3001181d52a4417b690e0059c256014e0eb54e812d99167d8d4604aaffe9dd4e2788000000000000000000\n00000026d5c44c70988a5ee4a4893b27d131eeb73f3ccf9f20b3c2000000000000000000cc04c1db27f90fc57dc5b63c5ce9fc12f802638d17f4af1b59ba33470059814fe080c969822d0118e563d3c27a690e000c4fc4038bf0dfdd197158c1bcaaceb57c8dd33373e712010000000000000000\n008005205c0abfa8da79e82052bcaf667e8264bac7e85e99d57f56000000000000000000b3d9b09c5535f3656bf4cb95ecd81d60cafcc03a766dc1626a31a71292b1054af773c969d82d0118191e1f5379690e00d5c44c70988a5ee4a4893b27d131eeb73f3ccf9f20b3c2000000000000000000\n00400220a2beec4171580625f043f0322d73d91a076cb1749b5a0e0000000000000000009af6c4eff30c46f921fac97ef6f77ceac4a3b6874e8a0ec9d347fa5c1056b8d3b472c969242e0118c5e029f478690e005c0abfa8da79e82052bcaf667e8264bac7e85e99d57f56000000000000000000\n0000c02076375309ea3eb20909854c8ab2c1eb0b0161398b9e7b01010000000000000000c83aef8a5423c364d2b7522caef9a6f2402ff8cee09ca85419e6cc964a4d008b5371c969d92e01188062eb9077690e00a2beec4171580625f043f0322d73d91a076cb1749b5a0e000000000000000000\n00c0852f159cdd2ecb6bbae359800ed36fb2b781bd9dc203416b1d000000000000000000920d1f24b5bcd9d7b0498335650cce9e246438fa65474a2a644af1eccbb299124371c969702f0118949913d376690e0076375309ea3eb20909854c8ab2c1eb0b0161398b9e7b01010000000000000000\n00600420281100112ac66f09e13111c57ecb3fdce119ba71cc681800000000000000000037dfcb3ec0cc84ff02d21d80af7ecf8222a8ffa879cb7ff11a3b068e70dcde74d070c9695b2f01189ab9962b75690e00159cdd2ecb6bbae359800ed36fb2b781bd9dc203416b1d000000000000000000\n00c0382b4859bf626ad11b0520016f0606f2eef4f74e38fc9233bc00000000000000000096a0f424a661e09ab17a65206e768d61cf3e534e24dbcb9f30252196561586e8356ec969fc2b0118493d2eaf74690e00281100112ac66f09e13111c57ecb3fdce119ba71cc6818000000000000000000\n00000036e64e683caa90abc10958886acf66a62af808aa5fbd441f010000000000000000caefa7dbcdc27e000aeb69220d992df2981e9ce219d9e182cec39f60e5939bfbf360c969d62b011857cade8c73690e004859bf626ad11b0520016f0606f2eef4f74e38fc9233bc000000000000000000\n0040402206dfad96ce566356e639022912f8f49128ffaf0ea209f40000000000000000003b3383a166801f60604f437781c505e0af8e63c9bf53e08bc3e612be03cee5f8225ec9694c2c011872857f7b72690e00e64e683caa90abc10958886acf66a62af808aa5fbd441f010000000000000000\n00c0a525e03b36ce20686567a5825d6d1c0ef6efa0dd0f9b95908e00000000000000000027d58b3698e7dd626fe34d23185a6250c2e179af98ac23b8ea6ea0f224736ce94c5dc969222a011844608f4771690e0006dfad96ce566356e639022912f8f49128ffaf0ea209f4000000000000000000\n00c00020362929b6e924ee5b9ebb1393c66efb7b044b5ea65266bf000000000000000000fb342452bb7289a3fa6bb96e9934fa90c8b6e74e2673169859d261ee9485bdcee653c9699e290118b180e77270690e00e03b36ce20686567a5825d6d1c0ef6efa0dd0f9b95908e000000000000000000\n00008020c6372cb83bcb8897daac37a24c39804fd2965a4a02c2d9000000000000000000ba9e00b06563743c632f6cdee6da2974477615618403bd184dca8230a8ac4b8cdc4fc969ce290118252d533c6f690e00362929b6e924ee5b9ebb1393c66efb7b044b5ea65266bf000000000000000000\n0000042093f94fe6f0eb88fa45b77f735c2624c81baf608413bc61000000000000000000cf02559f7433e593f19184819d6da6463c390c9acbe789fd773d3b0ed07bdfa2214ec9693428011814b7b11a6e690e00c6372cb83bcb8897daac37a24c39804fd2965a4a02c2d9000000000000000000\n00e0002013eebae45115a4dea63b448a46c0d99bedb645dacd16d0000000000000000000c25bcf961c139082a390fe0b79ff1017cbf82af5a08c743dd8a09e9ca26081298946c969332601181da7c1366d690e0093f94fe6f0eb88fa45b77f735c2624c81baf608413bc61000000000000000000\n0000a0205dd158fd0b41905b39c2151309d93e87ef46149e3d9d21010000000000000000bf458e620cd5601a8466b7c9fb45c16ae602cff1cf31f7e58a56ff70402c77c3933dc969ea260118115b740e6c690e0013eebae45115a4dea63b448a46c0d99bedb645dacd16d0000000000000000000\n00e0ff3f358b7c781b64532163815b3ffe2e5121492d8ff6ca910901000000000000000024a02b03c64f7deda1761e4d4d93b271f86b5e0f6fc81e8d2a5f0e59ca0d06ef973dc96936260118c261d06b6b690e005dd158fd0b41905b39c2151309d93e87ef46149e3d9d21010000000000000000\n00000026698e77fcd1d6a8e8f6f3222f0d6783a11149320aec4a120100000000000000003d8766704d2a7c7f3fb6bce6f3b08400ae87957b1ccf9bc7b0f644c2e8710fe1ef38c969a9260118243491ad6a690e00358b7c781b64532163815b3ffe2e5121492d8ff6ca9109010000000000000000\n0000c020dcfb59e8df172c0e3eeea46ec6828576d8e66a37eee3460000000000000000000279ef77cbaa6d7a283273488fa7aaea5ced8fd42d76892ae242d4ba3fe257eb1238c969022601183c40c9db69690e00698e77fcd1d6a8e8f6f3222f0d6783a11149320aec4a12010000000000000000\n00a0bd22c8e8d116998a05ac6d44f67b7122b645aadfd9bcb86a5100000000000000000047e20cc7c2a8b4c67ef71657446b5bc6c186a3fe51db16a42786600d653355819133c9691125011888030a1868690e00dcfb59e8df172c0e3eeea46ec6828576d8e66a37eee346000000000000000000\n00c08922cff193a8b12d1ac022bf0563968b0cadb59c497079074b00000000000000000054dda700b3a9b054ff265efe064da6fff699df5ecb85822921b895dac8d6faed162ec969ab2501185cc829b467690e00c8e8d116998a05ac6d44f67b7122b645aadfd9bcb86a51000000000000000000\n00a07332a7af62d6b31d5d823af3a987229bab602aee5391d7c77300000000000000000021b6d077e1c9c7d9936a02fbfb4e45af3262b8dad7c89cb5a4f3d220206f2b6dc02dc969472601187f38ca1f66690e00cff193a8b12d1ac022bf0563968b0cadb59c497079074b000000000000000000\n00808a2822d18ec4d90b2f2265694a95ad96095eebbfa9181b5b930000000000000000000780d943108759b7711b274c7be4c4dbe976fe04dcf6f26fbb78e32a768d38c16d2dc969352601181764300465690e00a7af62d6b31d5d823af3a987229bab602aee5391d7c773000000000000000000\n000060204e22c4b7e9d296a4b4d8a25f9992146604027beabddea80000000000000000008a704c9c923d9ebe7382c5ecf588278fe25f51890427063044e7ae7dd6b833ded82ac969b726011847a865f364690e0022d18ec4d90b2f2265694a95ad96095eebbfa9181b5b93000000000000000000\n006046224ac4511706718f0424f504bbaaa2664e1f7644b2bc37dd000000000000000000321674d41f623d6e9c3d061390123c55bdbaac9989c1475e89ddbf0329a741682f2ac969402701181ec4786a63690e004e22c4b7e9d296a4b4d8a25f9992146604027beabddea8000000000000000000\n0000722a4e4f3e85806f5718ce15fe21df308ca04cb194df47430b000000000000000000e0c24ae528f71b403ec84a66406983b1c09d4223d6e7e2b25b158cd2bfddff969a29c9693b2601185cde395862690e004ac4511706718f0424f504bbaaa2664e1f7644b2bc37dd000000000000000000\n0000002a9c5fc862046e9240864276e5994c4deb5fcbe3d2bcaab80000000000000000004c07c4becac0f0e80dd1c178561d0b973687cb827372f5a2d304c5ae7b8c58d8e423c969d32601184d42a89961690e004e4f3e85806f5718ce15fe21df308ca04cb194df47430b000000000000000000\n0040af3070e10dd61ef26ab51e8d4bf765f37a64a700459df77c8a000000000000000000dcdc2d7fdfee17504750651a892b0c557c96003c41e4ec06761f2fa56ae7897e8623c9699c2601185a571ae660690e009c5fc862046e9240864276e5994c4deb5fcbe3d2bcaab8000000000000000000\n0000003ecc7f23840551f5ab33eddbd230199d9581fe465bdb6c6b00000000000000000013fb1d46d4b683ddf88c40fcb98b5c58e35d76ed6b9daaf725f3b8b21df4d5257420c9691a270118f34ff20c5f690e0070e10dd61ef26ab51e8d4bf765f37a64a700459df77c8a000000000000000000\n0060a320eddfe25dcadff05dc2984204c5b28af43234cccdaac611000000000000000000c1740533667c071b9df661ab9bf00afe5306eb434599039dd00389e5254f28ddbd1fc9699d27011800e91e6a5e690e00cc7f23840551f5ab33eddbd230199d9581fe465bdb6c6b000000000000000000\n0040e2205b5e54b46bae547776c28dbe7c3ede6cfd91a79b745b8800000000000000000084e31da9613ee7fb3252c6addeac02214ed5248532ac855f5a7f990ad6e2a3ed161fc969da2701183af31cb45d690e00eddfe25dcadff05dc2984204c5b28af43234cccdaac611000000000000000000\n00800e20912b0d89ef302b386f5226ffd068171b04d9a19eeab621010000000000000000c3548cbe1edbaeacaaeb87aeb7be46120ca49d8f5967109c51254456d11317f7891dc969842701185d57b0855c690e005b5e54b46bae547776c28dbe7c3ede6cfd91a79b745b88000000000000000000\n"
  },
  {
    "path": "bitcoincashkit/src/main/resources/TestNetBitcoinCash-bip44.checkpoint",
    "content": "02000000420ad5d432dfa48f61fa5b0b6b65f4587291761d56b8d271cad9bb010000000031d3e2406e954acbf9134d2420ddb8f607cb29dc7cdbd9153af5751ad5c7f8ff444d1153ac02061cb3e8dba3370b03003c0205fa47283109a3272a9c305c28329bc6e7bc12e31598c015d60500000000\n02000000bed0b6cb5da8dce215d80720905bbb6cb19b8418f82df68d3bac3803000000002b7f607d44ef06ad1f96c8c042bb4c60694891a6bed5c3a6d708369aa0d3811c3a4d1153ac02061c48168705360b0300420ad5d432dfa48f61fa5b0b6b65f4587291761d56b8d271cad9bb0100000000\n02000000af6a359719e05a5715138aa42e6a67aca19c1ae26e6a7c66ce42cb0000000000cebc8e0f1d52ec0e55077decf69ae44dbc65820694d29a7227b17caa9ab5b5ee294d1153ac02061cc2a6875e350b0300bed0b6cb5da8dce215d80720905bbb6cb19b8418f82df68d3bac380300000000\n02000000d51bb879dda44622477ffcac907e44f3d09595fac165eb9f056cd903000000007dc5bec62bd9404560a021c5b77ff78178eec09c12da1d226d4ed9e276e25a23244d1153ac02061c3892904e340b0300af6a359719e05a5715138aa42e6a67aca19c1ae26e6a7c66ce42cb0000000000\n02000000a46f94687fb64c70e84661a57db04a4e09c32bc555b60e8a145c8f05000000005a4d29814979830a2a40b636c6723796b60b18a232f6c6296f9b6e0b0e2c793e174d1153ac02061ce78fa008330b0300d51bb879dda44622477ffcac907e44f3d09595fac165eb9f056cd90300000000\n02000000d4f4c2e43c564cbba9de9abe5b9cde022ac2ee30db331458fc2c9502000000005df7d4998b04d50745ea21eed93901a993e3550809a66a53b5479e9bda7febfa054d1153ac02061c4313b257320b0300a46f94687fb64c70e84661a57db04a4e09c32bc555b60e8a145c8f0500000000\n020000002ed46bc395a09a8b034c926950f8679c19338f198203598bd30fdf04000000001abc28e9e4f36ccae15bfd0bad028f145f290dd80bf58b91fbd674bd6357b332fb4c1153ac02061c4742d3ef310b0300d4f4c2e43c564cbba9de9abe5b9cde022ac2ee30db331458fc2c950200000000\n020000006cd566a53e2ada5163f46871c6103380512320b905f7363ceb4e9004000000009c371894786679561f6d891b6e725120191d190bd9d1bc59dfbfc1f94189c277f34c1153ac02061ca9af858c300b03002ed46bc395a09a8b034c926950f8679c19338f198203598bd30fdf0400000000\n0200000070361e684698c0858b7c2ec5346be8238b66cf7e08ea6bb2fd06a502000000000f0452bf84f8023fee8cda28a06f1edab760ee4fb57ec26b4f344f15c33a77d9ec4c1153ac02061c173f247d2f0b03006cd566a53e2ada5163f46871c6103380512320b905f7363ceb4e900400000000\n020000009605e44b7924bc2d3db9e7beff7ed0594d6ee79dfc95d7cc80a1520400000000e0008e3ca601062432b1c9e04930ff8b6c43b6ef38bc9ee3bf021a943ee22a6cde4c1153ac02061c5d11a3b02e0b030070361e684698c0858b7c2ec5346be8238b66cf7e08ea6bb2fd06a50200000000\n02000000a462900dc91079e7da04bae7f81f05d36199e548833c306caf31ff0300000000246914c192775e6318b5a578de640b68d43deea82a97db37a11ecac86d221009d84c1153ac02061ca1bb08bd2d0b03009605e44b7924bc2d3db9e7beff7ed0594d6ee79dfc95d7cc80a1520400000000\n02000000a266e7267075eda858897c09a1183586bec11526270ade4f4d299002000000008af392f52425c25f7abf8150ed2965f4bec3d3a3cc6c518905d7e8fb09dbf510d34c1153ac02061c4712c7a62c0b0300a462900dc91079e7da04bae7f81f05d36199e548833c306caf31ff0300000000\n02000000b2f4e9fff46b4a8d9cfc0ab633a801745c506cafed94a7669773d20000000000b5e18120fa7b2d6d74a7752f4c137651e0a0d9de133a9f07c0719f6e0a9d1949cd4c1153ac02061c574e76e12b0b0300a266e7267075eda858897c09a1183586bec11526270ade4f4d29900200000000\n02000000e63b556f7d28ed2b44e3ab175accc1878317c6e98bd8fbff70640e050000000081c1c5a8c313362b8171c0bc13263c30159b54baae97524aa722d21e67162f2fc74c1153ac02061c66935a282a0b0300b2f4e9fff46b4a8d9cfc0ab633a801745c506cafed94a7669773d20000000000\n02000000b377dc6ed586b0cb3d0b06c2af258cec7df9e826fb39b6e99f625a0200000000a548db3c14d3eaedf24fbc204b57922ec5274b45a08dbf3a973b15dfddc70af8bb4c1153ac02061c166bfae7290b0300e63b556f7d28ed2b44e3ab175accc1878317c6e98bd8fbff70640e0500000000\n020000000f183315940e93ff1476b3fe618cbf48d8f190bc826f3ab3da7a760100000000fccbf3dd196a761d21d0a68e5b41c9afc10604f6b537e93c8ca37238c9f3eae7b44c1153ac02061c7266300d280b0300b377dc6ed586b0cb3d0b06c2af258cec7df9e826fb39b6e99f625a0200000000\n0200000099dff232d4ce1de8057f8898bd4b740d3b8d0f77f2fd571c6e334b0500000000a99425697f55c5c114b1eed2452d51646923b01fd313dccd3ec4528001e88b6eaf4c1153ac02061cbf05bf99270b03000f183315940e93ff1476b3fe618cbf48d8f190bc826f3ab3da7a760100000000\n02000000fe3f131d633fd10057298eb603607f5ddcf045874afd9e2c75f497020000000089ca2b380865f57e72fe71dcfefbf68565aeef6914b60452f60ddbdf046de25faa4c1153ac02061cf2d85fe9260b030099dff232d4ce1de8057f8898bd4b740d3b8d0f77f2fd571c6e334b0500000000\n0200000013e039297871ccaaae40f4cecf8266bb8e9570d4f56864656815b80300000000dd9c94d54ed4e65289a867c129040240e4e2771ae7f8ce13069933a9c2b55ee6a44c1153ac02061cd3739bc9250b0300fe3f131d633fd10057298eb603607f5ddcf045874afd9e2c75f4970200000000\n02000000005975c7805234e00a2313e479ac3ca46d7dbac3eb0bc8433df15f01000000009d265c29b1c066d62ebb2147fb019e13a3a4d2786395397385e41c83925556b6a14c1153ac02061c1432e4f4240b030013e039297871ccaaae40f4cecf8266bb8e9570d4f56864656815b80300000000\n0200000068f841818be2d3709222b9254c825a79bbad91691dcf9070057bd20100000000c3d70a8ed7cb613e370d3429fd99507d9d197f2f3a167e207d5c47211362e2c09b4c1153ac02061c832ad33a230b0300005975c7805234e00a2313e479ac3ca46d7dbac3eb0bc8433df15f0100000000\n020000009557a1d63f67d6babfc253672339d98ea9a472f62672114488c8870500000000e8d58136fd1e6860f09b9297a88c3bd7dbeb1746cee8dfb71c362b9e6ce79138944c1153ac02061cc8158561220b030068f841818be2d3709222b9254c825a79bbad91691dcf9070057bd20100000000\n020000003a3c82afa1c0bd0bc05775b578e28f40c6ece502e844706682545b030000000091f371e37146aeaca5a1e0d2a5decbd61f917712ae2a2119fe81c3d3e7425ef1904c1153ac02061c0f00dd01210b03009557a1d63f67d6babfc253672339d98ea9a472f62672114488c8870500000000\n02000000e14ef72a8aea472614f3ae1306823cf0b325813f9f95987a47a7680400000000db5ea894cf5eab01dedf900c6b0387de31125fbe57df99c59d02637b71f42ecf8c4c1153ac02061cbaca34d2200b03003a3c82afa1c0bd0bc05775b578e28f40c6ece502e844706682545b0300000000\n0200000010ba8a1d57c99940327489d731248ef96850a0e25a6deec897ef7502000000006331673396115467ce6d1c3891c7926c68f70b091e092d24d058143a8d3d7273844c1153ac02061ca4a8d43a1f0b0300e14ef72a8aea472614f3ae1306823cf0b325813f9f95987a47a7680400000000\n02000000497def0e8859e314f7d0905fde763ad7f3888aad922d856674d57c05000000005ee1c0ad6dccf278cd96dc2cb3504f785686c1ea2ecca85f273c3b8f7a602c79694c1153ac02061c5cb6363c1e0b030010ba8a1d57c99940327489d731248ef96850a0e25a6deec897ef750200000000\n02000000b2665f20f90efeb3aacd2dc1b581fbb1bf7ee9f31590e3b51a6d0c00000000005f83b6a96f27609b6b98c11afbff350f83e533b1964bc70bd14027aab3127834314c1153ac02061cafb7a87d1d0b0300497def0e8859e314f7d0905fde763ad7f3888aad922d856674d57c0500000000\n02000000fcd266090fff4266d6e0759b5bacbbf971c07b61ec90d2d72903c60100000000afd22e7737e0eedf237d5e29da7fa8184a9883404a54aa16dc01f6c1d376124d544c1153ac02061cb75bcc9f1c0b0300b2665f20f90efeb3aacd2dc1b581fbb1bf7ee9f31590e3b51a6d0c0000000000\n02000000d017b6b0019acccb2102ff9a6cee8977c6989cb42cdabaccc1366403000000008d3f96674b1e20d8067027a71b4c136ba2959ce4bb444ee2d7b2bb8c9110d4e7404c1153ac02061ceba14e841b0b0300fcd266090fff4266d6e0759b5bacbbf971c07b61ec90d2d72903c60100000000\n020000004f6c33cae0a3b1aca6fbdb2a03b501dfc806ff072fbd2f65a7fdbd0100000000c098ca28eda4b933b287683ad38673544337837dfc352ef3d3ab72c6e05a75ee234c1153ac02061ca9763c611a0b0300d017b6b0019acccb2102ff9a6cee8977c6989cb42cdabaccc136640300000000\n02000000fa4d0441e31932a3ae75334beb88f57dfc269ee838b922d8b9e16d01000000003ce85bdf82f02fb7e851e0b663c1c25df820e05975ae3b5beaf17ab4a9e0e80eef4b1153ac02061ce296a3fa190b03004f6c33cae0a3b1aca6fbdb2a03b501dfc806ff072fbd2f65a7fdbd0100000000\n0200000042b12eeebba4e237177181ea6b224b0d99b41a1a0eb58d69601b1001000000004995bc1f26218cfcfe59fdb8542ef5aed8874c9e661eb4d69162e1bf2cd39c2e624b1153ac02061c5b366525180b0300fa4d0441e31932a3ae75334beb88f57dfc269ee838b922d8b9e16d0100000000\n02000000038bc3e0b917bb7904ae3cc885c7319fdc39b798a09667beabdac60400000000375d6d578405acba5581101377e0b40da9ad500d58d8e63b66f39345336b2a00564b1153ac02061cbbd9a280170b030042b12eeebba4e237177181ea6b224b0d99b41a1a0eb58d69601b100100000000\n0200000067e715e9adda369988f53551c853bdf6e1856d39a202c055326d010300000000bd47a543f8e6abfe2ccd008133fd77c4b09fd612a7e778bbf9f510b584cc61190e4b1153ac02061c4bcdd106160b0300038bc3e0b917bb7904ae3cc885c7319fdc39b798a09667beabdac60400000000\n02000000a932d3f845fc285e7f9752459ff1120d2ea40435a26c551e3e449502000000008d9b9bc7865c84b7fbc8797fb443b4308144eef8741c2484a222988f232be5d1444a1153ac02061c79ec5745150b030067e715e9adda369988f53551c853bdf6e1856d39a202c055326d010300000000\n02000000aadb6fe4be6d5de511d3b560b06ce13af8177105ddbfd01d7775600500000000183d45be68725b5dd1e53895bb7eb1019e8c25a5abb61b05450f1237d2fccb165e4a1153ac02061ca46351cd140b0300a932d3f845fc285e7f9752459ff1120d2ea40435a26c551e3e44950200000000\n02000000cb8d219b3cc17d24b350c8d69138f42ea997e79d0e6aacffa537ee0200000000b443d514354e340cf0c9ffec1935c565cef89a49fc7848b7a59c5f379f53f0774b4a1153ac02061ca5b217fb130b0300aadb6fe4be6d5de511d3b560b06ce13af8177105ddbfd01d7775600500000000\n02000000821e772f11b097d2f781b58900c7f7525d1c77bf12f3180b10b5d003000000008275b0f861bf8fdaf9c1cc1fe529fa11c0932413d127ca8268c899f737ca582d364a1153ac02061c2c5c0d18120b0300cb8d219b3cc17d24b350c8d69138f42ea997e79d0e6aacffa537ee0200000000\n020000006222b9c99f08b6947464ce617f845b03c52517adc2da9dbbeb6f2e0100000000dee8069db8c146081bcbb52eadaa9720f3041d56e7914557f9c53274f74399b1064a1153ac02061c137d19bc110b0300821e772f11b097d2f781b58900c7f7525d1c77bf12f3180b10b5d00300000000\n020000003c55fcde162e3f7a9ac2f4e401b5b3e14c89a2a347e0d0e1c9669f05000000002c2697645581d00006caeddb4cac857259a74ace02b8377729d2d22ce28cc989a4491153ac02061cf3c87e27100b03006222b9c99f08b6947464ce617f845b03c52517adc2da9dbbeb6f2e0100000000\n02000000bbce18a1c5b6c13acf8f032466cbd0ddcb477097ce59f14c2df9e20000000000a632f1256ba784578fc431c79cc76cecee33ece48c3e1c26aab5e1d5160e3ebe85491153ac02061c8ab84f1f0f0b03003c55fcde162e3f7a9ac2f4e401b5b3e14c89a2a347e0d0e1c9669f0500000000\n0200000038a87b508346358907e79245f9d2558d574dc98adb814940e3bbec04000000007613ebe4ba5fa0f1998de982425d0e3027f741c9a86164c33474fca8a0e5615177491153ac02061c0729ca190e0b0300bbce18a1c5b6c13acf8f032466cbd0ddcb477097ce59f14c2df9e20000000000\n0200000038274e05ac9f07b81123e904a69fe55006fd828eb939eff152a4de0100000000c281b9c080273f21a426584f735afbd1748effce10761d793c8ebbf7008725c0da551153ac02061c3696e1210d0b030038a87b508346358907e79245f9d2558d574dc98adb814940e3bbec0400000000\n"
  },
  {
    "path": "bitcoincashkit/src/main/resources/TestNetBitcoinCash.checkpoint",
    "content": "004093243e7e074cda6e106eb27af345c7a6da4bedb1d9d7d7e9f2631ba301000000000071b0865b1bd59a7eda02e2ad2926db5f90a077e34896c46d441940427e7c9a8e66fb1567c8df011b7cb0e69b9fd8180012b24547b805dc4017f3cbe0f114cc25029b5aab72ab14c0fc48010000000000\n00a0e7274c2644d8ff4bcfbf4bccba2295d7b3f258cfefff6f8052936238000000000000b8332c3bef6e3fa9b9a98f74c7a5ede8048a4bb74add966e387e00b49b5dd3ac66fb15676f1a021b1f7b304e9ed818003e7e074cda6e106eb27af345c7a6da4bedb1d9d7d7e9f2631ba3010000000000\n00409e200bbd95b80c6bfdce7704a4305b92ed0857f5e7bff530638430bb010000000000183077cfc23b97ec9c19d8e2cdb46fde93759220a0d1294da15f917cc966346365fb1567555c021b870b99799dd818004c2644d8ff4bcfbf4bccba2295d7b3f258cfefff6f8052936238000000000000\n00e05622ae207c44af6cee8127f0cc41e64cfa599be79db37b5907e9c2cc020000000000b538fe1eb3b1cbe88024fcfe037bf741f3f698d578f5be38bba74c8bf12d252c65fb156723a6021b372aeed69cd818000bbd95b80c6bfdce7704a4305b92ed0857f5e7bff530638430bb010000000000\n00e015213c4094614092bc01c2af21a544c4502f5f8cf61f2195f68c31590100000000001653a38728a0feb3fd9adf67ab1c720d43bd6518290e891c0ea87dc07d53380464fb15670df9021b3e1d53a09bd81800ae207c44af6cee8127f0cc41e64cfa599be79db37b5907e9c2cc020000000000\n002029251a9a204b1cdf069324863b1913800971a381510dca007853402401000000000091294a3307f6cb8f8af14c027a93037bb5586eb08d8ed2da1e02a7a1765ef98e63fb15675856031b8a8e311f9ad818003c4094614092bc01c2af21a544c4502f5f8cf61f2195f68c3159010000000000\n00004d242942e04615ad7323e046933e50054d5779b3da44e25ab7a26876000000000000b606a2b0cf7b9e4062929f88a417475c8a859d54acca0145b5c85e80b7e0ffc063fb1567d6be031b8c347aa899d818001a9a204b1cdf069324863b1913800971a381510dca0078534024010000000000\n0080d4211a8fa1db28d4c621ae30a9c1540fc0aa910c8f1c2175bc94bea9010000000000fdb9b4a61aa2b68120a28178c1d1868dc119163fd3eb9a2cd52b252fa0a71c6462fb15673f34041b1a72ffe998d818002942e04615ad7323e046933e50054d5779b3da44e25ab7a26876000000000000\n0020c021a420d5675845e048a801c95c22c21b7d79c8f0bcdd89def82e320500000000002009946c1cb04aa95158e6b58f76cadb7e9bdac57e91e29bf51546d158ba06f062fb1567bfb7041b548e323297d818001a8fa1db28d4c621ae30a9c1540fc0aa910c8f1c2175bc94bea9010000000000\n0080b1212c03dc9df67e5e2e9392b3ba1de938b48846615755abf45c71cf050000000000d2b95ae67e6388a209e76b8d873789f4fdcc62ffa7d45146b4dee0c8824fd41261fb15677a4b051b0e017f8996d81800a420d5675845e048a801c95c22c21b7d79c8f0bcdd89def82e32050000000000\n00a01b211cd27fc8e11b9398746082e89518d74064741064fbf4ad1381cd030000000000a0e3fb7d7c9586667070742c89e1dc07a519117f09be8decce118cfbb39f70a761fb15673af1051b54c7b54295d818002c03dc9df67e5e2e9392b3ba1de938b48846615755abf45c71cf050000000000\n00e0332707c877145f4bd0833c742694f6a64f67d89aad96b7bf665be9ab0400000000006affe66e60501c2d8dc6bb8f0b551e9689f125ce55b0442b4822f84ff9eabe4a60fb1567b9ab061b6390422494d818001cd27fc8e11b9398746082e89518d74064741064fbf4ad1381cd030000000000\n00e00020be985044803398898a15ca61be3d40358a287447687b0831142e030000000000bd8ddf2e019ae275195bf458b6ac06dfebbbb13747d611784b836d7738832dc460fb1567907c071b93999c7993d8180007c877145f4bd0833c742694f6a64f67d89aad96b7bf665be9ab040000000000\n00e0af274bd83e94909a69c3546ca4285f13aaeceebd11b6d2bb7ecb63410800000000002654714cf775b2cd9f160eff28d83f249152d207d94c2f3e60f1a1baae3710585ffb15673f67081b362aec6b92d81800be985044803398898a15ca61be3d40358a287447687b0831142e030000000000\n00604621f70ae82d2564f5d38b1bb164489399964b8c5c088a84509e90560300000000008daccd62f889d64528b279eda9def34e5aa305863df532601cb8b6c6778132455ffb15678e6e091b055f753491d818004bd83e94909a69c3546ca4285f13aaeceebd11b6d2bb7ecb6341080000000000\n0020fb2290468b43228ed10ba88068314e2c7eb67646326741fa0a4bd0a50400000000004bf3197f68ac29fd0de315594745b9c3b2b1b0c5e0b1e39be5762b7365704dcf5ffb15676d950a1b9d13d68090d81800f70ae82d2564f5d38b1bb164489399964b8c5c088a84509e9056030000000000\n00603b20c45feac2f6753db94953fdf77187018e5d51f429e01891b3b3b80000000000000c50bf2b0d9e53ab37f2cf69d1ef5fbf834ba06da8882a3de849ceb4d55d561c5efb1567ace00b1b838965c38fd8180090468b43228ed10ba88068314e2c7eb67646326741fa0a4bd0a5040000000000\n00204b241233f1fcf274d1b637a3429ad4fd106a8e87f85901ffa8ba0f760c000000000082e7bc538aa9535d2067615672893b44d13d5b4f642fdd9edf135507cc8755fe5dfb156772550d1b26abd10a8ed81800c45feac2f6753db94953fdf77187018e5d51f429e01891b3b3b8000000000000\n00c00820184160c27f0b4517c85f1299a58631679dae710d3dcbf0e8b6b2010000000000f65822cd32d08233883fc47687a3eded05491bb3417c97656d5233a52cd89f825dfb1567e1f60e1b22ea68cb8dd818001233f1fcf274d1b637a3429ad4fd106a8e87f85901ffa8ba0f760c0000000000\n00006125cd4eafd323475c8522186f1d5b9159758047d3a7d24ee83c88bc04000000000047826ef768c06cae73f2a379c85fb272db71e34d2f99956d78c2a30121f38b7c5cfb1567ffcb101b59697ee48cd81800184160c27f0b4517c85f1299a58631679dae710d3dcbf0e8b6b2010000000000\n00409022bb3d14e6d9f7e17af0fe90a011a6013f99d06bec61764e700ec101000000000066620a38736f8608c1daa7a959e5c5a57332f0d46ae5fa53680c52a50107278c5cfb15675dd9121b7313fc6f8bd81800cd4eafd323475c8522186f1d5b9159758047d3a7d24ee83c88bc040000000000\n00c0b821db9a987ca98db2e141e4a3fe8e6dd26214e76496f2d69dcba9ef000000000000a8fea18945ccdd333f86142f36f5107ea699d6c46594c45ef30ae51c2dbde11f5bfb1567aa27151b9eb12f888ad81800bb3d14e6d9f7e17af0fe90a011a6013f99d06bec61764e700ec1010000000000\n00c0b22252056ead699536d2cb4b6e026aa37af77be37ac6ebd874dd449e0a0000000000c085adb2dfb33c19c39d576e8191cc67194172f821329f1c2eae588ee56f179b5bfb1567e8bd171b7050bc0a89d81800db9a987ca98db2e141e4a3fe8e6dd26214e76496f2d69dcba9ef000000000000\n00c01b23dcbff6fbc855fb0350b4672f6e69476caf80d93fedd6d099268d060000000000e2e659f36519415643429292071cd14ebb9eb8a2655dfb958de23d048717052e5afb1567f5a61a1b44bee14088d8180052056ead699536d2cb4b6e026aa37af77be37ac6ebd874dd449e0a0000000000\n00e0952266f606f5454afcac22b094c1dde282e1c8b0fca690bde93b68440500000000007f54a07f57e4ff8e2dac8a02e8ce41f67d8565148c6024683b9ecbc91aa2cbe75afb156762e91d1b1b27352c87d81800dcbff6fbc855fb0350b4672f6e69476caf80d93fedd6d099268d060000000000\n00406c20adb469ff3f9c73898e89a86517a2a6d89ef1b0c328b9f667ca4a080000000000c46ed762a7fe9b3a8e8ee8d41defcb4ffd35a009741734153dfac4b27828d35459fb1567fe92211b219c2c7c86d8180066f606f5454afcac22b094c1dde282e1c8b0fca690bde93b6844050000000000\n00201a2028b7e731985a5a373bdd6d57167fa013e7d86cea84afa03f6b66030000000000aca83dd1ffe99413a0fa9f4416c27a58f1ecacb0c8a2e661db7c821b9d694f5e59fb15673aad251b11f2fff385d81800adb469ff3f9c73898e89a86517a2a6d89ef1b0c328b9f667ca4a080000000000\n0060172078f7600e7fb149b677d25e634e6d8e00ea57556fa189111f858e00000000000098963e33a6e31389669ad2ba25eeb57661c71ef7f46bbecff6f14d30f63b036058fb156735492a1b2d4d3cda84d8180028b7e731985a5a373bdd6d57167fa013e7d86cea84afa03f6b66030000000000\n00c0312106783a22afa168146ba8f32662a3618fdc268010bcb518ecdd940700000000002fc291471c6158521c083fe83f2810da540b527289235dd4a64d9ff7406d749e58fb1567b0742f1b61b3dda883d8180078f7600e7fb149b677d25e634e6d8e00ea57556fa189111f858e000000000000\n00c00a20084b914953faa3d4e3c4c4b8d4cf288846dbf2dcebcf121fa12109000000000053ca9a1bb5bd9d84cfb678b18e82c474787727702c4334167b821a981839f75157fb15670a46351b156a17d682d8180006783a22afa168146ba8f32662a3618fdc268010bcb518ecdd94070000000000\n00e0b9221defe65f9d08b13833b1ead43536c8287335210697c07e257b390a0000000000314c28967af504c45de23817244ead3afe8532d97f2ab2955f0638a19c75d21257fb1567e4c93b1b6a730b7181d81800084b914953faa3d4e3c4c4b8d4cf288846dbf2dcebcf121fa121090000000000\n0060de217350d76ee31e869fd91ecf6f6029c68b54ba1a12ceb222e4f2de0600000000006a767988a158fa81601e9599dfb4034d3f920419e4b16139bd8ced614344404756fb15673c1c431b6005e4e080d818001defe65f9d08b13833b1ead43536c8287335210697c07e257b390a0000000000\n0020c221a602cbd6f755ca63f9535e8c6e277ec6f1033f2979981bc32a6f0a0000000000306c8441cdd0efcf20124dbfe7ba4ddd009dc9de52e63efce4447196221cdd3d56fb1567344f4b1b1656ba867fd818007350d76ee31e869fd91ecf6f6029c68b54ba1a12ceb222e4f2de060000000000\n00002e25d3021fc4cf1a0bfe2ef2837ca5e98bc2fd6236f8d9751d898950000000000000eef6964dcf319a880e19292e4b4666e398d9dadf8baa6aecb38cff1d11f20b1c55fb1567eb85541b206444fc7ed81800a602cbd6f755ca63f9535e8c6e277ec6f1033f2979981bc32a6f0a0000000000\n000054214d6e10bcb2bca72d2b43487cea7e15b2bcfc546fb84d7c252fe70b0000000000539297f833644889564552da6aa7e1b92b8d39fc1b80de033a31413bba986cec55fb156721db5e1b61e389f87dd81800d3021fc4cf1a0bfe2ef2837ca5e98bc2fd6236f8d9751d898950000000000000\n00600d211bb4c5903e42cdb5d5bdc4aff4da8287fd51eed9b82be8a837250a000000000024f2e74baf3d6004435b3f8f5eb4b285c508aae8c3eec815f8256f60c16b200d54fb1567557c6a1b1d8539447cd818004d6e10bcb2bca72d2b43487cea7e15b2bcfc546fb84d7c252fe70b0000000000\n00000e211a9a38abca937234c37b0f6f186a8ecd536a345f807c9c135f720300000000002a222bdbcd671298300b924b21ded21b15a229aa282c0c9a1c690fa480577cec54fb15674882771b98067af97bd818001bb4c5903e42cdb5d5bdc4aff4da8287fd51eed9b82be8a837250a0000000000\n00803820883abf639c4627d632b9348db7f627b9c2d364ca5d9f209b62a30a0000000000b4496358d67d6ff7f25f0eec6cd1082b55ccd9c00f6ce29958729edf45c51f8253fb15672486001c41808fee7ad818001a9a38abca937234c37b0f6f186a8ecd536a345f807c9c135f72030000000000\n00808425e5f97525abf6e656bfe990b4f810c572b4b0172d7c98afb77a310200000000002c59f7fb9cc5dcad2be90d76794e03ec8048c31db1d3acd1c6532f76516408e853fb15678896001c7a826c5979d81800883abf639c4627d632b9348db7f627b9c2d364ca5d9f209b62a30a0000000000\n00a0da21a8f788c4bc93e2feb3c804a9871e9bc54019368736f8b637155b0200000000007cd729ccd7604e31b0b249cdc7c65e548414fb173553fec84e115dceabef669952fb1567f2a8001c49f7fa8078d81800e5f97525abf6e656bfe990b4f810c572b4b0172d7c98afb77a31020000000000\n00a0fa20869300f6995bd06464772ba0af360536f19ebee72b82277c555e0000000000005a0ee9cf9d290bbb849396c68b1cd0e102c5bd9c5ed4604dbff1c04f249622f452fb15679abd001c821983ec77d81800a8f788c4bc93e2feb3c804a9871e9bc54019368736f8b637155b020000000000\n00c0db2168e2234520d1175ad51cb66c6b244aae6b3311021b6fda4bd7bb0800000000003a1addc7ba49cd1e8653d8611a11b623e7b0fd05e885ac21dbdeb45671f6394451fb156790d2001c78c7f26e76d81800869300f6995bd06464772ba0af360536f19ebee72b82277c555e000000000000\n00a05423de785cef7b30270c742260769936fe7f39e4cd6f63974dc0a8d25c0000000000a179fc852648449d615eae731e23034cc4d12661e24bc1afe6c5bf2658af9fb719fb156720cc001c7ecdc66875d8180068e2234520d1175ad51cb66c6b244aae6b3311021b6fda4bd7bb080000000000\n000000205683adbd47f50964db920dc011214b2b65723e4a294ecce2324c04000000000098c18cf413f28f7668d056e6a6d23247d6f148bccbcb502dabe686544f2ce51a20f815672edf001c4a432d8e74d81800de785cef7b30270c742260769936fe7f39e4cd6f63974dc0a8d25c0000000000\n0000002002f80e56e80344bac1c66656b153aa6eb14dc99480937f5a4fae210000000000b5997cb43fd18967570e05527f16dfb25eae7dfa3fdae21f156cdcbe23478dca97f7156789f2001cc94f624b73d818005683adbd47f50964db920dc011214b2b65723e4a294ecce2324c040000000000\n000000206d88b938b589f5c3a151d59935bda8995d6e6e4b64851aa37777d900000000007f762a53a4513a0379f6c167e8b5ab9b62a9adc8beedcbdc13870c33d093d139eff6156779f3001c08500a9e72d8180002f80e56e80344bac1c66656b153aa6eb14dc99480937f5a4fae210000000000\n000000202fb28c31fed707bd8476c9c82792e276ea02c40029d45f674eeb4421000000008f510e690bbbe11a579bf8d2447a8099dcec3dd1b4d612041fa0cc4e1a9f3477abf4156704fd001ce242283471d818006d88b938b589f5c3a151d59935bda8995d6e6e4b64851aa37777d90000000000\n000000209951f67775f133cf5043691d0089075d019a8694a7821709fa218a00000000002a69bd43a8c63779f1682daca00c9993b87107fd424462c4c7674018a3e2a4a61bf31567ffff001d8c22287970d818002fb28c31fed707bd8476c9c82792e276ea02c40029d45f674eeb442100000000\n00000020c247853de3872d1103cca1a95bb351faa974cef37d709d9c9d745c0000000000609477407c36a62f4001b244665a1858a6bfcf1a8377179825ad785c4643e5a669ee156739f4001c608501b26fd818009951f67775f133cf5043691d0089075d019a8694a7821709fa218a0000000000\n00000020d47a8f385b1700c6d28858d1fe0943860f895c04421ee9d95c7ad000000000008f7e30eac567090ff9b6d7cf570592da2188d9345c4827e0f1f014766c0c2601b3ed1567a90f011c3b105e416ed81800c247853de3872d1103cca1a95bb351faa974cef37d709d9c9d745c0000000000\n0000002046d25e01882b21bc91121213871e460912e7f0d37ef9ae75e47cc4000000000092787921ad287fe490f2b4d42d50490e8556d70c4ff66b17a500f507116f662584ed1567440d011c3d9070c86dd81800d47a8f385b1700c6d28858d1fe0943860f895c04421ee9d95c7ad00000000000\n000000200a2eab08ac94c75795d5c22385aca08de32f04f8913ca6c53a286c0000000000dde8b1a392946c1bd3da8798465350cd6b10ebfd871f50c2e87a6746340e792bfeea15678b1a011c0d1d54506cd8180046d25e01882b21bc91121213871e460912e7f0d37ef9ae75e47cc40000000000\n00000020e8f7144466a50a02cfcd648eee6d3d63cfc224144dc516f3b865d900000000003b1fb0217d8d427c4fddb4916cfdfcba94247ddbb984bf4c7ff4c7e3625263d2a0e915677b08011c66e4ef5a6bd818000a2eab08ac94c75795d5c22385aca08de32f04f8913ca6c53a286c0000000000\n00000020f5673d0f7fde67da5b0f0b4a50ed9a93ff5823f2f69559cee98ae60000000000ed06f5f6a3b0ef7cb402dfe5aab05821bca12d9d06cd160e92418cf2b43dd53df1e51567d608011cdde7ea6b6ad81800e8f7144466a50a02cfcd648eee6d3d63cfc224144dc516f3b865d90000000000\n00000020f12ceb5a2ffa6564ce6691171b6468b59d0d9201b6c3580f7da20701000000002a12dcb3d96bc257a0dbdbbfe73cc6525d85654a7d4466984d6710b2978b0871a0e31567f90b011ce7ac469e69d81800f5673d0f7fde67da5b0f0b4a50ed9a93ff5823f2f69559cee98ae60000000000\n000000204cf289757e216959aded643784aa2b5cab1f5a844b4288e33de124370000000019e0d7682af4fe16efe1577417181d4a8b42ff4d31bfa18836015d463bc6082e85e11567d719011ce95bd73c68d81800f12ceb5a2ffa6564ce6691171b6468b59d0d9201b6c3580f7da2070100000000\n0000002017247ca9ad6bb39ecf37c6a54b1d1d41cba5231ed9fdd4033720e20f00000000020f87169231194013c4d8abc00a995c36e7408c07ac5744db2a0c8a8de638b833e01567ffff001d493d1d2d67d818004cf289757e216959aded643784aa2b5cab1f5a844b4288e33de1243700000000\n00000020e11ead6f7d4aa9adcfdc43269b3dadb02bfff9e2b08069b5c6e7373000000000a0263415a57bcf573e19ed44dfd3fbdfd11f58078757f21c1f57ed6cc95cc40561db1567ffff001d3f0c937366d8180017247ca9ad6bb39ecf37c6a54b1d1d41cba5231ed9fdd4033720e20f00000000\n00000020f76480043ffd13969a45569ed5bb0d10fb29da5e9ff04b3f6dba383500000000b45a17c1ada58993fefcf4db256ded79341da97d044ad0ab1dc620317a5bf22385d61567ffff001d6100057d65d81800e11ead6f7d4aa9adcfdc43269b3dadb02bfff9e2b08069b5c6e7373000000000\n000000200d22a74c56f931764655a79d37548d26605e2af78217f725abb6bd0000000000693eb694cb3c5005bc685169fa2a513cfdb756bae5e30519a6f28281fc326248c7d11567ffff001dd8744eff64d81800f76480043ffd13969a45569ed5bb0d10fb29da5e9ff04b3f6dba383500000000\n00000020918b6e625c2567713e8e6e0287eaf4329a83f808446cac8b65cf2f000000000060b40be7fd8db178a5a456da182681489d4a11ded79c299380f96b14e1b919d60bcd1567d0c1001ce73732a763d818000d22a74c56f931764655a79d37548d26605e2af78217f725abb6bd0000000000\n00000020f3d112dc59e7af1604dce090c0051ea814c3f2486570bac34fecc10000000000600f1f898b6c642d60a984c6cffa339d267cbcc4615fc63b0fe350821916f16ae3cc15671ad0001c0fa9867662d81800918b6e625c2567713e8e6e0287eaf4329a83f808446cac8b65cf2f0000000000\n00000020cd2e0e4a35c52bf84d35b7159b503777a24f1235b9b6bce585007d3c00000000c62a91ce03291b35e7bf4f2283a58b13b9e3646528ac102bf47727e3d2fe3336fccb15671fe8001c63677da461d81800f3d112dc59e7af1604dce090c0051ea814c3f2486570bac34fecc10000000000\n00000020b3eff784cd2542dc2c8ccb7935f41f506cbf17f3a8d2c55b3ee13300000000001ce99fdb279dbf31ed3293ba30f78683a5319182f4999d24e81159a6862fdddddbcb1567ffff001dc498787160d81800cd2e0e4a35c52bf84d35b7159b503777a24f1235b9b6bce585007d3c00000000\n000000209a09071a9ebaeab8e34994cd218ee704a91bb42ee5956e541ed8700000000000929694faae58420fc46024f17fca9cba15e8f9bc752d6361d6dc0a5da0b6c51f03c71567abda001c17643edd5fd81800b3eff784cd2542dc2c8ccb7935f41f506cbf17f3a8d2c55b3ee1330000000000\n00000020fc783b2b3b4320e7f8c3916cf12ca0064ed98fdbfe2fd7bcc22a140000000000b977dfa11fc1c2b3e620560d10a53cd581f073d867163b7ca11c1266a9f8d361f5c5156775f4001c3840d9ed5ed818009a09071a9ebaeab8e34994cd218ee704a91bb42ee5956e541ed8700000000000\n000000209e146621c29d86d8ea55afd4af7a319f8d649db67149139842828300000000007af760a271692c9fa6ac8419a02d1f10f02332a14f32ea31c179f818eec64b74e0c5156702e7001cf42c335b5dd81800fc783b2b3b4320e7f8c3916cf12ca0064ed98fdbfe2fd7bcc22a140000000000\n0000002093ed6bbe75c7f410e37797fca00676d3154d9a9181b38327dd870800000000008aa7f7a7bcd8efb3ea0b718df37cc1c55a56878dbc46056d6abf5b53a01edeb262c215672400011c276aa4b35cd818009e146621c29d86d8ea55afd4af7a319f8d649db6714913984282830000000000\n00000020de7b70a66b9b2c306411039e9096a998aefb52b3f10c571fc7cc8b0000000000817d565660758fcfb70e58455b5c92b064503f8c608443302730aa71ed55956d23c215676ff7001c397fd8c35bd8180093ed6bbe75c7f410e37797fca00676d3154d9a9181b38327dd87080000000000\n00000020b7bbc78bacff866e79980a29ff5dc511c255fc51096cc7fa575cf700000000005ae8bb32a1bf4dff207eebbfedcac655f05d11de0cf5acbb883223914a6373c417bf1567ea09011c46125bd65ad81800de7b70a66b9b2c306411039e9096a998aefb52b3f10c571fc7cc8b0000000000\n00000020cd7e5dc240792766521619e28d66a05fa4c8ed178f6a68d909d9ef00000000005aca898480772b90cffc974f2606015c4ce4218e9a87f640ff5cea5ec837837f35be15674120011ca0f85fa159d81800b7bbc78bacff866e79980a29ff5dc511c255fc51096cc7fa575cf70000000000\n000000209bcc46d9d831473fa39171151ab52778d7ca381f36175d22fe1c0600000000007781070da6fccb3781dc5faaa12edf2bec09527c40e72a16ac849240d8884a8d80bd1567d035011c41b5750a58d81800cd7e5dc240792766521619e28d66a05fa4c8ed178f6a68d909d9ef0000000000\n000000204187a6865a66b8340a9ca6d2e7655d92c7ea24c53b19549ab07aa70000000000c825243c7b1703a2712035fc995781b748fb7599488eebdb7afc6b89c166406e9fbc1567e64b011c29de52df57d818009bcc46d9d831473fa39171151ab52778d7ca381f36175d22fe1c060000000000\n000000204fa8621ae8f42551c924aae569d92424d75a730c37551c37b4b52301000000008ba05b523dcd5a3a3b28bd18a2c32246c94788e6447b452d003f93840ab387d6adbb15674c67011c92a1603956d818004187a6865a66b8340a9ca6d2e7655d92c7ea24c53b19549ab07aa70000000000\n000000201c40e4c198d03447f54b8913ecf8b26065b55fa4da093420e111a60d000000004d977b318b1c929a3d03e76aead8ffd02a196c570b36e9b3281c1c59e61025bcf1ba1567f07b011cd9e51e4b55d818004fa8621ae8f42551c924aae569d92424d75a730c37551c37b4b5230100000000\n0000002083a3a2fe0e444e13530c7c99a5c63320071df0872b0f52aff82ae4000000000088dd6753f9c91af085e5fe7396c71dd81cc0d34e1dd24169d2892204acc9bbf9bbb91567ffff001d036733c754d818001c40e4c198d03447f54b8913ecf8b26065b55fa4da093420e111a60d00000000\n00000020738670aad6b4eeb71cacc6e878ce2cd40142f701917208c33af27100000000000931bf5ad891f15bcfdce066144cd24fad83ef5e54ff25538f20aadd0d05fd1b07b515675373011c928b373953d8180083a3a2fe0e444e13530c7c99a5c63320071df0872b0f52aff82ae40000000000\n0000002057f4dfbe0930cb10ecdb13b3a0d0b4532650f4010012508291cf7c0100000000b28cc2a36b7e47552c041899effbcdb6cfd52962fc3f3909259950ce87b30ac194b41567288d011c2e1aea4152d81800738670aad6b4eeb71cacc6e878ce2cd40142f701917208c33af2710000000000\n0000002060cad9e4fff60d1f6f748a4bb33f2bd8928b126e67369b8fc1da870100000000485a26097ae5970d1ebdd1c2840f13c16e365e2304dd4310283782ed659331da99b3156712b3011c324e6c3551d8180057f4dfbe0930cb10ecdb13b3a0d0b4532650f4010012508291cf7c0100000000\n0000002049fc944f9b027aac2a232fb1e460ff331f506b3fe7a01e7e4067f000000000000a6c133d04f4330051237b23faed95fb10aff4f661569677c6bc9e3f1160391b1ab315670ed1011c1238bd8f50d8180060cad9e4fff60d1f6f748a4bb33f2bd8928b126e67369b8fc1da870100000000\n00000020b3a40184afffb741916bab97fa6cedc4bf9ca0ffbd1d59ef7af0a00000000000ce0466fbf9b7e771c9b9e8f4fbaa9727d4cace1a0e0cc9f5ea2407a883a81c011cb21567dd02021ce921df794fd8180049fc944f9b027aac2a232fb1e460ff331f506b3fe7a01e7e4067f00000000000\n00000020e4e5110183d1088c30e8497ccce952e03905e023a5da6b779f464301000000000a9e7a52f5e7413179ba0c1ff0d713b7bb0ec46e87f64994ca9de32b566913bbd5b11567f735021c2f58267f4ed81800b3a40184afffb741916bab97fa6cedc4bf9ca0ffbd1d59ef7af0a00000000000\n000000201703518d7676787b5b14d2ba72530b80d80714eed84677cbf53d4d0000000000a231b3768a10a731ca6346dcc553a0ee831c65c4994793b83ed435417af721d368b115676b3d021c31f158b94dd81800e4e5110183d1088c30e8497ccce952e03905e023a5da6b779f46430100000000\n00000020bde2f16016d9e28c0c5b6dcfe10ac0ef0c3fa8447ba1e4264f1e5901000000001adca2b87d539a9032a11db06dc8951123311ecfb135484485499e357e3f7a5e54af1567db44021c83de7a044cd818001703518d7676787b5b14d2ba72530b80d80714eed84677cbf53d4d0000000000\n00000020a9ee7692cef7581cdea03ae0e2ff22445ae28a82d97ddbe515ec5701000000009d23a13938495f72e3250fbd46580dd2c5c286ddc9b284075ea820c51c58ac5f3fad1567d731021ce817479d4bd81800bde2f16016d9e28c0c5b6dcfe10ac0ef0c3fa8447ba1e4264f1e590100000000\n00000020df79e7e83877d148a8960986aa0d6179bff24102c06bf76474ff8301000000002867a60c8495d4661ec235de0322d01eb6b58f870ed2687442438812bb92cd9a3aaa1567d372021c1045213d4ad81800a9ee7692cef7581cdea03ae0e2ff22445ae28a82d97ddbe515ec570100000000\n000000201ab7c27c049ede85dc61bb9e8f0b749185cf10f3e3396e2ee972b9010000000011694723696f0730908e5b4159327854700d859c13cb1729e51ee8e8cc26a37f1baa1567e160021c74fb01d849d81800df79e7e83877d148a8960986aa0d6179bff24102c06bf76474ff830100000000\n0000002077674004842534eb4c9c3fd73214f9326a78429b848799f94f759201000000007cf330d6f0093606c94d82dc7732c2ed6d134c683e3341854a2b1b83a39577e22ca71567a95e021cf0333bb648d818001ab7c27c049ede85dc61bb9e8f0b749185cf10f3e3396e2ee972b90100000000\n0000002067c336ec6e86320427f5e33b2d955a3b7f42b0f322f9bcfd139129010000000050d9f68dc6cdd5483658346c94a6bc43c99d38c259725e8af625755c27e4880cc1a415678960021cb5278c2e47d8180077674004842534eb4c9c3fd73214f9326a78429b848799f94f75920100000000\n00000020c13147a9412775593165a5e7f0d4f80eb950f33ee35a7e3479a1320000000000ff5c774b0e972f90ae5ff97c3436beab7c7ce1fa417aa06a8d8ecbf68de9eb8279a21567cd9c021c4512b09646d8180067c336ec6e86320427f5e33b2d955a3b7f42b0f322f9bcfd1391290100000000\n00000020dc7f869fe517d7136f98eb406017362ace0eee1b864de413c3b5b70000000000107205223253790c628bcfad4a8d9260a71c493f9566e42d6dcd93f77c54186b0ca215672db5021ca4a6d46e45d81800c13147a9412775593165a5e7f0d4f80eb950f33ee35a7e3479a1320000000000\n000000204999c372551e26e6d60cf182b50d3371121b433ce9521c61a2279202000000002a7d5ba03596ba7784c77ebde8acdaa5923a750f124f1f1cbacde4b9befa57a86ea015674dc7021cebcb20f444d81800dc7f869fe517d7136f98eb406017362ace0eee1b864de413c3b5b70000000000\n00000020fb051bfe737055d01c5bab5b5d49d498317eb3895ceb0031cb925c00000000008e52641c90dcd1ec2d8ab3c83084915aaa905be9da9367fb2a7c33ce9b23e7ab9c9e1567621b031c50284b0443d818004999c372551e26e6d60cf182b50d3371121b433ce9521c61a227920200000000\n0000002085ec9142aca0044a7e4af79863139da6e54fe02c77c39902d7edae0000000000ca88cea16f04f6aae116322bb603cc48b3f7c2ff52a5050f7f504f4648040a46889e1567aa69031c96f4b31342d81800fb051bfe737055d01c5bab5b5d49d498317eb3895ceb0031cb925c0000000000\n00000020f783ac6d17a7a4750afe63a269a32dd153c452822396fc40174cba0000000000f9c5eb291fceeabebb10e304b71773912a600fbb22b9cdd8dbbc22042aa7389d179e156754d0031cf5c433c441d8180085ec9142aca0044a7e4af79863139da6e54fe02c77c39902d7edae0000000000\n000000206955c9b6dfc661647edb4175b769fb871d2f9c5c4d03f840fad2a803000000003e13f88b5916214f264c896f673784ebfbef676077a77e82d6dfa6bbbc1a35a9009e15678730041cff0f027640d81800f783ac6d17a7a4750afe63a269a32dd153c452822396fc40174cba0000000000\n00000020c9b571386ecfcc2533f930fc4e7cd0d1ce1e4f715fcca6e74de6e101000000005d8faa2e6fdda033a4d096efd21bc5d1d3e09c8063da6e0a44ad1e8a7d17c364909d15671f0d041c85a4b7293fd818006955c9b6dfc661647edb4175b769fb871d2f9c5c4d03f840fad2a80300000000\n00000020d737c641440d37f0d80dc41165adfcb7bd2b4151c21efdbf6ebf9d020000000013404b7c7a15decadf5d67b300bf138481fe5e90907deea85acba9f47dbbcaf48a9a15679732041c901798a03ed81800c9b571386ecfcc2533f930fc4e7cd0d1ce1e4f715fcca6e74de6e10100000000\n0000002091be5f359f4246e86da9e7d100e0e4887288927d6c8a42882965b60300000000daf111cb14f721a0b5ac9e2cfff8e9d5b8c5b0535d0f67d41aeaea61023e8399ea98156707b1041cc3e023b03dd81800d737c641440d37f0d80dc41165adfcb7bd2b4151c21efdbf6ebf9d0200000000\n00000020b474866bded7f4e02304b6a355bef87991cbb6e3e3186cc0bfdb610500000000c858e31a63d9abc835598ef3b2ee14dd401b3359aedd71fd5d365cfbd4126ab1d4981567ee32051ca6428eb43cd8180091be5f359f4246e86da9e7d100e0e4887288927d6c8a42882965b60300000000\n00000020a935768401aa823248d843b4698a5b942a21d04fc313dd020abaa90300000000ae01f7846312a9d6e09ed8e51509f8b6ab3f7930afe42f333f6adfeaf981d515929815675eab051cf7af714d3bd81800b474866bded7f4e02304b6a355bef87991cbb6e3e3186cc0bfdb610500000000\n0000002028c6ce90f7a4672eee5f0e81930f6b68b9a3995434a0963bcc97090600000000c675e0c046830ae78fafba3bac1ccdddb41db62eddddead30ca2d2f298ea1fb1fc971567cd54061c06fa46a43ad81800a935768401aa823248d843b4698a5b942a21d04fc313dd020abaa90300000000\n0000002026a9876124cac60e0bf432c2aacbdf49f8a740b8e181e94087c8eb01000000001a4c5fad0cefb178711d827847e8a0f99dd6a7dacd8dea1fad59191faf137496e197156741c8061cc03bb48c39d8180028c6ce90f7a4672eee5f0e81930f6b68b9a3995434a0963bcc97090600000000\n00000020ee2d3dc30ea9f3bb4a80b88bd95e4f93a8dee9f33a05128a31ac3f0600000000a61bcf82ac516494e0bce1ce338802df6de6b88862c03d4913f7ff3d7ed68351ee961567cc85071c290eb39038d8180026a9876124cac60e0bf432c2aacbdf49f8a740b8e181e94087c8eb0100000000\n00000020abed54a4c588c42af15ed8800b9707fd91d20f8e10c9ce2a6123f80800000000ee0ac5685e81771a6a7b7fa446a0345921627ce1f4251bb1275c05598e4bc092b0961567475b081cf7550dc637d81800ee2d3dc30ea9f3bb4a80b88bd95e4f93a8dee9f33a05128a31ac3f0600000000\n0000002028894f5dedc184927922cef8458846bbd135c6b9be447e882479c00000000000d47db521352838808c23b08c6ef738521f55326cee97450b9d9037e50cf929ab7a9615678656091cf7a1f74a36d81800abed54a4c588c42af15ed8800b9707fd91d20f8e10c9ce2a6123f80800000000\n000000204f97f197d66a0e9f287554b6cabaf8af91efcc0da25a6a47226f8105000000005857e3c8be38f386713c969332483ec5b198b4777b8d047e6f7deb2b29fdadf763961567f6c6091c48a10a1a35d8180028894f5dedc184927922cef8458846bbd135c6b9be447e882479c00000000000\n000000202a50bfd3a6ada400c0caa0f60a998e5e2561b4f48d643f11edb7c8070000000090a7395dd26a4862309544938938bd96b42a0e1ca5a28e149790c255ec2fd144fa94156705c90a1c058a818034d818004f97f197d66a0e9f287554b6cabaf8af91efcc0da25a6a47226f810500000000\n0000002034e1e693d776c18081372ef1c5efac1b0ac9309a61598b6db98f3d0a000000008f1b52795272b7d6adc3232e3fbe8d6ee6ccb775acaf0cd87cc5043de7f37592a0941567ecf20b1c4f586a7a33d818002a50bfd3a6ada400c0caa0f60a998e5e2561b4f48d643f11edb7c80700000000\n00000020b8cf569a5f9ad35fc30ab84914b09d0b9af73d8cd5017a7b6152f306000000008f8f03341a8b29be0b3411d4e7c62aff8b85ad17022a7c57b8851660899359465c9415670a5c0d1c735cd42c32d8180034e1e693d776c18081372ef1c5efac1b0ac9309a61598b6db98f3d0a00000000\n000000208df5386cd8abf7c779cdde8e7f28f68639e60bda690d07e8cef2cf0700000000f22619206b9bc8a4b7f42230212c2441dae3da38c5e0a7a9f267392d5f7016994794156721f90e1c2baf301931d81800b8cf569a5f9ad35fc30ab84914b09d0b9af73d8cd5017a7b6152f30600000000\n000000207f2548616187b8281627fed1581ab18f51f9104c50cbf311d7ecd60100000000ec2b974d8bbb9919727303860b32f18790076d1273cd676ebc969a20851c33f53f9415673fc6101c947eefb430d818008df5386cd8abf7c779cdde8e7f28f68639e60bda690d07e8cef2cf0700000000\n00000020c0099927346f259e5071fb88b5ad636bdda2cbb2d18dd8bac5aae70500000000cc8eac474f318e24920b1546be159b1676a0851ebfcf2efbcdc42a85493b9337359415677dcb121c459d49412fd818007f2548616187b8281627fed1581ab18f51f9104c50cbf311d7ecd60100000000\n00000020735bf018e66df73ce9b365f2404cdf203da5b1dfdcbf7d782479cc0d0000000071d3c9e89bf8dce967d5d9fdf6eabfda0a80482878ecfa21c7bb507305568a742c941567ea12151c3806ff152ed81800c0099927346f259e5071fb88b5ad636bdda2cbb2d18dd8bac5aae70500000000\n000000206b9d248ceb623b2723c59fa63a3158562e9669f87bca6ab9fdeb341400000000984bfa9668046dbab637d37b169d3bcb1ea3dc610c5262217e1bbe9fd33fedea27941567c884171c7e9c4b3f2dd81800735bf018e66df73ce9b365f2404cdf203da5b1dfdcbf7d782479cc0d00000000\n00000020e4124bb038ee9e5379bb44173889ce30b0c1f77116eef50852803f1100000000c9566196044e24c29204326d9e152c97f06eae809bf86136ae6a32098185f8da0994156725101a1c0bdc5a242cd818006b9d248ceb623b2723c59fa63a3158562e9669f87bca6ab9fdeb341400000000\n00000020d7a55bfad16f99ecd70223779e5c769746b037886cd9949b144c801a00000000b6346a616ad1bbcaaef9b8654e40ac7032a022bc660d099f5fe3970321792a94c693156782231d1c0a298f902bd81800e4124bb038ee9e5379bb44173889ce30b0c1f77116eef50852803f1100000000\n00000020831c863b54b09536510a99b0af79cc74d40fde96dbefc1d00e6e1502000000002cc093cc9b3ca4be8497863072b8fb7cc92e929e67a0078ce206935cbc04c45cb1931567ff9d201c7a13f27e2ad81800d7a55bfad16f99ecd70223779e5c769746b037886cd9949b144c801a00000000\n00000020793d89e88aaa7c09c780b6af25a5be6f70660a9ff007a4c6bf63110000000000489e0e8853bc5dde9ad7a2a6ef2aba789af4f5adc85a48899e6c677a8e6bef80a3931567fb42241ced90d2bd29d81800831c863b54b09536510a99b0af79cc74d40fde96dbefc1d00e6e150200000000\n00000020a43e94174e8d6aa0a2b2d1f936c1e49fd02cc98a107767adf898ee29000000006cb00dd6580ace289cad242328157418fbedef7440600e0910692776dcc457b171931567770d281c616d0bbc28d81800793d89e88aaa7c09c780b6af25a5be6f70660a9ff007a4c6bf63110000000000\n00000020458f13f06f76e0e47a21a61475876f203041282de728b921b47bed1400000000dbbad083fd50f9436ae3fad1392d71c5039b51b77aeb0d9c6c2a9a61a8b1be091e93156733dc2c1c2a18d67927d81800a43e94174e8d6aa0a2b2d1f936c1e49fd02cc98a107767adf898ee2900000000\n000000208eb4ce910dc1a0341a387381ed937026f3c1f12a01adb9e4973b731c00000000411754d04898237f37771e35199357d7bf5522d9ee301eda4daad5e71aa7c803139315672d4f321c4d74fdbe26d81800458f13f06f76e0e47a21a61475876f203041282de728b921b47bed1400000000\n00000020c50e0c111662a993fb6d6cf91e466b4aa99c255cc6a8612873dc1b2c0000000083376e05fd5c9be645e4d708a1e7f2abd11cd333aae57d629f5dd2fcc23dd68c0e931567a771381cb665e38f25d818008eb4ce910dc1a0341a387381ed937026f3c1f12a01adb9e4973b731c00000000\n00000020b3191c21d8aa6d94efeb2ebb65bd9f35abe48344332772eef1907841000000003f713ac328242003e3cd0d4715ca89819de05d3fa0ad0033b07b64fe957e3ad10b931567a0503f1c37cf5cc224d81800c50e0c111662a993fb6d6cf91e466b4aa99c255cc6a8612873dc1b2c00000000\n0000002025f872c817d788da59ad654df0fd2895e6700f8a26402891beba8f49000000007883b7294bff267bb3c61aa70c1348be7fc3c53ce5a80196bf0dfcd39b1235ec08931567f9ef461cc3dc8d5623d81800b3191c21d8aa6d94efeb2ebb65bd9f35abe48344332772eef190784100000000\n00000020aaa3842ee1e22e242176519a984dfbaf558448c937ec6129844cb02100000000cfce30aafed7a1e3efe0c12bfcfb4a6a47f7641318e8185532370ecda969900ffe921567b0524f1c1c6756b322d8180025f872c817d788da59ad654df0fd2895e6700f8a26402891beba8f4900000000\n00000020a762cab2afab365f8bbf58465e2f63121175a6c91a32f108d7e33e16000000006aa42d9b61a7b06df420ca7b1c90869aeeeefab083352a6cdcd1736c0741fa8feb92156767dc581c9c9e217321d81800aaa3842ee1e22e242176519a984dfbaf558448c937ec6129844cb02100000000\n000000205c19be814987ba2d18c83a6e9722a3a2a47627ad0bbf8385e87ae05c00000000c0bf70910c61012eec994a8c2cd1c0bf3b94c940e18c3232e47b5d7b0e2740aae19215675c71631c0b5871d120d81800a762cab2afab365f8bbf58465e2f63121175a6c91a32f108d7e33e1600000000\n000000205a10863decf29c0336d4e1be102fa4f9b3f067f951a7a8330e9371770000000042832d8055ec064abf3f601af956a5fccea5d5f5781fd62d9865a7949510f923d1921567d09c6f1cb5f329761fd818005c19be814987ba2d18c83a6e9722a3a2a47627ad0bbf8385e87ae05c00000000\n00000020c767ee2c73cf71071002c065dc838e9723d75add1d23bc8e0b0f0314000000003056bf5a7d15db2b619c42f017bf8e0160089676139db4546d3cc9f64e4ef46cd0921567822d7d1c6a61eb991ed818005a10863decf29c0336d4e1be102fa4f9b3f067f951a7a8330e93717700000000\n00000020cfd2ddb5c222ab99e558e1b6c50ff85fc9a08c8704c627bfc2c2276d0000000061c5a4a073c8f7dc201078e80d96614be5587a4e2ec30fab50486b0ff81ac27acc9215671b8c001dc635a5fe1dd81800c767ee2c73cf71071002c065dc838e9723d75add1d23bc8e0b0f031400000000\n00000020c1d145730504ddb5c042ed094c6addfd03e1046613d3af4383d1e88a000000002685a4a1cc6c7b0695ca62b67fcf78109d95f62dc3e6d26a7c684922ca8bedfebd921567119d001d9e031d1b1cd81800cfd2ddb5c222ab99e558e1b6c50ff85fc9a08c8704c627bfc2c2276d00000000\n00000020b70ccf589c86245e55ead7e8970fb72c85a53ab633e3f52de1c5997a00000000afa3fe2aa125450f76f19bd9de85e4f02ed9d7aba0077c2fc03a1baf49dd1d7bb792156738b0001d1066747d1bd81800c1d145730504ddb5c042ed094c6addfd03e1046613d3af4383d1e88a00000000\n000000207e9c8b0662f61ffc5f9b7dcd86891eccb6e24e645ddbabb7ae0affb200000000d7c7743a05d6670a1c64dd421da1fad184f534971a4fb44b9d5412b8ed265ce1b5921567bdc5001da800cf201ad81800b70ccf589c86245e55ead7e8970fb72c85a53ab633e3f52de1c5997a00000000\n00000020fe477042ffbe805253b1defd4687f0ba6784d3c3e87d2ef3c27a3cc10000000049e9235b8c408e0811462392f07a0d551d71cc3766883af9d695df844768a20eb3921567b9dd001d71b91e6d19d818007e9c8b0662f61ffc5f9b7dcd86891eccb6e24e645ddbabb7ae0affb200000000\n0000002074985ba5cab78b61151f2c6d3334fbee693335f556b0b48da8753454000000000da3a5e1aa72b466b237f53e25452a38622513530f5ec5a91b9e8049a9917ffead921567d2f8001d1745c03918d81800fe477042ffbe805253b1defd4687f0ba6784d3c3e87d2ef3c27a3cc100000000\n00000020af8c60bdd4fecddb89cbe25a248d8b85314e0e97bcbb78d0adac9f4900000000f02676f4701d1ae030fe8d268dc50c5750d2cff7496eb6791e180e0144efa2b4ac921567ffff001d6ff38d6617d8180074985ba5cab78b61151f2c6d3334fbee693335f556b0b48da875345400000000\n00000020b6885756a0ab5f93607f00428278acd8456c5b43424af1a920edcf7a0000000043f1cc4acbf0eec1737de0cce554d3808d8996708e7f80b9f987df7fa1f5f0dea5921567ffff001d10906f4016d81800af8c60bdd4fecddb89cbe25a248d8b85314e0e97bcbb78d0adac9f4900000000\n0000002089ecf9f0d5ee75d8ae7e3a0e1fe6f3e073577d400cbf0fe39ee4414f00000000cd1609eeb47af28f2d6d091219b251d282aaa0df60125a4688be99816a89eb87a5921567ffff001d3c1dd5c615d81800b6885756a0ab5f93607f00428278acd8456c5b43424af1a920edcf7a00000000\n000000200ae374a8a7edf108a4d0f70c35686b31d3c1f1fd5be0f86ca654b8f20000000027a3a578ab6363ece973240623cc2b160a5bd5628ee1dd07f249ca1155f85ab4a3921567ffff001d7819e23414d8180089ecf9f0d5ee75d8ae7e3a0e1fe6f3e073577d400cbf0fe39ee4414f00000000\n000000201a3f5f3e1b3cf01943ed7dd499fcbf4392939320cfb4223989df3159000000005acd3fed31aa38779636b981ffe1f70c3f4e90ad2a8498525e06cec38ddebce89f921567ffff001d6cb4ba5013d818000ae374a8a7edf108a4d0f70c35686b31d3c1f1fd5be0f86ca654b8f200000000\n00000020f83c7d24b4441d88dd1892cdab490e4c2b1135dbd0bcf1269e6e0a5e00000000227370b60690f639258af0c25b51a0d43b0db4f47d6439e4b72a3043ddf9dc879e921567ffff001d2957844712d818001a3f5f3e1b3cf01943ed7dd499fcbf4392939320cfb4223989df315900000000\n000000204ae694d624b6a800584349f4c76c13dee03667af52c3a76ce385136b0000000063ed2013a7c8ddcf0550a05c816e692d4794496a4e7281c0d864e1974c5ad42895921567ffff001debc5d6ee11d81800f83c7d24b4441d88dd1892cdab490e4c2b1135dbd0bcf1269e6e0a5e00000000\n0000002005340644900ee6bc15d1374b0d4d60b4cfee58d80434f7bd5047c4f60000000045fa9766c6921410d373f080d3553c2081324f889974f83bfb224646dd9f410890921567ffff001da8cbcc3c10d818004ae694d624b6a800584349f4c76c13dee03667af52c3a76ce385136b00000000\n0000002027fca7ff9a5092a768030008162d4c493e3651351131a2ba48689bd20000000048185acd3f5c52a9a5e980e3ce7eaa4e6bb4808934ea0f7693e2ff431e3b37fa8e921567ffff001da5bdbf010fd8180005340644900ee6bc15d1374b0d4d60b4cfee58d80434f7bd5047c4f600000000\n00000020f45ce2d7ea0bbf654e8dce0006fb8231989f12faf7843f3fe1b82894000000006d3fd45df954ce6fd4dbe044ae7257eb60ddb115a28685c62c9211812b52515686921567ffff001d9d4d3bb30ed8180027fca7ff9a5092a768030008162d4c493e3651351131a2ba48689bd200000000\n000000207d0c3b9391761bbe6689b5a70f804d0227105c6097ce326675ca9193000000001ff8755ae64c0cd0ec1b0186285b44f0075cbd06b9e5145ac4321b7d75f23f9882921567ffff001d6eb91a930dd81800f45ce2d7ea0bbf654e8dce0006fb8231989f12faf7843f3fe1b8289400000000\n"
  },
  {
    "path": "bitcoincashkit/src/test/kotlin/io/horizontalsystems/Fixtures.kt",
    "content": "package io.horizontalsystems\n\nimport io.horizontalsystems.bitcoincore.extensions.hexToByteArray\nimport io.horizontalsystems.bitcoincore.extensions.toReversedByteArray\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.models.TransactionInput\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.storage.BlockHeader\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport io.horizontalsystems.bitcoincore.utils.HashUtils\n\nobject Fixtures {\n\n    val checkpointBlock1\n        get() = Block(\n            height = 0, // 536256\n            header = BlockHeader(\n                version = 536870912,\n                previousBlockHeaderHash = HashUtils.toBytesAsLE(\"00000000000000000000943de85f4495f053ff55f27d135edc61c27990c2eec5\"),\n                merkleRoot = HashUtils.toBytesAsLE(\"167bf70981d49388d07881b1a448ff9b79cf2a32716e45c535345823d8cdd541\"),\n                timestamp = 1533980459,\n                bits = 388763047,\n                nonce = 1545867530,\n                hash = \"000000000000000000262e508512ce2e6a018e181fb2e5efe048a4e01d21fa7a\".toReversedByteArray()\n            )\n        )\n\n    val block1\n        get() = Block(\n            height = 2013, // 538269\n            header = BlockHeader(\n                version = 536870912,\n                previousBlockHeaderHash = HashUtils.toBytesAsLE(\"00000000000000000011206e641083b68ffc41b7fe6ee1af4a5d69995d1b2d0e\"),\n                merkleRoot = HashUtils.toBytesAsLE(\"5510c0c3d1fd9d2b56a34aab98c29860015caf248fa62a1907b197ddec17c788\"),\n                timestamp = 1535128609,\n                bits = 388763047,\n                nonce = 2295801359,\n                hash = \"0000000000000000000a876dbca5804f792afa90b6dc7946dedb5866245d0c55\".toReversedByteArray()\n            )\n        )\n\n    val block2\n        get() = Block(\n            height = 2014, // 538270\n            header = BlockHeader(\n                version = 536870912,\n                previousBlockHeaderHash = HashUtils.toBytesAsLE(\"0000000000000000000a876dbca5804f792afa90b6dc7946dedb5866245d0c55\"),\n                merkleRoot = HashUtils.toBytesAsLE(\"ccf2737e44e435e2e11481755b00d161815a24e605d605a17bf20da49320ad7d\"),\n                timestamp = 1535128839,\n                bits = 388763047,\n                nonce = 3401296263,\n                hash = \"000000000000000000124a73e879fd66a1b29d1b4b3f1a81de3cbcbe579e21a8\".toReversedByteArray()\n            )\n        )\n\n    val block3\n        get() = Block(\n            height = 2015, // 538271\n            header = BlockHeader(\n                version = 536870912,\n                previousBlockHeaderHash = HashUtils.toBytesAsLE(\"000000000000000000124a73e879fd66a1b29d1b4b3f1a81de3cbcbe579e21a8\"),\n                merkleRoot = HashUtils.toBytesAsLE(\"7904930640df999005df3b57f9c6f542088af33c3d773dcec2939f55ced359b8\"),\n                timestamp = 1535129301,\n                bits = 388763047,\n                nonce = 59591417,\n                hash = \"0000000000000000001d9d48d93793aaa85b5f6d17c176d4ef905c7e7112b1cf\".toReversedByteArray()\n            )\n        )\n\n    val checkpointBlock2\n        get() = Block(\n            height = 2016, // 538272\n            header = BlockHeader(\n                version = 536870912,\n                previousBlockHeaderHash = HashUtils.toBytesAsLE(\"0000000000000000001d9d48d93793aaa85b5f6d17c176d4ef905c7e7112b1cf\"),\n                merkleRoot = HashUtils.toBytesAsLE(\"3ad0fa0e8c100db5831ebea7cabf6addae2c372e6e1d84f6243555df5bbfa351\"),\n                timestamp = 1535129431,\n                bits = 388618029,\n                nonce = 2367954839,\n                hash = \"00000000000000000004f11858464cc6113248537a01e628324968b499848a60\".toReversedByteArray()\n            )\n        )\n\n    //    P2PKH: TestNet tx => 68f297d8a8c9af30cd5a9d6d1eeec5ed3df7be1e4b62f2ced135af6ffe7814c2\n    val transactionP2PKH\n        get() = FullTransaction(\n            header = Transaction(),\n            inputs = listOf(\n                TransactionInput(\n                    previousOutputTxHash = \"093f5f5c5e57ae2ae9728147547e183e2ef5c9e6e879a78bee6ceb59db2b4797\".hexToByteArray(),\n                    previousOutputIndex = 1,\n                    sigScript = \"473044022018f03676d057a3cb350d9778697ff61da47b813c82fe9fb0f2ea87b231fb865b02200706f5cbbc5ebae6f7bd77e346767bce11c8476aea607671d7321e86a3186ec1012102ce0ef85579f055e2184c935e75e71458db8c4b759cd455b0aa5d91761794eef0\".hexToByteArray(),\n                    sequence = 4294967295\n                )\n            ),\n            outputs = listOf(\n                TransactionOutput(\n                    value = 94734191,\n                    index = 0,\n                    script = \"76a91437a9bfe84d9e4883ace248509bbf14c9d72af01788ac\".hexToByteArray(),\n                    type = ScriptType.UNKNOWN\n                ),\n                TransactionOutput(\n                    value = 100000,\n                    index = 1,\n                    script = \"76a91437a9bfe84d9e4883ace248509bbf14c9d72af01788ac\".hexToByteArray(),\n                    type = ScriptType.UNKNOWN\n                )\n            )\n        )\n\n    //  P2SH: TestNet tx => 761cc7102efe24f4353ae7dc816fbed5e15963d11ca93e36449d521bda21ac4d\n    val transactionP2SH\n        get() = FullTransaction(\n            header = Transaction(),\n            inputs = listOf(\n                TransactionInput(\n                    previousOutputTxHash = \"b6f0ede9cc38cdbceb91936619f89b648bb912f4c42773567037ea5de164873d\".hexToByteArray(),\n                    previousOutputIndex = 0,\n                    sigScript = \"004830450221008c203a0881f75c731d9a3a2e6d2ffa37da7095b7dde61a9e7a906659219cd0fa02202677097ca7f7e164f73924fe8f84e1e6fc6611450efcda360ce771e98af9f73d0147304402201cba9b641483476f67a4cef08d7280f51de8d7615fcce76642d944dc07132a990220323d13175477bbf67c8c36fb243bec0e4c410bc9173a186d9f8e98ce3445363601475221025b64f7c63e30f315259393f64dcca269d18386997b1cc93da1388c4021e3ea8e210386d42d5d7027ac08ddcbb066e2140575091fe7dc1d202a008eb5e036725e975652ae\".hexToByteArray(),\n                    sequence = 4294967295\n                )\n            ),\n            outputs = listOf(\n                TransactionOutput(\n                    value = 617795422,\n                    index = 0,\n                    script = \"a914cdfb2eb01489e9fe8bd9b878ce4a7084dd88776487\".hexToByteArray(),\n                    type = ScriptType.UNKNOWN\n                ),\n                TransactionOutput(\n                    value = 1407000,\n                    index = 1,\n                    script = \"a914aed6f804c63da80800892f8fd4cdbad0d3ad6d1287\".hexToByteArray(),\n                    type = ScriptType.UNKNOWN\n                )\n            )\n        )\n\n    //  P2PK: TestNet tx => 75b84cb54351866cb5248158735e801d9b2c56592633157ba10d08affa2ffbab\n    val transactionP2PK\n        get() = FullTransaction(\n            header = Transaction(),\n            inputs = listOf(\n                TransactionInput(\n                    previousOutputTxHash = \"978530798f3979322351c190856d17b9e9e7e470c5be4ce87a60bd7a9f7756ac\".toReversedByteArray(),\n                    previousOutputIndex = 0,\n                    sigScript = \"473044022003f9d150b4e291de2825af19dbe1846cc80caf3535d7e9fa03743b2ad019cc47022073294e520c508f702e3ad7a085ecce4a4b311d43faa1e6eb685ec78c002e795d01\".hexToByteArray(),\n                    sequence = 4294967295\n                )\n            ),\n            outputs = listOf(\n                TransactionOutput(\n                    value = 1000000000,\n                    index = 0,\n                    script = \"4104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac\".hexToByteArray(),\n                    type = ScriptType.UNKNOWN\n                ),\n                TransactionOutput(\n                    value = 4000000000,\n                    index = 1,\n                    script = \"410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac\".hexToByteArray(),\n                    type = ScriptType.UNKNOWN\n                )\n            )\n        )\n\n    val transactionP2WPKH\n        get() = FullTransaction(\n            header = Transaction(version = 1),\n            inputs = listOf(\n                TransactionInput(\n                    previousOutputTxHash = \"a6d1ce683f38a84cfd88a9d48b0ba2d7a8def00f8517e3da02c86fce6c7863d7\".toReversedByteArray(),\n                    previousOutputIndex = 0,\n                    sigScript = \"4730440220302e597d74aebcb0bf7f372be156252017af190bd586466104b079fba4b7efa7022037ebbf84e096ef3d966123a93a83586012353c1d2c11c967d21acf1c94c45df001210347235e12207d21b6093d9fd93a0df4d589a0d44252b98b2e934a8da5ab1d1654\".hexToByteArray(),\n                    sequence = 4294967295\n                )\n            ),\n            outputs = listOf(\n                TransactionOutput(\n                    value = 10792000,\n                    index = 0,\n                    script = \"00148749115073ad59a6f3587f1f9e468adedf01473f\".hexToByteArray(),\n                    type = ScriptType.P2WPKH,\n                    lockingScriptPayload = byteArrayOf()\n                ),\n                TransactionOutput(\n                    value = 0,\n                    index = 0,\n                    script = \"6a4c500000b919000189658af37cd16dbd16e4186ea13c5d8e1f40c5b5a0958326067dd923b8fc8f0767f62eb9a7fd57df4f3e775a96ca5b5eabf5057dff98997a3bbd011366703f5e45075f397f7f3c8465da\".hexToByteArray(),\n                    type = ScriptType.P2PK,\n                    lockingScriptPayload = byteArrayOf()\n                )\n            )\n        )\n}\n"
  },
  {
    "path": "bitcoincashkit/src/test/kotlin/io/horizontalsystems/bitcoincash/blocks/BitcoinCashBlockValidatorHelperTest.kt",
    "content": "package io.horizontalsystems.bitcoincash.blocks\n\nimport com.nhaarman.mockito_kotlin.mock\nimport com.nhaarman.mockito_kotlin.whenever\nimport io.horizontalsystems.Fixtures\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.bitcoincore.storage.BlockHeader\nimport org.junit.Assert\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\nimport java.security.SecureRandom\n\nobject BitcoinCashBlockValidatorHelperTest : Spek({\n    lateinit var helper: BitcoinCashBlockValidatorHelper\n\n    beforeEachTest {\n        helper = BitcoinCashBlockValidatorHelper(storage)\n    }\n\n    describe(\"#medianTimePast\") {\n        it(\"passes\") {\n            val block3 = Fixtures.block3\n            block3.timestamp = 1000\n\n            val block = chain(block3, 11)\n\n            val medianTime = helper.medianTimePast(block)\n            Assert.assertEquals(1600, medianTime)\n\n        }\n    }\n\n    describe(\"#getSuitableBlock\") {\n        it(\"getSuitableBlock\") {\n            val block1 = Block(height = 1, header = header(10))\n            val block2 = Block(height = 2, header = header(20))\n            val block3 = Block(height = 3, header = header(30))\n\n            whenever(storage.getBlock(hashHash = block2.previousBlockHash)).thenReturn(block1)\n            whenever(storage.getBlock(hashHash = block3.previousBlockHash)).thenReturn(block2)\n\n            Assert.assertEquals(block2, helper.getSuitableBlock(mutableListOf(block1, block2, block3)))\n        }\n\n        it(\"getSuitableBlock_sameTime\") {\n            val block1 = Block(height = 1, header = header(1536779466))\n            val block2 = Block(height = 2, header = header(1536780486))\n            val block3 = Block(height = 3, header = header(1536780486))\n\n            whenever(storage.getBlock(hashHash = block2.previousBlockHash)).thenReturn(block1)\n            whenever(storage.getBlock(hashHash = block3.previousBlockHash)).thenReturn(block2)\n\n            Assert.assertEquals(block2, helper.getSuitableBlock(mutableListOf(block1, block2, block3)))\n        }\n    }\n\n\n})\n\nprivate val storage = mock<IStorage>()\nprivate val secRandom = SecureRandom()\n\nprivate fun chain(block: Block, size: Int, timeInterval: Int = 100): Block {\n    var currentBlock = block\n\n    lateinit var nextBlock: Block\n\n    for (i in 0 until size) {\n        val hash = ByteArray(5)\n        secRandom.nextBytes(hash)\n\n        val header = BlockHeader(\n                version = 0,\n                timestamp = currentBlock.timestamp + timeInterval,\n                previousBlockHeaderHash = hash,\n                merkleRoot = byteArrayOf(),\n                bits = 0,\n                nonce = 0,\n                hash = byteArrayOf()\n        )\n\n        nextBlock = Block(header, currentBlock)\n\n        whenever(storage.getBlock(hashHash = nextBlock.previousBlockHash)).thenReturn(currentBlock)\n\n        currentBlock = nextBlock\n    }\n\n    return currentBlock\n}\n\nprivate fun header(time: Long): BlockHeader {\n    val hash = ByteArray(5)\n    secRandom.nextBytes(hash)\n\n    return BlockHeader(\n            version = 0,\n            timestamp = time,\n            previousBlockHeaderHash = hash,\n            merkleRoot = byteArrayOf(),\n            bits = 0,\n            nonce = 0,\n            hash = byteArrayOf(1)\n    )\n}\n\n"
  },
  {
    "path": "bitcoincashkit/src/test/kotlin/io/horizontalsystems/bitcoincash/blocks/validators/DAAValidatorTest.kt",
    "content": "package io.horizontalsystems.bitcoincash.blocks.validators\n\nimport com.nhaarman.mockito_kotlin.any\nimport com.nhaarman.mockito_kotlin.mock\nimport com.nhaarman.mockito_kotlin.whenever\nimport io.horizontalsystems.bitcoincash.blocks.BitcoinCashBlockValidatorHelper\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.bitcoincore.storage.BlockHeader\nimport org.junit.Assert\nimport org.junit.jupiter.api.Assertions\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\nimport java.security.SecureRandom\n\nobject DAAValidatorTest : Spek({\n    val storage = mock<IStorage>()\n    val previousBlock = mock<Block>()\n    val bitsArray = arrayOf(402767433, 402767160, 402767859, 402769255, 402769073, 402768230, 402768938, 402770129, 402769178, 402768431, 402768344, 402768058, 402770579, 402770265, 402769425, 402770133, 402770219, 402770568, 402769751, 402771344, 402774815, 402776069, 402775738, 402776029, 402776668, 402776963, 402777030, 402777486, 402777676, 402777850, 402778389, 402777853, 402777917, 402778925, 402778150, 402776959, 402777541, 402777275, 402776614, 402776265, 402775085, 402776388, 402776022, 402781121, 402779383, 402780026, 402780560, 402781377, 402781639, 402782369, 402782262, 402782501, 402781920, 402778431, 402777875, 402778488, 402778162, 402782064, 402781536, 402780576, 402780417, 402779271, 402779413, 402779788, 402780701, 402779986, 402781638, 402780582, 402782111, 402782023, 402781298, 402782926, 402782587, 402782374, 402782451, 402783036, 402782787, 402782866, 402785537, 402785404, 402785252, 402784800, 402784388, 402784384, 402786868, 402788360, 402788623, 402788330, 402788717, 402789228, 402789871, 402790079, 402790023, 402789814, 402789044, 402788470, 402788538, 402789725, 402789457, 402789246, 402789231, 402789817, 402789385, 402788616, 402789043, 402788902, 402787857, 402785442, 402784682, 402785035, 402786724, 402787705, 402790426, 402792855, 402793573, 402793315, 402792921, 402792516, 402792972, 402791014, 402789795, 402789735, 402789753, 402788776, 402788733, 402788222, 402787366, 402787077, 402785903, 402785470, 402785407, 402785746, 402786248, 402785956, 402784777, 402779948, 402779736, 402779667, 402781944, 402783593, 402784052, 402784620, 402786084, 402784457, 402782480, 402782849, 402781867, 402780446, 402780490, 402780410, 402779819, 402779544, 402780526, 402779837, 402780226, 402781418, 402778933, 402780586, 402780729, 402780332, 402780383, 402778461, 402778469, 402779123, 402775348, 402775650, 402777259, 402776904, 402778138, 402779910, 402781496, 402782315, 402781953, 402784546, 402782688, 402784672, 402785706, 402785254, 402790104, 402791738, 402792541, 402792952, 402792917, 402794729, 402794723, 402793392, 402793524, 402788688, 402791629, 402792162, 402792893, 402793340, 402794146, 402795886, 402796014, 402794823, 402794627, 402795020, 402794758, 402795508)\n    val timestampArray = arrayOf(1534692576, 1534693663, 1534693995, 1534694138, 1534695348, 1534696396, 1534696419, 1534696941, 1534697105, 1534697149, 1534699039, 1534699259, 1534699320, 1534699919, 1534700218, 1534701579, 1534701682, 1534703052, 1534705846, 1534706820, 1534706916, 1534707344, 1534708220, 1534708496, 1534708760, 1534709180, 1534709647, 1534709970, 1534711721, 1534711835, 1534712110, 1534712958, 1534713719, 1534714227, 1534714820, 1534714964, 1534715189, 1534715317, 1534715540, 1534716543, 1534716620, 1534720460, 1534720507, 1534720953, 1534721684, 1534722494, 1534722680, 1534723189, 1534723220, 1534724252, 1534724596, 1534724909, 1534725260, 1534725847, 1534725973, 1534728904, 1534729325, 1534729900, 1534730155, 1534730317, 1534730900, 1534731800, 1534732502, 1534732549, 1534733804, 1534734537, 1534735934, 1534737036, 1534737121, 1534738423, 1534738611, 1534739296, 1534739444, 1534739859, 1534739960, 1534740549, 1534742748, 1534742792, 1534742886, 1534743800, 1534744431, 1534744450, 1534746241, 1534748488, 1534748581, 1534749377, 1534750209, 1534750609, 1534751075, 1534751148, 1534751183, 1534751344, 1534751379, 1534752873, 1534752888, 1534753741, 1534754135, 1534754719, 1534754806, 1534755196, 1534755374, 1534755557, 1534755827, 1534755924, 1534756357, 1534756456, 1534756520, 1534756864, 1534758162, 1534758945, 1534761518, 1534763240, 1534764480, 1534764958, 1534765043, 1534765130, 1534765387, 1534765868, 1534766168, 1534767148, 1534767633, 1534767653, 1534768040, 1534768520, 1534768580, 1534769111, 1534769654, 1534769757, 1534769793, 1534770036, 1534770327, 1534770411, 1534772903, 1534772944, 1534773192, 1534773280, 1534774833, 1534776308, 1534776682, 1534777031, 1534778060, 1534778536, 1534778717, 1534779080, 1534779236, 1534779285, 1534779574, 1534779606, 1534780360, 1534781160, 1534781785, 1534781797, 1534782163, 1534782945, 1534783111, 1534784367, 1534784480, 1534784761, 1534785036, 1534785064, 1534785121, 1534786886, 1534787123, 1534788260, 1534789423, 1534789617, 1534791306, 1534792756, 1534794067, 1534795017, 1534795222, 1534797244, 1534797740, 1534799140, 1534800080, 1534800595, 1534804536, 1534806096, 1534807163, 1534807507, 1534807636, 1534808886, 1534809025, 1534809060, 1534809128, 1534809700, 1534811600, 1534812360, 1534813513, 1534814559, 1534815216, 1534816810, 1534816866, 1534817055, 1534817207, 1534817720, 1534817840, 1534818838, 1534819474, 1534820021) // 544319\n\n    describe(\"#isBlockValidatable\") {\n        val blockHelper = mock<BitcoinCashBlockValidatorHelper>()\n        val validator by memoized {\n            DAAValidator(10 * 60, blockHelper)\n        }\n\n        val block = mock<Block>()\n\n        it(\"is true when median time past later then or equal to 2017 November 3, 14:06 GMT\") {\n            whenever(blockHelper.medianTimePast(block)).thenReturn(1510600000)\n            Assert.assertTrue(validator.isBlockValidatable(block, previousBlock))\n\n        }\n\n        it(\"is false when median time past earlier then 2017 November 3, 14:06 GMT\") {\n            whenever(blockHelper.medianTimePast(block)).thenReturn(1510600000 - 1)\n            Assert.assertFalse(validator.isBlockValidatable(block, previousBlock))\n        }\n    }\n\n    describe(\"#validate\") {\n        val validator by memoized {\n            DAAValidator(10 * 60, BitcoinCashBlockValidatorHelper(storage))\n        }\n\n        it(\"passes\") {\n            val candidateHeader = BlockHeader(\n                    version = 1,\n                    previousBlockHeaderHash = byteArrayOf(1),\n                    merkleRoot = byteArrayOf(1),\n                    timestamp = 1534820198,\n                    bits = 402796414,\n                    nonce = 1748283264,\n                    hash = byteArrayOf(1)\n            )\n\n            val candidate = Block(candidateHeader, 148)\n            var lastBlock = candidate\n            val secRandom = SecureRandom()\n\n            val chunks = mutableListOf<Block>()\n\n            repeat(147) { i ->\n                val hash = ByteArray(5)\n                secRandom.nextBytes(hash)\n\n                val block = Block(\n                        height = candidate.height - i - 1,\n                        header = BlockHeader(\n                                version = 536870912,\n                                previousBlockHeaderHash = hash,\n                                merkleRoot = byteArrayOf(),\n                                timestamp = timestampArray[timestampArray.size - i - 1].toLong(),\n                                bits = bitsArray[bitsArray.size - i - 1].toLong(),\n                                nonce = 0,\n                                hash = byteArrayOf(1)\n                        ))\n\n                whenever(storage.getBlock(hashHash = lastBlock.previousBlockHash)).thenReturn(block)\n\n                lastBlock = block\n                chunks.add(block)\n            }\n\n            val prevBlock = storage.getBlock(candidate.previousBlockHash)!!\n\n            whenever(storage.getBlocksChunk(any(), any())).thenReturn(chunks.sortedBy { it.height })\n\n            Assertions.assertDoesNotThrow {\n                validator.validate(candidate, prevBlock)\n            }\n        }\n    }\n\n})\n"
  },
  {
    "path": "bitcoincashkit/src/test/kotlin/io/horizontalsystems/bitcoincash/blocks/validators/ForkValidatorTest.kt",
    "content": "package io.horizontalsystems.bitcoincash.blocks.validators\n\nimport com.nhaarman.mockito_kotlin.mock\nimport com.nhaarman.mockito_kotlin.whenever\nimport io.horizontalsystems.bitcoincore.blocks.validators.BlockValidatorException\nimport io.horizontalsystems.bitcoincore.blocks.validators.IBlockValidator\nimport io.horizontalsystems.bitcoincore.models.Block\nimport org.junit.Assert\nimport org.junit.jupiter.api.Assertions\nimport org.junit.jupiter.api.assertThrows\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nobject ForkValidatorTest : Spek({\n    val forkHeight = 100\n    val expectedBlockHash = byteArrayOf(1, 2, 3)\n    val concreteBlockValidator = mock<IBlockValidator>()\n    val validator by memoized { ForkValidator(forkHeight, expectedBlockHash, concreteBlockValidator) }\n\n    describe(\"#isBlockValidatable\") {\n        val block = mock<Block>()\n\n        it(\"is true when block height is equal to fork height\") {\n            whenever(block.height).thenReturn(forkHeight)\n            Assert.assertTrue(validator.isBlockValidatable(block, mock()))\n        }\n\n        it(\"is false when block height is not equal to fork height\") {\n            whenever(block.height).thenReturn(104)\n            Assert.assertFalse(validator.isBlockValidatable(block, mock()))\n        }\n\n    }\n\n    describe(\"#validate\") {\n        val block = mock<Block>()\n\n        it(\"validates without any error when block hash is equal to expected block hash\") {\n            whenever(block.headerHash).thenReturn(expectedBlockHash)\n            Assertions.assertDoesNotThrow {\n                validator.validate(block, mock())\n            }\n        }\n\n        it(\"throws exception when block hash is not equal to expected block hash\") {\n            whenever(block.headerHash).thenReturn(byteArrayOf(3, 2, 1))\n            assertThrows<BlockValidatorException.WrongBlockHash> {\n                validator.validate(block, mock())\n            }\n        }\n    }\n\n})\n"
  },
  {
    "path": "bitcoincashkit/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker",
    "content": "mock-maker-inline"
  },
  {
    "path": "bitcoincore/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "bitcoincore/build.gradle",
    "content": "plugins {\n    id 'com.android.library'\n    id 'kotlin-android'\n    id 'kotlin-kapt'\n    id 'maven-publish'\n}\n\nafterEvaluate {\n    publishing {\n        publications {\n            release(MavenPublication) {\n                from components.release\n            }\n        }\n    }\n}\n\nandroid {\n    namespace 'io.horizontalsystems.bitcoincore'\n    compileSdk 34\n\n    defaultConfig {\n        minSdkVersion 23\n        targetSdkVersion 34\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    kapt {\n        arguments {\n            arg(\"room.schemaLocation\", \"$projectDir/schemas\".toString())\n            arg(\"room.incremental\", true)\n        }\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    sourceSets {\n        main.java.srcDirs += 'src/main/kotlin'\n        test.java.srcDirs += 'src/test/kotlin'\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_17\n        targetCompatibility JavaVersion.VERSION_17\n    }\n    kotlinOptions { jvmTarget = '17' }\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version\"\n    implementation 'androidx.annotation:annotation:1.1.0'\n\n    // RxJava\n    implementation 'io.reactivex.rxjava2:rxjava:2.2.19'\n    implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.5'\n\n    // Room\n    implementation 'androidx.room:room-runtime:2.5.0'\n    kapt 'androidx.room:room-compiler:2.5.0'\n\n    implementation 'androidx.room:room-rxjava2:2.5.0'\n\n    // For cryptography\n    implementation 'org.bouncycastle:bcpkix-jdk15on:1.65'\n\n    // JSON\n    implementation 'com.eclipsesource.minimal-json:minimal-json:0.9.5'\n\n    // OkHTTPClient3\n    implementation 'com.squareup.okhttp3:okhttp:4.5.0'\n\n    // HDWallet Kit\n    api 'com.github.horizontalsystems:hd-wallet-kit-android:a74b51f3'\n\n    // Test helpers\n    testImplementation 'junit:junit:4.13.2'\n    testImplementation 'org.junit.jupiter:junit-jupiter:5.6.1'\n    testImplementation \"com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0\"\n    testImplementation \"org.powermock:powermock-api-mockito2:2.0.7\"\n    testImplementation \"org.powermock:powermock-module-junit4:2.0.7\"\n    testImplementation 'org.mockito:mockito-inline:4.4.0'\n    testImplementation \"org.jetbrains.kotlin:kotlin-test-junit:$kotlin_version\"\n\n    // Spek\n    testImplementation \"org.spekframework.spek2:spek-dsl-jvm:2.0.9\"\n    testRuntimeOnly \"org.spekframework.spek2:spek-runner-junit5:2.0.9\"\n    testImplementation \"org.jetbrains.kotlin:kotlin-reflect:$kotlin_version\"\n\n    testImplementation \"com.linkedin.dexmaker:dexmaker-mockito-inline:2.28.3\"\n    testImplementation 'androidx.test.ext:junit:1.1.1'\n}\n"
  },
  {
    "path": "bitcoincore/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "bitcoincore/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n</manifest>\n"
  },
  {
    "path": "bitcoincore/src/main/java/io/horizontalsystems/bitcoincore/DustCalculator.kt",
    "content": "package io.horizontalsystems.bitcoincore\n\nimport io.horizontalsystems.bitcoincore.transactions.TransactionSizeCalculator\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\n\n/**\n * Calculates the minimum amount of BTC or \"dust\" required to broadcast a transaction and pay miner fees.\n * @param dustRelayTxFee\n * @param sizeCalculator\n *\n */\nclass DustCalculator(dustRelayTxFee: Int, val sizeCalculator: TransactionSizeCalculator) {\n    val minFeeRate = dustRelayTxFee / 1000\n\n    /**\n     *@param type The ScriptType (Ex: P2PKH, P2WPKH, P2SH, etc.)\n     *@return The minimum amount of satoshis required to make a transaction.\n     */\n    fun dust(type: ScriptType): Int {\n        // https://github.com/bitcoin/bitcoin/blob/c536dfbcb00fb15963bf5d507b7017c241718bf6/src/policy/policy.cpp#L18\n\n        var size = sizeCalculator.outputSize(type)\n\n        size += when (type) {\n            ScriptType.P2WPKH,\n            ScriptType.P2WSH,\n                -> {\n                sizeCalculator.inputSize(ScriptType.P2WPKH) + sizeCalculator.witnessSize(ScriptType.P2WPKH) / 4\n            }\n\n            ScriptType.P2TR -> {\n                sizeCalculator.inputSize(ScriptType.P2TR) + sizeCalculator.witnessSize(ScriptType.P2TR) / 4\n            }\n\n            else -> {\n                sizeCalculator.inputSize(ScriptType.P2PKH)\n            }\n        }\n\n        return size * minFeeRate\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/java/io/horizontalsystems/bitcoincore/crypto/Base58.java",
    "content": "package io.horizontalsystems.bitcoincore.crypto;\n\nimport java.io.UnsupportedEncodingException;\nimport java.util.Arrays;\n\nimport io.horizontalsystems.bitcoincore.utils.Utils;\n\n/**\n * Provides Base-58 encoding and decoding\n */\npublic class Base58 {\n\n    /** Alphabet used for encoding and decoding */\n    private static final char[] ALPHABET = \"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz\".toCharArray();\n\n    /** Lookup index for US-ASCII characters (code points 0-127) */\n    private static final int[] INDEXES = new int[128];\n\n    static {\n        for (int i=0; i<INDEXES.length; i++)\n            INDEXES[i] = -1;\n        for (int i=0; i<ALPHABET.length; i++)\n            INDEXES[ALPHABET[i]] = i;\n    }\n\n    /**\n     * Encodes a byte array as a Base58 string\n     *\n     * @param   bytes   Array to be encoded\n     * @return          Encoded string\n     */\n    public static String encode(byte[] bytes) {\n        //\n        // Nothing to do for an empty array\n        //\n        if (bytes.length == 0)\n            return \"\";\n        //\n        // Make a copy of the input since we will be modifying it as we go along\n        //\n        byte[] input = Arrays.copyOf(bytes, bytes.length);\n        //\n        // Count the number of leading zeroes (we will need to prefix the encoded result\n        // with this many zero characters)\n        //\n        int zeroCount = 0;\n        while (zeroCount < input.length && input[zeroCount] == 0)\n            zeroCount++;\n        //\n        // Encode the input starting with the first non-zero byte\n        //\n        int offset = zeroCount;\n        byte[] encoded = new byte[input.length*2];\n        int encodedOffset = encoded.length;\n        while (offset < input.length) {\n            byte mod = divMod58(input, offset);\n            if (input[offset] == 0)\n                offset++;\n            encoded[--encodedOffset] = (byte)ALPHABET[mod];\n        }\n        //\n        // Strip any leading zero values in the encoded result\n        //\n        while (encodedOffset < encoded.length && encoded[encodedOffset] == (byte)ALPHABET[0])\n            encodedOffset++;\n        //\n        // Now add the number of leading zeroes that we found in the input array\n        //\n        for (int i=0; i<zeroCount; i++)\n            encoded[--encodedOffset] = (byte)ALPHABET[0];\n        //\n        // Create the return string from the encoded bytes\n        //\n        String encodedResult;\n        try {\n            byte[] stringBytes = Arrays.copyOfRange(encoded, encodedOffset, encoded.length);\n            encodedResult = new String(stringBytes, \"US-ASCII\");\n        } catch (UnsupportedEncodingException exc) {\n            encodedResult = \"\";             // Should never happen\n        }\n        return encodedResult;\n    }\n\n    /**\n     * Decodes a Base58 string\n     *\n     * @param   string                          Encoded string\n     * @return                                  Decoded bytes\n     * @throws      IllegalArgumentException    Invalid Base-58 encoded string\n     */\n    public static byte[] decode(String string) throws IllegalArgumentException {\n        //\n        // Nothing to do if we have an empty string\n        //\n        if (string.length() == 0)\n            return new byte[0];\n        //\n        // Convert the input string to a byte sequence\n        //\n        byte[] input = new byte[string.length()];\n        for (int i=0; i<string.length(); i++) {\n            int codePoint = string.codePointAt(i);\n            int digit = -1;\n            if (codePoint>=0 && codePoint<INDEXES.length)\n                digit = INDEXES[codePoint];\n            if (digit < 0)\n                throw new IllegalArgumentException(\n                        String.format(\"Illegal character %c at index %d\", string.charAt(i), i));\n            input[i] = (byte)digit;\n        }\n        //\n        // Count the number of leading zero characters\n        //\n        int zeroCount = 0;\n        while (zeroCount < input.length && input[zeroCount] == 0)\n            zeroCount++;\n        //\n        // Convert from Base58 encoding starting with the first non-zero character\n        //\n        byte[] decoded = new byte[input.length];\n        int decodedOffset = decoded.length;\n        int offset = zeroCount;\n        while (offset < input.length) {\n            byte mod = divMod256(input, offset);\n            if (input[offset] == 0)\n                offset++;\n            decoded[--decodedOffset] = mod;\n        }\n        //\n        // Strip leading zeroes from the decoded result\n        //\n        while (decodedOffset < decoded.length && decoded[decodedOffset] == 0)\n            decodedOffset++;\n        //\n        // Return the decoded result prefixed with the number of leading zeroes\n        // that were in the original string\n        //\n        byte[] output = Arrays.copyOfRange(decoded, decodedOffset-zeroCount, decoded.length);\n        return output;\n    }\n\n    /**\n     * Decode a Base58-encoded checksummed string and verify the checksum.  The\n     * checksum will then be removed from the decoded value.\n     *\n     * @param   string                      Base-58 encoded checksummed string\n     * @return                              Decoded value\n     * @throws  IllegalArgumentException    The string is not valid or the checksum is incorrect\n     */\n    public static byte[] decodeChecked(String string) throws IllegalArgumentException {\n        //\n        // Decode the string\n        //\n        byte[] decoded = decode(string);\n        if (decoded.length < 4)\n            throw new IllegalArgumentException(\"Decoded string is too short\");\n        //\n        // Verify the checksum contained in the last 4 bytes\n        //\n        byte[] bytes = Arrays.copyOfRange(decoded, 0, decoded.length-4);\n        byte[] checksum = Arrays.copyOfRange(decoded, decoded.length-4, decoded.length);\n        byte[] hash = Arrays.copyOfRange(Utils.doubleDigest(bytes), 0, 4);\n        if (!Arrays.equals(hash, checksum))\n            throw new IllegalArgumentException(\"Checksum is not correct\");\n        //\n        // Return the result without the checksum bytes\n        //\n        return bytes;\n    }\n\n    /**\n     * Divide the current number by 58 and return the remainder.  The input array\n     * is updated for the next round.\n     *\n     * @param   number  Number array\n     * @param   offset  Offset within the array\n     * @return          The remainder\n     */\n    private static byte divMod58(byte[] number, int offset) {\n        int remainder = 0;\n        for (int i=offset; i<number.length; i++) {\n            int digit = (int)number[i]&0xff;\n            int temp = remainder*256 + digit;\n            number[i] = (byte)(temp/58);\n            remainder = temp%58;\n        }\n        return (byte)remainder;\n    }\n\n    /**\n     * Divide the current number by 256 and return the remainder.  The input array\n     * is updated for the next round.\n     *\n     * @param   number  Number array\n     * @param   offset  Offset within the array\n     * @return          The remainder\n     */\n    private static byte divMod256(byte[] number, int offset) {\n        int remainder = 0;\n        for (int i=offset; i<number.length; i++) {\n            int digit = (int)number[i]&0xff;\n            int temp = remainder*58 + digit;\n            number[i] = (byte)(temp/256);\n            remainder = temp%256;\n        }\n        return (byte)remainder;\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/java/io/horizontalsystems/bitcoincore/crypto/Bech32.java",
    "content": "package io.horizontalsystems.bitcoincore.crypto;\n\nimport androidx.annotation.NonNull;\n\npublic class Bech32 {\n    /** The Bech32 character set for encoding. */\n    static final String CHARSET = \"qpzry9x8gf2tvdw0s3jn54khce6mua7l\";\n\n    /** The Bech32 character set for decoding. */\n    static final byte[] CHARSET_REV = {\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n            -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1,\n            15, -1, 10, 17, 21, 20, 26, 30,  7,  5, -1, -1, -1, -1, -1, -1,\n            -1, 29, -1, 24, 13, 25,  9,  8, 23, -1, 18, 22, 31, 27, 19, -1,\n            1,  0,  3, 16, 11, 28, 12, 14,  6,  4,  2, -1, -1, -1, -1, -1,\n            -1, 29, -1, 24, 13, 25,  9,  8, 23, -1, 18, 22, 31, 27, 19, -1,\n            1,  0,  3, 16, 11, 28, 12, 14,  6,  4,  2, -1, -1, -1, -1, -1\n    };\n\n    public static class Bech32Data {\n        public final String hrp;\n        public final byte[] data;\n        @NonNull\n        public final Encoding encoding;\n\n        Bech32Data(final String hrp, final byte[] data) {\n            this.hrp = hrp;\n            this.data = data;\n            this.encoding = (data[0] == 0x00 ? Encoding.BECH32 : Encoding.BECH32M);\n        }\n\n        public Bech32Data(String hrp, byte[] data, Encoding encoding) {\n            this.hrp = hrp;\n            this.data = data;\n            this.encoding = encoding;\n        }\n    }\n\n    public enum Encoding {\n        BECH32(1), BECH32M(0x2bc830a3);\n\n        final int checksumConstant;\n\n        Encoding(int checksumConstant) {\n            this.checksumConstant = checksumConstant;\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/java/io/horizontalsystems/bitcoincore/crypto/Bech32Cash.java",
    "content": "\npackage io.horizontalsystems.bitcoincore.crypto;\n\nimport io.horizontalsystems.bitcoincore.exceptions.AddressFormatException;\n\npublic class Bech32Cash extends Bech32 {\n\n    /**\n     * This function will compute what 8 5-bit values to XOR into the last 8 input\n     * values, in order to make the checksum 0. These 8 values are packed together\n     * in a single 40-bit integer. The higher bits correspond to earlier values.\n     */\n    private static long polymod(final byte[] v) {\n\n        long c = 1;\n        for (byte d : v) {\n            // First, determine the value of c0:\n            byte c0 = (byte) (c >> 35);\n\n            // Then compute c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + d:\n            c = ((c & 0x07ffffffffL) << 5) ^ d;\n\n            // Finally, for each set bit n in c0, conditionally add {2^n}k(x):\n            if ((c0 & 0x01) != 0) c ^= 0x98f2bc8e61L;\n            if ((c0 & 0x02) != 0) c ^= 0x79b76d99e2L;\n            if ((c0 & 0x04) != 0) c ^= 0xf33e5fb3c4L;\n            if ((c0 & 0x08) != 0) c ^= 0xae2eabe2a8L;\n            if ((c0 & 0x10) != 0) c ^= 0x1e4f43e470L;\n        }\n\n        // polymod computes what value to xor into the final values to make the\n        // checksum 0. However, if we required that the checksum was 0, it would be\n        // the case that appending a 0 to a valid list of values would result in a\n        // new valid list. For that reason, cashaddr requires the resulting checksum\n        // to be 1 instead.\n        return c ^ 1;\n    }\n\n    /** Convert to lower case. Assume the input is a character. */\n    static char lowerCase(char c) {\n        // ASCII black magic.\n        return (char) (c | 0x20);\n    }\n\n    /** Expand the address prefix for the checksum computation. */\n    static byte[] expandPrefix(String prefix) {\n        byte[] ret = new byte[prefix.length() + 1];\n        byte[] prefixBytes = prefix.getBytes();\n\n        for (int i = 0; i < prefix.length(); ++i) {\n            ret[i] = (byte) (prefixBytes[i] & 0x1f);\n        }\n\n        ret[prefix.length()] = 0;\n        return ret;\n    }\n\n    /** Verify a checksum. */\n    static boolean verifyChecksum(String prefix, byte[] payload) {\n        return polymod(cat(expandPrefix(prefix), payload)) == 0;\n    }\n\n    /** Create a checksum. */\n    static byte[] createChecksum(String prefix, final byte[] payload) {\n        byte[] enc = cat(expandPrefix(prefix), payload);\n        // Append 8 zeroes.\n        byte[] enc2 = new byte[enc.length + 8];\n        System.arraycopy(enc, 0, enc2, 0, enc.length);\n        // Determine what to XOR into those 8 zeroes.\n        long mod = polymod(enc2);\n        byte[] ret = new byte[8];\n        for (int i = 0; i < 8; ++i) {\n            // Convert the 5-bit groups in mod to checksum values.\n            ret[i] = (byte) ((mod >> (5 * (7 - i))) & 0x1f);\n        }\n\n        return ret;\n    }\n\n    /** Encode a cashaddr string. */\n    public static String encode(String prefix, byte[] payload) {\n        byte[] checksum = createChecksum(prefix, payload);\n        byte[] combined = cat(payload, checksum);\n        StringBuilder ret = new StringBuilder(prefix + ':');\n\n        //ret.setLength(ret.length() + combined.length);\n        for (byte c : combined) {\n            ret.append(CHARSET.charAt(c));\n        }\n\n        return ret.toString();\n    }\n\n    /**  Decode a cashaddr string. */\n    public static Bech32Data decode(String str, String defaultPrefix) {\n        // Go over the string and do some sanity checks.\n        boolean lower = false, upper = false, hasNumber = false;\n        int prefixSize = 0;\n        for (int i = 0; i < str.length(); ++i) {\n            char c = str.charAt(i);\n            if (c >= 'a' && c <= 'z') {\n                lower = true;\n                continue;\n            }\n\n            if (c >= 'A' && c <= 'Z') {\n                upper = true;\n                continue;\n            }\n\n            if (c >= '0' && c <= '9') {\n                // We cannot have numbers in the prefix.\n                hasNumber = true;\n                continue;\n            }\n\n            if (c == ':') {\n                // The separator cannot be the first character, cannot have number\n                // and there must not be 2 separators.\n                if (hasNumber || i == 0 || prefixSize != 0) {\n                    throw new AddressFormatException(\"cashaddr:  \" + str + \": The separator cannot be the first character, cannot have number and there must not be 2 separators\");\n                }\n\n                prefixSize = i;\n                continue;\n            }\n\n            // We have an unexpected character.\n            throw new AddressFormatException(\"cashaddr:  \" + str + \": Unexpected character at pos \" + i);\n        }\n\n        // We can't have both upper case and lowercase.\n        if (upper && lower) {\n            throw new AddressFormatException(\"cashaddr:  \" + str + \": Cannot contain both upper and lower case letters\");\n        }\n\n        // Get the prefix.\n        StringBuilder prefix;\n        if (prefixSize == 0) {\n            prefix = new StringBuilder(defaultPrefix);\n        } else {\n            prefix = new StringBuilder(str.substring(0, prefixSize).toLowerCase());\n\n            // Now add the ':' in the size.\n            prefixSize++;\n        }\n\n        // Decode values.\n        final int valuesSize = str.length() - prefixSize;\n        byte[] values = new byte[valuesSize];\n        for (int i = 0; i < valuesSize; ++i) {\n            char c = str.charAt(i + prefixSize);\n            // We have an invalid char in there.\n            if (c > 127 || CHARSET_REV[c] == -1) {\n                throw new AddressFormatException(\"cashaddr:  \" + str + \": Unexpected character at pos \" + i);\n            }\n\n            values[i] = CHARSET_REV[c];\n        }\n\n        // Verify the checksum.\n        if (!verifyChecksum(prefix.toString(), values)) {\n            throw new AddressFormatException(\"cashaddr:  \" + str + \": Invalid Checksum \");\n        }\n\n        byte[] result = new byte[values.length - 8];\n        System.arraycopy(values, 0, result, 0, values.length - 8);\n\n        return new Bech32Data(prefix.toString(), result);\n    }\n\n    /**\n     * Convert from one power-of-2 number base to another.\n     *\n     * If padding is enabled, this always return true. If not, then it returns true\n     * of all the bits of the input are encoded in the output.\n     */\n    public static boolean convertBits(byte[] out, byte[] it, int fromBits, int toBits, boolean pad) {\n        int acc = 0;\n        int bits = 0;\n        final int maxv = (1 << toBits) - 1;\n        final int max_acc = (1 << (fromBits + toBits - 1)) - 1;\n        int x = 0;\n        for (int i = 0; i < it.length; ++i) {\n            acc = ((acc << fromBits) | (it[i] & 0xff)) & max_acc;\n            bits += fromBits;\n            while (bits >= toBits) {\n                bits -= toBits;\n                out[x] = (byte) ((acc >> bits) & maxv);\n                ++x;\n            }\n        }\n\n        // We have remaining bits to encode but do not pad.\n        if (!pad && bits != 0) {\n            return false;\n        }\n\n        // We have remaining bits to encode so we do pad.\n        if (pad && bits != 0) {\n            out[x] = (byte) ((acc << (toBits - bits)) & maxv);\n            ++x;\n        }\n\n        return true;\n    }\n\n    /** Concatenate two byte arrays. */\n    private static byte[] cat(final byte[] x, final byte[] y) {\n        byte[] z = new byte[x.length + y.length];\n        System.arraycopy(x, 0, z, 0, x.length);\n        System.arraycopy(y, 0, z, x.length, y.length);\n        return z;\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/java/io/horizontalsystems/bitcoincore/crypto/Bech32Segwit.java",
    "content": "/*\n * Copyright 2018 Coinomi Ltd\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\npackage io.horizontalsystems.bitcoincore.crypto;\n\nimport java.io.ByteArrayOutputStream;\nimport java.util.Arrays;\nimport java.util.Locale;\n\nimport io.horizontalsystems.bitcoincore.exceptions.AddressFormatException;\n\npublic class Bech32Segwit extends Bech32 {\n\n    /** Find the polynomial with value coefficients mod the generator as 30-bit. */\n    private static int polymod(final byte[] values) {\n        int c = 1;\n        for (byte v_i : values) {\n            int c0 = (c >>> 25) & 0xff;\n            c = ((c & 0x1ffffff) << 5) ^ (v_i & 0xff);\n            if ((c0 & 1) != 0) c ^= 0x3b6a57b2;\n            if ((c0 & 2) != 0) c ^= 0x26508e6d;\n            if ((c0 & 4) != 0) c ^= 0x1ea119fa;\n            if ((c0 & 8) != 0) c ^= 0x3d4233dd;\n            if ((c0 & 16) != 0) c ^= 0x2a1462b3;\n        }\n        return c;\n    }\n\n    /** Expand a address prefix for use in checksum computation. */\n    private static byte[] expandPrefix(final String prefix) {\n        int prefixLength = prefix.length();\n        byte ret[] = new byte[prefixLength * 2 + 1];\n        for (int i = 0; i < prefixLength; ++i) {\n            int c = prefix.charAt(i) & 0x7f; // Limit to standard 7-bit ASCII\n            ret[i] = (byte) ((c >>> 5) & 0x07);\n            ret[i + prefixLength + 1] = (byte) (c & 0x1f);\n        }\n        ret[prefixLength] = 0;\n        return ret;\n    }\n\n    /** Verify a checksum. */\n    private static Encoding verifyChecksum(final String prefix, final byte[] values) {\n        byte[] prefixExpanded = expandPrefix(prefix);\n        byte[] combined = new byte[prefixExpanded.length + values.length];\n        System.arraycopy(prefixExpanded, 0, combined, 0, prefixExpanded.length);\n        System.arraycopy(values, 0, combined, prefixExpanded.length, values.length);\n        int check = polymod(combined);\n        for (Encoding encoding : Encoding.values()) {\n            if (check == encoding.checksumConstant) {\n                return encoding;\n            }\n        }\n        return null;\n    }\n\n    /** Create a checksum. */\n    private static byte[] createChecksum(final String prefix, Encoding encoding, final byte[] values) {\n        byte[] prefixExpanded = expandPrefix(prefix);\n        byte[] enc = new byte[prefixExpanded.length + values.length + 6];\n        System.arraycopy(prefixExpanded, 0, enc, 0, prefixExpanded.length);\n        System.arraycopy(values, 0, enc, prefixExpanded.length, values.length);\n        int mod = polymod(enc) ^ encoding.checksumConstant;\n        byte[] ret = new byte[6];\n        for (int i = 0; i < 6; ++i) {\n            ret[i] = (byte) ((mod >>> (5 * (5 - i))) & 31);\n        }\n        return ret;\n    }\n\n    /** Encode a Bech32 string. */\n    public static String encode(final Bech32Data bech32) throws AddressFormatException {\n        return encode(bech32.hrp, bech32.encoding, bech32.data);\n    }\n\n    /** Encode a Bech32 string. */\n    public static String encode(String prefix, Encoding encoding, final byte[] values) throws AddressFormatException {\n        if (prefix.length() < 1) throw new AddressFormatException(\"Human-readable part is too short\");\n        if (prefix.length() > 83) throw new AddressFormatException(\"Human-readable part is too long\");\n        prefix = prefix.toLowerCase(Locale.ROOT);\n        byte[] checksum = createChecksum(prefix, encoding, values);\n        byte[] combined = new byte[values.length + checksum.length];\n        System.arraycopy(values, 0, combined, 0, values.length);\n        System.arraycopy(checksum, 0, combined, values.length, checksum.length);\n        StringBuilder sb = new StringBuilder(prefix.length() + 1 + combined.length);\n        sb.append(prefix);\n        sb.append('1');\n        for (byte b : combined) {\n            sb.append(CHARSET.charAt(b));\n        }\n        return sb.toString();\n    }\n\n    /** Decode a Bech32 string. */\n    public static Bech32Data decode(final String str) throws AddressFormatException {\n        boolean lower = false, upper = false;\n        if (str.length() < 8) throw new AddressFormatException(\"Input too short\");\n        if (str.length() > 90) throw new AddressFormatException(\"Input too long\");\n        for (int i = 0; i < str.length(); ++i) {\n            char c = str.charAt(i);\n            if (c < 33 || c > 126) throw new AddressFormatException(\"Characters out of range\");\n            if (c >= 'a' && c <= 'z') lower = true;\n            if (c >= 'A' && c <= 'Z') upper = true;\n        }\n        if (lower && upper) throw new AddressFormatException(\"Cannot mix upper and lower cases\");\n        int pos = str.lastIndexOf('1');\n        if (pos < 1) throw new AddressFormatException(\"Missing human-readable part\");\n        if (pos + 7 > str.length()) throw new AddressFormatException(\"Data part too short\");\n        byte[] values = new byte[str.length() - 1 - pos];\n        for (int i = 0; i < str.length() - 1 - pos; ++i) {\n            char c = str.charAt(i + pos + 1);\n            if (CHARSET_REV[c] == -1) throw new AddressFormatException(\"Characters out of range\");\n            values[i] = CHARSET_REV[c];\n        }\n        String prefix = str.substring(0, pos).toLowerCase(Locale.ROOT);\n        Encoding encoding = verifyChecksum(prefix, values);\n        if (encoding == null) {\n            throw new AddressFormatException(\"Invalid checksum\");\n        }\n        return new Bech32Data(prefix, Arrays.copyOfRange(values, 0, values.length - 6), encoding);\n    }\n\n    /** General power-of-2 base conversion */\n    public static byte[] convertBits(final byte[] data, final int start, final int size, final int fromBits, final int toBits, final boolean pad) throws AddressFormatException {\n        int acc = 0;\n        int bits = 0;\n        ByteArrayOutputStream out = new ByteArrayOutputStream(64);\n        final int maxv = (1 << toBits) - 1;\n        final int max_acc = (1 << (fromBits + toBits - 1)) - 1;\n        for (int i = 0; i < size; i++) {\n            int value = data[i + start] & 0xff;\n            if ((value >>> fromBits) != 0) {\n                throw new AddressFormatException(\"Invalid data range: data[\" + i + \"]=\" + value + \" (fromBits=\" + fromBits + \")\");\n            }\n            acc = ((acc << fromBits) | value) & max_acc;\n            bits += fromBits;\n            while (bits >= toBits) {\n                bits -= toBits;\n                out.write((acc >>> bits) & maxv);\n            }\n        }\n        if (pad) {\n            if (bits > 0)\n                out.write((acc << (toBits - bits)) & maxv);\n        } else if (bits >= fromBits || ((acc << (toBits - bits)) & maxv) != 0) {\n            throw new AddressFormatException(\"Could not convert bits, invalid padding\");\n        }\n        return out.toByteArray();\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/java/io/horizontalsystems/bitcoincore/crypto/CompactBits.java",
    "content": "package io.horizontalsystems.bitcoincore.crypto;\n\nimport java.math.BigInteger;\n\nimport io.horizontalsystems.bitcoincore.utils.Utils;\n\npublic class CompactBits {\n\n    /**\n     * The \"compact\" format is a representation of a whole\n     * number N using an unsigned 32bit number similar to a\n     * floating point format.\n     * The most significant 8 bits are the unsigned exponent of base 256.\n     * This exponent can be thought of as \"number of bytes of N\".\n     * The lower 23 bits are the mantissa.\n     * Bit number 24 (0x800000) represents the sign of N.\n     * N = (-1^sign) * mantissa * 256^(exponent-3)\n     *\n     * Satoshi's original implementation used BN_bn2mpi() and BN_mpi2bn().\n     * MPI uses the most significant bit of the first byte as sign.\n     * Thus 0x1234560000 is compact (0x05123456)\n     * and  0xc0de000000 is compact (0x0600c0de)\n     *\n     * Bitcoin only uses this \"compact\" format for encoding difficulty\n     * targets, which are unsigned 256bit quantities.  Thus, all the\n     * complexities of the sign bit and using base 256 are probably an\n     * implementation accident.\n     */\n    public static BigInteger decode(long compact) {\n        int size = ((int) (compact >> 24)) & 0xFF;\n        byte[] bytes = new byte[4 + size];\n        bytes[3] = (byte) size;\n        if (size >= 1) bytes[4] = (byte) ((compact >> 16) & 0xFF);\n        if (size >= 2) bytes[5] = (byte) ((compact >> 8) & 0xFF);\n        if (size >= 3) bytes[6] = (byte) (compact & 0xFF);\n\n        return decodeMPI(bytes, true);\n    }\n\n    /**\n     * MPI encoded numbers are produced by the OpenSSL BN_bn2mpi function.\n     * They consist of a 4 byte big endian length field,\n     * followed by the stated number of bytes\n     * representing the number in big endian format (with a sign bit).\n     *\n     * @param hasLength can be set to false if the given array is missing the 4 byte length field\n     */\n    public static BigInteger decodeMPI(byte[] mpi, boolean hasLength) {\n        byte[] buf;\n        if (hasLength) {\n            int length = (int) Utils.readUint32BE(mpi, 0);\n            buf = new byte[length];\n            System.arraycopy(mpi, 4, buf, 0, length);\n        } else\n            buf = mpi;\n        if (buf.length == 0)\n            return BigInteger.ZERO;\n        boolean isNegative = (buf[0] & 0x80) == 0x80;\n        if (isNegative)\n            buf[0] &= 0x7f;\n        BigInteger result = new BigInteger(buf);\n        return isNegative ? result.negate() : result;\n    }\n\n    /**\n     * @see CompactBits#decode\n     */\n    public static long encode(BigInteger value) {\n        long result;\n        int size = value.toByteArray().length;\n        if (size <= 3)\n            result = value.longValue() << 8 * (3 - size);\n        else\n            result = value.shiftRight(8 * (size - 3)).longValue();\n        // The 0x00800000 bit denotes the sign.\n        // Thus, if it is already set, divide the mantissa by 256 and increase the exponent.\n        if ((result & 0x00800000L) != 0) {\n            result >>= 8;\n            size++;\n        }\n        result |= size << 24;\n        result |= value.signum() == -1 ? 0x00800000 : 0;\n        return result;\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/java/io/horizontalsystems/bitcoincore/crypto/MurmurHash3.java",
    "content": "package io.horizontalsystems.bitcoincore.crypto;\n\n/**\n * Applies the MurmurHash3 (x86_32) algorithm to the given data.\n * See this <a href=\"https://github.com/aappleby/smhasher/blob/master/src/MurmurHash3.cpp\">C++ code for the original.</a>\n */\npublic final class MurmurHash3 {\n\n    /**\n     * Rotate a 32-bit value left by the specified number of bits\n     *\n     * @param       x               The bit value\n     * @param       count           The number of bits to rotate\n     * @return                      The rotated value\n     */\n    private static int rotateLeft32(int x, int count) {\n        return (x<<count) | (x>>>(32-count));\n    }\n\n    /**\n     * Performs a MurmurHash3\n     *\n     * @param       filter          Filter data\n     * @param       nTweak          Random value to add to the hash seed\n     * @param       hashNum         The hash number\n     * @param       object          The byte array to hash\n     * @return                      The hash of the object using the specified hash number\n     */\n    public static int hash(byte[] filter, long nTweak, int hashNum, byte[] object) {\n        int h1 = (int)(hashNum * 0xFBA4C795L + nTweak);\n        final int c1 = 0xcc9e2d51;\n        final int c2 = 0x1b873593;\n\n        int numBlocks = (object.length / 4) * 4;\n        // body\n        for(int i = 0; i < numBlocks; i += 4) {\n            int k1 = (object[i] & 0xFF) |\n                    ((object[i+1] & 0xFF) << 8) |\n                    ((object[i+2] & 0xFF) << 16) |\n                    ((object[i+3] & 0xFF) << 24);\n\n            k1 *= c1;\n            k1 = rotateLeft32(k1, 15);\n            k1 *= c2;\n\n            h1 ^= k1;\n            h1 = rotateLeft32(h1, 13);\n            h1 = h1*5+0xe6546b64;\n        }\n\n        int k1 = 0;\n        switch(object.length & 3) {\n            case 3:\n                k1 ^= (object[numBlocks + 2] & 0xff) << 16;\n                // Fall through.\n            case 2:\n                k1 ^= (object[numBlocks + 1] & 0xff) << 8;\n                // Fall through.\n            case 1:\n                k1 ^= (object[numBlocks] & 0xff);\n                k1 *= c1; k1 = rotateLeft32(k1, 15); k1 *= c2; h1 ^= k1;\n                // Fall through.\n            default:\n                // Do nothing.\n                break;\n        }\n\n        // finalization\n        h1 ^= object.length;\n        h1 ^= h1 >>> 16;\n        h1 *= 0x85ebca6b;\n        h1 ^= h1 >>> 13;\n        h1 *= 0xc2b2ae35;\n        h1 ^= h1 >>> 16;\n\n        return (int)((h1&0xFFFFFFFFL) % (filter.length * 8));\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/java/io/horizontalsystems/bitcoincore/crypto/schnorr/Pair.java",
    "content": "package io.horizontalsystems.bitcoincore.crypto.schnorr;\n\npublic class Pair<K, V> {\n\n    private K elementLeft = null;\n    private V elementRight = null;\n\n    protected Pair() {\n    }\n\n    public static <K, V> Pair<K, V> of(K elementLeft, V elementRight) {\n        return new Pair<K, V>(elementLeft, elementRight);\n    }\n\n    public Pair(K elementLeft, V elementRight) {\n        this.elementLeft = elementLeft;\n        this.elementRight = elementRight;\n    }\n\n    public K getLeft() {\n        return elementLeft;\n    }\n\n    public V getRight() {\n        return elementRight;\n    }\n\n    public boolean equals(Pair<K, V> p) {\n        return (this.elementLeft.equals(p.getLeft())) && (this.elementRight.equals(p.getRight()));\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/java/io/horizontalsystems/bitcoincore/crypto/schnorr/Point.java",
    "content": "package io.horizontalsystems.bitcoincore.crypto.schnorr;\n\nimport java.math.BigInteger;\nimport java.security.NoSuchAlgorithmException;\n\npublic class Point  {\n\n    final static private BigInteger p = new BigInteger(\"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F\", 16);\n    final static private BigInteger n = new BigInteger(\"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141\", 16);\n\n    final static private Point G = new Point(\n            new BigInteger(\"79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798\", 16),\n            new BigInteger(\"483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8\", 16)\n    );\n\n    private Pair<BigInteger,BigInteger> pair = null;\n\n    public Point(BigInteger x , BigInteger y) {\n        pair = Pair.of(x, y);\n    }\n\n    public Point(byte[] b0, byte[] b1) {\n        pair = Pair.of(new BigInteger(1, b0), new BigInteger(1, b1));\n    }\n\n    public static BigInteger getp() {\n        return p;\n    }\n\n    public static BigInteger getn() {\n        return n;\n    }\n\n    public static Point getG() {\n        return G;\n    }\n\n    public BigInteger getX() {\n        return pair.getLeft();\n    }\n\n    public BigInteger getY() {\n        return pair.getRight();\n    }\n\n    public static BigInteger getX(Point P) {\n        return P.getX();\n    }\n\n    public static BigInteger getY(Point P) {\n        return P.getY();\n    }\n\n    public Pair<BigInteger,BigInteger> getPair() {\n        return pair;\n    }\n\n    public boolean isInfinite() {\n        return pair == null || pair.getLeft() == null || pair.getRight() == null;\n    }\n\n    public static boolean isInfinite(Point P) {\n        return P.isInfinite();\n    }\n\n    public Point add(Point P) {\n        return add(this, P);\n    }\n\n    public static Point add(Point P1, Point P2) {\n\n        if((P1 != null && P2 != null && P1.isInfinite() && P2.isInfinite())) {\n            return infinityPoint();\n        }\n        if(P1 == null || P1.isInfinite()) {\n            return P2;\n        }\n        if(P2 == null || P2.isInfinite()) {\n            return P1;\n        }\n        if(P1.getX().equals(P2.getX()) && !P1.getY().equals(P2.getY())) {\n            return infinityPoint();\n        }\n\n        BigInteger lam = null;\n        if(P1.equals(P2)) {\n            BigInteger base = P2.getY().multiply(BigInteger.valueOf(2));\n            lam = (BigInteger.valueOf(3L).multiply(P1.getX()).multiply(P1.getX()).multiply(base.modPow(p.subtract(BigInteger.valueOf(2)), p))).mod(p);\n        }\n        else {\n            BigInteger base = P2.getX().subtract(P1.getX());\n            lam = ((P2.getY().subtract(P1.getY())).multiply(base.modPow(p.subtract(BigInteger.valueOf(2)), p))).mod(p);\n        }\n\n        BigInteger x3 = (lam.multiply(lam).subtract(P1.getX()).subtract(P2.getX())).mod(p);\n        return new Point(x3, lam.multiply(P1.getX().subtract(x3)).subtract(P1.getY()).mod(p));\n    }\n\n    public Point mul(BigInteger n) {\n        return mul(this, n);\n    }\n\n    public static Point mul(Point P, BigInteger n) {\n\n        Point R = null;\n\n        for(int i = 0; i < 256; i++) {\n            if (n.shiftRight(i).and(BigInteger.ONE).compareTo(BigInteger.ZERO) > 0) {\n                R = add(R, P);\n            }\n            P = add(P, P);\n        }\n\n        return R;\n    }\n\n    public boolean hasEvenY() {\n        return hasEvenY(this);\n    }\n\n    public static boolean hasEvenY(Point P) {\n        return P.getY().mod(BigInteger.valueOf(2)).compareTo(BigInteger.ZERO) == 0;\n    }\n\n    public static boolean isSquare(BigInteger x) {\n        return x.modPow(p.subtract(BigInteger.ONE).mod(BigInteger.valueOf(2)), p).longValue() == 1L;\n    }\n\n    public boolean hasSquareY() {\n        return hasSquareY(this);\n    }\n\n    public static boolean hasSquareY(Point P) {\n        return isSquare(P.getY());\n    }\n\n    public static byte[] taggedHash(String tag, byte[] msg) throws NoSuchAlgorithmException {\n        byte[] tagHash = Util.sha256(tag.getBytes());\n        int len = (tagHash.length * 2) + msg.length;\n        byte[] buf = new byte[len];\n        System.arraycopy(tagHash, 0, buf, 0, tagHash.length);\n        System.arraycopy(tagHash, 0, buf, tagHash.length, tagHash.length);\n        System.arraycopy(msg, 0, buf, tagHash.length * 2, msg.length);\n\n        return Util.sha256(buf);\n    }\n\n    public static byte[] genPubKey(byte[] secKey) throws Exception {\n        BigInteger x = Util.bigIntFromBytes(secKey);\n        if(!(BigInteger.ONE.compareTo(x) <= 0 && x.compareTo(getn().subtract(BigInteger.ONE)) <= 0)) {\n            throw new Exception(\"The secret key must be an integer in the range 1..n-1.\");\n        }\n        Point ret = Point.mul(G, x);\n        return bytesFromPoint(ret);\n    }\n\n    public byte[] toBytes() {\n        return bytesFromPoint(this);\n    }\n\n    public static byte[] bytesFromPoint(Point P) {\n        return Util.bytesFromBigInteger(P.getX());\n    }\n\n    public static Point liftX(byte[] b) {\n\n        BigInteger x = Util.bigIntFromBytes(b);\n        if(x.compareTo(p) >= 0) {\n            return null;\n        }\n        BigInteger y_sq = x.modPow(BigInteger.valueOf(3L), p).add(BigInteger.valueOf(7L)).mod(p);\n        BigInteger y = y_sq.modPow(p.add(BigInteger.ONE).divide(BigInteger.valueOf(4L)), p);\n\n        if(y.modPow(BigInteger.valueOf(2), p).compareTo(y_sq) != 0) {\n            return null;\n        }\n        else {\n            return new Point(x, y.and(BigInteger.ONE).compareTo(BigInteger.ZERO) == 0 ? y : p.subtract(y));\n        }\n    }\n\n    public static Point infinityPoint() {\n        return new Point((BigInteger) null, (BigInteger) null);\n    }\n\n    public boolean equals(Point P) {\n        return getPair().equals(P.getPair());\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/java/io/horizontalsystems/bitcoincore/crypto/schnorr/Schnorr.java",
    "content": "package io.horizontalsystems.bitcoincore.crypto.schnorr;\n\nimport java.math.BigInteger;\nimport java.util.Arrays;\n\npublic class Schnorr    {\n\n    public static byte[] sign(byte[] msg, byte[] secKey, byte[] auxRand) throws Exception    {\n        if(msg.length != 32)    {\n            throw new Exception(\"The message must be a 32-byte array.\");\n        }\n        BigInteger secKey0 = Util.bigIntFromBytes(secKey);\n\n        if(!(BigInteger.ONE.compareTo(secKey0) <= 0 && secKey0.compareTo(Point.getn().subtract(BigInteger.ONE)) <= 0)) {\n            throw new Exception(\"The secret key must be an integer in the range 1..n-1.\");\n        }\n        Point P = Point.mul(Point.getG(), secKey0);\n        if(!P.hasEvenY())    {\n            secKey0 = Point.getn().subtract(secKey0);\n        }\n        int len = Util.bytesFromBigInteger(secKey0).length + P.toBytes().length + msg.length;\n        byte[] buf = new byte[len];\n        byte[] t = Util.xor(Util.bytesFromBigInteger(secKey0), Point.taggedHash(\"BIP0340/aux\", auxRand));\n        System.arraycopy(t, 0, buf, 0, t.length);\n        System.arraycopy(P.toBytes(), 0, buf, t.length, P.toBytes().length);\n        System.arraycopy(msg, 0, buf, t.length + P.toBytes().length, msg.length);\n        BigInteger k0 = Util.bigIntFromBytes(Point.taggedHash(\"BIP0340/nonce\", buf)).mod(Point.getn());\n        if(k0.compareTo(BigInteger.ZERO) == 0)    {\n            throw new Exception(\"Failure. This happens only with negligible probability.\");\n        }\n        Point R = Point.mul(Point.getG(), k0);\n        BigInteger k = null;\n        if(!R.hasEvenY())    {\n            k = Point.getn().subtract(k0);\n        }\n        else    {\n            k = k0;\n        }\n        len = R.toBytes().length + P.toBytes().length + msg.length;\n        buf = new byte[len];\n        System.arraycopy(R.toBytes(), 0, buf, 0, R.toBytes().length);\n        System.arraycopy(P.toBytes(), 0, buf, R.toBytes().length, P.toBytes().length);\n        System.arraycopy(msg, 0, buf, R.toBytes().length + P.toBytes().length, msg.length);\n        BigInteger e = Util.bigIntFromBytes(Point.taggedHash(\"BIP0340/challenge\", buf)).mod(Point.getn());\n        BigInteger kes = k.add(e.multiply(secKey0)).mod(Point.getn());\n        len = R.toBytes().length + Util.bytesFromBigInteger(kes).length;\n        byte[] sig = new byte[len];\n        System.arraycopy(R.toBytes(), 0, sig, 0, R.toBytes().length);\n        System.arraycopy(Util.bytesFromBigInteger(kes), 0, sig, R.toBytes().length, Util.bytesFromBigInteger(kes).length);\n        if(!verify(msg, P.toBytes(), sig))    {\n            throw new Exception(\"The signature does not pass verification.\");\n        }\n        return sig;\n    }\n\n    public static boolean verify(byte[] msg, byte[] pubkey, byte[] sig) throws Exception    {\n        if(msg.length != 32)    {\n            throw new Exception(\"The message must be a 32-byte array.\");\n        }\n        if(pubkey.length != 32)    {\n            throw new Exception(\"The public key must be a 32-byte array.\");\n        }\n        if(sig.length != 64)    {\n            throw new Exception(\"The signature must be a 64-byte array.\");\n        }\n\n        Point P = Point.liftX(pubkey);\n        if(P == null)    {\n            return false;\n        }\n        BigInteger r = Util.bigIntFromBytes(Arrays.copyOfRange(sig,0, 32));\n        BigInteger s = Util.bigIntFromBytes(Arrays.copyOfRange(sig,32, 64));\n        if(r.compareTo(Point.getp()) >= 0 || s.compareTo(Point.getn()) >= 0)    {\n            return false;\n        }\n        int len = 32 + pubkey.length + msg.length;\n        byte[] buf = new byte[len];\n        System.arraycopy(sig, 0, buf, 0, 32);\n        System.arraycopy(pubkey, 0, buf, 32, pubkey.length);\n        System.arraycopy(msg, 0, buf, 32 + pubkey.length, msg.length);\n        BigInteger e = Util.bigIntFromBytes(Point.taggedHash(\"BIP0340/challenge\", buf)).mod(Point.getn());\n        Point R = Point.add(Point.mul(Point.getG(), s), Point.mul(P, Point.getn().subtract(e)));\n        if(R == null || !R.hasEvenY() || R.getX().compareTo(r) != 0)    {\n            return false;\n        }\n        else    {\n            return true;\n        }\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/java/io/horizontalsystems/bitcoincore/crypto/schnorr/SchnorrOld.java",
    "content": "package io.horizontalsystems.bitcoincore.crypto.schnorr;\n\nimport java.math.BigInteger;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\n\npublic class SchnorrOld {\n    public static final BigInteger p = new BigInteger(\"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F\", 16);\n    public static final BigInteger n = new BigInteger(\"FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141\", 16);\n    public static final BigInteger[] G = {\n            new BigInteger(\"79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798\", 16),\n            new BigInteger(\"483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8\", 16)\n    };\n\n    public static final BigInteger TWO = BigInteger.valueOf(2);\n    public static final BigInteger THREE = BigInteger.valueOf(3);\n\n    private final static char[] hexArray = \"0123456789ABCDEF\".toCharArray();\n\n    public static BigInteger[] point_add(BigInteger[] p1, BigInteger[] p2) {\n        if (p1 == null || p1.length != 2)\n            return p2;\n\n        if (p2 == null || p2.length != 2)\n            return p1;\n\n        if (p1[0].compareTo(p2[0]) == 0 && p1[1].compareTo(p2[1]) != 0)\n            return null;\n\n        BigInteger lam;\n        if (p1[0].compareTo(p2[0]) == 0 && p1[1].compareTo(p2[1]) == 0)\n            lam = (THREE.multiply(p1[0]).multiply(p1[0]).multiply(TWO.multiply(p1[1]).modPow(p.subtract(TWO), p))).mod(p);\n        else\n            lam = (p2[1].subtract(p1[1]).multiply(p2[0].subtract(p1[0]).modPow(p.subtract(TWO), p))).mod(p);\n\n        BigInteger x3 = (lam.multiply(lam).subtract(p1[0]).subtract(p2[0])).mod(p);\n\n        return new BigInteger[]{x3, lam.multiply(p1[0].subtract(x3)).subtract(p1[1]).mod(p)};\n    }\n\n    public static BigInteger[] point_mul(BigInteger[] P, BigInteger n) {\n        BigInteger[] R = null;\n        for (int i = 0; i < 256; i++) {\n            if (BigInteger.ONE.compareTo(n.shiftRight(i).and(BigInteger.ONE)) == 0)\n                R = point_add(R, P);\n            P = point_add(P, P);\n        }\n        return R;\n    }\n\n    public static BigInteger jacobi(BigInteger x) {\n        return x.modPow(p.subtract(BigInteger.ONE).divide(TWO), p);\n    }\n\n    public static BigInteger[] point_from_bytes(byte[] b) {\n        if (b[0] != 2 && b[0] != 3)\n            return null;\n\n        BigInteger odd = b[0] == 3 ? BigInteger.ONE : BigInteger.ZERO;\n        BigInteger x = toBigInteger(b, 1, 32);\n        BigInteger y_sq = x.modPow(THREE, p).add(BigInteger.valueOf(7)).mod(p);\n        BigInteger y0 = y_sq.modPow(p.add(BigInteger.ONE).divide(BigInteger.valueOf(4)), p);\n        if (y_sq.compareTo(y0.modPow(TWO, p)) != 0)\n            return null;\n\n        BigInteger y = y0.and(BigInteger.ONE).compareTo(odd) != 0 ? p.subtract(y0) : y0;\n\n        return new BigInteger[]{x, y};\n    }\n\n    public static byte[] to32BytesData(BigInteger num) {\n        String hexNum = num.toString(16);\n        if (hexNum.length() < 64) {\n            StringBuilder sb = new StringBuilder();\n            for (int i = 0; i < 64 - hexNum.length(); i++)\n                sb.append(\"0\");\n\n            hexNum = sb.append(hexNum).toString();\n        }\n        return hexStringToByteArray(hexNum);\n    }\n\n    public static BigInteger toBigInteger(byte[] data, int startPos, int len) {\n        return new BigInteger(bytesToHex(data, startPos, len), 16);\n    }\n\n    public static BigInteger toBigInteger(byte[] data) {\n        return new BigInteger(bytesToHex(data), 16);\n    }\n\n    public static byte[] bytes_from_point(BigInteger[] point) {\n        byte[] res = new byte[33];\n        res[0] = BigInteger.ONE.compareTo(point[1].and(BigInteger.ONE)) == 0 ? (byte) 0x03 : (byte) 0x02;\n        System.arraycopy(to32BytesData(point[0]), 0, res, 1, 32);\n        return res;\n    }\n\n    public static byte[] schnorr_sign(byte[] msg, BigInteger seckey) {\n        if (msg.length != 32)\n            throw new RuntimeException(\"The message must be a 32-byte array.\");\n\n        if (BigInteger.ZERO.compareTo(seckey) > 0 || seckey.compareTo(n.subtract(BigInteger.ONE)) > 0)\n            throw new RuntimeException(\"The secret key must be an integer in the range 1..n-1.\");\n\n        byte[] resultData = new byte[32 + msg.length];\n        System.arraycopy(to32BytesData(seckey), 0, resultData, 0, 32);\n        System.arraycopy(msg, 0, resultData, 32, msg.length);\n\n        try {\n            BigInteger k0 = toBigInteger(sha256(resultData)).mod(n);\n            if (BigInteger.ZERO.compareTo(k0) == 0)\n                throw new RuntimeException(\"Failure. This happens only with negligible probability.\");\n\n            BigInteger[] R = point_mul(G, k0);\n\n            BigInteger k = BigInteger.ONE.compareTo(jacobi(R[1])) != 0 ? n.subtract(k0) : k0;\n            byte[] R0Bytes = to32BytesData(R[0]);\n            byte[] eData = new byte[32 + 33 + 32];\n            System.arraycopy(R0Bytes, 0, eData, 0, 32);\n            System.arraycopy(bytes_from_point(point_mul(G, seckey)), 0, eData, 32, 33);\n            System.arraycopy(msg, 0, eData, 65, 32);\n            eData = sha256(eData);\n            BigInteger e = toBigInteger(eData).mod(n);\n\n            byte[] finalData = new byte[64];\n            System.arraycopy(R0Bytes, 0, finalData, 0, 32);\n            System.arraycopy(to32BytesData(e.multiply(seckey).add(k).mod(n)), 0, finalData, 32, 32);\n\n            return finalData;\n        } catch (Exception e) {\n            e.printStackTrace();\n            throw new RuntimeException(\"Error occurs during schnorr_sign, e=\" + e);\n        }\n    }\n\n    public static boolean schnorr_verify(byte[] msg, byte[] pubkey, byte[] sig) {\n        if (msg.length != 32)\n            throw new RuntimeException(\"The message must be a 32-byte array.\");\n\n        if (pubkey.length != 33)\n            throw new RuntimeException(\"The public key must be a 33-byte array.\");\n\n        if (sig.length != 64)\n            throw new RuntimeException(\"The signature must be a 64-byte array.\");\n\n        BigInteger[] P = point_from_bytes(pubkey);\n        if (P == null)\n            return false;\n\n        BigInteger r = toBigInteger(sig, 0, 32);\n        BigInteger s = toBigInteger(sig, 32, 32);\n\n        if (r.compareTo(p) >= 0 || s.compareTo(n) >= 0)\n            return false;\n\n        try {\n            byte[] eData = new byte[32 + 33 + 32];\n            System.arraycopy(sig, 0, eData, 0, 32);\n            System.arraycopy(bytes_from_point(P), 0, eData, 32, 33);\n            System.arraycopy(msg, 0, eData, 65, 32);\n            eData = sha256(eData);\n            BigInteger e = toBigInteger(eData).mod(n);\n\n            BigInteger[] R = point_add(point_mul(G, s), point_mul(P, n.subtract(e)));\n            if (R == null || BigInteger.ONE.compareTo(jacobi(R[1])) != 0 || r.compareTo(R[0]) != 0)\n                return false;\n\n            return true;\n        } catch (Exception e) {\n            e.printStackTrace();\n            throw new RuntimeException(\"Error occurs during schnorr_verify, e=\" + e);\n        }\n    }\n\n    public static byte[] hexStringToByteArray(String s) {\n        int len = s.length();\n        byte[] data = new byte[len / 2];\n        for (int i = 0; i < len; i += 2) {\n            data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4)\n                    + Character.digit(s.charAt(i + 1), 16));\n        }\n        return data;\n    }\n\n    public static String bytesToHex(byte[] bytes) {\n        char[] hexChars = new char[bytes.length * 2];\n        for (int j = 0; j < bytes.length; j++) {\n            int v = bytes[j] & 0xFF;\n            hexChars[j * 2] = hexArray[v >>> 4];\n            hexChars[j * 2 + 1] = hexArray[v & 0x0F];\n        }\n        return new String(hexChars);\n    }\n\n    public static String bytesToHex(byte[] bytes, int startPos, int len) {\n        char[] hexChars = new char[len * 2];\n        for (int j = 0, i = startPos; j < len; j++, i++) {\n            int v = bytes[i] & 0xFF;\n            hexChars[j * 2] = hexArray[v >>> 4];\n            hexChars[j * 2 + 1] = hexArray[v & 0x0F];\n        }\n        return new String(hexChars);\n    }\n\n    public static byte[] sha256(byte[] input) throws NoSuchAlgorithmException {\n        MessageDigest digest = MessageDigest.getInstance(\"SHA-256\");\n        return digest.digest(input);\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/java/io/horizontalsystems/bitcoincore/crypto/schnorr/Util.java",
    "content": "package io.horizontalsystems.bitcoincore.crypto.schnorr;\n\nimport java.math.BigInteger;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.util.Arrays;\n\npublic class Util  {\n\n    private static final char[] HEX_ARRAY = \"0123456789ABCDEF\".toCharArray();\n\n    public static byte[] bytesFromBigInteger(BigInteger n) {\n\n        byte[] b = n.toByteArray();\n\n        if(b.length == 32) {\n            return b;\n        }\n        else if(b.length > 32) {\n            return Arrays.copyOfRange(b, b.length - 32, b.length);\n        }\n        else {\n            byte[] buf = new byte[32];\n            System.arraycopy(b, 0, buf, buf.length - b.length, b.length);\n            return buf;\n        }\n    }\n\n    public static BigInteger bigIntFromBytes(byte[] b) {\n        return new BigInteger(1, b);\n    }\n\n    public static byte[] sha256(byte[] b) throws NoSuchAlgorithmException {\n        MessageDigest digest = MessageDigest.getInstance(\"SHA-256\");\n        return digest.digest(b);\n    }\n\n    public static byte[] xor(byte[] b0, byte[] b1)   {\n\n        if(b0.length != b1.length)   {\n            return  null;\n        }\n\n        byte[] ret = new byte[b0.length];\n        int i = 0;\n        for (byte b : b0)   {\n            ret[i] = (byte)(b ^ b1[i]);\n            i++;\n        }\n\n        return ret;\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/java/io/horizontalsystems/bitcoincore/exceptions/AddressFormatException.java",
    "content": "/**\n * Copyright 2013-2014 Ronald W Hoffman\n * <p>\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n * <p>\n * http://www.apache.org/licenses/LICENSE-2.0\n * <p>\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.horizontalsystems.bitcoincore.exceptions;\n\n/**\n * AddressFormatException is thrown if an invalid Bitcoin address is detected.  An\n * address is invalid if it is not 20-bytes, has an incorrect version, or the\n * checksum is not valid.\n */\npublic class AddressFormatException extends BitcoinException {\n\n    public AddressFormatException(String msg) {\n        super(msg);\n    }\n\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/java/io/horizontalsystems/bitcoincore/exceptions/BitcoinException.java",
    "content": "package io.horizontalsystems.bitcoincore.exceptions;\n\n/**\n * Base exception for bitcoin app.\n *\n * @author liaoxuefeng\n */\npublic class BitcoinException extends RuntimeException {\n\n    public BitcoinException() {\n    }\n\n    public BitcoinException(String message) {\n        super(message);\n    }\n\n    public BitcoinException(Throwable cause) {\n        super(cause);\n    }\n\n    public BitcoinException(String message, Throwable cause) {\n        super(message, cause);\n    }\n\n}\n\n"
  },
  {
    "path": "bitcoincore/src/main/java/io/horizontalsystems/bitcoincore/io/BitcoinInput.java",
    "content": "package io.horizontalsystems.bitcoincore.io;\n\nimport java.io.EOFException;\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.nio.charset.StandardCharsets;\n\n/**\n * Input \"stream\" for bitcoin protocol.\n *\n * @author Michael Liao\n */\npublic class BitcoinInput implements AutoCloseable {\n\n    private static byte[] EMPTY_BYTES = new byte[0];\n    protected final InputStream in;\n    private byte[] bufferOf8bytes = new byte[8];\n\n    public BitcoinInput(InputStream in) {\n        this.in = in;\n    }\n\n    /**\n     * Read btc var int (1~4 bytes). Reference:\n     * https://en.bitcoin.it/wiki/Protocol_documentation#Variable_length_integer\n     *\n     * @return long value as int.\n     * @throws IOException\n     */\n    public long readVarInt() throws IOException {\n        byte[] buffer = new byte[1];\n        if (in.read(buffer) < 0) {\n            throw new EOFException();\n        }\n        int ch = 0xff & buffer[0];\n        if (ch < 0xfd) {\n            return ch;\n        }\n        if (ch == 0xfd) {\n            int ch1 = in.read();\n            int ch2 = in.read();\n            if ((ch1 | ch2) < 0) {\n                throw new EOFException();\n            }\n            return (ch2 << 8) + (ch1 << 0);\n        }\n        if (ch == 0xfe) {\n            return readInt();\n        }\n        return readLong();\n    }\n\n    /**\n     * Read next byte of data.\n     *\n     * @return int value as byte, or -1 if EOF.\n     * @throws IOException\n     */\n    public int read() throws IOException {\n        return in.read();\n    }\n\n    /**\n     * Read and fill bytes into byte array.\n     *\n     * @param b Arbitrary byte array\n     * @throws IOException\n     */\n    public void readFully(byte b[]) throws IOException {\n        int off = 0;\n        int len = b.length;\n        int n = 0;\n        while (n < len) {\n            int count = in.read(b, off + n, len - n);\n            if (count < 0) {\n                throw new EOFException();\n            }\n            n += count;\n        }\n    }\n\n    /**\n     * Read and castes the read value to byte\n     * @return The converted int\n     * @throws IOException\n     */\n    public byte readByte() throws IOException {\n        int ch = in.read();\n        if (ch < 0) {\n            throw new EOFException();\n        }\n        return (byte) (ch);\n    }\n    /**\n     * Read and return\n     * @return The read value\n     * @throws IOException\n     */\n    public int readUnsignedByte() throws IOException {\n        int ch = in.read();\n        if (ch < 0) {\n            throw new EOFException();\n        }\n        return ch;\n    }\n    /**\n     * Read and create a short\n     *\n     * @return Converted Byte Array\n     * @throws IOException\n     */\n    public short readShort() throws IOException {\n        int ch1 = in.read();\n        int ch2 = in.read();\n        if ((ch1 | ch2) < 0) {\n            throw new EOFException();\n        }\n        return (short) ((ch2 << 8) + (ch1 << 0));\n    }\n    /**\n     * Read and create an unsigned short\n     *\n     * @return Converted Byte Array\n     * @throws IOException\n     */\n    public int readUnsignedShort() throws IOException {\n        int ch1 = in.read();\n        int ch2 = in.read();\n        if ((ch1 | ch2) < 0) {\n            throw new EOFException();\n        }\n        return (ch2 << 8) + (ch1 << 0);\n    }\n\n    public char readChar() throws IOException {\n        int ch1 = in.read();\n        int ch2 = in.read();\n        if ((ch1 | ch2) < 0) {\n            throw new EOFException();\n        }\n        return (char) ((ch2 << 8) + (ch1 << 0));\n    }\n    /**\n     * Read and create an int\n     *\n     * @return Converted Byte Array\n     * @throws IOException\n     */\n    public int readInt() throws IOException {\n        int ch1 = in.read();\n        int ch2 = in.read();\n        int ch3 = in.read();\n        int ch4 = in.read();\n        if ((ch1 | ch2 | ch3 | ch4) < 0) {\n            throw new EOFException();\n        }\n        return ((ch4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0));\n    }\n    /**\n     * Read and create an unsigned int\n     *\n     * @return Converted Byte Array\n     * @throws IOException\n     */\n    public long readUnsignedInt() throws IOException {\n        int ch1 = in.read();\n        int ch2 = in.read();\n        int ch3 = in.read();\n        int ch4 = in.read();\n        if ((ch1 | ch2 | ch3 | ch4) < 0) {\n            throw new EOFException();\n        }\n        long ln4 = ch4 & 0x00000000ffffffffL;\n        return (ln4 << 24) + (ch3 << 16) + (ch2 << 8) + (ch1 << 0);\n    }\n    /**\n     * Read and create a long\n     *\n     * @return Converted Byte Array\n     * @throws IOException\n     */\n    public long readLong() throws IOException {\n        readFully(bufferOf8bytes);\n        return (((long) bufferOf8bytes[7] << 56) + ((long) (bufferOf8bytes[6] & 255) << 48)\n                + ((long) (bufferOf8bytes[5] & 255) << 40) + ((long) (bufferOf8bytes[4] & 255) << 32)\n                + ((long) (bufferOf8bytes[3] & 255) << 24) + ((bufferOf8bytes[2] & 255) << 16)\n                + ((bufferOf8bytes[1] & 255) << 8) + ((bufferOf8bytes[0] & 255) << 0));\n    }\n\n    /**\n     * Read and create a String\n     *\n     * @return Converted Byte Array with Charset UTF-8 format\n     * @throws IOException\n     */\n    public String readString() throws IOException {\n        long len = readVarInt();\n        if (len == 0) {\n            return \"\";\n        }\n        byte[] buffer = new byte[(int) len];\n        readFully(buffer);\n        return new String(buffer, StandardCharsets.UTF_8);\n    }\n\n    /**\n     * Read and put values into a byte array\n     *\n     * @param len The desired length of the byte array\n     * @return A byte array with a length of len\n     * @throws IOException\n     */\n    public byte[] readBytes(int len) throws IOException {\n        if (len == 0) {\n            return EMPTY_BYTES;\n        }\n        byte[] buffer = new byte[len];\n        readFully(buffer);\n        return buffer;\n    }\n\n    @Override\n    public void close() throws IOException {\n        in.close();\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/java/io/horizontalsystems/bitcoincore/io/BitcoinInputMarkable.java",
    "content": "package io.horizontalsystems.bitcoincore.io;\n\nimport java.io.ByteArrayInputStream;\nimport java.io.IOException;\n\n/**\n * A child class of BitcoinInput and extends its functionality.\n * @see BitcoinInput\n */\npublic final class BitcoinInputMarkable extends BitcoinInput {\n    //Stores the length of the data byte array.\n    public int count;\n    /**\n    @param data Byte array that is going to by read by the InputStream\n    */\n    public BitcoinInputMarkable(byte[] data) {\n        super(new ByteArrayInputStream(data));\n        this.count = data.length;\n    }\n\n    /**\n     *Marks the InputStream\n     */\n    public void mark() {\n        // since the readlimit for ByteArrayInputStream has no meaning set it to 0\n        in.mark(0);\n    }\n    /**\n     *Resets the InputStream\n     */\n    public void reset() throws IOException {\n        in.reset();\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/java/io/horizontalsystems/bitcoincore/io/BitcoinOutput.java",
    "content": "package io.horizontalsystems.bitcoincore.io;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.nio.charset.StandardCharsets;\n\n/**\n * Output \"stream\" for bitcoin protocol.\n *\n * @author Michael Liao\n *\n */\npublic final class BitcoinOutput {\n\n    private UnsafeByteArrayOutputStream out;\n\n    /**\n     * Creates an instance of UnsafeByteArrayOutputStream of size 1024.\n     */\n    public BitcoinOutput() {\n        this.out = new UnsafeByteArrayOutputStream(1024);\n    }\n\n    /**\n     * Writes a byte array to the OutputStream.\n     * @param bytes Byte array that's to be written.\n     * @return The newly written OutputStream.\n     */\n    public BitcoinOutput write(byte[] bytes) {\n        try {\n            out.write(bytes);\n        } catch (IOException e) {\n            throw new RuntimeException(e);\n        }\n        return this;\n    }\n    /**\n     * Writes a byte to the OutputStream.\n     * @param v The byte to be written.\n     * @return The newly written OutputStream.\n     */\n    public BitcoinOutput writeByte(int v) {\n        out.write(v);\n        return this;\n    }\n    /**\n     * Writes a short to the OutputStream.\n     * @param v The short to be written.\n     * @return The newly written OutputStream.\n     */\n    public BitcoinOutput writeShort(short v) {\n        out.write(0xff & v);\n        out.write(0xff & (v >> 8));\n        return this;\n    }\n    /**\n     * Writes an int to the OutputStream.\n     * @param v Int value.\n     * @return The newly written OutputStream.\n     */\n    public BitcoinOutput writeInt(int v) {\n        out.write(0xff & v);\n        out.write(0xff & (v >> 8));\n        out.write(0xff & (v >> 16));\n        out.write(0xff & (v >> 24));\n        return this;\n    }\n    /**\n     * Writes a 32-bit int to the OutputStream.\n     * @param v Long value to be converted.\n     * @return The newly written OutputStream.\n     */\n    public BitcoinOutput writeInt32(long v) {\n        out.write((int)(0xff & v));\n        out.write((int)(0xff & (v >> 8)));\n        out.write((int)(0xff & (v >> 16)));\n        out.write((int)(0xff & (v >> 24)));\n        return this;\n    }\n\n    /**\n     * Writes a Long to the OutputStream.\n     * @param v Long value to be converted.\n     * @return The newly written OutputStream.\n     */\n    public BitcoinOutput writeLong(long v) {\n        out.write((int) (0xff & v));\n        out.write((int) (0xff & (v >> 8)));\n        out.write((int) (0xff & (v >> 16)));\n        out.write((int) (0xff & (v >> 24)));\n        out.write((int) (0xff & (v >> 32)));\n        out.write((int) (0xff & (v >> 40)));\n        out.write((int) (0xff & (v >> 48)));\n        out.write((int) (0xff & (v >> 56)));\n        return this;\n    }\n\n    /**\n     * Writes a var int to the OutputStream.\n     * @param n Long value to be converted.\n     * @return The newly written OutputStream.\n     */\n    public BitcoinOutput writeVarInt(long n) {\n        if (n < 0xfd) {\n            writeByte((int) n);\n        } else if (n <= 0xffff) {\n            writeByte(0xfd);\n            writeByte((int) (n & 0xff));\n            writeByte((int) ((n >> 8) & 0xff));\n        } else if (n <= 0xffffffff) {\n            writeByte(0xfe);\n            writeInt((int) n);\n        } else {\n            writeByte(0xff);\n            writeLong(n);\n        }\n        return this;\n    }\n\n    /**\n     *  Writes an unsigned int to the OutputStream.\n     * @param ln Long value to be converted.\n     * @return The newly written OutputStream.\n     */\n    public BitcoinOutput writeUnsignedInt(long ln) {\n        int n = (int) (0xffffffff & ln);\n        writeInt(n);\n        return this;\n    }\n\n    /**\n     * Writes an unsigned short to the OutputStream.\n     * @param i integer value to be casted as an unsigned short.\n     * @return The newly written OutputStream.\n     */\n    public BitcoinOutput writeUnsignedShort(int i) {\n        short n = (short) (0xffff & i);\n        writeShort(n);\n        return this;\n    }\n\n    /**\n     * Writes a String to the OutputStream.\n     * @param str String value to be written.\n     * @return The newly written OutputStream.\n     */\n    public BitcoinOutput writeString(String str) {\n        byte[] bs = str.getBytes(StandardCharsets.UTF_8);\n        writeVarInt(bs.length);\n        write(bs);\n        return this;\n    }\n\n    public byte[] toByteArray() {\n        return out.toByteArray();\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/java/io/horizontalsystems/bitcoincore/io/UnsafeByteArrayOutputStream.java",
    "content": "package io.horizontalsystems.bitcoincore.io;\n\nimport java.io.ByteArrayOutputStream;\nimport java.io.IOException;\nimport java.io.OutputStream;\n\npublic class UnsafeByteArrayOutputStream extends ByteArrayOutputStream {\n\n    public UnsafeByteArrayOutputStream() {\n        super(32);\n    }\n\n    public UnsafeByteArrayOutputStream(int size) {\n        super(size);\n    }\n\n    /**\n     * Writes the specified byte to this byte array output stream.\n     *\n     * @param b the byte to be written.\n     */\n    @Override\n    public void write(int b) {\n        int newcount = count + 1;\n        if (newcount > buf.length) {\n            buf = copyOf(buf, Math.max(buf.length << 1, newcount));\n        }\n        buf[count] = (byte) b;\n        count = newcount;\n    }\n\n    /**\n     * Writes {@code len} bytes from the specified byte array\n     * starting at offset {@code off} to this byte array output stream.\n     *\n     * @param b   the data.\n     * @param off the start offset in the data.\n     * @param len the number of bytes to write.\n     */\n    @Override\n    public void write(byte[] b, int off, int len) {\n        if ((off < 0) || (off > b.length) || (len < 0) ||\n                ((off + len) > b.length) || ((off + len) < 0)) {\n            throw new IndexOutOfBoundsException();\n        } else if (len == 0) {\n            return;\n        }\n        int newcount = count + len;\n        if (newcount > buf.length) {\n            buf = copyOf(buf, Math.max(buf.length << 1, newcount));\n        }\n        System.arraycopy(b, off, buf, count, len);\n        count = newcount;\n    }\n\n    /**\n     * Writes the complete contents of this byte array output stream to\n     * the specified output stream argument, as if by calling the output\n     * stream's write method using {@code out.write(buf, 0, count)}.\n     *\n     * @param out the output stream to which to write the data.\n     * @throws IOException if an I/O error occurs.\n     */\n    @Override\n    public void writeTo(OutputStream out) throws IOException {\n        out.write(buf, 0, count);\n    }\n\n    /**\n     * Resets the {@code count} field of this byte array output\n     * stream to zero, so that all currently accumulated output in the\n     * output stream is discarded. The output stream can be used again,\n     * reusing the already allocated buffer space.\n     *\n     * @see java.io.ByteArrayInputStream#count\n     */\n    @Override\n    public void reset() {\n        count = 0;\n    }\n\n    /**\n     * Creates a newly allocated byte array. Its size is the current\n     * size of this output stream and the valid contents of the buffer\n     * have been copied into it.\n     *\n     * @return the current contents of this output stream, as a byte array.\n     * @see java.io.ByteArrayOutputStream#size()\n     */\n    @Override\n    public byte toByteArray()[] {\n        return count == buf.length ? buf : copyOf(buf, count);\n    }\n\n    /**\n     * Returns the current size of the buffer.\n     *\n     * @return the value of the {@code count} field, which is the number\n     *         of valid bytes in this output stream.\n     * @see java.io.ByteArrayOutputStream#count\n     */\n    @Override\n    public int size() {\n        return count;\n    }\n\n    private static byte[] copyOf(byte[] in, int length) {\n        byte[] out = new byte[length];\n        System.arraycopy(in, 0, out, 0, Math.min(length, in.length));\n        return out;\n    }\n}"
  },
  {
    "path": "bitcoincore/src/main/java/io/horizontalsystems/bitcoincore/utils/HashUtils.java",
    "content": "package io.horizontalsystems.bitcoincore.utils;\n\nimport org.bouncycastle.crypto.Digest;\nimport org.bouncycastle.crypto.digests.SHA256Digest;\nimport org.bouncycastle.crypto.generators.SCrypt;\nimport org.bouncycastle.util.Arrays;\n\npublic class HashUtils {\n\n    private static final char[] HEX_CHARS = \"0123456789abcdef\".toCharArray();\n\n    /**\n     * Get SHA-256 hash.\n     * @param input     Byte array representing a transaction\n     */\n    public static byte[] sha256(byte[] input) {\n        Digest d = new SHA256Digest();\n        d.update(input, 0, input.length);\n        byte[] out = new byte[d.getDigestSize()];\n        d.doFinal(out, 0);\n        return out;\n    }\n\n    /**\n     * Get double SHA-256 hash.\n     * @param input     Byte array representing a transaction\n     */\n    public static byte[] doubleSha256(byte[] input) {\n        byte[] round1 = sha256(input);\n        return sha256(round1);\n    }\n\n    /**\n     * Generate a key using the scrypt key derivation function.\n     *\n     * @param P     the bytes of the pass phrase.\n     * @param S     the salt to use for this invocation.\n     * @param N     CPU/Memory cost parameter. Must be larger than 1, a power of 2 and less than\n     *              <code>2^(128 * r / 8)</code>.\n     * @param r     the block size, must be >= 1.\n     * @param p     Parallelization parameter. Must be a positive integer less than or equal to\n     *              <code>Integer.MAX_VALUE / (128 * r * 8)</code>.\n     * @param dkLen the length of the key to generate.\n     * @return      the generated key.\n     */\n    public static byte[] scrypt(byte[] P, byte[] S, int N, int r, int p, int dkLen) {\n        return SCrypt.generate(P, S, N, r, p, dkLen);\n    }\n\n    /**\n     * Convert byte array to hex string without separating characters.\n     *\n     * @param b   Arbitrary byte array\n     */\n    public static String toHexString(byte[] b) {\n        return toHexString(b, false);\n    }\n    /**\n     * Convert byte array to hex string.\n     *\n     * @param b     Arbitrary byte array\n     * @param sep   Determines if data is separated via a space ' '\n     */\n    public static String toHexString(byte[] b, boolean sep) {\n        StringBuilder sb = new StringBuilder(b.length << 2);\n        for (byte x : b) {\n            int hi = (x & 0xf0) >> 4;\n            int lo = x & 0x0f;\n            sb.append(HEX_CHARS[hi]);\n            sb.append(HEX_CHARS[lo]);\n            if (sep) {\n                sb.append(' ');\n            }\n        }\n        return sb.toString().trim();\n    }\n\n    /**\n     * Convert byte array (little endian format) to hex string.\n     *\n     * @param b     A byte array in little endian format\n     */\n    public static String toHexStringAsLE(byte[] b) {\n        StringBuilder sb = new StringBuilder(b.length << 2);\n        for (int i = b.length - 1; i >= 0; i--) {\n            byte x = b[i];\n            int hi = (x & 0xf0) >> 4;\n            int lo = x & 0x0f;\n            sb.append(HEX_CHARS[hi]);\n            sb.append(HEX_CHARS[lo]);\n        }\n        return sb.toString();\n    }\n    /**\n     * Convert string hash to byte array in little endian format\n     *\n     * @param hash      String hash\n     */\n    public static byte[] toBytesAsLE(String hash) {\n        byte[] r = toBytes(hash);\n        return Arrays.reverse(r);\n    }\n    /**\n     * Convert string hash to byte array.\n     *\n     * @param hash      String hash\n     */\n    public static byte[] toBytes(String hash) {\n        if (hash.length() % 2 == 1) {\n            throw new IllegalArgumentException(\"Invalid hash length.\");\n        }\n        byte[] data = new byte[hash.length() / 2];\n        for (int i = 0; i < data.length; i++) {\n            char c1 = hash.charAt(2 * i);\n            char c2 = hash.charAt(2 * i + 1);\n            int n1 = char2int(c1);\n            int n2 = char2int(c2);\n            int n = n1 << 4 | n2;\n            data[i] = (byte) n;\n        }\n        return data;\n    }\n    /**\n     * Convert char to int.\n     *\n     * @param ch    Arbitrary char value\n     */\n    static int char2int(char ch) {\n        if (ch >= '0' && ch <= '9') {\n            return ch - '0';\n        }\n        if (ch >= 'a' && ch <= 'f') {\n            return ch - 'a' + 10;\n        }\n        if (ch >= 'A' && ch <= 'F') {\n            return ch - 'A' + 10;\n        }\n        throw new IllegalArgumentException(\"Bad char.\");\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/java/io/horizontalsystems/bitcoincore/utils/Utils.java",
    "content": "/**\n * Copyright 2011 Google Inc.\n * Copyright 2013-2016 Ronald W Hoffman\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\npackage io.horizontalsystems.bitcoincore.utils;\n\nimport org.bouncycastle.crypto.digests.RIPEMD160Digest;\nimport org.bouncycastle.crypto.digests.SHA512Digest;\nimport org.bouncycastle.crypto.macs.HMac;\nimport org.bouncycastle.crypto.params.KeyParameter;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.security.MessageDigest;\nimport java.security.NoSuchAlgorithmException;\nimport java.security.SecureRandom;\nimport java.util.Random;\n\n/**\n * Static utility methods\n */\npublic class Utils {\n    /** Bit masks (Low-order bit is bit 0 and high-order bit is bit 7) */\n    private static final int bitMask[] = {0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80};\n\n    /** Strong random number generator */\n    private static final Random rnd = new SecureRandom();\n\n    /** Instance of a SHA-256 digest which we will use as needed */\n    private static final MessageDigest digest;\n\n    static {\n        try {\n            digest = MessageDigest.getInstance(\"SHA-256\");\n        } catch (NoSuchAlgorithmException e) {\n            throw new RuntimeException(e);  // Can't happen.\n        }\n    }\n\n    /**\n     * Calculate the SHA-256 hash of the input and then hash the resulting hash again\n     *\n     * @param       input           Data to be hashed\n     * @return                      The hash digest\n     */\n    public static byte[] doubleDigest(byte[] input) {\n        return doubleDigest(input, 0, input.length);\n    }\n\n    /**\n     * Calculate the SHA-256 hash of the input and then hash the resulting hash again\n     *\n     * @param       input           Data to be hashed\n     * @param       offset          Starting offset within the data\n     * @param       length          Number of data bytes to hash\n     * @return                      The hash digest\n     */\n    public static byte[] doubleDigest(byte[] input, int offset, int length) {\n        byte[] bytes;\n        synchronized (digest) {\n            digest.reset();\n            digest.update(input, offset, length);\n            byte[] first = digest.digest();\n            bytes = digest.digest(first);\n        }\n        return bytes;\n    }\n\n    /**\n     * Calculate RIPEMD160(SHA256(input)).  This is used in Address calculations.\n     *\n     * @param       input           The byte array to be hashed\n     * @return                      The hashed result\n     */\n    public static byte[] sha256Hash160(byte[] input) {\n        byte[] out = new byte[20];\n        synchronized(digest) {\n            digest.reset();\n            byte[] sha256 = digest.digest(input);\n            RIPEMD160Digest rDigest = new RIPEMD160Digest();\n            rDigest.update(sha256, 0, sha256.length);\n            rDigest.doFinal(out, 0);\n        }\n        return out;\n    }\n\n    /**\n     * Calculate the HMAC-SHA512 digest for use with BIP 32\n     *\n     * @param       key             Key\n     * @param       input           Bytes to be hashed\n     * @return                      Hashed result\n     */\n    public static byte[] hmacSha512(byte[] key, byte[] input) {\n        HMac hmac = new HMac(new SHA512Digest());\n        hmac.init(new KeyParameter(key));\n        hmac.update(input, 0, input.length);\n        byte[] out = new byte[64];\n        hmac.doFinal(out, 0);\n        return out;\n    }\n\n    /**\n     * Checks if the specified bit is set\n     *\n     * @param       data            Byte array to check\n     * @param       index           Bit position\n     * @return                      TRUE if the bit is set\n     */\n    public static boolean checkBitLE(byte[] data, int index) {\n        return (data[index>>>3] & bitMask[7&index]) != 0;\n    }\n\n    /**\n     * Sets the specified bit\n     * @param       data            Byte array\n     * @param       index           Bit position\n     */\n    public static void setBitLE(byte[] data, int index) {\n        data[index >>> 3] |= bitMask[7 & index];\n    }\n\n    /**\n     * Calculate SHA256(SHA256(byte range 1 + byte range 2)).\n     *\n     * @param       input1          First input byte array\n     * @param       offset1         Starting position in the first array\n     * @param       length1         Number of bytes to process in the first array\n     * @param       input2          Second input byte array\n     * @param       offset2         Starting position in the second array\n     * @param       length2         Number of bytes to process in the second array\n     * @return                      The SHA-256 digest\n     */\n    public static byte[] doubleDigestTwoBuffers(byte[]input1, int offset1, int length1,\n                                                byte[]input2, int offset2, int length2) {\n        byte[] bytes;\n        synchronized (digest) {\n            digest.reset();\n            digest.update(input1, offset1, length1);\n            digest.update(input2, offset2, length2);\n            byte[]first = digest.digest();\n            bytes = digest.digest(first);\n        }\n        return bytes;\n    }\n\n    /**\n     * Form a long value from a 4-byte array in big-endian format\n     *\n     * @param       bytes           The byte array\n     * @param       offset          Starting offset within the array\n     * @return                      The long value\n     */\n    public static long readUint32BE(byte[] bytes, int offset) {\n        return (((long) bytes[offset++] & 0x00FFL) << 24) |\n                (((long) bytes[offset++] & 0x00FFL) << 16) |\n                (((long) bytes[offset++] & 0x00FFL) << 8) |\n                ((long) bytes[offset] & 0x00FFL);\n    }\n\n    /** Parse 2 bytes from the stream as unsigned 16-bit integer in little endian format.\n     * @param       is       InputStream\n     * @return               The int value\n     */\n    public static int readUint16FromStream(InputStream is) {\n        try {\n            return (is.read() & 0xff) |\n                    ((is.read() & 0xff) << 8);\n        } catch (IOException x) {\n            throw new RuntimeException(x);\n        }\n    }\n\n    /** Parse 4 bytes from the stream as unsigned 32-bit integer in little endian format.\n     * @param       is InputStream\n     * @return         The long value\n     */\n    public static long readUint32FromStream(InputStream is) {\n        try {\n            return (is.read() & 0xffl) |\n                    ((is.read() & 0xffl) << 8) |\n                    ((is.read() & 0xffl) << 16) |\n                    ((is.read() & 0xffl) << 24);\n        } catch (IOException x) {\n            throw new RuntimeException(x);\n        }\n    }\n    /**\n     * Convert int to byte array\n     *\n     * @param       value Arbitrary int value\n     * @return            Converted int\n     */\n    public static byte[] intToByteArray(int value) {\n        return new byte[]{(byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) value};\n    }\n    /**\n     * Convert int to byte array\n     *\n     * @param       data        Arbitrary byte array\n     * @return                  Converted byte array\n     */\n    public static int byteArrayToUInt16LE(byte[] data) {\n        return (data[0] & 0xff) | ((data[1] & 0xff) << 8);\n    }\n\n    /**\n     * Parse an int value from bytes\n     *\n     * @param       b1      Arbitrary byte\n     * @param       b2      Arbitrary byte\n     * @param       b3      Arbitrary byte\n     * @param       b4      Arbitrary byte\n     * @return              Parsed int value\n     */\n    public static int intFromBytes(byte b1, byte b2, byte b3, byte b4) {\n        return b1 << 24 | (b2 & 255) << 16 | (b3 & 255) << 8 | b4 & 255;\n    }\n\n    /** Generate random long number */\n    public static long randomLong() {\n        return (long) (rnd.nextDouble() * Long.MAX_VALUE);\n    }\n\n    /** Generate random number */\n    public static int randomInt() {\n        return (int) (rnd.nextDouble() * Integer.MAX_VALUE);\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/AbstractKit.kt",
    "content": "package io.horizontalsystems.bitcoincore\n\nimport io.horizontalsystems.bitcoincore.core.IPluginData\nimport io.horizontalsystems.bitcoincore.models.BitcoinPaymentData\nimport io.horizontalsystems.bitcoincore.models.BitcoinSendInfo\nimport io.horizontalsystems.bitcoincore.models.PublicKey\nimport io.horizontalsystems.bitcoincore.models.TransactionDataSortType\nimport io.horizontalsystems.bitcoincore.models.TransactionFilterType\nimport io.horizontalsystems.bitcoincore.models.TransactionInfo\nimport io.horizontalsystems.bitcoincore.models.UsedAddress\nimport io.horizontalsystems.bitcoincore.network.Network\nimport io.horizontalsystems.bitcoincore.rbf.ReplacementTransaction\nimport io.horizontalsystems.bitcoincore.rbf.ReplacementTransactionInfo\nimport io.horizontalsystems.bitcoincore.rbf.ReplacementType\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutputInfo\nimport io.horizontalsystems.bitcoincore.storage.UtxoFilters\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport io.reactivex.Single\n\nabstract class AbstractKit {\n\n    protected abstract var bitcoinCore: BitcoinCore\n    protected abstract var network: Network\n\n    fun getUnspentOutputs(filters: UtxoFilters): List<UnspentOutputInfo> {\n        return bitcoinCore.getUnspentOutputs(filters)\n    }\n\n    fun selectUnspentOutputs(value: Long, feeRate: Int): List<UnspentOutputInfo> {\n        return bitcoinCore.selectUnspentOutputs(value, feeRate)\n    }\n\n    val balance\n        get() = bitcoinCore.balance\n\n    val lastBlockInfo\n        get() = bitcoinCore.lastBlockInfo\n\n    val networkName: String\n        get() = network.javaClass.simpleName\n\n    val syncState get() = bitcoinCore.syncState\n\n    val watchAccount: Boolean\n        get() = bitcoinCore.watchAccount\n\n    fun start() {\n        bitcoinCore.start()\n    }\n\n    fun stop() {\n        bitcoinCore.stop()\n    }\n\n    fun refresh() {\n        bitcoinCore.refresh()\n    }\n\n    fun onEnterForeground() {\n        bitcoinCore.onEnterForeground()\n    }\n\n    fun onEnterBackground() {\n        bitcoinCore.onEnterBackground()\n    }\n\n    fun transactions(fromUid: String? = null, type: TransactionFilterType? = null, limit: Int? = null): Single<List<TransactionInfo>> {\n        return bitcoinCore.transactions(fromUid, type, limit)\n    }\n\n    fun getTransaction(hash: String): TransactionInfo? {\n        return bitcoinCore.getTransaction(hash)\n    }\n\n    fun sendInfo(\n        value: Long,\n        address: String? = null,\n        memo: String?,\n        senderPay: Boolean = true,\n        feeRate: Int,\n        unspentOutputs: List<UnspentOutputInfo>?,\n        pluginData: Map<Byte, IPluginData> = mapOf(),\n        changeToFirstInput: Boolean,\n        filters: UtxoFilters\n    ): BitcoinSendInfo {\n        return bitcoinCore.sendInfo(\n            value = value,\n            address = address,\n            memo = memo,\n            senderPay = senderPay,\n            feeRate = feeRate,\n            unspentOutputs = unspentOutputs,\n            pluginData = pluginData,\n            changeToFirstInput = changeToFirstInput,\n            filters = filters,\n        )\n    }\n\n    fun send(\n        address: String,\n        memo: String?,\n        value: Long,\n        senderPay: Boolean = true,\n        feeRate: Int,\n        sortType: TransactionDataSortType,\n        unspentOutputs: List<UnspentOutputInfo>? = null,\n        pluginData: Map<Byte, IPluginData> = mapOf(),\n        rbfEnabled: Boolean,\n        changeToFirstInput: Boolean,\n        filters: UtxoFilters,\n    ): FullTransaction {\n        return bitcoinCore.send(\n            address,\n            memo,\n            value,\n            senderPay,\n            feeRate,\n            sortType,\n            unspentOutputs,\n            pluginData,\n            rbfEnabled,\n            changeToFirstInput,\n            filters,\n        )\n    }\n\n    fun send(\n        address: String,\n        memo: String?,\n        value: Long,\n        senderPay: Boolean = true,\n        feeRate: Int,\n        sortType: TransactionDataSortType,\n        pluginData: Map<Byte, IPluginData> = mapOf(),\n        rbfEnabled: Boolean,\n        changeToFirstInput: Boolean,\n        filters: UtxoFilters,\n    ): FullTransaction {\n        return bitcoinCore.send(\n            address,\n            memo,\n            value,\n            senderPay,\n            feeRate,\n            sortType,\n            null,\n            pluginData,\n            rbfEnabled,\n            changeToFirstInput,\n            filters,\n        )\n    }\n\n    fun send(\n        hash: ByteArray,\n        memo: String?,\n        scriptType: ScriptType,\n        value: Long,\n        senderPay: Boolean = true,\n        feeRate: Int,\n        sortType: TransactionDataSortType,\n        unspentOutputs: List<UnspentOutputInfo>? = null,\n        rbfEnabled: Boolean,\n        changeToFirstInput: Boolean,\n        filters: UtxoFilters,\n    ): FullTransaction {\n        return bitcoinCore.send(\n            hash,\n            memo,\n            scriptType,\n            value,\n            senderPay,\n            feeRate,\n            sortType,\n            unspentOutputs,\n            rbfEnabled,\n            changeToFirstInput,\n            filters,\n        )\n    }\n\n    fun send(\n        hash: ByteArray,\n        memo: String?,\n        scriptType: ScriptType,\n        value: Long,\n        senderPay: Boolean = true,\n        feeRate: Int,\n        sortType: TransactionDataSortType,\n        rbfEnabled: Boolean,\n        changeToFirstInput: Boolean,\n        filters: UtxoFilters,\n    ): FullTransaction {\n        return bitcoinCore.send(\n            hash,\n            memo,\n            scriptType,\n            value,\n            senderPay,\n            feeRate,\n            sortType,\n            null,\n            rbfEnabled,\n            changeToFirstInput,\n            filters,\n        )\n    }\n\n    fun redeem(unspentOutput: UnspentOutput, address: String, memo: String?, feeRate: Int, sortType: TransactionDataSortType, rbfEnabled: Boolean): FullTransaction {\n        return bitcoinCore.redeem(unspentOutput, address, memo, feeRate, sortType, rbfEnabled)\n    }\n\n    fun receiveAddress(): String {\n        return bitcoinCore.receiveAddress()\n    }\n\n    fun usedAddresses(change: Boolean): List<UsedAddress> {\n        return bitcoinCore.usedAddresses(change)\n    }\n\n    fun receivePublicKey(): PublicKey {\n        return bitcoinCore.receivePublicKey()\n    }\n\n    fun changePublicKey(): PublicKey {\n        return bitcoinCore.changePublicKey()\n    }\n\n    fun validateAddress(address: String, pluginData: Map<Byte, IPluginData>) {\n        bitcoinCore.validateAddress(address, pluginData)\n    }\n\n    fun parsePaymentAddress(paymentAddress: String): BitcoinPaymentData {\n        return bitcoinCore.parsePaymentAddress(paymentAddress)\n    }\n\n    fun showDebugInfo() {\n        bitcoinCore.showDebugInfo()\n    }\n\n    fun statusInfo(): Map<String, Any> {\n        return bitcoinCore.statusInfo()\n    }\n\n    fun getPublicKeyByPath(path: String): PublicKey {\n        return bitcoinCore.getPublicKeyByPath(path)\n    }\n\n    fun watchTransaction(filter: TransactionFilter, listener: WatchedTransactionManager.Listener) {\n        bitcoinCore.watchTransaction(filter, listener)\n    }\n\n    fun maximumSpendableValue(\n        address: String?,\n        memo: String?,\n        feeRate: Int,\n        unspentOutputInfos: List<UnspentOutputInfo>?,\n        pluginData: Map<Byte, IPluginData>,\n        changeToFirstInput: Boolean,\n        filters: UtxoFilters\n    ): Long {\n        return bitcoinCore.maximumSpendableValue(\n            address,\n            memo,\n            feeRate,\n            unspentOutputInfos,\n            pluginData,\n            changeToFirstInput,\n            filters,\n        )\n    }\n\n    fun minimumSpendableValue(address: String?): Int {\n        return bitcoinCore.minimumSpendableValue(address)\n    }\n\n    fun getRawTransaction(transactionHash: String): String? {\n        return bitcoinCore.getRawTransaction(transactionHash)\n    }\n\n    fun speedUpTransaction(\n        transactionHash: String,\n        minFee: Long\n    ): ReplacementTransaction {\n        return bitcoinCore.replacementTransaction(\n            transactionHash,\n            minFee,\n            ReplacementType.SpeedUp\n        )\n    }\n\n    fun cancelTransaction(\n        transactionHash: String,\n        minFee: Long\n    ): ReplacementTransaction {\n        val publicKey = bitcoinCore.receivePublicKey()\n        val address = bitcoinCore.address(publicKey)\n        return bitcoinCore.replacementTransaction(\n            transactionHash,\n            minFee,\n            ReplacementType.Cancel(address, publicKey)\n        )\n    }\n\n    fun send(replacementTransaction: ReplacementTransaction): FullTransaction {\n        return bitcoinCore.send(replacementTransaction)\n    }\n\n    fun speedUpTransactionInfo(transactionHash: String): ReplacementTransactionInfo? {\n        return bitcoinCore.replacementTransactionInfo(\n            transactionHash,\n            ReplacementType.SpeedUp\n        )\n    }\n\n    fun cancelTransactionInfo(transactionHash: String): ReplacementTransactionInfo? {\n        val receivePublicKey = bitcoinCore.receivePublicKey()\n        val address = bitcoinCore.address(receivePublicKey)\n        val type = ReplacementType.Cancel(address, receivePublicKey)\n        return bitcoinCore.replacementTransactionInfo(transactionHash, type)\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/BitcoinCore.kt",
    "content": "package io.horizontalsystems.bitcoincore\n\nimport io.horizontalsystems.bitcoincore.apisync.blockchair.BlockchairApi\nimport io.horizontalsystems.bitcoincore.blocks.IPeerSyncListener\nimport io.horizontalsystems.bitcoincore.core.AccountWallet\nimport io.horizontalsystems.bitcoincore.core.DataProvider\nimport io.horizontalsystems.bitcoincore.core.IConnectionManager\nimport io.horizontalsystems.bitcoincore.core.IInitialDownload\nimport io.horizontalsystems.bitcoincore.core.IKitStateListener\nimport io.horizontalsystems.bitcoincore.core.IPluginData\nimport io.horizontalsystems.bitcoincore.core.IPublicKeyManager\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.core.PluginManager\nimport io.horizontalsystems.bitcoincore.core.Wallet\nimport io.horizontalsystems.bitcoincore.core.WatchAccountWallet\nimport io.horizontalsystems.bitcoincore.core.description\nimport io.horizontalsystems.bitcoincore.core.scriptType\nimport io.horizontalsystems.bitcoincore.extensions.toHexString\nimport io.horizontalsystems.bitcoincore.managers.IRestoreKeyConverter\nimport io.horizontalsystems.bitcoincore.managers.IUnspentOutputSelector\nimport io.horizontalsystems.bitcoincore.managers.RestoreKeyConverterChain\nimport io.horizontalsystems.bitcoincore.managers.SyncManager\nimport io.horizontalsystems.bitcoincore.managers.UnspentOutputSelectorChain\nimport io.horizontalsystems.bitcoincore.models.Address\nimport io.horizontalsystems.bitcoincore.models.BalanceInfo\nimport io.horizontalsystems.bitcoincore.models.BitcoinPaymentData\nimport io.horizontalsystems.bitcoincore.models.BitcoinSendInfo\nimport io.horizontalsystems.bitcoincore.models.BlockInfo\nimport io.horizontalsystems.bitcoincore.models.PublicKey\nimport io.horizontalsystems.bitcoincore.models.TransactionDataSortType\nimport io.horizontalsystems.bitcoincore.models.TransactionFilterType\nimport io.horizontalsystems.bitcoincore.models.TransactionInfo\nimport io.horizontalsystems.bitcoincore.models.UsedAddress\nimport io.horizontalsystems.bitcoincore.network.Network\nimport io.horizontalsystems.bitcoincore.network.messages.IMessageParser\nimport io.horizontalsystems.bitcoincore.network.messages.IMessageSerializer\nimport io.horizontalsystems.bitcoincore.network.messages.NetworkMessageParser\nimport io.horizontalsystems.bitcoincore.network.messages.NetworkMessageSerializer\nimport io.horizontalsystems.bitcoincore.network.peer.IInventoryItemsHandler\nimport io.horizontalsystems.bitcoincore.network.peer.IPeerTaskHandler\nimport io.horizontalsystems.bitcoincore.network.peer.InventoryItemsHandlerChain\nimport io.horizontalsystems.bitcoincore.network.peer.PeerGroup\nimport io.horizontalsystems.bitcoincore.network.peer.PeerManager\nimport io.horizontalsystems.bitcoincore.network.peer.PeerTaskHandlerChain\nimport io.horizontalsystems.bitcoincore.rbf.ReplacementTransaction\nimport io.horizontalsystems.bitcoincore.rbf.ReplacementTransactionBuilder\nimport io.horizontalsystems.bitcoincore.rbf.ReplacementTransactionInfo\nimport io.horizontalsystems.bitcoincore.rbf.ReplacementType\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutputInfo\nimport io.horizontalsystems.bitcoincore.storage.UtxoFilters\nimport io.horizontalsystems.bitcoincore.transactions.TransactionCreator\nimport io.horizontalsystems.bitcoincore.transactions.TransactionFeeCalculator\nimport io.horizontalsystems.bitcoincore.transactions.TransactionSyncer\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport io.horizontalsystems.bitcoincore.utils.AddressConverterChain\nimport io.horizontalsystems.bitcoincore.utils.DirectExecutor\nimport io.horizontalsystems.bitcoincore.utils.IAddressConverter\nimport io.horizontalsystems.bitcoincore.utils.PaymentAddressParser\nimport io.horizontalsystems.hdwalletkit.HDExtendedKey\nimport io.horizontalsystems.hdwalletkit.HDWallet\nimport io.horizontalsystems.hdwalletkit.HDWallet.Purpose\nimport io.horizontalsystems.hdwalletkit.HDWalletAccount\nimport io.horizontalsystems.hdwalletkit.HDWalletAccountWatch\nimport io.reactivex.Single\nimport java.util.Date\nimport java.util.concurrent.Executor\nimport kotlin.math.max\nimport kotlin.math.roundToInt\n\nclass BitcoinCore(\n    private val storage: IStorage,\n    private val dataProvider: DataProvider,\n    private val publicKeyManager: IPublicKeyManager,\n    private val addressConverter: AddressConverterChain,\n    private val restoreKeyConverterChain: RestoreKeyConverterChain,\n    private val transactionCreator: TransactionCreator?,\n    private val transactionFeeCalculator: TransactionFeeCalculator?,\n    private val replacementTransactionBuilder: ReplacementTransactionBuilder?,\n    private val paymentAddressParser: PaymentAddressParser,\n    private val syncManager: SyncManager,\n    private val purpose: Purpose,\n    private var peerManager: PeerManager,\n    private val dustCalculator: DustCalculator?,\n    private val pluginManager: PluginManager,\n    private val connectionManager: IConnectionManager\n) : IKitStateListener, DataProvider.Listener {\n\n    interface Listener {\n        fun onTransactionsUpdate(inserted: List<TransactionInfo>, updated: List<TransactionInfo>) = Unit\n        fun onTransactionsDelete(hashes: List<String>) = Unit\n        fun onBalanceUpdate(balance: BalanceInfo) = Unit\n        fun onLastBlockInfoUpdate(blockInfo: BlockInfo) = Unit\n        fun onKitStateUpdate(state: KitState) = Unit\n    }\n\n    // START: Extending\n    lateinit var peerGroup: PeerGroup\n    lateinit var transactionSyncer: TransactionSyncer\n    lateinit var networkMessageParser: NetworkMessageParser\n    lateinit var networkMessageSerializer: NetworkMessageSerializer\n    lateinit var initialDownload: IInitialDownload\n    lateinit var unspentOutputSelector: UnspentOutputSelectorChain\n    lateinit var watchedTransactionManager: WatchedTransactionManager\n\n    val inventoryItemsHandlerChain = InventoryItemsHandlerChain()\n    val peerTaskHandlerChain = PeerTaskHandlerChain()\n\n    fun addPeerSyncListener(peerSyncListener: IPeerSyncListener): BitcoinCore {\n        initialDownload.addPeerSyncListener(peerSyncListener)\n        return this\n    }\n\n    fun addRestoreKeyConverter(keyConverter: IRestoreKeyConverter) {\n        restoreKeyConverterChain.add(keyConverter)\n    }\n\n    fun addMessageParser(messageParser: IMessageParser): BitcoinCore {\n        networkMessageParser.add(messageParser)\n        return this\n    }\n\n    fun addMessageSerializer(messageSerializer: IMessageSerializer): BitcoinCore {\n        networkMessageSerializer.add(messageSerializer)\n        return this\n    }\n\n    fun addInventoryItemsHandler(handler: IInventoryItemsHandler) {\n        inventoryItemsHandlerChain.addHandler(handler)\n    }\n\n    fun addPeerTaskHandler(handler: IPeerTaskHandler) {\n        peerTaskHandlerChain.addHandler(handler)\n    }\n\n    fun addPeerGroupListener(listener: PeerGroup.Listener) {\n        peerGroup.addPeerGroupListener(listener)\n    }\n\n    fun prependUnspentOutputSelector(selector: IUnspentOutputSelector) {\n        unspentOutputSelector.prependSelector(selector)\n    }\n\n    fun prependAddressConverter(converter: IAddressConverter) {\n        addressConverter.prependConverter(converter)\n    }\n\n    // END: Extending\n\n    var listenerExecutor: Executor = DirectExecutor()\n\n    //  DataProvider getters\n    val balance get() = dataProvider.balance\n    val lastBlockInfo get() = dataProvider.lastBlockInfo\n    val syncState get() = syncManager.syncState\n\n    var listener: Listener? = null\n\n    val watchAccount: Boolean\n        get() = transactionCreator == null\n\n    fun getUnspentOutputs(filters: UtxoFilters): List<UnspentOutputInfo> {\n        return unspentOutputSelector.getAllSpendable(filters).map {\n            UnspentOutputInfo.fromUnspentOutput(it)\n        }\n    }\n\n    fun selectUnspentOutputs(value: Long, feeRate: Int): List<UnspentOutputInfo> {\n        val sendInfo = transactionFeeCalculator?.sendInfo(\n            value = value,\n            feeRate = feeRate,\n            senderPay = true,\n            toAddress = null,\n            memo = null,\n            unspentOutputs = null,\n            pluginData = mapOf(),\n            changeToFirstInput = false,\n            filters = UtxoFilters()\n        ) ?: throw CoreError.ReadOnlyCore\n        return sendInfo.unspentOutputs.map { UnspentOutputInfo.fromUnspentOutput(it) }\n    }\n\n    //\n    // API methods\n    //\n    fun start() {\n        connectionManager.onEnterForeground()\n        syncManager.start()\n    }\n\n    fun stop() {\n        connectionManager.onEnterBackground()\n        dataProvider.clear()\n        syncManager.stop()\n    }\n\n    fun refresh() {\n        start()\n    }\n\n    fun onEnterForeground() {\n        connectionManager.onEnterForeground()\n    }\n\n    fun onEnterBackground() {\n        connectionManager.onEnterBackground()\n    }\n\n    fun transactions(fromUid: String? = null, type: TransactionFilterType? = null, limit: Int? = null): Single<List<TransactionInfo>> {\n        return dataProvider.transactions(fromUid, type, limit)\n    }\n\n    fun sendInfo(\n        value: Long,\n        address: String? = null,\n        memo: String?,\n        senderPay: Boolean = true,\n        feeRate: Int,\n        unspentOutputs: List<UnspentOutputInfo>?,\n        pluginData: Map<Byte, IPluginData>,\n        changeToFirstInput: Boolean,\n        filters: UtxoFilters\n    ): BitcoinSendInfo {\n        val outputs = unspentOutputs?.mapNotNull {\n            unspentOutputSelector.getAllSpendable(filters).firstOrNull { unspentOutput ->\n                unspentOutput.transaction.hash.contentEquals(it.transactionHash) && unspentOutput.output.index == it.outputIndex\n            }\n        }\n        return transactionFeeCalculator?.sendInfo(\n            value = value,\n            feeRate = feeRate,\n            senderPay = senderPay,\n            toAddress = address,\n            memo = memo,\n            unspentOutputs = outputs,\n            pluginData = pluginData,\n            changeToFirstInput = changeToFirstInput,\n            filters = filters,\n        ) ?: throw CoreError.ReadOnlyCore\n    }\n\n    fun send(\n        address: String,\n        memo: String?,\n        value: Long,\n        senderPay: Boolean = true,\n        feeRate: Int,\n        sortType: TransactionDataSortType,\n        unspentOutputs: List<UnspentOutputInfo>?,\n        pluginData: Map<Byte, IPluginData>,\n        rbfEnabled: Boolean,\n        changeToFirstInput: Boolean,\n        filters: UtxoFilters\n    ): FullTransaction {\n        val outputs = unspentOutputs?.mapNotNull {\n            unspentOutputSelector.getAllSpendable(filters).firstOrNull { unspentOutput ->\n                unspentOutput.transaction.hash.contentEquals(it.transactionHash) && unspentOutput.output.index == it.outputIndex\n            }\n        }\n        return transactionCreator?.create(\n            toAddress = address,\n            memo = memo,\n            value = value,\n            feeRate = feeRate,\n            senderPay = senderPay,\n            sortType = sortType,\n            unspentOutputs = outputs,\n            pluginData = pluginData,\n            rbfEnabled = rbfEnabled,\n            changeToFirstInput = changeToFirstInput,\n            filters = filters,\n        ) ?: throw CoreError.ReadOnlyCore\n    }\n\n    fun send(\n        hash: ByteArray,\n        memo: String?,\n        scriptType: ScriptType,\n        value: Long,\n        senderPay: Boolean = true,\n        feeRate: Int,\n        sortType: TransactionDataSortType,\n        unspentOutputs: List<UnspentOutputInfo>?,\n        rbfEnabled: Boolean,\n        changeToFirstInput: Boolean,\n        filters: UtxoFilters\n    ): FullTransaction {\n        val address = addressConverter.convert(hash, scriptType)\n        val outputs = unspentOutputs?.mapNotNull {\n            unspentOutputSelector.getAllSpendable(filters).firstOrNull { unspentOutput ->\n                unspentOutput.transaction.hash.contentEquals(it.transactionHash) && unspentOutput.output.index == it.outputIndex\n            }\n        }\n        return transactionCreator?.create(\n            toAddress = address.stringValue,\n            memo = memo,\n            value = value,\n            feeRate = feeRate,\n            senderPay = senderPay,\n            sortType = sortType,\n            unspentOutputs = outputs,\n            pluginData = mapOf(),\n            rbfEnabled = rbfEnabled,\n            changeToFirstInput = changeToFirstInput,\n            filters = filters,\n        ) ?: throw CoreError.ReadOnlyCore\n    }\n\n    fun redeem(unspentOutput: UnspentOutput, address: String, memo: String?, feeRate: Int, sortType: TransactionDataSortType, rbfEnabled: Boolean): FullTransaction {\n        return transactionCreator?.create(unspentOutput, address, memo, feeRate, sortType, rbfEnabled) ?: throw CoreError.ReadOnlyCore\n    }\n\n    fun receiveAddress(): String {\n        return addressConverter.convert(publicKeyManager.receivePublicKey(), purpose.scriptType).stringValue\n    }\n\n    fun address(publicKey: PublicKey): Address {\n        return addressConverter.convert(publicKey, purpose.scriptType)\n    }\n\n    fun usedAddresses(change: Boolean): List<UsedAddress> {\n        return publicKeyManager.usedExternalPublicKeys(change).map {\n            UsedAddress(\n                index = it.index,\n                address = addressConverter.convert(it, purpose.scriptType).stringValue\n            )\n        }.sortedBy { it.index }\n    }\n\n    fun receivePublicKey(): PublicKey {\n        return publicKeyManager.receivePublicKey()\n    }\n\n    fun changePublicKey(): PublicKey {\n        return publicKeyManager.changePublicKey()\n    }\n\n    fun getPublicKeyByPath(path: String): PublicKey {\n        return publicKeyManager.getPublicKeyByPath(path)\n    }\n\n    fun validateAddress(address: String, pluginData: Map<Byte, IPluginData> = mapOf()) {\n        pluginManager.validateAddress(addressConverter.convert(address), pluginData)\n    }\n\n    fun parsePaymentAddress(paymentAddress: String): BitcoinPaymentData {\n        return paymentAddressParser.parse(paymentAddress)\n    }\n\n    fun showDebugInfo() {\n        publicKeyManager.fillGap()\n        storage.getPublicKeys().forEach { pubKey ->\n            try {\n//                    val scriptType = if (network is MainNetBitcoinCash || network is TestNetBitcoinCash)\n//                        ScriptType.P2PKH else\n//                        ScriptType.P2WPKH\n\n                val legacy = addressConverter.convert(pubKey.publicKeyHash, ScriptType.P2PKH).stringValue\n//                    val wpkh = addressConverter.convert(pubKey.scriptHashP2WPKH, ScriptType.P2SH).string\n//                    val bechAddress = try {\n//                        addressConverter.convert(OpCodes.push(0) + OpCodes.push(pubKey.publicKeyHash), scriptType).string\n//                    } catch (e: Exception) {\n//                        \"\"\n//                    }\n                println(\"${pubKey.index} --- extrnl: ${pubKey.external} --- hash: ${pubKey.publicKeyHash.toHexString()} ---- legacy: $legacy\")\n//                    println(\"legacy: $legacy --- bech32: $bechAddress --- SH(WPKH): $wpkh\")\n            } catch (e: Exception) {\n                println(e.message)\n            }\n        }\n    }\n\n    fun statusInfo(): Map<String, Any> {\n        val statusInfo = LinkedHashMap<String, Any>()\n\n        statusInfo[\"Synced Until\"] = lastBlockInfo?.timestamp?.let { Date(it * 1000) } ?: \"N/A\"\n        statusInfo[\"Syncing Peer\"] = initialDownload.syncPeer?.host ?: \"N/A\"\n        statusInfo[\"Derivation\"] = purpose.description\n        statusInfo[\"Sync State\"] = syncState.toString()\n        statusInfo[\"Last Block Height\"] = lastBlockInfo?.height ?: \"N/A\"\n\n        val peers = LinkedHashMap<String, Any>()\n        peerManager.connected().forEachIndexed { index, peer ->\n\n            val peerStatus = LinkedHashMap<String, Any>()\n            peerStatus[\"Status\"] = if (peer.synced) \"Synced\" else \"Not Synced\"\n            peerStatus[\"Host\"] = peer.host\n            peerStatus[\"Best Block\"] = peer.announcedLastBlockHeight\n            peerStatus[\"User Agent\"] = peer.subVersion\n\n            peer.tasks.let { peerTasks ->\n                if (peerTasks.isEmpty()) {\n                    peerStatus[\"tasks\"] = \"no tasks\"\n                } else {\n                    val tasks = LinkedHashMap<String, Any>()\n                    peerTasks.forEach { task ->\n                        tasks[task.javaClass.simpleName] = \"[${task.state}]\"\n                    }\n                    peerStatus[\"tasks\"] = tasks\n                }\n            }\n\n            peers[\"Peer ${index + 1}\"] = peerStatus\n        }\n\n        statusInfo.putAll(peers)\n\n        return statusInfo\n    }\n\n    //\n    // DataProvider Listener implementations\n    //\n    override fun onTransactionsUpdate(inserted: List<TransactionInfo>, updated: List<TransactionInfo>) {\n        listenerExecutor.execute {\n            listener?.onTransactionsUpdate(inserted, updated)\n        }\n    }\n\n    override fun onTransactionsDelete(hashes: List<String>) {\n        listenerExecutor.execute {\n            listener?.onTransactionsDelete(hashes)\n        }\n    }\n\n    override fun onBalanceUpdate(balance: BalanceInfo) {\n        listenerExecutor.execute {\n            listener?.onBalanceUpdate(balance)\n        }\n    }\n\n    override fun onLastBlockInfoUpdate(blockInfo: BlockInfo) {\n        listenerExecutor.execute {\n            listener?.onLastBlockInfoUpdate(blockInfo)\n        }\n    }\n\n    //\n    // IKitStateManagerListener implementations\n    //\n    override fun onKitStateUpdate(state: KitState) {\n        listenerExecutor.execute {\n            listener?.onKitStateUpdate(state)\n        }\n    }\n\n    fun watchTransaction(filter: TransactionFilter, listener: WatchedTransactionManager.Listener) {\n        watchedTransactionManager.add(filter, listener)\n    }\n\n    fun maximumSpendableValue(\n        address: String?,\n        memo: String?,\n        feeRate: Int,\n        unspentOutputInfos: List<UnspentOutputInfo>?,\n        pluginData: Map<Byte, IPluginData>,\n        changeToFirstInput: Boolean,\n        filters: UtxoFilters\n    ): Long {\n        if (transactionFeeCalculator == null) throw CoreError.ReadOnlyCore\n\n        val outputs = unspentOutputInfos?.let { getOutputsFromInfos(it, filters) }\n\n        val spendableBalance = when {\n            outputs == null && filters.isEmpty() -> {\n                balance.spendable\n            }\n            outputs != null -> {\n                outputs.sumOf { it.output.value }\n            }\n            else -> {\n                unspentOutputSelector.getAllSpendable(filters).sumOf { it.output.value }\n            }\n        }\n\n        val sendAllFee = transactionFeeCalculator.sendInfo(\n            value = spendableBalance,\n            feeRate = feeRate,\n            senderPay = false,\n            toAddress = address,\n            memo = memo,\n            unspentOutputs = outputs,\n            pluginData = pluginData,\n            changeToFirstInput = changeToFirstInput,\n            filters = filters,\n        ).fee\n\n        return max(0L, spendableBalance - sendAllFee)\n    }\n\n    private fun getOutputsFromInfos(\n        unspentOutputInfos: List<UnspentOutputInfo>,\n        filters: UtxoFilters,\n    ): List<UnspentOutput> {\n        val allSpendable = unspentOutputSelector.getAllSpendable(filters)\n\n        return unspentOutputInfos.mapNotNull { unspentOutputInfo ->\n            allSpendable.firstOrNull { unspentOutput ->\n                unspentOutput.transaction.hash.contentEquals(unspentOutputInfo.transactionHash) &&\n                    unspentOutput.output.index == unspentOutputInfo.outputIndex\n            }\n        }\n    }\n\n    fun minimumSpendableValue(address: String?): Int {\n        // by default script type is P2PKH, since it is most used\n        val scriptType = when {\n            address != null -> addressConverter.convert(address).scriptType\n            else -> ScriptType.P2PKH\n        }\n\n        return dustCalculator?.dust(scriptType) ?: throw CoreError.ReadOnlyCore\n    }\n\n    fun getRawTransaction(transactionHash: String): String? {\n        return dataProvider.getRawTransaction(transactionHash)\n    }\n\n    fun getTransaction(hash: String): TransactionInfo? {\n        return dataProvider.getTransaction(hash)\n    }\n\n    fun replacementTransaction(\n        transactionHash: String,\n        minFee: Long,\n        type: ReplacementType\n    ): ReplacementTransaction {\n        val replacementTransactionBuilder = this.replacementTransactionBuilder ?: throw CoreError.ReadOnlyCore\n\n        val (mutableTransaction, fullInfo, descendantTransactionHashes) =\n            replacementTransactionBuilder.replacementTransaction(\n                transactionHash,\n                minFee,\n                type\n            )\n        val info = dataProvider.transactionInfo(fullInfo)\n        return ReplacementTransaction(mutableTransaction, info, descendantTransactionHashes)\n    }\n\n    fun send(replacementTransaction: ReplacementTransaction): FullTransaction {\n        val transactionCreator = this.transactionCreator ?: throw CoreError.ReadOnlyCore\n\n        return transactionCreator.create(replacementTransaction.mutableTransaction)\n    }\n\n    fun replacementTransactionInfo(\n        transactionHash: String,\n        type: ReplacementType\n    ): ReplacementTransactionInfo? {\n        return replacementTransactionBuilder?.replacementInfo(transactionHash, type)\n    }\n\n    sealed class KitState {\n        object Synced : KitState()\n        class NotSynced(val exception: Throwable) : KitState()\n        class Syncing(val progress: Double, val blocksRemaining: Int? = null) : KitState()\n        class ApiSyncing(val transactions: Int) : KitState()\n\n        override fun equals(other: Any?) = when {\n            this is Synced && other is Synced -> true\n            this is NotSynced && other is NotSynced -> exception == other.exception\n            this is Syncing && other is Syncing -> this.progress == other.progress\n            this is ApiSyncing && other is ApiSyncing -> this.transactions == other.transactions\n            else -> false\n        }\n\n        override fun toString() = when (this) {\n            is Synced -> \"Synced\"\n            is NotSynced -> \"NotSynced-${this.exception.javaClass.simpleName}\"\n            is Syncing -> \"Syncing-${(this.progress * 100).roundToInt() / 100.0}\"\n            is ApiSyncing -> \"ApiSyncing-$transactions\"\n        }\n\n        override fun hashCode(): Int {\n            var result = javaClass.hashCode()\n            if (this is Syncing) {\n                result = 31 * result + progress.hashCode()\n            }\n            if (this is NotSynced) {\n                result = 31 * result + exception.hashCode()\n            }\n            if (this is ApiSyncing) {\n                result = 31 * result + transactions.hashCode()\n            }\n            return result\n        }\n    }\n\n    sealed class SyncMode {\n        class Full : SyncMode()\n        class Api : SyncMode()\n        class Blockchair : SyncMode()\n    }\n\n    sealed class StateError : Exception() {\n        class NotStarted : StateError()\n        class NoInternet : StateError()\n    }\n\n    sealed class CoreError : Exception() {\n        object ReadOnlyCore : CoreError()\n    }\n\n    sealed class SendType {\n        object P2P: SendType()\n        class API(val blockchairApi: BlockchairApi): SendType()\n    }\n\n    companion object {\n        fun firstAddress(seed: ByteArray, purpose: Purpose, network: Network, addressConverter: AddressConverterChain) : Address {\n            val wallet = Wallet(HDWallet(seed, network.coinType, purpose), 20)\n            val publicKey = wallet.publicKey(0, 0, true)\n\n            return addressConverter.convert(publicKey, purpose.scriptType)\n        }\n\n        fun firstAddress(\n            extendedKey: HDExtendedKey,\n            purpose: Purpose,\n            network: Network,\n            addressConverter: AddressConverterChain\n        ): Address {\n            val publicKey = if (!extendedKey.isPublic) {\n                when (extendedKey.derivedType) {\n                    HDExtendedKey.DerivedType.Master -> {\n                        val wallet = Wallet(HDWallet(extendedKey.key, network.coinType, purpose), 0)\n                        wallet.publicKey(0, 0, true)\n                    }\n\n                    HDExtendedKey.DerivedType.Account -> {\n                        val wallet = AccountWallet(HDWalletAccount(extendedKey.key), 0)\n                        wallet.publicKey(0,true)\n                    }\n\n                    HDExtendedKey.DerivedType.Bip32 -> {\n                        throw IllegalStateException(\"Custom Bip32 Extended Keys are not supported\")\n                    }\n                }\n            } else {\n                when (extendedKey.derivedType) {\n                    HDExtendedKey.DerivedType.Account -> {\n                        val wallet = WatchAccountWallet(HDWalletAccountWatch(extendedKey.key), 0)\n                        wallet.publicKey(0,true)\n                    }\n\n                    HDExtendedKey.DerivedType.Bip32, HDExtendedKey.DerivedType.Master -> {\n                        throw IllegalStateException(\"Only Account Extended Public Keys are supported\")\n                    }\n                }\n            }\n\n            return addressConverter.convert(publicKey, purpose.scriptType)\n        }\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/BitcoinCoreBuilder.kt",
    "content": "package io.horizontalsystems.bitcoincore\n\nimport android.content.Context\nimport io.horizontalsystems.bitcoincore.apisync.blockchair.BlockchairApi\nimport io.horizontalsystems.bitcoincore.apisync.blockchair.BlockchairApiSyncer\nimport io.horizontalsystems.bitcoincore.apisync.blockchair.BlockchairLastBlockProvider\nimport io.horizontalsystems.bitcoincore.apisync.blockchair.BlockchairTransactionProvider\nimport io.horizontalsystems.bitcoincore.apisync.legacy.ApiSyncer\nimport io.horizontalsystems.bitcoincore.apisync.legacy.BlockHashDiscoveryBatch\nimport io.horizontalsystems.bitcoincore.apisync.legacy.BlockHashScanHelper\nimport io.horizontalsystems.bitcoincore.apisync.legacy.BlockHashScanner\nimport io.horizontalsystems.bitcoincore.apisync.legacy.IMultiAccountPublicKeyFetcher\nimport io.horizontalsystems.bitcoincore.apisync.legacy.IPublicKeyFetcher\nimport io.horizontalsystems.bitcoincore.apisync.legacy.MultiAccountPublicKeyFetcher\nimport io.horizontalsystems.bitcoincore.apisync.legacy.PublicKeyFetcher\nimport io.horizontalsystems.bitcoincore.apisync.legacy.WatchAddressBlockHashScanHelper\nimport io.horizontalsystems.bitcoincore.apisync.legacy.WatchPublicKeyFetcher\nimport io.horizontalsystems.bitcoincore.blocks.BlockDownload\nimport io.horizontalsystems.bitcoincore.blocks.BlockSyncer\nimport io.horizontalsystems.bitcoincore.blocks.Blockchain\nimport io.horizontalsystems.bitcoincore.blocks.BloomFilterLoader\nimport io.horizontalsystems.bitcoincore.blocks.InitialBlockDownload\nimport io.horizontalsystems.bitcoincore.blocks.MerkleBlockExtractor\nimport io.horizontalsystems.bitcoincore.blocks.validators.IBlockValidator\nimport io.horizontalsystems.bitcoincore.core.AccountWallet\nimport io.horizontalsystems.bitcoincore.core.BaseTransactionInfoConverter\nimport io.horizontalsystems.bitcoincore.core.DataProvider\nimport io.horizontalsystems.bitcoincore.core.DoubleSha256Hasher\nimport io.horizontalsystems.bitcoincore.core.IApiSyncer\nimport io.horizontalsystems.bitcoincore.core.IApiTransactionProvider\nimport io.horizontalsystems.bitcoincore.core.IHasher\nimport io.horizontalsystems.bitcoincore.core.IInitialDownload\nimport io.horizontalsystems.bitcoincore.core.IPlugin\nimport io.horizontalsystems.bitcoincore.core.IPrivateWallet\nimport io.horizontalsystems.bitcoincore.core.IPublicKeyManager\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.core.ITransactionInfoConverter\nimport io.horizontalsystems.bitcoincore.core.PluginManager\nimport io.horizontalsystems.bitcoincore.core.TransactionDataSorterFactory\nimport io.horizontalsystems.bitcoincore.core.TransactionInfoConverter\nimport io.horizontalsystems.bitcoincore.core.Wallet\nimport io.horizontalsystems.bitcoincore.core.WatchAccountWallet\nimport io.horizontalsystems.bitcoincore.core.scriptType\nimport io.horizontalsystems.bitcoincore.managers.AccountPublicKeyManager\nimport io.horizontalsystems.bitcoincore.managers.ApiSyncStateManager\nimport io.horizontalsystems.bitcoincore.managers.BloomFilterManager\nimport io.horizontalsystems.bitcoincore.managers.ConnectionManager\nimport io.horizontalsystems.bitcoincore.managers.IBloomFilterProvider\nimport io.horizontalsystems.bitcoincore.managers.IrregularOutputFinder\nimport io.horizontalsystems.bitcoincore.managers.PendingOutpointsProvider\nimport io.horizontalsystems.bitcoincore.managers.PublicKeyManager\nimport io.horizontalsystems.bitcoincore.managers.RestoreKeyConverterChain\nimport io.horizontalsystems.bitcoincore.managers.SyncManager\nimport io.horizontalsystems.bitcoincore.managers.UnspentOutputProvider\nimport io.horizontalsystems.bitcoincore.managers.UnspentOutputSelector\nimport io.horizontalsystems.bitcoincore.managers.UnspentOutputSelectorChain\nimport io.horizontalsystems.bitcoincore.managers.UnspentOutputSelectorSingleNoChange\nimport io.horizontalsystems.bitcoincore.models.Checkpoint\nimport io.horizontalsystems.bitcoincore.models.WatchAddressPublicKey\nimport io.horizontalsystems.bitcoincore.network.Network\nimport io.horizontalsystems.bitcoincore.network.messages.AddrMessageParser\nimport io.horizontalsystems.bitcoincore.network.messages.FilterLoadMessageSerializer\nimport io.horizontalsystems.bitcoincore.network.messages.GetBlocksMessageSerializer\nimport io.horizontalsystems.bitcoincore.network.messages.GetDataMessageParser\nimport io.horizontalsystems.bitcoincore.network.messages.GetDataMessageSerializer\nimport io.horizontalsystems.bitcoincore.network.messages.InvMessageParser\nimport io.horizontalsystems.bitcoincore.network.messages.InvMessageSerializer\nimport io.horizontalsystems.bitcoincore.network.messages.MempoolMessageSerializer\nimport io.horizontalsystems.bitcoincore.network.messages.MerkleBlockMessageParser\nimport io.horizontalsystems.bitcoincore.network.messages.NetworkMessageParser\nimport io.horizontalsystems.bitcoincore.network.messages.NetworkMessageSerializer\nimport io.horizontalsystems.bitcoincore.network.messages.PingMessageParser\nimport io.horizontalsystems.bitcoincore.network.messages.PingMessageSerializer\nimport io.horizontalsystems.bitcoincore.network.messages.PongMessageParser\nimport io.horizontalsystems.bitcoincore.network.messages.PongMessageSerializer\nimport io.horizontalsystems.bitcoincore.network.messages.RejectMessageParser\nimport io.horizontalsystems.bitcoincore.network.messages.TransactionMessageParser\nimport io.horizontalsystems.bitcoincore.network.messages.TransactionMessageSerializer\nimport io.horizontalsystems.bitcoincore.network.messages.VerAckMessageParser\nimport io.horizontalsystems.bitcoincore.network.messages.VerAckMessageSerializer\nimport io.horizontalsystems.bitcoincore.network.messages.VersionMessageParser\nimport io.horizontalsystems.bitcoincore.network.messages.VersionMessageSerializer\nimport io.horizontalsystems.bitcoincore.network.peer.MempoolTransactions\nimport io.horizontalsystems.bitcoincore.network.peer.PeerAddressManager\nimport io.horizontalsystems.bitcoincore.network.peer.PeerGroup\nimport io.horizontalsystems.bitcoincore.network.peer.PeerManager\nimport io.horizontalsystems.bitcoincore.rbf.ReplacementTransactionBuilder\nimport io.horizontalsystems.bitcoincore.serializers.BlockHeaderParser\nimport io.horizontalsystems.bitcoincore.serializers.TransactionSerializer\nimport io.horizontalsystems.bitcoincore.transactions.BlockTransactionProcessor\nimport io.horizontalsystems.bitcoincore.transactions.PendingTransactionProcessor\nimport io.horizontalsystems.bitcoincore.transactions.SendTransactionsOnPeersSynced\nimport io.horizontalsystems.bitcoincore.transactions.TransactionConflictsResolver\nimport io.horizontalsystems.bitcoincore.transactions.TransactionCreator\nimport io.horizontalsystems.bitcoincore.transactions.TransactionFeeCalculator\nimport io.horizontalsystems.bitcoincore.transactions.TransactionInvalidator\nimport io.horizontalsystems.bitcoincore.transactions.TransactionSendTimer\nimport io.horizontalsystems.bitcoincore.transactions.TransactionSender\nimport io.horizontalsystems.bitcoincore.transactions.TransactionSizeCalculator\nimport io.horizontalsystems.bitcoincore.transactions.TransactionSyncer\nimport io.horizontalsystems.bitcoincore.transactions.builder.EcdsaInputSigner\nimport io.horizontalsystems.bitcoincore.transactions.builder.InputSetter\nimport io.horizontalsystems.bitcoincore.transactions.builder.LockTimeSetter\nimport io.horizontalsystems.bitcoincore.transactions.builder.OutputSetter\nimport io.horizontalsystems.bitcoincore.transactions.builder.RecipientSetter\nimport io.horizontalsystems.bitcoincore.transactions.builder.SchnorrInputSigner\nimport io.horizontalsystems.bitcoincore.transactions.builder.TransactionBuilder\nimport io.horizontalsystems.bitcoincore.transactions.builder.TransactionSigner\nimport io.horizontalsystems.bitcoincore.transactions.extractors.MyOutputsCache\nimport io.horizontalsystems.bitcoincore.transactions.extractors.TransactionExtractor\nimport io.horizontalsystems.bitcoincore.transactions.extractors.TransactionMetadataExtractor\nimport io.horizontalsystems.bitcoincore.transactions.extractors.TransactionOutputProvider\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport io.horizontalsystems.bitcoincore.utils.AddressConverterChain\nimport io.horizontalsystems.bitcoincore.utils.Base58AddressConverter\nimport io.horizontalsystems.bitcoincore.utils.PaymentAddressParser\nimport io.horizontalsystems.hdwalletkit.HDExtendedKey\nimport io.horizontalsystems.hdwalletkit.HDWallet\nimport io.horizontalsystems.hdwalletkit.HDWalletAccount\nimport io.horizontalsystems.hdwalletkit.HDWalletAccountWatch\n\nclass BitcoinCoreBuilder {\n\n    val addressConverter = AddressConverterChain()\n\n    // required parameters\n    private var context: Context? = null\n    private var extendedKey: HDExtendedKey? = null\n    private var watchAddressPublicKey: WatchAddressPublicKey? = null\n    private var purpose: HDWallet.Purpose? = null\n    private var network: Network? = null\n    private var paymentAddressParser: PaymentAddressParser? = null\n    private var storage: IStorage? = null\n    private var apiTransactionProvider: IApiTransactionProvider? = null\n    private var blockHeaderHasher: IHasher? = null\n    private var transactionInfoConverter: ITransactionInfoConverter? = null\n    private var blockValidator: IBlockValidator? = null\n    private var checkpoint: Checkpoint? = null\n    private var apiSyncStateManager: ApiSyncStateManager? = null\n\n    // parameters with default values\n    private var confirmationsThreshold = 6\n    private var syncMode: BitcoinCore.SyncMode = BitcoinCore.SyncMode.Api()\n    private var peerSize = 10\n    private val plugins = mutableListOf<IPlugin>()\n    private var handleAddrMessage = true\n    private var sendType: BitcoinCore.SendType = BitcoinCore.SendType.P2P\n\n    fun setContext(context: Context): BitcoinCoreBuilder {\n        this.context = context\n        return this\n    }\n\n    fun setExtendedKey(extendedKey: HDExtendedKey?): BitcoinCoreBuilder {\n        this.extendedKey = extendedKey\n        return this\n    }\n\n    fun setWatchAddressPublicKey(publicKey: WatchAddressPublicKey?): BitcoinCoreBuilder {\n        this.watchAddressPublicKey = publicKey\n        return this\n    }\n\n    fun setPurpose(purpose: HDWallet.Purpose): BitcoinCoreBuilder {\n        this.purpose = purpose\n        return this\n    }\n\n    fun setNetwork(network: Network): BitcoinCoreBuilder {\n        this.network = network\n        return this\n    }\n\n    fun setPaymentAddressParser(paymentAddressParser: PaymentAddressParser): BitcoinCoreBuilder {\n        this.paymentAddressParser = paymentAddressParser\n        return this\n    }\n\n    fun setConfirmationThreshold(confirmationsThreshold: Int): BitcoinCoreBuilder {\n        this.confirmationsThreshold = confirmationsThreshold\n        return this\n    }\n\n    fun setSyncMode(syncMode: BitcoinCore.SyncMode): BitcoinCoreBuilder {\n        this.syncMode = syncMode\n        return this\n    }\n\n    fun setSendType(sendType: BitcoinCore.SendType): BitcoinCoreBuilder {\n        this.sendType = sendType\n        return this\n    }\n\n    fun setPeerSize(peerSize: Int): BitcoinCoreBuilder {\n        if (peerSize < TransactionSender.minConnectedPeerSize) {\n            throw Error(\"Peer size cannot be less than ${TransactionSender.minConnectedPeerSize}\")\n        }\n\n        this.peerSize = peerSize\n        return this\n    }\n\n    fun setStorage(storage: IStorage): BitcoinCoreBuilder {\n        this.storage = storage\n        return this\n    }\n\n    fun setBlockHeaderHasher(blockHeaderHasher: IHasher): BitcoinCoreBuilder {\n        this.blockHeaderHasher = blockHeaderHasher\n        return this\n    }\n\n    fun setApiTransactionProvider(apiTransactionProvider: IApiTransactionProvider?): BitcoinCoreBuilder {\n        this.apiTransactionProvider = apiTransactionProvider\n        return this\n    }\n\n    fun setTransactionInfoConverter(transactionInfoConverter: ITransactionInfoConverter): BitcoinCoreBuilder {\n        this.transactionInfoConverter = transactionInfoConverter\n        return this\n    }\n\n    fun setBlockValidator(blockValidator: IBlockValidator): BitcoinCoreBuilder {\n        this.blockValidator = blockValidator\n        return this\n    }\n\n    fun setHandleAddrMessage(handle: Boolean): BitcoinCoreBuilder {\n        handleAddrMessage = handle\n        return this\n    }\n\n    fun addPlugin(plugin: IPlugin): BitcoinCoreBuilder {\n        plugins.add(plugin)\n        return this\n    }\n\n    fun setCheckpoint(checkpoint: Checkpoint): BitcoinCoreBuilder {\n        this.checkpoint = checkpoint\n        return this\n    }\n\n    fun setApiSyncStateManager(apiSyncStateManager: ApiSyncStateManager): BitcoinCoreBuilder {\n        this.apiSyncStateManager = apiSyncStateManager\n        return this\n    }\n\n    fun build(): BitcoinCore {\n        val context = checkNotNull(this.context)\n        val extendedKey = this.extendedKey\n        val watchAddressPublicKey = this.watchAddressPublicKey\n        val purpose = checkNotNull(this.purpose)\n        val network = checkNotNull(this.network)\n        val paymentAddressParser = checkNotNull(this.paymentAddressParser)\n        val storage = checkNotNull(this.storage)\n        val apiTransactionProvider = checkNotNull(this.apiTransactionProvider)\n        val checkpoint = checkNotNull(this.checkpoint)\n        val apiSyncStateManager = checkNotNull(this.apiSyncStateManager)\n        val blockHeaderHasher = this.blockHeaderHasher ?: DoubleSha256Hasher()\n        val transactionInfoConverter = this.transactionInfoConverter ?: TransactionInfoConverter()\n\n        val restoreKeyConverterChain = RestoreKeyConverterChain()\n\n        val pluginManager = PluginManager()\n        plugins.forEach { pluginManager.addPlugin(it) }\n\n        transactionInfoConverter.baseConverter = BaseTransactionInfoConverter(pluginManager)\n\n        val unspentOutputProvider = UnspentOutputProvider(storage, confirmationsThreshold, pluginManager)\n\n        val dataProvider = DataProvider(storage, unspentOutputProvider, transactionInfoConverter)\n\n        val connectionManager = ConnectionManager(context)\n\n        var privateWallet: IPrivateWallet? = null\n        val publicKeyFetcher: IPublicKeyFetcher\n        var multiAccountPublicKeyFetcher: IMultiAccountPublicKeyFetcher? = null\n        val publicKeyManager: IPublicKeyManager\n        val bloomFilterProvider: IBloomFilterProvider\n        val gapLimit = 20\n\n        if (watchAddressPublicKey != null) {\n            storage.savePublicKeys(listOf(watchAddressPublicKey))\n\n            WatchAddressPublicKeyManager(watchAddressPublicKey, restoreKeyConverterChain).let {\n                publicKeyFetcher = it\n                publicKeyManager = it\n                bloomFilterProvider = it\n            }\n        } else if (extendedKey != null) {\n            if (!extendedKey.isPublic) {\n                when (extendedKey.derivedType) {\n                    HDExtendedKey.DerivedType.Master -> {\n                        val wallet = Wallet(HDWallet(extendedKey.key, network.coinType, purpose), gapLimit)\n                        privateWallet = wallet\n                        val fetcher = MultiAccountPublicKeyFetcher(wallet)\n                        publicKeyFetcher = fetcher\n                        multiAccountPublicKeyFetcher = fetcher\n                        PublicKeyManager.create(storage, wallet, restoreKeyConverterChain).apply {\n                            publicKeyManager = this\n                            bloomFilterProvider = this\n                        }\n                    }\n\n                    HDExtendedKey.DerivedType.Account -> {\n                        val wallet = AccountWallet(HDWalletAccount(extendedKey.key), gapLimit)\n                        privateWallet = wallet\n                        val fetcher = PublicKeyFetcher(wallet)\n                        publicKeyFetcher = fetcher\n                        AccountPublicKeyManager.create(storage, wallet, restoreKeyConverterChain).apply {\n                            publicKeyManager = this\n                            bloomFilterProvider = this\n                        }\n\n                    }\n\n                    HDExtendedKey.DerivedType.Bip32 -> {\n                        throw IllegalStateException(\"Custom Bip32 Extended Keys are not supported\")\n                    }\n                }\n            } else {\n                when (extendedKey.derivedType) {\n                    HDExtendedKey.DerivedType.Account -> {\n                        val wallet = WatchAccountWallet(HDWalletAccountWatch(extendedKey.key), gapLimit)\n                        val fetcher = WatchPublicKeyFetcher(wallet)\n                        publicKeyFetcher = fetcher\n                        AccountPublicKeyManager.create(storage, wallet, restoreKeyConverterChain).apply {\n                            publicKeyManager = this\n                            bloomFilterProvider = this\n                        }\n\n                    }\n\n                    HDExtendedKey.DerivedType.Bip32, HDExtendedKey.DerivedType.Master -> {\n                        throw IllegalStateException(\"Only Account Extended Public Keys are supported\")\n                    }\n                }\n            }\n        } else {\n            throw IllegalStateException(\"Both extendedKey and watchAddressPublicKey are NULL!\")\n        }\n\n        val pendingOutpointsProvider = PendingOutpointsProvider(storage)\n\n        val additionalScriptTypes = if (watchAddressPublicKey != null) listOf(ScriptType.P2PKH) else emptyList()\n        val irregularOutputFinder = IrregularOutputFinder(storage, additionalScriptTypes)\n        val metadataExtractor = TransactionMetadataExtractor(\n            MyOutputsCache.create(storage),\n            TransactionOutputProvider(storage)\n        )\n        val transactionExtractor = TransactionExtractor(addressConverter, storage, pluginManager, metadataExtractor)\n\n        val conflictsResolver = TransactionConflictsResolver(storage)\n        val ignorePendingIncoming = syncMode is BitcoinCore.SyncMode.Blockchair\n        val pendingTransactionProcessor = PendingTransactionProcessor(\n            storage,\n            transactionExtractor,\n            publicKeyManager,\n            irregularOutputFinder,\n            dataProvider,\n            conflictsResolver,\n            ignorePendingIncoming\n        )\n        val invalidator = TransactionInvalidator(storage, transactionInfoConverter, dataProvider)\n        val blockTransactionProcessor = BlockTransactionProcessor(\n            storage,\n            transactionExtractor,\n            publicKeyManager,\n            irregularOutputFinder,\n            dataProvider,\n            conflictsResolver,\n            invalidator\n        )\n\n        val peerHostManager = PeerAddressManager(network, storage)\n        val bloomFilterManager = BloomFilterManager()\n\n        val peerManager = PeerManager()\n\n        val networkMessageParser = NetworkMessageParser(network.magic)\n        val networkMessageSerializer = NetworkMessageSerializer(network.magic)\n\n        val blockchain = Blockchain(storage, blockValidator, dataProvider)\n        val blockSyncer = BlockSyncer(storage, blockchain, blockTransactionProcessor, publicKeyManager, checkpoint)\n\n\n        val peerGroup = PeerGroup(\n            peerHostManager,\n            network,\n            peerManager,\n            peerSize,\n            networkMessageParser,\n            networkMessageSerializer,\n            connectionManager,\n            blockSyncer.localDownloadedBestBlockHeight,\n            handleAddrMessage\n        )\n        peerHostManager.listener = peerGroup\n\n        val blockHashScanHelper = if (watchAddressPublicKey == null) BlockHashScanHelper() else WatchAddressBlockHashScanHelper()\n        val blockHashScanner = BlockHashScanner(restoreKeyConverterChain, apiTransactionProvider, blockHashScanHelper)\n\n        val apiSyncer: IApiSyncer\n        val initialDownload: IInitialDownload\n        val merkleBlockExtractor = MerkleBlockExtractor(network.maxBlockSize)\n\n        when (val syncMode = syncMode) {\n            is BitcoinCore.SyncMode.Blockchair -> {\n                val blockchairApi = if (apiTransactionProvider is BlockchairTransactionProvider) {\n                    apiTransactionProvider.blockchairApi\n                } else {\n                    BlockchairApi(network.blockchairChainId)\n                }\n                val lastBlockProvider = BlockchairLastBlockProvider(blockchairApi)\n                apiSyncer = BlockchairApiSyncer(\n                    storage,\n                    restoreKeyConverterChain,\n                    apiTransactionProvider,\n                    lastBlockProvider,\n                    publicKeyManager,\n                    blockchain,\n                    apiSyncStateManager\n                )\n                initialDownload = BlockDownload(blockSyncer, peerManager, merkleBlockExtractor)\n            }\n\n            else -> {\n                val blockDiscovery = BlockHashDiscoveryBatch(blockHashScanner, publicKeyFetcher, checkpoint.block.height, gapLimit)\n                apiSyncer = ApiSyncer(\n                    storage,\n                    blockDiscovery,\n                    publicKeyManager,\n                    multiAccountPublicKeyFetcher,\n                    apiSyncStateManager\n                )\n                initialDownload = InitialBlockDownload(blockSyncer, peerManager, merkleBlockExtractor)\n            }\n        }\n\n        val syncManager = SyncManager(connectionManager, apiSyncer, peerGroup, storage, syncMode, blockSyncer.localDownloadedBestBlockHeight)\n        apiSyncer.listener = syncManager\n        connectionManager.listener = syncManager\n        blockSyncer.listener = syncManager\n        initialDownload.listener = syncManager\n        blockHashScanner.listener = syncManager\n\n        val unspentOutputSelector = UnspentOutputSelectorChain(unspentOutputProvider)\n        val pendingTransactionSyncer = TransactionSyncer(storage, pendingTransactionProcessor, invalidator, publicKeyManager)\n        val transactionDataSorterFactory = TransactionDataSorterFactory()\n\n        var dustCalculator: DustCalculator? = null\n        var transactionSizeCalculator: TransactionSizeCalculator? = null\n        var transactionFeeCalculator: TransactionFeeCalculator? = null\n        var transactionSender: TransactionSender? = null\n        var transactionCreator: TransactionCreator? = null\n        var replacementTransactionBuilder: ReplacementTransactionBuilder? = null\n\n        if (privateWallet != null) {\n            val ecdsaInputSigner = EcdsaInputSigner(privateWallet, network)\n            val schnorrInputSigner = SchnorrInputSigner(privateWallet)\n            val transactionSizeCalculatorInstance = TransactionSizeCalculator()\n            val dustCalculatorInstance = DustCalculator(network.dustRelayTxFee, transactionSizeCalculatorInstance)\n            val recipientSetter = RecipientSetter(addressConverter, pluginManager)\n            val outputSetter = OutputSetter(transactionDataSorterFactory)\n            val inputSetter = InputSetter(\n                unspentOutputSelector,\n                publicKeyManager,\n                addressConverter,\n                purpose.scriptType,\n                transactionSizeCalculatorInstance,\n                pluginManager,\n                dustCalculatorInstance,\n                transactionDataSorterFactory\n            )\n            val lockTimeSetter = LockTimeSetter(storage)\n            val transactionBuilder = TransactionBuilder(recipientSetter, outputSetter, inputSetter, lockTimeSetter)\n            transactionFeeCalculator = TransactionFeeCalculator(\n                recipientSetter,\n                inputSetter,\n                addressConverter,\n                publicKeyManager,\n                purpose.scriptType,\n            )\n            val transactionSendTimer = TransactionSendTimer(60)\n            val transactionSenderInstance = TransactionSender(\n                pendingTransactionSyncer,\n                peerManager,\n                initialDownload,\n                storage,\n                transactionSendTimer,\n                sendType,\n                TransactionSerializer\n            )\n\n            dustCalculator = dustCalculatorInstance\n            transactionSizeCalculator = transactionSizeCalculatorInstance\n            transactionSender = transactionSenderInstance\n\n            transactionSendTimer.listener = transactionSender\n            val signer = TransactionSigner(ecdsaInputSigner, schnorrInputSigner)\n            transactionCreator = TransactionCreator(transactionBuilder, pendingTransactionProcessor, transactionSenderInstance, signer, bloomFilterManager)\n            replacementTransactionBuilder = ReplacementTransactionBuilder(\n                storage, transactionSizeCalculator, dustCalculator, metadataExtractor, pluginManager, unspentOutputProvider, publicKeyManager, conflictsResolver, lockTimeSetter\n            )\n        }\n\n        val bitcoinCore = BitcoinCore(\n            storage,\n            dataProvider,\n            publicKeyManager,\n            addressConverter,\n            restoreKeyConverterChain,\n            transactionCreator,\n            transactionFeeCalculator,\n            replacementTransactionBuilder,\n            paymentAddressParser,\n            syncManager,\n            purpose,\n            peerManager,\n            dustCalculator,\n            pluginManager,\n            connectionManager\n        )\n\n        dataProvider.listener = bitcoinCore\n        syncManager.listener = bitcoinCore\n\n        val watchedTransactionManager = WatchedTransactionManager()\n        bloomFilterManager.addBloomFilterProvider(watchedTransactionManager)\n        bloomFilterManager.addBloomFilterProvider(bloomFilterProvider)\n        bloomFilterManager.addBloomFilterProvider(pendingOutpointsProvider)\n        bloomFilterManager.addBloomFilterProvider(irregularOutputFinder)\n\n        bitcoinCore.watchedTransactionManager = watchedTransactionManager\n        pendingTransactionProcessor.transactionListener = watchedTransactionManager\n        blockTransactionProcessor.transactionListener = watchedTransactionManager\n\n        bitcoinCore.peerGroup = peerGroup\n        bitcoinCore.transactionSyncer = pendingTransactionSyncer\n        bitcoinCore.networkMessageParser = networkMessageParser\n        bitcoinCore.networkMessageSerializer = networkMessageSerializer\n        bitcoinCore.unspentOutputSelector = unspentOutputSelector\n\n        peerGroup.peerTaskHandler = bitcoinCore.peerTaskHandlerChain\n        peerGroup.inventoryItemsHandler = bitcoinCore.inventoryItemsHandlerChain\n\n        bitcoinCore.prependAddressConverter(Base58AddressConverter(network.addressVersion, network.addressScriptVersion))\n\n        // this part can be moved to another place\n\n        bitcoinCore.addMessageParser(AddrMessageParser())\n            .addMessageParser(MerkleBlockMessageParser(BlockHeaderParser(blockHeaderHasher)))\n            .addMessageParser(InvMessageParser())\n            .addMessageParser(GetDataMessageParser())\n            .addMessageParser(PingMessageParser())\n            .addMessageParser(PongMessageParser())\n            .addMessageParser(TransactionMessageParser())\n            .addMessageParser(VerAckMessageParser())\n            .addMessageParser(VersionMessageParser())\n            .addMessageParser(RejectMessageParser())\n\n        bitcoinCore.addMessageSerializer(FilterLoadMessageSerializer())\n            .addMessageSerializer(GetBlocksMessageSerializer())\n            .addMessageSerializer(InvMessageSerializer())\n            .addMessageSerializer(GetDataMessageSerializer())\n            .addMessageSerializer(MempoolMessageSerializer())\n            .addMessageSerializer(PingMessageSerializer())\n            .addMessageSerializer(PongMessageSerializer())\n            .addMessageSerializer(TransactionMessageSerializer())\n            .addMessageSerializer(VerAckMessageSerializer())\n            .addMessageSerializer(VersionMessageSerializer())\n\n        val bloomFilterLoader = BloomFilterLoader(bloomFilterManager, peerManager)\n        bloomFilterManager.listener = bloomFilterLoader\n        bitcoinCore.addPeerGroupListener(bloomFilterLoader)\n\n        // todo: now this part cannot be moved to another place since bitcoinCore requires initialBlockDownload to be set. find solution to do so\n        bitcoinCore.initialDownload = initialDownload\n        bitcoinCore.addPeerTaskHandler(initialDownload)\n        bitcoinCore.addInventoryItemsHandler(initialDownload)\n        bitcoinCore.addPeerGroupListener(initialDownload)\n\n\n        val mempoolTransactions = MempoolTransactions(pendingTransactionSyncer, transactionSender)\n        bitcoinCore.addPeerTaskHandler(mempoolTransactions)\n        bitcoinCore.addInventoryItemsHandler(mempoolTransactions)\n        bitcoinCore.addPeerGroupListener(mempoolTransactions)\n\n        transactionSender?.let {\n            bitcoinCore.addPeerSyncListener(SendTransactionsOnPeersSynced(transactionSender))\n            bitcoinCore.addPeerTaskHandler(transactionSender)\n        }\n\n        if (transactionSizeCalculator != null && dustCalculator != null) {\n            bitcoinCore.prependUnspentOutputSelector(\n                UnspentOutputSelector(\n                    transactionSizeCalculator,\n                    dustCalculator,\n                    unspentOutputProvider\n                )\n            )\n            bitcoinCore.prependUnspentOutputSelector(\n                UnspentOutputSelectorSingleNoChange(\n                    transactionSizeCalculator,\n                    dustCalculator,\n                    unspentOutputProvider\n                )\n            )\n        }\n\n        return bitcoinCore\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/WatchAddressPublicKeyManager.kt",
    "content": "package io.horizontalsystems.bitcoincore\n\nimport io.horizontalsystems.bitcoincore.apisync.legacy.IPublicKeyFetcher\nimport io.horizontalsystems.bitcoincore.core.IPublicKeyManager\nimport io.horizontalsystems.bitcoincore.managers.AccountPublicKeyManager\nimport io.horizontalsystems.bitcoincore.managers.BloomFilterManager\nimport io.horizontalsystems.bitcoincore.managers.IBloomFilterProvider\nimport io.horizontalsystems.bitcoincore.managers.RestoreKeyConverterChain\nimport io.horizontalsystems.bitcoincore.models.PublicKey\nimport io.horizontalsystems.bitcoincore.models.WatchAddressPublicKey\n\nclass WatchAddressPublicKeyManager(\n    private val publicKey: WatchAddressPublicKey,\n    private val restoreKeyConverter: RestoreKeyConverterChain\n) : IPublicKeyFetcher, IPublicKeyManager, IBloomFilterProvider {\n\n    override fun publicKeys(indices: IntRange, external: Boolean) = listOf(publicKey)\n\n    override fun changePublicKey() = publicKey\n\n    override fun receivePublicKey() = publicKey\n\n    override fun usedExternalPublicKeys(change: Boolean): List<PublicKey> = listOf(publicKey)\n\n    override fun fillGap() {\n        bloomFilterManager?.regenerateBloomFilter()\n    }\n\n    override fun addKeys(keys: List<PublicKey>) = Unit\n\n    override fun gapShifts(): Boolean = false\n\n    override fun getPublicKeyByPath(path: String): PublicKey {\n        throw AccountPublicKeyManager.Error.InvalidPath\n    }\n\n    override var bloomFilterManager: BloomFilterManager? = null\n\n    override fun getBloomFilterElements() = restoreKeyConverter.bloomFilterElements(publicKey)\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/WatchedTransactionManager.kt",
    "content": "package io.horizontalsystems.bitcoincore\n\nimport io.horizontalsystems.bitcoincore.extensions.toHexString\nimport io.horizontalsystems.bitcoincore.extensions.toReversedHex\nimport io.horizontalsystems.bitcoincore.managers.BloomFilterManager\nimport io.horizontalsystems.bitcoincore.managers.IBloomFilterProvider\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.bitcoincore.utils.Utils\nimport java.util.logging.Logger\n\nclass WatchedTransactionManager : IBloomFilterProvider {\n    interface Listener {\n        fun onTransactionSeenP2SH(tx: FullTransaction, outputIndex: Int) = Unit\n        fun onTransactionSeenOutpoint(tx: FullTransaction, inputIndex: Int) = Unit\n    }\n\n    override var bloomFilterManager: BloomFilterManager? = null\n\n    private val filters = mutableMapOf<TransactionFilter, Listener>()\n    private val logger = Logger.getLogger(\"Watcher\")\n\n    fun add(filter: TransactionFilter, listener: Listener) {\n        logger.info(\"Add filter: $filter\")\n\n        filters[filter] = listener\n        bloomFilterManager?.regenerateBloomFilter()\n    }\n\n    override fun getBloomFilterElements(): List<ByteArray> {\n        return filters.keys.map { it.getBloomFilterElement() }\n    }\n\n    fun onTransactionReceived(transaction: FullTransaction) {\n        filters.forEach {\n            val filter = it.key\n            val listener = it.value\n\n            when (filter) {\n                is TransactionFilter.P2SHOutput -> {\n                    transaction.outputs.find { it.lockingScriptPayload?.contentEquals(filter.scriptHash) == true }?.let { output ->\n                        logger.info(\"Transaction received ${transaction.header.hash.toReversedHex()} for filter: $filter\")\n\n                        listener.onTransactionSeenP2SH(transaction, output.index)\n                    }\n                }\n                is TransactionFilter.Outpoint -> {\n                    val inputs = transaction.inputs\n                    val i =\n                        inputs.indexOfFirst { it.previousOutputTxHash.contentEquals(filter.transactionHash) && it.previousOutputIndex == filter.index }\n                    inputs.getOrNull(i)?.let {\n                        logger.info(\"Transaction received ${transaction.header.hash.toReversedHex()} for filter: $filter\")\n\n                        listener.onTransactionSeenOutpoint(transaction, i)\n                    }\n                }\n            }\n        }\n    }\n}\n\nsealed class TransactionFilter {\n    abstract fun getBloomFilterElement(): ByteArray\n\n    class P2SHOutput(val scriptHash: ByteArray) : TransactionFilter() {\n        override fun getBloomFilterElement(): ByteArray {\n            return scriptHash\n        }\n\n        override fun toString(): String {\n            return \"P2SHOutputFilter(${scriptHash.toHexString()})\"\n        }\n    }\n\n    class Outpoint(val transactionHash: ByteArray, val index: Long) : TransactionFilter() {\n        override fun getBloomFilterElement(): ByteArray {\n            return transactionHash + Utils.intToByteArray(index.toInt()).reversedArray()\n        }\n\n        override fun toString(): String {\n            return \"OutpointFilter(${transactionHash.toReversedHex()}, $index)\"\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/BCoinApi.kt",
    "content": "package io.horizontalsystems.bitcoincore.apisync\n\nimport com.eclipsesource.json.Json\nimport com.eclipsesource.json.JsonObject\nimport io.horizontalsystems.bitcoincore.apisync.model.AddressItem\nimport io.horizontalsystems.bitcoincore.apisync.model.TransactionItem\nimport io.horizontalsystems.bitcoincore.core.IApiTransactionProvider\nimport io.horizontalsystems.bitcoincore.managers.ApiManager\nimport java.util.logging.Logger\n\nclass BCoinApi(host: String) : IApiTransactionProvider {\n    private val apiManager = ApiManager(host)\n    private val logger = Logger.getLogger(\"BCoinApi\")\n\n    override fun transactions(addresses: List<String>, stopHeight: Int?): List<TransactionItem> {\n        val requestData = JsonObject().apply {\n            this[\"addresses\"] = Json.array(*addresses.toTypedArray())\n        }\n\n        logger.info(\"Request transactions for ${addresses.size} addresses: [${addresses.first()}, ...]\")\n\n        val response = apiManager.post(\"tx/address\", requestData.toString()).asArray()\n\n        logger.info(\"Got ${response.size()} transactions for requested addresses\")\n\n        val transactions = mutableListOf<TransactionItem>()\n\n        for (txItem in response) {\n            val tx = txItem.asObject()\n\n            val blockHashJson = tx[\"block\"] ?: continue\n            val blockHash = if (blockHashJson.isString) blockHashJson.asString() else continue\n\n            val outputs = mutableListOf<AddressItem>()\n            for (outputItem in tx[\"outputs\"].asArray()) {\n                val outputJson = outputItem.asObject()\n\n                val scriptJson = outputJson[\"script\"] ?: continue\n                val addressJson = outputJson[\"address\"]\n\n                if (scriptJson.isString) {\n                    outputs.add(AddressItem(scriptJson.asString(), addressJson?.asString()))\n                }\n            }\n\n            transactions.add(TransactionItem(blockHash, tx[\"height\"].asInt(), outputs))\n        }\n\n        return transactions\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/BiApiTransactionProvider.kt",
    "content": "package io.horizontalsystems.bitcoincore.apisync\n\nimport io.horizontalsystems.bitcoincore.apisync.model.TransactionItem\nimport io.horizontalsystems.bitcoincore.core.IApiTransactionProvider\nimport io.horizontalsystems.bitcoincore.managers.ApiSyncStateManager\n\nclass BiApiTransactionProvider(\n    private val restoreProvider: IApiTransactionProvider,\n    private val syncProvider: IApiTransactionProvider,\n    private val syncStateManager: ApiSyncStateManager\n) : IApiTransactionProvider {\n\n    override fun transactions(addresses: List<String>, stopHeight: Int?): List<TransactionItem> =\n        if (syncStateManager.restored) {\n            syncProvider.transactions(addresses, stopHeight)\n        } else {\n            restoreProvider.transactions(addresses, stopHeight)\n        }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/BlockHashFetcher.kt",
    "content": "package io.horizontalsystems.bitcoincore.apisync\n\nimport io.horizontalsystems.bitcoincore.apisync.blockchair.BlockchairBlockHashFetcher\nimport io.horizontalsystems.bitcoincore.apisync.blockchair.IBlockHashFetcher\n\nclass BlockHashFetcher(\n    private val hsBlockHashFetcher: HsBlockHashFetcher,\n    private val blockchairBlockHashFetcher: BlockchairBlockHashFetcher,\n    private val checkpointHeight: Int\n) : IBlockHashFetcher {\n\n    override fun fetch(heights: List<Int>): Map<Int, String> {\n        val beforeCheckpoint = heights.filter { it <= checkpointHeight }\n        val afterCheckpoint = heights.filter { it > checkpointHeight }\n\n        val blockHashes = mutableMapOf<Int, String>()\n        if (beforeCheckpoint.isNotEmpty()) {\n            blockHashes += hsBlockHashFetcher.fetch(beforeCheckpoint)\n        }\n        if (afterCheckpoint.isNotEmpty()) {\n            blockHashes += blockchairBlockHashFetcher.fetch(afterCheckpoint)\n        }\n\n        return blockHashes\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/BlockchainComApi.kt",
    "content": "package io.horizontalsystems.bitcoincore.apisync\n\nimport com.eclipsesource.json.JsonValue\nimport io.horizontalsystems.bitcoincore.apisync.blockchair.IBlockHashFetcher\nimport io.horizontalsystems.bitcoincore.apisync.model.AddressItem\nimport io.horizontalsystems.bitcoincore.apisync.model.TransactionItem\nimport io.horizontalsystems.bitcoincore.core.IApiTransactionProvider\nimport io.horizontalsystems.bitcoincore.managers.ApiManager\nimport java.util.concurrent.Callable\nimport java.util.concurrent.Executors\n\nclass BlockchainComApi(\n    transactionApiUrl: String,\n    private val blockHashFetcher: IBlockHashFetcher\n) : IApiTransactionProvider {\n\n    private val transactionsApiManager = ApiManager(transactionApiUrl)\n\n    private fun getTransactions(addresses: List<String>, offset: Int = 0): List<TransactionResponse> {\n        val joinedAddresses = addresses.joinToString(\"|\")\n        val json = requestInQueue(transactionsApiManager, \"multiaddr?active=$joinedAddresses&n=$paginationLimit&offset=$offset\").asObject()\n\n        val transactionsArray = json[\"txs\"].asArray()\n        return transactionsArray.map { transactionJson ->\n            val transaction = transactionJson.asObject()\n            val outputs = transaction[\"out\"].asArray().map { outputJson ->\n                val output = outputJson.asObject()\n\n                TransactionOutputResponse(\n                    output[\"script\"].asString(),\n                    output[\"addr\"]?.asString()\n                )\n            }\n            TransactionResponse(\n                if (transaction[\"block_height\"].isNumber) transaction[\"block_height\"].asInt() else null,\n                outputs\n            )\n\n        }\n    }\n\n    private fun fetchTransactionsChunk(addresses: List<String>, stopHeight: Int?, offset: Int = 0): List<TransactionResponse> {\n        val transactions = getTransactions(addresses, offset)\n\n        val filteredTransactions = transactions.filter { transaction ->\n            if (transaction.blockHeight != null && stopHeight != null) {\n                transaction.blockHeight > stopHeight\n            } else {\n                true\n            }\n        }\n\n        if (filteredTransactions.size < paginationLimit) {\n            return filteredTransactions\n        }\n\n        val nextTransactions = fetchTransactionsChunk(addresses, stopHeight, offset + paginationLimit)\n        return filteredTransactions + nextTransactions\n    }\n\n    private fun fetchTransactions(allAddresses: List<String>, stopHeight: Int?): List<TransactionResponse> {\n        val transactions = mutableListOf<TransactionResponse>()\n        for (chunk in allAddresses.chunked(addressesLimit)) {\n            transactions += fetchTransactionsChunk(chunk, stopHeight)\n        }\n        return transactions\n    }\n\n    override fun transactions(addresses: List<String>, stopHeight: Int?): List<TransactionItem> {\n        val transactions = fetchTransactions(addresses, stopHeight)\n        val blockHeights = transactions.mapNotNull { it.blockHeight }.distinct()\n\n        if (blockHeights.isEmpty()) return listOf()\n\n        val hashesMap = blockHashFetcher.fetch(blockHeights)\n\n        val items = transactions.mapNotNull { response ->\n            val blockHeight = response.blockHeight ?: return@mapNotNull null\n            val blockHash = hashesMap[response.blockHeight] ?: return@mapNotNull null\n\n            TransactionItem(\n                blockHash = blockHash,\n                blockHeight = blockHeight,\n                addressItems = response.outputs.map {\n                    AddressItem(it.script, it.address)\n                }.toMutableList()\n            )\n\n        }\n\n        return items\n    }\n\n    data class TransactionResponse(\n        val blockHeight: Int?,\n        val outputs: List<TransactionOutputResponse>\n    )\n\n    data class TransactionOutputResponse(\n        val script: String,\n        val address: String?\n    )\n\n    companion object {\n        private const val paginationLimit = 100\n        private const val addressesLimit = 50\n\n        private val executor = Executors.newSingleThreadExecutor()\n\n        fun requestInQueue(apiManager: ApiManager, path: String): JsonValue {\n            val callable = Callable {\n                Thread.sleep(500)\n                apiManager.doOkHttpGet(path)\n            }\n\n            return executor.submit(callable).get()\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/HsBlockHashFetcher.kt",
    "content": "package io.horizontalsystems.bitcoincore.apisync\n\nimport io.horizontalsystems.bitcoincore.apisync.blockchair.IBlockHashFetcher\nimport io.horizontalsystems.bitcoincore.managers.ApiManager\n\nclass HsBlockHashFetcher(url: String) : IBlockHashFetcher {\n    private val apiManager = ApiManager(url)\n\n    override fun fetch(heights: List<Int>): Map<Int, String> {\n        val joinedHeights = heights.sorted().joinToString(\",\") { it.toString() }\n        val blocks = apiManager.doOkHttpGet(\"hashes?numbers=$joinedHeights\").asArray()\n\n        return blocks.associate { blockJson ->\n            val block = blockJson.asObject()\n            Pair(\n                block[\"number\"].asInt(),\n                block[\"hash\"].asString()\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/InsightApi.kt",
    "content": "package io.horizontalsystems.bitcoincore.apisync\n\nimport io.horizontalsystems.bitcoincore.apisync.model.AddressItem\nimport io.horizontalsystems.bitcoincore.apisync.model.TransactionItem\nimport io.horizontalsystems.bitcoincore.core.IApiTransactionProvider\nimport io.horizontalsystems.bitcoincore.managers.ApiManager\nimport java.util.logging.Logger\n\nclass InsightApi(host: String) : IApiTransactionProvider {\n    private val maxAddressSize = 100\n    private val apiManager = ApiManager(host)\n    private val logger = Logger.getLogger(\"InsightApi\")\n\n    override fun transactions(addresses: List<String>, stopHeight: Int?): List<TransactionItem> {\n        logger.info(\"Request transactions for ${addresses.size} addresses: [${addresses.first()}, ...]\")\n\n        val transactions = mutableListOf<TransactionItem>()\n\n        addresses.chunked(maxAddressSize).forEach { addrs ->\n            fetchTransactions(addrs, transactions, 0, 50)\n        }\n\n        return transactions\n    }\n\n    private fun fetchTransactions(addrs: List<String>, txs: MutableList<TransactionItem>, from: Int, to: Int) {\n        val joinedAddresses = addrs.joinToString(\",\")\n        val json = apiManager.doOkHttpGet(\"addrs/$joinedAddresses/txs?from=$from&to=$to\").asObject()\n\n        val totalItems = json[\"totalItems\"].asInt()\n        val receivedTo = json[\"to\"].asInt()\n\n        val items = json[\"items\"].asArray()\n        for (item in items) {\n            val tx = item.asObject()\n\n            val blockHash = tx[\"blockhash\"] ?: continue\n            val blockheight = tx[\"blockheight\"] ?: continue\n            val addressItems = mutableListOf<AddressItem>()\n\n            for (outputItem in tx[\"vout\"].asArray()) {\n                val outputJson = outputItem.asObject()\n\n                val scriptJson = (outputJson[\"scriptPubKey\"] ?: continue).asObject()\n                val script = (scriptJson[\"hex\"] ?: continue).asString()\n                val addresses = (scriptJson[\"addresses\"] ?: continue).asArray()\n\n                addressItems.add(AddressItem(script, addresses[0].asString()))\n            }\n\n            txs.add(TransactionItem(blockHash.asString(), blockheight.asInt(), addressItems))\n        }\n\n        if (totalItems > to) {\n            fetchTransactions(addrs, txs, receivedTo, receivedTo + 50)\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/blockchair/BlockchairApi.kt",
    "content": "package io.horizontalsystems.bitcoincore.apisync.blockchair\n\nimport com.eclipsesource.json.Json\nimport com.eclipsesource.json.JsonObject\nimport io.horizontalsystems.bitcoincore.apisync.model.AddressItem\nimport io.horizontalsystems.bitcoincore.apisync.model.BlockHeaderItem\nimport io.horizontalsystems.bitcoincore.apisync.model.TransactionItem\nimport io.horizontalsystems.bitcoincore.extensions.hexToByteArray\nimport io.horizontalsystems.bitcoincore.managers.ApiManager\nimport io.horizontalsystems.bitcoincore.managers.ApiManagerException\nimport java.text.ParseException\nimport java.text.SimpleDateFormat\nimport java.util.Locale\nimport java.util.TimeZone\n\nclass BlockchairApi(\n    private val chainId: String,\n) {\n    private val apiManager = ApiManager(\"https://api.blocksdecoded.com/v1/blockchair\")\n    private val limit = 10000\n    private val dateFormat = SimpleDateFormat(\"yyyy-MM-dd HH:mm:ss\", Locale.getDefault())\n\n    init {\n        dateFormat.timeZone = TimeZone.getTimeZone(\"GMT\")\n    }\n\n    fun transactions(addresses: List<String>, stopHeight: Int?): List<TransactionItem> {\n        val transactionItemsMap = mutableMapOf<String, TransactionItem>()\n\n        for (chunk in addresses.chunked(100)) {\n            val (addressItems, transactions) = fetchTransactions(chunk, stopHeight)\n\n            for (transaction in transactions) {\n                val blockHeight = transaction.blockId ?: continue\n                val existing = transactionItemsMap[transaction.hash]\n\n                val transactionItem: TransactionItem\n                if (existing != null) {\n                    transactionItem = existing\n                } else {\n                    transactionItem = TransactionItem(\n                        blockHash = \"\",\n                        blockHeight = blockHeight,\n                        addressItems = mutableListOf()\n                    )\n                    transactionItemsMap[transaction.hash] = transactionItem\n                }\n                addressItems.firstOrNull { it.address == transaction.address }?.let { addressItem ->\n                    transactionItem.addressItems += addressItem\n                }\n            }\n        }\n        return transactionItemsMap.values.toList()\n    }\n\n    fun blockHashes(heights: List<Int>): Map<Int, String> {\n        val hashesMap = mutableMapOf<Int, String>()\n\n        for (chunk in heights.chunked(10)) {\n            val map = fetchBlockHashes(chunk)\n            hashesMap += map\n        }\n\n        return hashesMap\n    }\n\n    fun lastBlockHeader(): BlockHeaderItem {\n        val params = \"?limit=0\"\n        val url = \"$chainId/stats\"\n        val response = apiManager.doOkHttpGet(url + params).asObject()\n        val data = response.get(\"data\").asObject()\n\n        val height = data[\"best_block_height\"].asInt()\n        val hash = data[\"best_block_hash\"].asString()\n        val date = data[\"best_block_time\"].asString()\n        val timestamp = dateStringToTimestamp(date)\n\n        return BlockHeaderItem(hash.hexToByteArray(), height, timestamp!!)\n    }\n\n    fun broadcastTransaction(rawTransactionHex: String) {\n        val apiManager = ApiManager(\"https://api.blockchair.com\")\n        val url = \"$chainId/push/transaction\"\n\n        val body = JsonObject().apply {\n            this[\"data\"] = Json.value(rawTransactionHex)\n        }.toString()\n\n        apiManager.post(url, body)\n    }\n\n    private fun fetchTransactions(\n        addresses: List<String>,\n        stopHeight: Int? = null,\n        receivedAddressItems: List<AddressItem> = emptyList(),\n        receivedTransactionItems: List<Transaction> = emptyList()\n    ): Pair<List<AddressItem>, List<Transaction>> {\n        try {\n            val params = \"?transaction_details=true&limit=$limit,0&offset=${receivedTransactionItems.size}\"\n            val url = \"$chainId/dashboards/addresses/${addresses.joinToString(separator = \",\")}\"\n            val response = apiManager.doOkHttpGet(url + params).asObject()\n            val data = response.get(\"data\").asObject()\n\n            val addressItems = data.get(\"addresses\").asObject().map {\n                val address = it.name\n                val script = it.value.asObject().getString(\"script_hex\", \"\")\n                AddressItem(script, address)\n            }\n\n            val transactionItems = data.get(\"transactions\").asArray().map {\n                val txObject = it.asObject()\n                Transaction(\n                    hash = txObject[\"hash\"].asString(),\n                    blockId = txObject[\"block_id\"]?.asInt(),\n                    balanceChange = txObject[\"balance_change\"].asLong(),\n                    address = txObject[\"address\"].asString()\n                )\n            }\n\n            val filteredTransactionItems = transactionItems.filter { it.blockId == null || stopHeight == null || it.blockId > stopHeight }\n            val addressesMerged = receivedAddressItems + addressItems\n            val transactionsMerged = receivedTransactionItems + filteredTransactionItems\n\n            return if (filteredTransactionItems.size < limit) {\n                Pair(addressesMerged, transactionsMerged)\n            } else {\n                fetchTransactions(\n                    addresses = addresses,\n                    stopHeight = stopHeight,\n                    receivedAddressItems = addressesMerged,\n                    receivedTransactionItems = transactionsMerged\n                )\n            }\n        } catch (http404Exception: ApiManagerException.Http404Exception) {\n            return Pair(emptyList(), emptyList())\n        }\n    }\n\n    private fun dateStringToTimestamp(date: String): Long? {\n        return try {\n            dateFormat.parse(date)?.time?.let { it / 1000 }\n        } catch (e: ParseException) {\n            null\n        }\n    }\n\n    private fun fetchBlockHashes(heights: List<Int>): Map<Int, String> {\n        try {\n            val params = \"?limit=0\"\n            val url = \"$chainId/dashboards/blocks/${heights.joinToString(separator = \",\")}\"\n            val response = apiManager.doOkHttpGet(url + params).asObject()\n\n            val map = mutableMapOf<Int, String>()\n            val data = response.get(\"data\").asObject()\n            data.forEach { blockElement ->\n                val block = blockElement.value.asObject()[\"block\"].asObject()\n                val blockHeight = block[\"id\"].asInt()\n                val blockHash = block[\"hash\"].asString()\n                map[blockHeight] = blockHash\n            }\n            return map\n        } catch (http404Exception: ApiManagerException.Http404Exception) {\n            return emptyMap()\n        }\n    }\n\n    private data class Transaction(\n        val hash: String,\n        val blockId: Int?,\n        val balanceChange: Long,\n        val address: String\n    )\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/blockchair/BlockchairApiSyncer.kt",
    "content": "package io.horizontalsystems.bitcoincore.apisync.blockchair\n\nimport io.horizontalsystems.bitcoincore.blocks.Blockchain\nimport io.horizontalsystems.bitcoincore.core.IApiSyncer\nimport io.horizontalsystems.bitcoincore.core.IApiSyncerListener\nimport io.horizontalsystems.bitcoincore.core.IApiTransactionProvider\nimport io.horizontalsystems.bitcoincore.core.IPublicKeyManager\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.extensions.toReversedByteArray\nimport io.horizontalsystems.bitcoincore.managers.ApiSyncStateManager\nimport io.horizontalsystems.bitcoincore.managers.IRestoreKeyConverter\nimport io.horizontalsystems.bitcoincore.models.BlockHash\nimport io.horizontalsystems.bitcoincore.models.BlockHashPublicKey\nimport io.horizontalsystems.bitcoincore.models.PublicKey\nimport io.horizontalsystems.bitcoincore.storage.BlockHeader\nimport io.reactivex.Single\nimport io.reactivex.disposables.CompositeDisposable\nimport io.reactivex.schedulers.Schedulers\nimport java.util.logging.Logger\n\nclass BlockchairApiSyncer(\n    private val storage: IStorage,\n    private val restoreKeyConverter: IRestoreKeyConverter,\n    private val transactionProvider: IApiTransactionProvider,\n    private val lastBlockProvider: BlockchairLastBlockProvider,\n    private val publicKeyManager: IPublicKeyManager,\n    private val blockchain: Blockchain,\n    private val apiSyncStateManager: ApiSyncStateManager,\n) : IApiSyncer {\n\n    private val logger = Logger.getLogger(\"BlockchairApiSyncer\")\n    private val disposables = CompositeDisposable()\n\n    override var listener: IApiSyncerListener? = null\n\n    override val willSync: Boolean = true\n\n    override fun sync() {\n        scanSingle()\n            .subscribeOn(Schedulers.io())\n            .observeOn(Schedulers.io())\n            .subscribe({}, {\n                handleError(it)\n            }).let {\n                disposables.add(it)\n            }\n    }\n\n    override fun terminate() {\n        disposables.clear()\n    }\n\n    private fun handleError(error: Throwable) {\n        logger.severe(\"Error: ${error.message}\")\n        listener?.onSyncFailed(error)\n    }\n\n    private fun fetchLastBlock() {\n        val blockHeaderItem = lastBlockProvider.lastBlockHeader()\n        val header = BlockHeader(\n            version = 0,\n            hash = blockHeaderItem.hash,\n            previousBlockHeaderHash = byteArrayOf(),\n            merkleRoot = byteArrayOf(),\n            timestamp = blockHeaderItem.timestamp,\n            bits = 0,\n            nonce = 0\n        )\n\n        blockchain.insertLastBlock(header, blockHeaderItem.height)\n    }\n\n    private fun scanSingle(): Single<Unit> = Single.create {\n        val allKeys = storage.getPublicKeys()\n        val stopHeight = storage.downloadedTransactionsBestBlockHeight()\n        fetchRecursive(allKeys, allKeys, stopHeight)\n        fetchLastBlock()\n\n        apiSyncStateManager.restored = true\n        listener?.onSyncSuccess()\n    }\n\n    private fun fetchRecursive(\n        keys: List<PublicKey>,\n        allKeys: List<PublicKey>,\n        stopHeight: Int\n    ) {\n        val publicKeyMap = mutableMapOf<String, PublicKey>()\n        val addresses = mutableListOf<String>()\n\n        for (key in keys) {\n            val restoreKeys = restoreKeyConverter.keysForApiRestore(key)\n            for (address in restoreKeys) {\n                addresses.add(address)\n                publicKeyMap[address] = key\n            }\n        }\n\n        val transactionItems = transactionProvider.transactions(addresses, stopHeight)\n        val blockHashes = mutableListOf<BlockHash>()\n        val blockHashPublicKeys = mutableListOf<BlockHashPublicKey>()\n\n        for (transactionItem in transactionItems) {\n            val hash = transactionItem.blockHash.toReversedByteArray()\n\n            if (blockHashes.none { it.headerHash.contentEquals(hash) }) {\n                BlockHash(hash, transactionItem.blockHeight).also {\n                    blockHashes.add(it)\n                }\n            }\n\n            transactionItem.addressItems.forEach { addressItem ->\n                val publicKey = publicKeyMap[addressItem.address] ?: publicKeyMap[addressItem.script]\n                if (publicKey != null) {\n                    blockHashPublicKeys.add(BlockHashPublicKey(hash, publicKey.path))\n                }\n            }\n        }\n\n        storage.addBlockHashes(blockHashes)\n        storage.addBockHashPublicKeys(blockHashPublicKeys)\n        listener?.onTransactionsFound(transactionItems.size)\n\n        publicKeyManager.fillGap()\n\n        val _allKeys = storage.getPublicKeys()\n        val newKeys = _allKeys.minus(allKeys.toSet())\n\n        if (newKeys.isNotEmpty()) {\n            fetchRecursive(newKeys, _allKeys, stopHeight)\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/blockchair/BlockchairBlockHashFetcher.kt",
    "content": "package io.horizontalsystems.bitcoincore.apisync.blockchair\n\n\ninterface IBlockHashFetcher {\n    fun fetch(heights: List<Int>): Map<Int, String>\n}\n\nclass BlockchairBlockHashFetcher(\n    private val blockchairApi: BlockchairApi\n) : IBlockHashFetcher {\n    override fun fetch(heights: List<Int>): Map<Int, String> {\n        return blockchairApi.blockHashes(heights)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/blockchair/BlockchairLastBlockProvider.kt",
    "content": "package io.horizontalsystems.bitcoincore.apisync.blockchair\n\nimport io.horizontalsystems.bitcoincore.apisync.model.BlockHeaderItem\n\nclass BlockchairLastBlockProvider(\n    private val blockchairApi: BlockchairApi\n) {\n    fun lastBlockHeader(): BlockHeaderItem {\n        return blockchairApi.lastBlockHeader()\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/blockchair/BlockchairTransactionProvider.kt",
    "content": "package io.horizontalsystems.bitcoincore.apisync.blockchair\n\nimport io.horizontalsystems.bitcoincore.apisync.model.TransactionItem\nimport io.horizontalsystems.bitcoincore.core.IApiTransactionProvider\n\nclass BlockchairTransactionProvider(\n    val blockchairApi: BlockchairApi,\n    private val blockHashFetcher: IBlockHashFetcher\n) : IApiTransactionProvider {\n\n    private fun fillBlockHashes(items: List<TransactionItem>): List<TransactionItem> {\n        val hashesMap = blockHashFetcher.fetch(items.map { it.blockHeight }.distinct())\n        return items.mapNotNull { item ->\n            hashesMap[item.blockHeight]?.let {\n                item.copy(blockHash = it)\n            }\n        }\n    }\n\n    override fun transactions(addresses: List<String>, stopHeight: Int?): List<TransactionItem> {\n        val items = blockchairApi.transactions(addresses, stopHeight)\n        return fillBlockHashes(items)\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/legacy/ApiSyncer.kt",
    "content": "package io.horizontalsystems.bitcoincore.apisync.legacy\n\nimport io.horizontalsystems.bitcoincore.core.IApiSyncer\nimport io.horizontalsystems.bitcoincore.core.IApiSyncerListener\nimport io.horizontalsystems.bitcoincore.core.IPublicKeyManager\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.managers.ApiSyncStateManager\nimport io.horizontalsystems.bitcoincore.models.BlockHash\nimport io.horizontalsystems.bitcoincore.models.PublicKey\nimport io.reactivex.disposables.CompositeDisposable\nimport io.reactivex.schedulers.Schedulers\nimport java.util.logging.Logger\n\nclass ApiSyncer(\n    private val storage: IStorage,\n    private val blockHashDiscovery: BlockHashDiscoveryBatch,\n    private val publicKeyManager: IPublicKeyManager,\n    private val multiAccountPublicKeyFetcher: IMultiAccountPublicKeyFetcher?,\n    private val apiSyncStateManager: ApiSyncStateManager\n) : IApiSyncer {\n\n    override val willSync: Boolean\n        get() = !apiSyncStateManager.restored\n\n    override var listener: IApiSyncerListener? = null\n\n    private val logger = Logger.getLogger(\"ApiSyncer\")\n    private val disposables = CompositeDisposable()\n\n    override fun terminate() {\n        disposables.clear()\n    }\n\n    override fun sync() {\n        val disposable = blockHashDiscovery.discoverBlockHashes()\n            .subscribeOn(Schedulers.io())\n            .observeOn(Schedulers.io())\n            .subscribe(\n                { (publicKeys, blockHashes) ->\n                    val sortedUniqueBlockHashes = blockHashes.distinctBy { it.height }.sortedBy { it.height }\n\n                    handle(publicKeys, sortedUniqueBlockHashes)\n                },\n                {\n                    handleError(it)\n                })\n\n        disposables.add(disposable)\n    }\n\n    private fun handle(keys: List<PublicKey>, blockHashes: List<BlockHash>) {\n        publicKeyManager.addKeys(keys)\n\n        if (multiAccountPublicKeyFetcher != null) {\n            if (blockHashes.isNotEmpty()) {\n                storage.addBlockHashes(blockHashes)\n                multiAccountPublicKeyFetcher.increaseAccount()\n                sync()\n            } else {\n                handleSuccess()\n            }\n        } else {\n            storage.addBlockHashes(blockHashes)\n            handleSuccess()\n        }\n    }\n\n    private fun handleSuccess() {\n        apiSyncStateManager.restored = true\n        listener?.onSyncSuccess()\n    }\n\n    private fun handleError(error: Throwable) {\n        logger.severe(\"Initial Sync Error: ${error.message}\")\n\n        listener?.onSyncFailed(error)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/legacy/BlockHashDiscoveryBatch.kt",
    "content": "package io.horizontalsystems.bitcoincore.apisync.legacy\n\nimport io.horizontalsystems.bitcoincore.models.BlockHash\nimport io.horizontalsystems.bitcoincore.models.PublicKey\nimport io.reactivex.Single\n\nclass BlockHashDiscoveryBatch(\n    private val blockHashScanner: BlockHashScanner,\n    private val publicKeyFetcher: IPublicKeyFetcher,\n    private val maxHeight: Int,\n    private val gapLimit: Int\n) {\n    fun discoverBlockHashes(): Single<Pair<List<PublicKey>, List<BlockHash>>> {\n        return Single.create { emitter ->\n            try {\n                emitter.onSuccess(fetchRecursive())\n            } catch (e: Exception) {\n                emitter.tryOnError(e)\n            }\n        }\n    }\n\n    private fun fetchRecursive(\n        blockHashes: List<BlockHash> = listOf(),\n        externalBatchInfo: KeyBlockHashBatchInfo = KeyBlockHashBatchInfo(),\n        internalBatchInfo: KeyBlockHashBatchInfo = KeyBlockHashBatchInfo()\n    ): Pair<List<PublicKey>, List<BlockHash>> {\n\n        val externalCount = gapLimit - externalBatchInfo.prevCount + externalBatchInfo.prevLastUsedIndex + 1\n        val internalCount = gapLimit - internalBatchInfo.prevCount + internalBatchInfo.prevLastUsedIndex + 1\n\n        val externalNewKeys = publicKeyFetcher.publicKeys(externalBatchInfo.startIndex until externalBatchInfo.startIndex + externalCount, true)\n        val internalNewKeys = publicKeyFetcher.publicKeys(internalBatchInfo.startIndex until internalBatchInfo.startIndex + internalCount, false)\n\n        val externalPublicKeys = externalBatchInfo.publicKeys + externalNewKeys\n        val internalPublicKeys = internalBatchInfo.publicKeys + internalNewKeys\n\n        val fetchResponse = blockHashScanner.getBlockHashes(externalNewKeys, internalNewKeys)\n        val resultBlockHashes = blockHashes + fetchResponse.blockHashes.filter { it.height <= maxHeight }\n\n        return when {\n            // found all unused keys\n            fetchResponse.externalLastUsedIndex < 0 && fetchResponse.internalLastUsedIndex < 0 -> {\n                Pair(externalPublicKeys + internalPublicKeys, resultBlockHashes)\n            }\n            // found some used keys\n            else -> {\n                val externalBatch = KeyBlockHashBatchInfo(\n                    externalPublicKeys,\n                    externalCount,\n                    fetchResponse.externalLastUsedIndex,\n                    externalBatchInfo.startIndex + externalCount\n                )\n                val internalBatch = KeyBlockHashBatchInfo(\n                    internalPublicKeys,\n                    internalCount,\n                    fetchResponse.internalLastUsedIndex,\n                    internalBatchInfo.startIndex + internalCount\n                )\n                fetchRecursive(resultBlockHashes, externalBatch, internalBatch)\n            }\n        }\n    }\n\n    private data class KeyBlockHashBatchInfo(\n        var publicKeys: List<PublicKey> = listOf(),\n        var prevCount: Int = 0,\n        var prevLastUsedIndex: Int = -1,\n        var startIndex: Int = 0\n    )\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/legacy/BlockHashScanHelper.kt",
    "content": "package io.horizontalsystems.bitcoincore.apisync.legacy\n\nimport io.horizontalsystems.bitcoincore.apisync.model.AddressItem\n\ninterface IBlockHashScanHelper {\n    fun lastUsedIndex(addresses: List<List<String>>, addressItems: List<AddressItem>): Int\n}\n\nclass BlockHashScanHelper : IBlockHashScanHelper {\n\n    override fun lastUsedIndex(addresses: List<List<String>>, addressItems: List<AddressItem>): Int {\n        val searchAddressStrings = addressItems.map { it.address }\n        val searchScriptStrings = addressItems.map { it.script }\n\n        for (i in addresses.size - 1 downTo 0) {\n            addresses[i].forEach { address ->\n                if (searchAddressStrings.contains(address) || searchScriptStrings.any { script -> script.contains(address) }) {\n                    return i\n                }\n            }\n        }\n\n        return -1\n    }\n\n}\n\nclass WatchAddressBlockHashScanHelper : IBlockHashScanHelper {\n\n    override fun lastUsedIndex(addresses: List<List<String>>, addressItems: List<AddressItem>): Int = -1\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/legacy/BlockHashScanner.kt",
    "content": "package io.horizontalsystems.bitcoincore.apisync.legacy\n\nimport io.horizontalsystems.bitcoincore.core.IApiSyncerListener\nimport io.horizontalsystems.bitcoincore.core.IApiTransactionProvider\nimport io.horizontalsystems.bitcoincore.extensions.toReversedByteArray\nimport io.horizontalsystems.bitcoincore.managers.IRestoreKeyConverter\nimport io.horizontalsystems.bitcoincore.models.BlockHash\nimport io.horizontalsystems.bitcoincore.models.PublicKey\n\nclass BlockHashScanner(\n    private val restoreKeyConverter: IRestoreKeyConverter,\n    private val transactionProvider: IApiTransactionProvider,\n    private val helper: IBlockHashScanHelper\n) {\n\n    var listener: IApiSyncerListener? = null\n\n    fun getBlockHashes(externalKeys: List<PublicKey>, internalKeys: List<PublicKey>): BlockHashesResponse {\n        val externalAddresses = externalKeys.map {\n            restoreKeyConverter.keysForApiRestore(it)\n        }\n        val internalAddresses = internalKeys.map {\n            restoreKeyConverter.keysForApiRestore(it)\n        }\n        val allAddresses = externalAddresses.flatten() + internalAddresses.flatten()\n        val transactions = transactionProvider.transactions(allAddresses, null)\n\n        if (transactions.isEmpty()) {\n            return BlockHashesResponse(listOf(), -1, -1)\n        }\n\n        listener?.onTransactionsFound(transactions.size)\n\n        val addressItems = transactions.flatMap { it.addressItems }\n        val externalLastUsedIndex = helper.lastUsedIndex(externalAddresses, addressItems)\n        val internalLastUsedIndex = helper.lastUsedIndex(internalAddresses, addressItems)\n\n        val blockHashes = transactions.map {\n            BlockHash(it.blockHash.toReversedByteArray(), it.blockHeight, 0)\n        }\n\n        return BlockHashesResponse(blockHashes, externalLastUsedIndex, internalLastUsedIndex)\n    }\n\n    data class BlockHashesResponse(\n        val blockHashes: List<BlockHash>,\n        val externalLastUsedIndex: Int,\n        val internalLastUsedIndex: Int\n    )\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/legacy/PublicKeyFetcher.kt",
    "content": "package io.horizontalsystems.bitcoincore.apisync.legacy\n\nimport io.horizontalsystems.bitcoincore.core.AccountWallet\nimport io.horizontalsystems.bitcoincore.core.Wallet\nimport io.horizontalsystems.bitcoincore.core.WatchAccountWallet\nimport io.horizontalsystems.bitcoincore.models.PublicKey\n\n\ninterface IPublicKeyFetcher {\n    fun publicKeys(indices: IntRange, external: Boolean): List<PublicKey>\n}\n\ninterface IMultiAccountPublicKeyFetcher {\n    val currentAccount: Int\n    fun increaseAccount()\n}\n\nclass PublicKeyFetcher(private val accountWallet: AccountWallet) : IPublicKeyFetcher {\n    override fun publicKeys(indices: IntRange, external: Boolean): List<PublicKey> {\n        return accountWallet.publicKeys(indices, external)\n    }\n}\n\nclass WatchPublicKeyFetcher(private val watchAccountWallet: WatchAccountWallet) : IPublicKeyFetcher {\n    override fun publicKeys(indices: IntRange, external: Boolean): List<PublicKey> {\n        return watchAccountWallet.publicKeys(indices, external)\n    }\n}\n\nclass MultiAccountPublicKeyFetcher(private val wallet: Wallet) : IPublicKeyFetcher, IMultiAccountPublicKeyFetcher {\n    override fun publicKeys(indices: IntRange, external: Boolean): List<PublicKey> {\n        return wallet.publicKeys(currentAccount, indices, external)\n    }\n\n    override var currentAccount: Int = 0\n        private set\n\n    @Synchronized\n    override fun increaseAccount() {\n        currentAccount++\n    }\n}"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/model/AddressItem.kt",
    "content": "package io.horizontalsystems.bitcoincore.apisync.model\n\ndata class AddressItem(\n    val script: String,\n    val address: String?\n)\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/model/BlockHeaderItem.kt",
    "content": "package io.horizontalsystems.bitcoincore.apisync.model\n\ndata class BlockHeaderItem(\n    val hash: ByteArray,\n    val height: Int,\n    val timestamp: Long\n)\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/apisync/model/TransactionItem.kt",
    "content": "package io.horizontalsystems.bitcoincore.apisync.model\n\ndata class TransactionItem(\n    val blockHash: String,\n    val blockHeight: Int,\n    val addressItems: MutableList<AddressItem>\n)\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/BlockDownload.kt",
    "content": "package io.horizontalsystems.bitcoincore.blocks\n\nimport io.horizontalsystems.bitcoincore.core.IBlockSyncListener\nimport io.horizontalsystems.bitcoincore.core.IInitialDownload\nimport io.horizontalsystems.bitcoincore.models.InventoryItem\nimport io.horizontalsystems.bitcoincore.models.MerkleBlock\nimport io.horizontalsystems.bitcoincore.network.peer.Peer\nimport io.horizontalsystems.bitcoincore.network.peer.PeerManager\nimport io.horizontalsystems.bitcoincore.network.peer.task.GetMerkleBlocksTask\nimport io.horizontalsystems.bitcoincore.network.peer.task.PeerTask\nimport java.util.concurrent.CopyOnWriteArrayList\nimport java.util.concurrent.Executors\nimport java.util.logging.Logger\n\nclass BlockDownload(\n    private var blockSyncer: BlockSyncer,\n    private val peerManager: PeerManager,\n    private val merkleBlockExtractor: MerkleBlockExtractor\n) : IInitialDownload, GetMerkleBlocksTask.MerkleBlockHandler {\n\n    override var listener: IBlockSyncListener? = null\n    override val syncedPeers = CopyOnWriteArrayList<Peer>()\n    private val peerSyncListeners = mutableListOf<IPeerSyncListener>()\n    private val peerSwitchMinimumRatio = 1.5\n\n    @Volatile\n    override var syncPeer: Peer? = null\n    private var selectNewPeer = false\n    private val peersQueue = Executors.newSingleThreadExecutor()\n    private val logger = Logger.getLogger(\"BlockDownload\")\n\n    private var minMerkleBlocks = 500.0\n    private var minTransactions = 50_000.0\n    private var minReceiveBytes = 100_000.0\n    private var slowPeersDisconnected = 0\n\n    override fun addPeerSyncListener(peerSyncListener: IPeerSyncListener) {\n        peerSyncListeners.add(peerSyncListener)\n    }\n\n    override fun handleInventoryItems(peer: Peer, inventoryItems: List<InventoryItem>) {\n        if (peer.synced && inventoryItems.any { it.type == InventoryItem.MSG_BLOCK }) {\n            peer.synced = false\n            syncedPeers.remove(peer)\n\n            assignNextSyncPeer()\n        }\n    }\n\n    override fun handleCompletedTask(peer: Peer, task: PeerTask): Boolean {\n        return when (task) {\n            is GetMerkleBlocksTask -> {\n                blockSyncer.downloadIterationCompleted()\n                true\n            }\n\n            else -> false\n        }\n    }\n\n    override fun handleMerkleBlock(merkleBlock: MerkleBlock) {\n        val maxBlockHeight = syncPeer?.announcedLastBlockHeight ?: 0\n        blockSyncer.handleMerkleBlock(merkleBlock, maxBlockHeight)\n    }\n\n    override fun onStart() {\n        blockSyncer.prepareForDownload()\n    }\n\n    override fun onStop() = Unit\n\n    override fun onRefresh() {\n        if (syncPeer == null) {\n            peerManager.connected().forEach { peer ->\n                peer.synced = false\n            }\n\n            assignNextSyncPeer()\n        }\n    }\n\n    override fun onPeerCreate(peer: Peer) {\n        peer.localBestBlockHeight = blockSyncer.localDownloadedBestBlockHeight\n    }\n\n    override fun onPeerConnect(peer: Peer) {\n        syncPeer?.let {\n            if (it.connectionTime > peer.connectionTime * peerSwitchMinimumRatio) {\n                selectNewPeer = true\n            }\n        }\n        assignNextSyncPeer()\n    }\n\n    override fun onPeerReady(peer: Peer) {\n        if (peer == syncPeer) {\n            downloadBlockchain()\n        }\n    }\n\n    override fun onPeerDisconnect(peer: Peer, e: Exception?) {\n        if (e is GetMerkleBlocksTask.PeerTooSlow) {\n            slowPeersDisconnected += 1\n\n            if (slowPeersDisconnected >= 3) {\n                slowPeersDisconnected = 0\n                minMerkleBlocks /= 3\n                minTransactions /= 3\n                minReceiveBytes /= 3\n            }\n        }\n\n        syncedPeers.remove(peer)\n\n        if (peer == syncPeer) {\n            syncPeer = null\n            blockSyncer.downloadFailed()\n            assignNextSyncPeer()\n        }\n    }\n\n    private fun assignNextSyncPeer() {\n        peersQueue.execute {\n            if (syncPeer == null) {\n                val notSyncedPeers = peerManager.sorted().filter { !it.synced }\n                if (notSyncedPeers.isEmpty()) {\n                    peerSyncListeners.forEach { it.onAllPeersSynced() }\n                }\n\n                notSyncedPeers.firstOrNull { it.ready }?.let { nonSyncedPeer ->\n                    syncPeer = nonSyncedPeer\n                    blockSyncer.downloadStarted()\n\n                    logger.info(\"Start syncing peer ${nonSyncedPeer.host}\")\n\n                    downloadBlockchain()\n                }\n            }\n        }\n    }\n\n    private fun downloadBlockchain() {\n        syncPeer?.let { peer ->\n            if (!peer.ready) return\n\n            if (selectNewPeer) {\n                selectNewPeer = false\n                blockSyncer.downloadCompleted()\n                syncPeer = null\n                assignNextSyncPeer()\n                return\n            }\n\n            val blockHashes = blockSyncer.getBlockHashes(limit = 50)\n            if (blockHashes.isEmpty()) {\n                peer.synced = true\n            } else {\n                peer.addTask(GetMerkleBlocksTask(blockHashes, this, merkleBlockExtractor, minMerkleBlocks, minTransactions, minReceiveBytes))\n            }\n\n            if (peer.synced) {\n                syncedPeers.add(peer)\n\n                blockSyncer.downloadCompleted()\n                peer.sendMempoolMessage()\n                logger.info(\"Peer synced ${peer.host}\")\n                syncPeer = null\n                assignNextSyncPeer()\n                peerSyncListeners.forEach { it.onPeerSynced(peer) }\n\n                listener?.onBlockSyncFinished()\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/BlockMedianTimeHelper.kt",
    "content": "package io.horizontalsystems.bitcoincore.blocks\n\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.models.Block\n\nclass BlockMedianTimeHelper(\n    private val storage: IStorage,\n    // This flag must be set ONLY when it's NOT possible to get needed blocks for median time calculation\n    private val approximate: Boolean = false\n) {\n    private val medianTimeSpan = 11\n\n    val medianTimePast: Long?\n        get() =\n            storage.lastBlock()?.let { lastBlock ->\n                if (approximate) {\n                    // The median time is 6 blocks earlier which is approximately equal to 1 hour.\n                    lastBlock.timestamp - 3600\n                } else {\n                    medianTimePast(lastBlock)\n                }\n            }\n\n    fun medianTimePast(block: Block): Long? {\n        val startIndex = block.height - medianTimeSpan + 1\n        val median = storage.timestamps(from = startIndex, to = block.height)\n\n        return when {\n            block.height >= medianTimeSpan && median.size < medianTimeSpan -> null\n            else -> median[median.size / 2]\n        }\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/BlockSyncer.kt",
    "content": "package io.horizontalsystems.bitcoincore.blocks\n\nimport io.horizontalsystems.bitcoincore.core.IBlockSyncListener\nimport io.horizontalsystems.bitcoincore.core.IPublicKeyManager\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.managers.BloomFilterManager\nimport io.horizontalsystems.bitcoincore.models.BlockHash\nimport io.horizontalsystems.bitcoincore.models.Checkpoint\nimport io.horizontalsystems.bitcoincore.models.MerkleBlock\nimport io.horizontalsystems.bitcoincore.transactions.BlockTransactionProcessor\n\nclass BlockSyncer(\n    private val storage: IStorage,\n    private val blockchain: Blockchain,\n    private val transactionProcessor: BlockTransactionProcessor,\n    private val publicKeyManager: IPublicKeyManager,\n    private val checkpoint: Checkpoint,\n    private val state: State = State()\n) {\n\n    var listener: IBlockSyncListener? = null\n\n    private val sqliteMaxVariableNumber = 999\n\n    val localDownloadedBestBlockHeight: Int\n        get() = storage.lastBlock()?.height ?: 0\n\n    val localKnownBestBlockHeight: Int\n        get() {\n            val blockHashes = storage.getBlockchainBlockHashes()\n            val headerHashes = blockHashes.map { it.headerHash }\n            val existingBlocksCount = headerHashes.chunked(sqliteMaxVariableNumber).map {\n                storage.blocksCount(it)\n            }.sum()\n\n            return localDownloadedBestBlockHeight.plus(blockHashes.size - existingBlocksCount)\n        }\n\n    fun prepareForDownload() {\n        handlePartialBlocks()\n\n        clearPartialBlocks()\n        clearBlockHashes() // we need to clear block hashes when \"syncPeer\" is disconnected\n\n        blockchain.handleFork()\n    }\n\n    fun downloadStarted() {\n    }\n\n    fun downloadIterationCompleted() {\n        if (state.iterationHasPartialBlocks) {\n            handlePartialBlocks()\n        }\n    }\n\n    fun downloadCompleted() {\n        blockchain.handleFork()\n    }\n\n    fun downloadFailed() {\n        prepareForDownload()\n    }\n\n    fun getBlockHashes(limit: Int): List<BlockHash> {\n        return storage.getBlockHashesSortedBySequenceAndHeight(limit)\n    }\n\n    fun getBlockLocatorHashes(peerLastBlockHeight: Int): List<ByteArray> {\n        val result = mutableListOf<ByteArray>()\n\n        storage.getLastBlockchainBlockHash()?.headerHash?.let {\n            result.add(it)\n        }\n\n        if (result.isEmpty()) {\n            storage.getBlocks(heightGreaterThan = checkpoint.block.height, sortedBy = \"height\", limit = 10).forEach {\n                result.add(it.headerHash)\n            }\n        }\n\n        val lastBlock = storage.getBlock(peerLastBlockHeight)\n        if (lastBlock == null) {\n            result.add(checkpoint.block.headerHash)\n        } else if (!result.contains(lastBlock.headerHash)) {\n            result.add(lastBlock.headerHash)\n        }\n\n        return result\n    }\n\n    fun addBlockHashes(blockHashes: List<ByteArray>) {\n        var lastSequence = storage.getLastBlockHash()?.sequence ?: 0\n\n        val existingHashes = storage.getBlockHashHeaderHashes()\n        val newBlockHashes = blockHashes.filter { existingHashes.none { n -> n.contentEquals(it) } }.map {\n            BlockHash(it, 0, ++lastSequence)\n        }\n\n        storage.addBlockHashes(newBlockHashes)\n    }\n\n    fun handleMerkleBlock(merkleBlock: MerkleBlock, maxBlockHeight: Int) {\n\n        val block = when (val height = merkleBlock.height) {\n            null -> blockchain.connect(merkleBlock)\n            else -> blockchain.forceAdd(merkleBlock, height)\n        }\n\n        try {\n            transactionProcessor.processReceived(merkleBlock.associatedTransactions, block, state.iterationHasPartialBlocks)\n        } catch (e: BloomFilterManager.BloomFilterExpired) {\n            state.iterationHasPartialBlocks = true\n        }\n\n        if (state.iterationHasPartialBlocks) {\n            storage.setBlockPartial(block.headerHash)\n        } else {\n            storage.deleteBlockHash(block.headerHash)\n        }\n\n        if (merkleBlock.height != null) {\n            listener?.onBlockForceAdded()\n        } else {\n            listener?.onCurrentBestBlockHeightUpdate(block.height, maxBlockHeight)\n        }\n    }\n\n    fun shouldRequest(blockHash: ByteArray): Boolean {\n        return storage.getBlock(blockHash) == null\n    }\n\n    private fun clearPartialBlocks() {\n        val excludedHashes = listOf(checkpoint.block.headerHash) + checkpoint.additionalBlocks.map { it.headerHash }\n        val toDelete = storage.getBlockHashHeaderHashes(except = excludedHashes)\n\n        toDelete.chunked(sqliteMaxVariableNumber).forEach {\n            val blocksToDelete = storage.getBlocks(hashes = it)\n            val partialBlocksToDelete = blocksToDelete.filter { block -> block.partial }\n\n            blockchain.deleteBlocks(partialBlocksToDelete)\n        }\n    }\n\n    private fun handlePartialBlocks() {\n        publicKeyManager.fillGap()\n        state.iterationHasPartialBlocks = false\n    }\n\n    private fun clearBlockHashes() {\n        storage.deleteBlockchainBlockHashes()\n    }\n\n    class State(var iterationHasPartialBlocks: Boolean = false)\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/Blockchain.kt",
    "content": "package io.horizontalsystems.bitcoincore.blocks\n\nimport io.horizontalsystems.bitcoincore.blocks.validators.BlockValidatorException\nimport io.horizontalsystems.bitcoincore.blocks.validators.IBlockValidator\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.extensions.toReversedHex\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.bitcoincore.models.MerkleBlock\nimport io.horizontalsystems.bitcoincore.storage.BlockHeader\n\nclass Blockchain(\n    private val storage: IStorage,\n    private val blockValidator: IBlockValidator?,\n    private val dataListener: IBlockchainDataListener\n) {\n\n    fun connect(merkleBlock: MerkleBlock): Block {\n        val blockInDB = storage.getBlock(merkleBlock.blockHash)\n        if (blockInDB != null) {\n            return blockInDB\n        }\n\n        val parentBlock = storage.getBlock(merkleBlock.header.previousBlockHeaderHash) ?: throw BlockValidatorException.NoPreviousBlock()\n\n        val block = Block(merkleBlock.header, parentBlock)\n        blockValidator?.validate(block, parentBlock)\n\n        block.stale = true\n\n        if (block.height % 2016 == 0) {\n            storage.deleteBlocksWithoutTransactions(block.height - 2016)\n        }\n\n        return addBlockAndNotify(block)\n    }\n\n    fun forceAdd(merkleBlock: MerkleBlock, height: Int): Block {\n        val blockInDB = storage.getBlock(merkleBlock.blockHash)\n        if (blockInDB != null) {\n            return blockInDB\n        }\n\n        return addBlockAndNotify(Block(merkleBlock.header, height))\n    }\n\n    fun insertLastBlock(header: BlockHeader, height: Int) {\n        if (storage.getBlock(header.hash) != null) {\n            return\n        }\n\n        addBlockAndNotify(Block(header, height))\n    }\n\n    fun handleFork() {\n        val firstStaleHeight = storage.getBlock(stale = true, sortedHeight = \"ASC\")?.height ?: return\n\n        val lastNotStaleHeight = storage.getBlock(stale = false, sortedHeight = \"DESC\")?.height ?: 0\n\n        if (firstStaleHeight <= lastNotStaleHeight) {\n            val lastStaleHeight = storage.getBlock(stale = true, sortedHeight = \"DESC\")?.height ?: firstStaleHeight\n\n            if (lastStaleHeight > lastNotStaleHeight) {\n                val notStaleBlocks = storage.getBlocks(heightGreaterOrEqualTo = firstStaleHeight, stale = false)\n                deleteBlocks(notStaleBlocks)\n                storage.unstaleAllBlocks()\n            } else {\n                val staleBlocks = storage.getBlocks(stale = true)\n                deleteBlocks(staleBlocks)\n            }\n        } else {\n            storage.unstaleAllBlocks()\n        }\n    }\n\n    fun deleteBlocks(blocksToDelete: List<Block>) {\n        val deletedTransactionIds = mutableListOf<String>()\n\n        blocksToDelete.forEach { block ->\n            deletedTransactionIds.addAll(storage.getBlockTransactions(block).map { it.hash.toReversedHex() })\n        }\n\n        storage.deleteBlocks(blocksToDelete)\n\n        dataListener.onTransactionsDelete(deletedTransactionIds)\n    }\n\n    private fun addBlockAndNotify(block: Block): Block {\n        storage.addBlock(block)\n        dataListener.onBlockInsert(block)\n\n        return block\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/BloomFilterLoader.kt",
    "content": "package io.horizontalsystems.bitcoincore.blocks\n\nimport io.horizontalsystems.bitcoincore.crypto.BloomFilter\nimport io.horizontalsystems.bitcoincore.managers.BloomFilterManager\nimport io.horizontalsystems.bitcoincore.network.peer.Peer\nimport io.horizontalsystems.bitcoincore.network.peer.PeerGroup\nimport io.horizontalsystems.bitcoincore.network.peer.PeerManager\n\nclass BloomFilterLoader(private val bloomFilterManager: BloomFilterManager, private val peerManager: PeerManager)\n    : PeerGroup.Listener, BloomFilterManager.Listener {\n\n    override fun onPeerConnect(peer: Peer) {\n        bloomFilterManager.bloomFilter?.let {\n            peer.filterLoad(it)\n        }\n    }\n\n    override fun onFilterUpdated(bloomFilter: BloomFilter) {\n        peerManager.connected().forEach {\n            it.filterLoad(bloomFilter)\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/IBlockchainDataListener.kt",
    "content": "package io.horizontalsystems.bitcoincore.blocks\n\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.bitcoincore.models.Transaction\n\ninterface IBlockchainDataListener {\n    fun onBlockInsert(block: Block)\n    fun onTransactionsUpdate(inserted: List<Transaction>, updated: List<Transaction>, block: Block?)\n    fun onTransactionsDelete(hashes: List<String>)\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/IPeerSyncListener.kt",
    "content": "package io.horizontalsystems.bitcoincore.blocks\n\nimport io.horizontalsystems.bitcoincore.network.peer.Peer\n\ninterface IPeerSyncListener {\n    fun onAllPeersSynced() = Unit\n    fun onPeerSynced(peer: Peer) = Unit\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/InitialBlockDownload.kt",
    "content": "package io.horizontalsystems.bitcoincore.blocks\n\nimport io.horizontalsystems.bitcoincore.core.IBlockSyncListener\nimport io.horizontalsystems.bitcoincore.core.IInitialDownload\nimport io.horizontalsystems.bitcoincore.models.InventoryItem\nimport io.horizontalsystems.bitcoincore.models.MerkleBlock\nimport io.horizontalsystems.bitcoincore.network.peer.Peer\nimport io.horizontalsystems.bitcoincore.network.peer.PeerManager\nimport io.horizontalsystems.bitcoincore.network.peer.task.GetBlockHashesTask\nimport io.horizontalsystems.bitcoincore.network.peer.task.GetMerkleBlocksTask\nimport io.horizontalsystems.bitcoincore.network.peer.task.PeerTask\nimport java.util.concurrent.CopyOnWriteArrayList\nimport java.util.concurrent.Executors\nimport java.util.logging.Logger\nimport kotlin.math.max\n\nclass InitialBlockDownload(\n    private var blockSyncer: BlockSyncer,\n    private val peerManager: PeerManager,\n    private val merkleBlockExtractor: MerkleBlockExtractor\n) : IInitialDownload, GetMerkleBlocksTask.MerkleBlockHandler {\n\n    override var listener: IBlockSyncListener? = null\n    override val syncedPeers = CopyOnWriteArrayList<Peer>()\n    private val peerSyncListeners = mutableListOf<IPeerSyncListener>()\n    private val peerSwitchMinimumRatio = 1.5\n\n    @Volatile\n    override var syncPeer: Peer? = null\n    private var selectNewPeer = false\n    private val peersQueue = Executors.newSingleThreadExecutor()\n    private val logger = Logger.getLogger(\"IBD\")\n\n    private var minMerkleBlocks = 500.0\n    private var minTransactions = 50_000.0\n    private var minReceiveBytes = 100_000.0\n    private var slowPeersDisconnected = 0\n\n    override fun addPeerSyncListener(peerSyncListener: IPeerSyncListener) {\n        peerSyncListeners.add(peerSyncListener)\n    }\n\n    override fun handleInventoryItems(peer: Peer, inventoryItems: List<InventoryItem>) {\n        if (peer.synced && inventoryItems.any { it.type == InventoryItem.MSG_BLOCK }) {\n            peer.synced = false\n            peer.blockHashesSynced = false\n            syncedPeers.remove(peer)\n\n            assignNextSyncPeer()\n        }\n    }\n\n    override fun handleCompletedTask(peer: Peer, task: PeerTask): Boolean {\n        return when (task) {\n            is GetBlockHashesTask -> {\n                if (task.blockHashes.isEmpty()) {\n                    peer.blockHashesSynced = true\n                } else {\n                    blockSyncer.addBlockHashes(task.blockHashes)\n                }\n                true\n            }\n\n            is GetMerkleBlocksTask -> {\n                blockSyncer.downloadIterationCompleted()\n                true\n            }\n\n            else -> false\n        }\n    }\n\n    override fun handleMerkleBlock(merkleBlock: MerkleBlock) {\n        val maxBlockHeight = syncPeer?.announcedLastBlockHeight ?: 0\n        blockSyncer.handleMerkleBlock(merkleBlock, maxBlockHeight)\n    }\n\n    override fun onStart() {\n        blockSyncer.prepareForDownload()\n    }\n\n    override fun onStop() = Unit\n\n    override fun onPeerCreate(peer: Peer) {\n        peer.localBestBlockHeight = blockSyncer.localDownloadedBestBlockHeight\n    }\n\n    override fun onPeerConnect(peer: Peer) {\n        syncPeer?.let {\n            if (it.connectionTime > peer.connectionTime * peerSwitchMinimumRatio) {\n                selectNewPeer = true\n            }\n        }\n        assignNextSyncPeer()\n    }\n\n    override fun onPeerReady(peer: Peer) {\n        if (peer == syncPeer) {\n            downloadBlockchain()\n        }\n    }\n\n    override fun onPeerDisconnect(peer: Peer, e: Exception?) {\n        if (e is GetMerkleBlocksTask.PeerTooSlow) {\n            slowPeersDisconnected += 1\n\n            if (slowPeersDisconnected >= 3) {\n                slowPeersDisconnected = 0\n                minMerkleBlocks /= 3\n                minTransactions /= 3\n                minReceiveBytes /= 3\n            }\n        }\n\n        syncedPeers.remove(peer)\n\n        if (peer == syncPeer) {\n            syncPeer = null\n            blockSyncer.downloadFailed()\n            assignNextSyncPeer()\n        }\n    }\n\n    private fun assignNextSyncPeer() {\n        peersQueue.execute {\n            if (syncPeer == null) {\n                val notSyncedPeers = peerManager.sorted().filter { !it.synced }\n                if (notSyncedPeers.isEmpty()) {\n                    peerSyncListeners.forEach { it.onAllPeersSynced() }\n                }\n\n                notSyncedPeers.firstOrNull { it.ready }?.let { nonSyncedPeer ->\n                    syncPeer = nonSyncedPeer\n                    blockSyncer.downloadStarted()\n\n                    logger.info(\"Start syncing peer ${nonSyncedPeer.host}\")\n\n                    downloadBlockchain()\n                }\n            }\n        }\n    }\n\n    private fun downloadBlockchain() {\n        syncPeer?.let { peer ->\n            if (!peer.ready) return\n\n            if (selectNewPeer) {\n                selectNewPeer = false\n                blockSyncer.downloadCompleted()\n                syncPeer = null\n                assignNextSyncPeer()\n                return\n            }\n\n            val blockHashes = blockSyncer.getBlockHashes(limit = 500)\n            if (blockHashes.isEmpty()) {\n                peer.synced = peer.blockHashesSynced\n            } else {\n                peer.addTask(GetMerkleBlocksTask(blockHashes, this, merkleBlockExtractor, minMerkleBlocks, minTransactions, minReceiveBytes))\n            }\n\n            if (!peer.blockHashesSynced) {\n                val expectedHashesMinCount = max(peer.announcedLastBlockHeight - blockSyncer.localKnownBestBlockHeight, 0)\n                peer.addTask(GetBlockHashesTask(blockSyncer.getBlockLocatorHashes(peer.announcedLastBlockHeight), expectedHashesMinCount))\n            }\n\n            if (peer.synced) {\n                syncedPeers.add(peer)\n\n                blockSyncer.downloadCompleted()\n                peer.sendMempoolMessage()\n                logger.info(\"Peer synced ${peer.host}\")\n                syncPeer = null\n                assignNextSyncPeer()\n                peerSyncListeners.forEach { it.onPeerSynced(peer) }\n\n                // Some peers fail to send InventoryMessage within expected time\n                // and become 'synced' in InitialBlockDownload without sending all of their blocks.\n                // In such case, we assume not all blocks are downloaded\n                if (blockSyncer.localDownloadedBestBlockHeight >= peer.announcedLastBlockHeight) {\n                    listener?.onBlockSyncFinished()\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/MerkleBlockExtractor.kt",
    "content": "package io.horizontalsystems.bitcoincore.blocks\n\nimport io.horizontalsystems.bitcoincore.core.HashBytes\nimport io.horizontalsystems.bitcoincore.models.MerkleBlock\nimport io.horizontalsystems.bitcoincore.network.messages.MerkleBlockMessage\nimport io.horizontalsystems.bitcoincore.utils.MerkleBranch\n\nclass MerkleBlockExtractor(private val maxBlockSize: Int) {\n\n    fun extract(message: MerkleBlockMessage): MerkleBlock {\n        val matchedHashes = mutableMapOf<HashBytes, Boolean>()\n        val merkleRoot = MerkleBranch().calculateMerkleRoot(message.txCount, message.hashes, message.flags, matchedHashes)\n\n        message.apply {\n            if (txCount < 1 || txCount > maxBlockSize / 60) {\n                throw InvalidMerkleBlockException(String.format(\"Transaction count %d is not valid\", txCount))\n            }\n\n            if (hashCount < 0 || hashCount > txCount) {\n                throw InvalidMerkleBlockException(String.format(\"Hash count %d is not valid\", hashCount))\n            }\n\n            if (flagsCount < 1) {\n                throw InvalidMerkleBlockException(String.format(\"Flag count %d is not valid\", flagsCount))\n            }\n\n            if (!header.merkleRoot.contentEquals(merkleRoot)) {\n                throw InvalidMerkleBlockException(\"Merkle root is not valid\")\n            }\n        }\n\n        return MerkleBlock(message.header, matchedHashes)\n    }\n}\n\nclass InvalidMerkleBlockException(message: String) : Exception(message)\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/validators/BitsValidator.kt",
    "content": "package io.horizontalsystems.bitcoincore.blocks.validators\n\nimport io.horizontalsystems.bitcoincore.models.Block\n\nclass BitsValidator : IBlockChainedValidator {\n\n    override fun isBlockValidatable(block: Block, previousBlock: Block): Boolean {\n        return true\n    }\n\n    override fun validate(block: Block, previousBlock: Block) {\n        if (block.bits != previousBlock.bits) {\n            throw BlockValidatorException.NotEqualBits()\n        }\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/validators/BlockValidatorChain.kt",
    "content": "package io.horizontalsystems.bitcoincore.blocks.validators\n\nimport io.horizontalsystems.bitcoincore.models.Block\n\nclass BlockValidatorChain : IBlockChainedValidator {\n\n    private val concreteValidators = mutableListOf<IBlockChainedValidator>()\n\n    override fun isBlockValidatable(block: Block, previousBlock: Block): Boolean {\n        return true\n    }\n\n    override fun validate(block: Block, previousBlock: Block) {\n        concreteValidators.firstOrNull { validator ->\n            validator.isBlockValidatable(block, previousBlock)\n        }?.validate(block, previousBlock)\n    }\n\n    fun add(validator: IBlockChainedValidator) {\n        concreteValidators.add(validator)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/validators/BlockValidatorException.kt",
    "content": "package io.horizontalsystems.bitcoincore.blocks.validators\n\nimport io.horizontalsystems.bitcoincore.extensions.toReversedHex\n\nopen class BlockValidatorException(msg: String) : RuntimeException(msg) {\n    class NoHeader : BlockValidatorException(\"No Header\")\n    class NoCheckpointBlock : BlockValidatorException(\"No Checkpoint Block\")\n    class NoPreviousBlock : BlockValidatorException(\"No PreviousBlock\")\n    class WrongPreviousHeader : BlockValidatorException(\"Wrong Previous Header Hash\")\n    class NotEqualBits : BlockValidatorException(\"Not Equal Bits\")\n    class NotDifficultyTransitionEqualBits : BlockValidatorException(\"Not Difficulty Transition Equal Bits\")\n    class InvalidProofOfWork : BlockValidatorException(\"Invalid Prove of Work\")\n    class WrongBlockHash(expected: ByteArray, actual: ByteArray) : BlockValidatorException(\"Wrong Block Hash ${actual.toReversedHex()} vs expected ${expected.toReversedHex()}\")\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/validators/BlockValidatorSet.kt",
    "content": "package io.horizontalsystems.bitcoincore.blocks.validators\n\nimport io.horizontalsystems.bitcoincore.models.Block\n\nclass BlockValidatorSet : IBlockValidator {\n    private var validators = mutableListOf<IBlockValidator>()\n\n    override fun validate(block: Block, previousBlock: Block) {\n        validators.forEach {\n            it.validate(block, previousBlock)\n        }\n    }\n\n    fun addBlockValidator(blockValidator: IBlockValidator) {\n        validators.add(blockValidator)\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/validators/IBlockValidator.kt",
    "content": "package io.horizontalsystems.bitcoincore.blocks.validators\n\nimport io.horizontalsystems.bitcoincore.models.Block\n\ninterface IBlockValidator {\n    fun validate(block: Block, previousBlock: Block)\n}\n\ninterface IBlockChainedValidator : IBlockValidator {\n    fun isBlockValidatable(block: Block, previousBlock: Block): Boolean\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/validators/LegacyDifficultyAdjustmentValidator.kt",
    "content": "package io.horizontalsystems.bitcoincore.blocks.validators\n\nimport io.horizontalsystems.bitcoincore.crypto.CompactBits\nimport io.horizontalsystems.bitcoincore.managers.BlockValidatorHelper\nimport io.horizontalsystems.bitcoincore.models.Block\nimport java.math.BigInteger\nimport kotlin.math.min\n\nclass LegacyDifficultyAdjustmentValidator(\n        private val validatorHelper: BlockValidatorHelper,\n        private val heightInterval: Long,\n        private val targetTimespan: Long,\n        private val maxTargetBits: Long\n) : IBlockChainedValidator {\n\n    override fun isBlockValidatable(block: Block, previousBlock: Block): Boolean {\n        return block.height % heightInterval == 0L\n    }\n\n    override fun validate(block: Block, previousBlock: Block) {\n        val lastCheckPointBlock = checkNotNull(validatorHelper.getPrevious(block, heightInterval.toInt())) {\n            BlockValidatorException.NoCheckpointBlock()\n        }\n\n        //  Limit the adjustment step\n        var timespan = previousBlock.timestamp - lastCheckPointBlock.timestamp\n        if (timespan < targetTimespan / 4)\n            timespan = targetTimespan / 4\n        if (timespan > targetTimespan * 4)\n            timespan = targetTimespan * 4\n\n        var newTarget = CompactBits.decode(previousBlock.bits)\n        newTarget = newTarget.multiply(BigInteger.valueOf(timespan))\n        newTarget = newTarget.divide(BigInteger.valueOf(targetTimespan))\n\n        //  Difficulty hit proof of work limit: newTarget.toString(16)\n        val newDifficulty = min(CompactBits.encode(newTarget), maxTargetBits)\n\n        if (newDifficulty != block.bits) {\n            throw BlockValidatorException.NotDifficultyTransitionEqualBits()\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/validators/LegacyTestNetDifficultyValidator.kt",
    "content": "package io.horizontalsystems.bitcoincore.blocks.validators\n\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.models.Block\n\nclass LegacyTestNetDifficultyValidator(\n        private val storage: IStorage,\n        private val heightInterval: Long,\n        private val targetSpacing: Int,\n        private val maxTargetBits: Long)\n    : IBlockChainedValidator {\n\n    private val diffDate = 1329264000 // February 16th 2012\n\n    override fun isBlockValidatable(block: Block, previousBlock: Block): Boolean {\n        return previousBlock.timestamp > diffDate\n    }\n\n    override fun validate(block: Block, previousBlock: Block) {\n        val timeDelta = block.timestamp - previousBlock.timestamp\n        if (timeDelta >= 0 && timeDelta <= targetSpacing * 2) {\n            var cursor = block\n\n            while (cursor.height % heightInterval != 0L && cursor.bits == maxTargetBits) {\n                val prevBlock = storage.getBlock(hashHash = cursor.previousBlockHash)\n                        ?: throw BlockValidatorException.NoPreviousBlock()\n\n                cursor = prevBlock\n            }\n\n            if (cursor.bits != block.bits) {\n                BlockValidatorException.NotEqualBits()\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/blocks/validators/ProofOfWorkValidator.kt",
    "content": "package io.horizontalsystems.bitcoincore.blocks.validators\n\nimport io.horizontalsystems.bitcoincore.crypto.CompactBits\nimport io.horizontalsystems.bitcoincore.extensions.toReversedHex\nimport io.horizontalsystems.bitcoincore.models.Block\nimport java.math.BigInteger\n\nclass ProofOfWorkValidator : IBlockChainedValidator {\n\n    override fun isBlockValidatable(block: Block, previousBlock: Block): Boolean {\n        return true\n    }\n\n    override fun validate(block: Block, previousBlock: Block) {\n        check(BigInteger(block.headerHash.toReversedHex(), 16) < CompactBits.decode(block.bits)) {\n            throw BlockValidatorException.InvalidProofOfWork()\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/core/AccountWallet.kt",
    "content": "package io.horizontalsystems.bitcoincore.core\n\nimport io.horizontalsystems.bitcoincore.models.PublicKey\nimport io.horizontalsystems.hdwalletkit.HDKey\nimport io.horizontalsystems.hdwalletkit.HDWallet.*\nimport io.horizontalsystems.hdwalletkit.HDWalletAccount\n\nclass AccountWallet(private val hdWallet: HDWalletAccount, override val gapLimit: Int): IPrivateWallet, IAccountWallet {\n\n    override fun publicKey(index: Int, external: Boolean): PublicKey {\n        val pubKey = hdWallet.publicKey(index, if (external) Chain.EXTERNAL else Chain.INTERNAL)\n        return PublicKey(0, index, external, pubKey.publicKey, pubKey.publicKeyHash)\n    }\n\n    override fun publicKeys(indices: IntRange, external: Boolean): List<PublicKey> {\n        val hdPublicKeys = hdWallet.publicKeys(indices, if (external) Chain.EXTERNAL else Chain.INTERNAL)\n\n        if (hdPublicKeys.size != indices.count()) {\n            throw Wallet.HDWalletError.PublicKeysDerivationFailed()\n        }\n\n        return indices.mapIndexed { position, index ->\n            val hdPublicKey = hdPublicKeys[position]\n            PublicKey(0, index, external, hdPublicKey.publicKey, hdPublicKey.publicKeyHash)\n        }\n    }\n\n    override fun privateKey(account: Int, index: Int, external: Boolean): HDKey {\n       return hdWallet.privateKey(index, if (external) Chain.EXTERNAL else Chain.INTERNAL)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/core/BaseTransactionInfoConverter.kt",
    "content": "package io.horizontalsystems.bitcoincore.core\n\nimport io.horizontalsystems.bitcoincore.extensions.toReversedHex\nimport io.horizontalsystems.bitcoincore.io.BitcoinInput\nimport io.horizontalsystems.bitcoincore.models.InvalidTransaction\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.models.TransactionInfo\nimport io.horizontalsystems.bitcoincore.models.TransactionInputInfo\nimport io.horizontalsystems.bitcoincore.models.TransactionMetadata\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.models.TransactionOutputInfo\nimport io.horizontalsystems.bitcoincore.models.TransactionStatus\nimport io.horizontalsystems.bitcoincore.models.rbfEnabled\nimport io.horizontalsystems.bitcoincore.storage.FullTransactionInfo\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport java.io.ByteArrayInputStream\n\nclass BaseTransactionInfoConverter(private val pluginManager: PluginManager) {\n\n    fun transactionInfo(fullTransaction: FullTransactionInfo): TransactionInfo {\n        val transaction = fullTransaction.header\n\n        if (transaction.status == Transaction.Status.INVALID) {\n            (transaction as? InvalidTransaction)?.let {\n                return getInvalidTransactionInfo(it, fullTransaction.metadata)\n            }\n        }\n\n        val inputsInfo = mutableListOf<TransactionInputInfo>()\n        val outputsInfo = mutableListOf<TransactionOutputInfo>()\n\n        fullTransaction.inputs.forEach { input ->\n            var mine = false\n            var value: Long? = null\n\n            if (input.previousOutput != null) {\n                value = input.previousOutput.value\n                if (input.previousOutput.publicKeyPath != null) {\n                    mine = true\n                }\n            }\n\n            inputsInfo.add(TransactionInputInfo(mine, value, input.input.address))\n        }\n\n        fullTransaction.outputs.forEach { output ->\n            val outputInfo = TransactionOutputInfo(mine = output.publicKeyPath != null,\n                    changeOutput = output.changeOutput,\n                    value = output.value,\n                    address = output.address,\n                    memo = parseMemo(output),\n                    pluginId = output.pluginId,\n                    pluginDataString = output.pluginData,\n                    pluginData = pluginManager.parsePluginData(output, transaction.timestamp))\n\n            outputsInfo.add(outputInfo)\n        }\n\n        val rbfEnabled = fullTransaction.inputs.any { it.input.rbfEnabled }\n\n        return TransactionInfo(\n            uid = transaction.uid,\n            transactionHash = transaction.hash.toReversedHex(),\n            transactionIndex = transaction.order,\n            inputs = inputsInfo,\n            outputs = outputsInfo,\n            amount = fullTransaction.metadata.amount,\n            type = fullTransaction.metadata.type,\n            fee = fullTransaction.metadata.fee,\n            blockHeight = fullTransaction.block?.height,\n            timestamp = transaction.timestamp,\n            status = TransactionStatus.getByCode(transaction.status) ?: TransactionStatus.NEW,\n            conflictingTxHash = transaction.conflictingTxHash?.toReversedHex(),\n            rbfEnabled = rbfEnabled\n        )\n    }\n\n    private fun parseMemo(output: TransactionOutput): String? {\n        if (output.scriptType != ScriptType.NULL_DATA) return null\n        val payload = output.lockingScriptPayload ?: return null\n        if (payload.isEmpty()) return null\n\n        return try {\n            val input = BitcoinInput(ByteArrayInputStream(payload))\n            input.readByte() // op_return\n            input.readString()\n        } catch (e: Throwable) {\n            null\n        }\n    }\n\n    private fun getInvalidTransactionInfo(\n        transaction: InvalidTransaction,\n        metadata: TransactionMetadata\n    ): TransactionInfo {\n        return try {\n            TransactionInfo(transaction.serializedTxInfo)\n        } catch (ex: Exception) {\n            TransactionInfo(\n                uid = transaction.uid,\n                transactionHash = transaction.hash.toReversedHex(),\n                transactionIndex = transaction.order,\n                inputs = listOf(),\n                outputs = listOf(),\n                amount = metadata.amount,\n                type = metadata.type,\n                fee = metadata.fee,\n                blockHeight = null,\n                timestamp = transaction.timestamp,\n                status = TransactionStatus.INVALID\n            )\n        }\n    }\n\n}"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/core/DataProvider.kt",
    "content": "package io.horizontalsystems.bitcoincore.core\n\nimport io.horizontalsystems.bitcoincore.blocks.IBlockchainDataListener\nimport io.horizontalsystems.bitcoincore.extensions.hexToByteArray\nimport io.horizontalsystems.bitcoincore.extensions.toReversedHex\nimport io.horizontalsystems.bitcoincore.managers.UnspentOutputProvider\nimport io.horizontalsystems.bitcoincore.models.BalanceInfo\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.bitcoincore.models.BlockInfo\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.models.TransactionFilterType\nimport io.horizontalsystems.bitcoincore.models.TransactionInfo\nimport io.horizontalsystems.bitcoincore.storage.FullTransactionInfo\nimport io.horizontalsystems.bitcoincore.storage.TransactionWithBlock\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\nimport io.horizontalsystems.bitcoincore.storage.UtxoFilters\nimport io.reactivex.Single\nimport io.reactivex.disposables.Disposable\nimport io.reactivex.subjects.PublishSubject\nimport java.util.concurrent.TimeUnit\n\nclass DataProvider(\n        private val storage: IStorage,\n        private val unspentOutputProvider: UnspentOutputProvider,\n        private val transactionInfoConverter: ITransactionInfoConverter\n) : IBlockchainDataListener {\n\n    interface Listener {\n        fun onTransactionsUpdate(inserted: List<TransactionInfo>, updated: List<TransactionInfo>)\n        fun onTransactionsDelete(hashes: List<String>)\n        fun onBalanceUpdate(balance: BalanceInfo)\n        fun onLastBlockInfoUpdate(blockInfo: BlockInfo)\n    }\n\n    var listener: Listener? = null\n    private val balanceUpdateSubject: PublishSubject<Boolean> = PublishSubject.create()\n    private val balanceSubjectDisposable: Disposable\n\n    //  Getters\n    var balance: BalanceInfo = unspentOutputProvider.getBalance()\n        private set(value) {\n            if (value != field) {\n                field = value\n                listener?.onBalanceUpdate(field)\n            }\n        }\n\n    var lastBlockInfo: BlockInfo?\n        private set\n\n    init {\n        lastBlockInfo = storage.lastBlock()?.let {\n            blockInfo(it)\n        }\n\n        balanceSubjectDisposable = balanceUpdateSubject.debounce(500, TimeUnit.MILLISECONDS)\n                .subscribe {\n                    balance = unspentOutputProvider.getBalance()\n                }\n    }\n\n    override fun onBlockInsert(block: Block) {\n        if (block.height > lastBlockInfo?.height ?: 0) {\n            val blockInfo = blockInfo(block)\n\n            lastBlockInfo = blockInfo\n            listener?.onLastBlockInfoUpdate(blockInfo)\n            balanceUpdateSubject.onNext(true)\n        }\n    }\n\n    override fun onTransactionsUpdate(inserted: List<Transaction>, updated: List<Transaction>, block: Block?) {\n        listener?.onTransactionsUpdate(\n                storage.getFullTransactionInfo(inserted.map { TransactionWithBlock(it, block) }).map { transactionInfoConverter.transactionInfo(it) },\n                storage.getFullTransactionInfo(updated.map { TransactionWithBlock(it, block) }).map { transactionInfoConverter.transactionInfo(it) }\n        )\n\n        balanceUpdateSubject.onNext(true)\n    }\n\n    override fun onTransactionsDelete(hashes: List<String>) {\n        listener?.onTransactionsDelete(hashes)\n        balanceUpdateSubject.onNext(true)\n    }\n\n    fun clear() {\n        balanceSubjectDisposable.dispose()\n    }\n\n    fun transactions(fromUid: String?, type: TransactionFilterType? = null, limit: Int? = null): Single<List<TransactionInfo>> {\n        return Single.create { emitter ->\n            val fromTransaction = fromUid?.let { storage.getValidOrInvalidTransaction(it) }\n            val transactions = storage.getFullTransactionInfo(fromTransaction, type, limit)\n            emitter.onSuccess(transactions.map { transactionInfoConverter.transactionInfo(it) })\n        }\n    }\n\n    fun getRawTransaction(transactionHash: String): String? {\n        val hashByteArray = transactionHash.hexToByteArray().reversedArray()\n        return storage.getFullTransactionInfo(hashByteArray)?.rawTransaction\n                ?: storage.getInvalidTransaction(hashByteArray)?.rawTransaction\n    }\n\n    fun getTransaction(transactionHash: String): TransactionInfo? {\n        return storage.getFullTransactionInfo(transactionHash.hexToByteArray().reversedArray())?.let {\n            transactionInfoConverter.transactionInfo(it)\n        }\n    }\n\n    fun getSpendableUtxo(filters: UtxoFilters): List<UnspentOutput> {\n        return unspentOutputProvider.getSpendableUtxo(filters)\n    }\n\n    fun transactionInfo(fullInfo: FullTransactionInfo): TransactionInfo {\n        return transactionInfoConverter.transactionInfo(fullInfo)\n    }\n\n    private fun blockInfo(block: Block) = BlockInfo(\n            block.headerHash.toReversedHex(),\n            block.height,\n            block.timestamp)\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/core/DoubleSha256Hasher.kt",
    "content": "package io.horizontalsystems.bitcoincore.core\n\nimport io.horizontalsystems.bitcoincore.utils.HashUtils\n\nclass DoubleSha256Hasher : IHasher {\n    override fun hash(data: ByteArray): ByteArray {\n        return HashUtils.doubleSha256(data)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/core/Extensions.kt",
    "content": "package io.horizontalsystems.bitcoincore.core\n\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport io.horizontalsystems.hdwalletkit.HDWallet.Purpose\nimport java.util.Stack\n\nfun List<FullTransaction>.inTopologicalOrder(): List<FullTransaction> {\n\n    fun visit(v: Int, visited: MutableList<Boolean>, stack: Stack<FullTransaction>) {\n        if (visited[v])\n            return\n\n        visited[v] = true\n        val currentTx = this[v]\n\n        for (i in 0 until this.size) {\n            for (input in this[i].inputs) {\n                if (input.previousOutputTxHash.contentEquals(currentTx.header.hash) && input.previousOutputIndex < currentTx.outputs.size) {\n                    visit(i, visited, stack)\n                }\n            }\n        }\n\n        stack.push(currentTx)\n    }\n\n    val stack = Stack<FullTransaction>()\n    val visited = MutableList(this.size) { false }\n\n    for (i in 0 until this.size) {\n        if (!visited[i]) {\n            visit(i, visited, stack)\n        }\n    }\n\n    val ordered = mutableListOf<FullTransaction>()\n    while (stack.isNotEmpty()) {\n        ordered.add(stack.pop())\n    }\n\n    return ordered\n}\n\nval Purpose.scriptType: ScriptType\n    get() = when (this) {\n        Purpose.BIP44 -> ScriptType.P2PKH\n        Purpose.BIP49 -> ScriptType.P2WPKHSH\n        Purpose.BIP84 -> ScriptType.P2WPKH\n        Purpose.BIP86 -> ScriptType.P2TR\n    }\n\nval Purpose.description: String\n    get() = when (this) {\n        Purpose.BIP44 -> \"bip44\"\n        Purpose.BIP49 -> \"bip49\"\n        Purpose.BIP84 -> \"bip84\"\n        Purpose.BIP86 -> \"bip86\"\n    }\n\nval ScriptType.purpose: Purpose?\n    get() = when (this) {\n        ScriptType.P2PKH -> Purpose.BIP44\n\n        ScriptType.P2SH,\n        ScriptType.P2WPKHSH -> Purpose.BIP49\n\n        ScriptType.P2WSH,\n        ScriptType.P2WPKH -> Purpose.BIP84\n\n        ScriptType.P2TR -> Purpose.BIP86\n\n        ScriptType.NULL_DATA,\n        ScriptType.UNKNOWN,\n        ScriptType.P2PK -> null\n    }\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/core/HashBytes.kt",
    "content": "package io.horizontalsystems.bitcoincore.core\n\nimport io.horizontalsystems.bitcoincore.extensions.toHexString\nimport io.horizontalsystems.bitcoincore.utils.Utils\n\n/**\n * Just wraps a ByteArray so that equals and hashcode work correctly, allowing it to be\n * used as keys in a map\n */\nclass HashBytes(val bytes: ByteArray) : Comparable<HashBytes> {\n    private val hashLength = 32\n\n    override fun equals(other: Any?): Boolean {\n        if (this === other) return true\n        if (other == null || other !is HashBytes) return false\n\n        return bytes.contentEquals(other.bytes)\n    }\n\n    override fun hashCode(): Int {\n        // Use the first 4 bytes, not the last 4 which are often zeros (in little-endian)\n        return Utils.intFromBytes(bytes[0], bytes[1], bytes[2], bytes[3])\n    }\n\n    override fun compareTo(other: HashBytes): Int {\n        for (i in 0 until hashLength) {\n            val b1 = bytes[i].toInt() and 0xff\n            val b2 = other.bytes[i].toInt() and 0xff\n\n            val res = b1.compareTo(b2)\n            if (res != 0) return res\n        }\n\n        return 0\n    }\n\n    override fun toString(): String {\n        return bytes.toHexString()\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/core/IHasher.kt",
    "content": "package io.horizontalsystems.bitcoincore.core\n\ninterface IHasher {\n    fun hash(data: ByteArray) : ByteArray\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/core/Interfaces.kt",
    "content": "package io.horizontalsystems.bitcoincore.core\n\nimport io.horizontalsystems.bitcoincore.BitcoinCore\nimport io.horizontalsystems.bitcoincore.apisync.model.TransactionItem\nimport io.horizontalsystems.bitcoincore.blocks.IPeerSyncListener\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.bitcoincore.models.BlockHash\nimport io.horizontalsystems.bitcoincore.models.BlockHashPublicKey\nimport io.horizontalsystems.bitcoincore.models.InvalidTransaction\nimport io.horizontalsystems.bitcoincore.models.PeerAddress\nimport io.horizontalsystems.bitcoincore.models.PublicKey\nimport io.horizontalsystems.bitcoincore.models.SentTransaction\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.models.TransactionDataSortType\nimport io.horizontalsystems.bitcoincore.models.TransactionFilterType\nimport io.horizontalsystems.bitcoincore.models.TransactionInfo\nimport io.horizontalsystems.bitcoincore.models.TransactionInput\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.network.peer.IInventoryItemsHandler\nimport io.horizontalsystems.bitcoincore.network.peer.IPeerTaskHandler\nimport io.horizontalsystems.bitcoincore.network.peer.Peer\nimport io.horizontalsystems.bitcoincore.network.peer.PeerGroup\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.bitcoincore.storage.FullTransactionInfo\nimport io.horizontalsystems.bitcoincore.storage.PublicKeyWithUsedState\nimport io.horizontalsystems.bitcoincore.storage.TransactionWithBlock\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\nimport io.horizontalsystems.bitcoincore.transactions.builder.MutableTransaction\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport io.horizontalsystems.hdwalletkit.HDKey\n\ninterface IStorage {\n\n    //  BlockchainState\n\n    val initialRestored: Boolean?\n    fun setInitialRestored(isRestored: Boolean)\n\n    //  PeerAddress\n\n    fun getLeastScoreFastestPeerAddressExcludingIps(ips: List<String>): PeerAddress?\n    fun deletePeerAddress(ip: String)\n    fun setPeerAddresses(list: List<PeerAddress>)\n    fun markConnected(ip: String, time: Long)\n\n    //  BlockHash\n\n    fun getBlockHashesSortedBySequenceAndHeight(limit: Int): List<BlockHash>\n    fun getBlockHashHeaderHashes(): List<ByteArray>\n    fun getBlockHashHeaderHashes(except: List<ByteArray>): List<ByteArray>\n    fun getLastBlockHash(): BlockHash?\n\n    fun getApiBlockHashesCount(): Int\n    fun getBlockchainBlockHashes(): List<BlockHash>\n    fun getLastBlockchainBlockHash(): BlockHash?\n    fun deleteBlockchainBlockHashes()\n    fun deleteBlockHash(hash: ByteArray)\n    fun addBlockHashes(hashes: List<BlockHash>)\n    fun addBockHashPublicKeys(blockHashPublicKeys: List<BlockHashPublicKey>)\n\n    //  Block\n\n    fun getBlockByHeightStalePrioritized(height: Int): Block?\n    fun getBlock(height: Int): Block?\n    fun getBlock(hashHash: ByteArray): Block?\n    fun getBlock(stale: Boolean, sortedHeight: String): Block?\n\n    fun getBlocks(stale: Boolean): List<Block>\n    fun getBlocks(heightGreaterThan: Int, sortedBy: String, limit: Int): List<Block>\n    fun getBlocks(heightGreaterOrEqualTo: Int, stale: Boolean): List<Block>\n    fun getBlocks(hashes: List<ByteArray>): List<Block>\n    fun getBlocksChunk(fromHeight: Int, toHeight: Int): List<Block>\n\n    fun addBlock(block: Block)\n    fun saveBlock(block: Block)\n    fun setBlockPartial(headerHash: ByteArray)\n\n    fun blocksCount(headerHashes: List<ByteArray>? = null): Int\n    fun lastBlock(): Block?\n    fun downloadedTransactionsBestBlockHeight(): Int\n    fun updateBlock(staleBlock: Block)\n    fun deleteBlocks(blocks: List<Block>)\n    fun deleteBlocksWithoutTransactions(toHeight: Int)\n    fun unstaleAllBlocks()\n    fun timestamps(from: Int, to: Int): List<Long>\n\n    //  Transaction\n\n    fun getFullTransactionInfo(transactions: List<TransactionWithBlock>): List<FullTransactionInfo>\n    fun getFullTransactionInfo(fromTransaction: Transaction?, type: TransactionFilterType?, limit: Int?): List<FullTransactionInfo>\n    fun getFullTransactionInfo(txHash: ByteArray): FullTransactionInfo?\n\n    fun getTransaction(hash: ByteArray): Transaction?\n    fun getFullTransaction(hash: ByteArray): FullTransaction?\n    fun getFullTransactions(transactions: List<Transaction>): List<FullTransaction>\n    fun getValidOrInvalidTransaction(uid: String): Transaction?\n    fun getTransactionOfOutput(output: TransactionOutput): Transaction?\n    fun addTransaction(transaction: FullTransaction)\n    fun updateTransaction(transaction: Transaction)\n    fun updateTransaction(transaction: FullTransaction)\n    fun getBlockTransactions(block: Block): List<Transaction>\n    fun getNewTransactions(): List<FullTransaction>\n    fun getNewTransaction(hash: ByteArray): Transaction?\n    fun isRelayedTransactionExists(hash: ByteArray): Boolean\n    fun isTransactionExists(hash: ByteArray): Boolean\n    fun getConflictingTransactions(transaction: FullTransaction): List<Transaction>\n    fun getIncomingPendingTxHashes(): List<ByteArray>\n    fun incomingPendingTransactionsExist(): Boolean\n\n    // InvalidTransaction\n\n    fun getInvalidTransaction(hash: ByteArray): InvalidTransaction?\n    fun getDescendantTransactionsFullInfo(txHash: ByteArray): List<FullTransactionInfo>\n    fun getDescendantTransactions(txHash: ByteArray): List<Transaction>\n    fun moveTransactionToInvalidTransactions(invalidTransactions: List<InvalidTransaction>)\n    fun moveInvalidTransactionToTransactions(invalidTransaction: InvalidTransaction, toTransactions: FullTransaction)\n    fun deleteAllInvalidTransactions()\n\n    //  Transaction Output\n\n    fun getUnspentOutputs(): List<UnspentOutput>\n    fun getPreviousOutput(input: TransactionInput): TransactionOutput?\n    fun getOutput(transactionHash: ByteArray, index: Int): TransactionOutput?\n    fun getTransactionOutputs(transaction: Transaction): List<TransactionOutput>\n    fun getTransactionOutputsCount(hash: ByteArray): Int\n    fun getOutputsOfPublicKey(publicKey: PublicKey): List<TransactionOutput>\n    fun getMyOutputs(): List<TransactionOutput>\n    fun getOutputsForBloomFilter(blockHeight: Int, irregularScriptTypes: List<ScriptType>): List<TransactionOutput>\n\n    // Transaction Input\n\n    fun getTransactionInputs(transaction: Transaction): List<TransactionInput>\n    fun getTransactionInputs(txHash: ByteArray): List<TransactionInput>\n    fun getTransactionInputs(txHashes: List<ByteArray>): List<TransactionInput>\n    fun getTransactionInput(previousOutputTxHash: ByteArray, previousOutputIndex: Long): TransactionInput?\n    fun getTransactionInputsByPrevOutputTxHash(txHash: ByteArray): List<TransactionInput>\n\n    // PublicKey\n\n    fun getPublicKeyByScriptHashForP2PWKH(keyHash: ByteArray): PublicKey?\n    fun getPublicKeyByKeyOrKeyHash(keyHash: ByteArray): PublicKey?\n    fun getPublicKeyByHashP2TR(hashP2TR: ByteArray): PublicKey?\n\n    fun getPublicKeys(): List<PublicKey>\n    fun getPublicKeysUsed(): List<PublicKey>\n    fun getPublicKeysUnused(): List<PublicKey>\n    fun getPublicKeysWithUsedState(): List<PublicKeyWithUsedState>\n    fun savePublicKeys(keys: List<PublicKey>)\n\n    //  SentTransaction\n\n    fun getSentTransaction(hash: ByteArray): SentTransaction?\n    fun addSentTransaction(transaction: SentTransaction)\n    fun updateSentTransaction(transaction: SentTransaction)\n    fun deleteSentTransaction(transaction: SentTransaction)\n}\n\ninterface ITransactionInfoConverter {\n    var baseConverter: BaseTransactionInfoConverter\n\n    fun transactionInfo(fullTransactionInfo: FullTransactionInfo): TransactionInfo\n}\n\ninterface IApiTransactionProvider {\n    fun transactions(addresses: List<String>, stopHeight: Int?): List<TransactionItem>\n}\n\ninterface IPeerAddressManager {\n    val listener: IPeerAddressManagerListener?\n    val hasFreshIps: Boolean\n    fun getIp(): String?\n    fun addIps(ips: List<String>)\n    fun markFailed(ip: String)\n    fun markSuccess(ip: String)\n    fun markConnected(peer: Peer)\n}\n\ninterface IPeerAddressManagerListener {\n    fun onAddAddress()\n}\n\ninterface IConnectionManager {\n    val listener: IConnectionManagerListener?\n    val isConnected: Boolean\n    fun onEnterForeground()\n    fun onEnterBackground()\n}\n\ninterface IConnectionManagerListener {\n    fun onConnectionChange(isConnected: Boolean)\n}\n\ninterface IRecipientSetter {\n    fun setRecipient(\n        mutableTransaction: MutableTransaction,\n        toAddress: String,\n        value: Long,\n        pluginData: Map<Byte, IPluginData>,\n        skipChecking: Boolean,\n        memo: String?\n    )\n}\n\ninterface ITransactionDataSorterFactory {\n    fun sorter(type: TransactionDataSortType): ITransactionDataSorter\n}\n\ninterface ITransactionDataSorter {\n    fun sortOutputs(outputs: List<TransactionOutput>): List<TransactionOutput>\n    fun sortUnspents(unspents: List<UnspentOutput>): List<UnspentOutput>\n}\n\ninterface IKitStateListener {\n    fun onKitStateUpdate(state: BitcoinCore.KitState)\n}\n\ninterface IInitialDownload : IPeerTaskHandler, IInventoryItemsHandler, PeerGroup.Listener {\n    var listener: IBlockSyncListener?\n    val syncPeer: Peer?\n    val syncedPeers: List<Peer>\n\n    fun addPeerSyncListener(peerSyncListener: IPeerSyncListener)\n}\n\ninterface IApiSyncer {\n    var listener: IApiSyncerListener?\n    val willSync: Boolean\n\n    fun sync()\n    fun terminate()\n}\n\ninterface IApiSyncerListener {\n    fun onSyncSuccess()\n    fun onTransactionsFound(count: Int)\n    fun onSyncFailed(error: Throwable)\n}\n\ninterface IBlockSyncListener {\n    fun onBlockSyncFinished()\n    fun onCurrentBestBlockHeightUpdate(height: Int, maxBlockHeight: Int)\n    fun onBlockForceAdded()\n}\n\ninterface IPrivateWallet {\n    fun privateKey(account: Int, index: Int, external: Boolean): HDKey\n}\n\ninterface IAccountWallet {\n    val gapLimit: Int\n\n    fun publicKey(index: Int, external: Boolean): PublicKey\n    fun publicKeys(indices: IntRange, external: Boolean): List<PublicKey>\n}\n\ninterface IPublicKeyManager {\n    fun changePublicKey(): PublicKey\n    fun receivePublicKey(): PublicKey\n    fun usedExternalPublicKeys(change: Boolean): List<PublicKey>\n    fun fillGap()\n    fun addKeys(keys: List<PublicKey>)\n    fun gapShifts(): Boolean\n    fun getPublicKeyByPath(path: String): PublicKey\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/core/PluginManager.kt",
    "content": "package io.horizontalsystems.bitcoincore.core\n\nimport io.horizontalsystems.bitcoincore.managers.IRestoreKeyConverter\nimport io.horizontalsystems.bitcoincore.models.Address\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.bitcoincore.storage.InputWithPreviousOutput\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\nimport io.horizontalsystems.bitcoincore.transactions.builder.MutableTransaction\nimport io.horizontalsystems.bitcoincore.transactions.scripts.Script\n\nclass PluginManager {\n    private val plugins = mutableMapOf<Byte, IPlugin>()\n\n    fun processOutputs(mutableTransaction: MutableTransaction, pluginData: Map<Byte, IPluginData>, skipChecking: Boolean) {\n        pluginData.forEach {\n            val plugin = checkNotNull(plugins[it.key])\n            plugin.processOutputs(mutableTransaction, it.value, skipChecking)\n        }\n    }\n\n    fun processInputs(mutableTransaction: MutableTransaction) {\n        for (inputToSign in mutableTransaction.inputsToSign) {\n            val pluginId = inputToSign.previousOutput.pluginId ?: continue\n            val plugin = checkNotNull(plugins[pluginId])\n            inputToSign.input.sequence = plugin.getInputSequence(inputToSign.previousOutput)\n        }\n    }\n\n    fun addPlugin(plugin: IPlugin) {\n        plugins[plugin.id] = plugin\n    }\n\n    fun processTransactionWithNullData(transaction: FullTransaction, nullDataOutput: TransactionOutput) {\n        val script = Script(nullDataOutput.lockingScript)\n        val nullDataChunksIterator = script.chunks.iterator()\n\n        // the first byte OP_RETURN\n        nullDataChunksIterator.next()\n\n        while (nullDataChunksIterator.hasNext()) {\n            val pluginId = nullDataChunksIterator.next()\n            val plugin = plugins[pluginId.opcode.toByte()] ?: break\n\n            try {\n                plugin.processTransactionWithNullData(transaction, nullDataChunksIterator)\n            } catch (e: Exception) {\n\n            }\n        }\n    }\n\n    /**\n     * Tell if UTXO is spendable using the corresponding plugin\n     *\n     * @return true if pluginId is null, false if no plugin found for pluginId,\n     * otherwise delegate it to corresponding plugin\n     */\n    fun isSpendable(unspentOutput: UnspentOutput): Boolean {\n        val pluginId = unspentOutput.output.pluginId ?: return true\n        val plugin = plugins[pluginId] ?: return false\n        return plugin.isSpendable(unspentOutput)\n    }\n\n    fun parsePluginData(output: TransactionOutput, txTimestamp: Long): IPluginOutputData? {\n        val plugin = plugins[output.pluginId] ?: return null\n\n        return try {\n            plugin.parsePluginData(output, txTimestamp)\n        } catch (e: Exception) {\n            null\n        }\n    }\n\n    fun validateAddress(address: Address, pluginData: Map<Byte, IPluginData>) {\n        pluginData.forEach {\n            val plugin = checkNotNull(plugins[it.key])\n\n            plugin.validateAddress(address)\n        }\n    }\n\n    fun incrementedSequence(inputWithPreviousOutput: InputWithPreviousOutput): Long {\n        val plugin = inputWithPreviousOutput.previousOutput?.pluginId?.let { plugins[it] }\n        val sequence = inputWithPreviousOutput.input.sequence\n\n        return plugin?.incrementSequence(sequence) ?: (sequence + 1)\n    }\n}\n\ninterface IPlugin : IRestoreKeyConverter {\n    val id: Byte\n\n    fun processOutputs(mutableTransaction: MutableTransaction, pluginData: IPluginData, skipChecking: Boolean)\n    fun processTransactionWithNullData(transaction: FullTransaction, nullDataChunks: Iterator<Script.Chunk>)\n    fun isSpendable(unspentOutput: UnspentOutput): Boolean\n    fun getInputSequence(output: TransactionOutput): Long\n    fun parsePluginData(output: TransactionOutput, txTimestamp: Long): IPluginOutputData\n    fun validateAddress(address: Address)\n    fun incrementSequence(sequence: Long): Long\n}\n\ninterface IPluginData\ninterface IPluginOutputData\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/core/TransactionDataSorterFactory.kt",
    "content": "package io.horizontalsystems.bitcoincore.core\n\nimport io.horizontalsystems.bitcoincore.models.TransactionDataSortType\nimport io.horizontalsystems.bitcoincore.utils.Bip69Sorter\nimport io.horizontalsystems.bitcoincore.utils.ShuffleSorter\nimport io.horizontalsystems.bitcoincore.utils.StraightSorter\n\nclass TransactionDataSorterFactory : ITransactionDataSorterFactory {\n    override fun sorter(type: TransactionDataSortType): ITransactionDataSorter {\n        return when (type) {\n            TransactionDataSortType.None -> StraightSorter()\n            TransactionDataSortType.Shuffle -> ShuffleSorter()\n            TransactionDataSortType.Bip69 -> Bip69Sorter()\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/core/TransactionInfoConverter.kt",
    "content": "package io.horizontalsystems.bitcoincore.core\n\nimport io.horizontalsystems.bitcoincore.models.TransactionInfo\nimport io.horizontalsystems.bitcoincore.storage.FullTransactionInfo\n\nclass TransactionInfoConverter : ITransactionInfoConverter {\n    override lateinit var baseConverter: BaseTransactionInfoConverter\n\n    override fun transactionInfo(fullTransactionInfo: FullTransactionInfo): TransactionInfo {\n        return baseConverter.transactionInfo(fullTransactionInfo)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/core/Wallet.kt",
    "content": "package io.horizontalsystems.bitcoincore.core\n\nimport io.horizontalsystems.bitcoincore.models.PublicKey\nimport io.horizontalsystems.hdwalletkit.HDKey\nimport io.horizontalsystems.hdwalletkit.HDWallet\nimport java.lang.Exception\n\nclass Wallet(private val hdWallet: HDWallet, val gapLimit: Int): IPrivateWallet {\n\n    fun publicKey(account: Int, index: Int, external: Boolean): PublicKey {\n        val hdPubKey = hdWallet.hdPublicKey(account, index, external)\n        return PublicKey(account, index, external, hdPubKey.publicKey, hdPubKey.publicKeyHash)\n    }\n\n    fun publicKeys(account: Int, indices: IntRange, external: Boolean): List<PublicKey> {\n        val hdPublicKeys = hdWallet.hdPublicKeys(account, indices, external)\n\n        if (hdPublicKeys.size != indices.count()) {\n            throw HDWalletError.PublicKeysDerivationFailed()\n        }\n\n        return indices.mapIndexed { position, index ->\n            val hdPublicKey = hdPublicKeys[position]\n            PublicKey(account, index, external, hdPublicKey.publicKey, hdPublicKey.publicKeyHash)\n        }\n    }\n\n    override fun privateKey(account: Int, index: Int, external: Boolean): HDKey {\n        return hdWallet.privateKey(account, index, external)\n    }\n\n    open class HDWalletError : Exception() {\n        class PublicKeysDerivationFailed : HDWalletError()\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/core/WatchAccountWallet.kt",
    "content": "package io.horizontalsystems.bitcoincore.core\n\nimport io.horizontalsystems.bitcoincore.models.PublicKey\nimport io.horizontalsystems.hdwalletkit.HDWallet\nimport io.horizontalsystems.hdwalletkit.HDWalletAccountWatch\n\nclass WatchAccountWallet(private val hdWallet: HDWalletAccountWatch, override val gapLimit: Int): IAccountWallet {\n\n    override fun publicKey(index: Int, external: Boolean): PublicKey {\n        val pubKey = hdWallet.publicKey(index, if (external) HDWallet.Chain.EXTERNAL else HDWallet.Chain.INTERNAL)\n        return PublicKey(0, index, external, pubKey.publicKey, pubKey.publicKeyHash)\n    }\n\n    override fun publicKeys(indices: IntRange, external: Boolean): List<PublicKey> {\n        val hdPublicKeys = hdWallet.publicKeys(indices, if (external) HDWallet.Chain.EXTERNAL else HDWallet.Chain.INTERNAL)\n\n        if (hdPublicKeys.size != indices.count()) {\n            throw Wallet.HDWalletError.PublicKeysDerivationFailed()\n        }\n\n        return indices.mapIndexed { position, index ->\n            val hdPublicKey = hdPublicKeys[position]\n            PublicKey(0, index, external, hdPublicKey.publicKey, hdPublicKey.publicKeyHash)\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/crypto/BloomFilter.kt",
    "content": "package io.horizontalsystems.bitcoincore.crypto\n\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\nimport io.horizontalsystems.bitcoincore.utils.Utils\nimport java.lang.Double.valueOf\nimport java.util.*\nimport kotlin.math.ln\nimport kotlin.math.min\nimport kotlin.math.pow\n\n/**\n * BloomFilter\n *\n *   Size       Field           Description\n *   ====       =====           ===========\n *   VarInt     Count           Number of bytes in the filter\n *   Variable   Filter          Filter data\n *   4 bytes    nHashFuncs      Number of hash functions\n *   4 bytes    nTweak          Random value to add to the hash seed\n *   1 byte     nFlags          Filter update flags\n */\nclass BloomFilter(elements: List<ByteArray>) {\n\n    /** Filter data  */\n    private val filter: ByteArray\n\n    /** Number of hash functions  */\n    private val nHashFuncs: Int\n\n    /** Random tweak nonce  */\n    private val nTweak = valueOf(Math.random() * Long.MAX_VALUE).toLong()\n\n    /** Filter update flags  */\n    private val nFlags = UPDATE_NONE\n\n    init {\n        val falsePositiveRate = 0.00005\n        //\n        // Allocate the filter array\n        //\n        val size = min((-1 / ln(2.0).pow(2.0) * elements.size.toDouble() * ln(falsePositiveRate)).toInt(),\n                MAX_FILTER_SIZE * 8) / 8\n        filter = ByteArray(if (size <= 0) 1 else size)\n        //\n        // Optimal number of hash functions for a given filter size and element count.\n        //\n        nHashFuncs = min((filter.size * 8 / elements.size.toDouble() * ln(2.0)).toInt(), MAX_HASH_FUNCS)\n\n        elements.forEach {\n            insert(it)\n        }\n    }\n\n    /**\n     * Inserts an bytes into the filter\n     *\n     * @param   bytes    Object to insert\n     */\n    private fun insert(bytes: ByteArray) {\n        for (i in 0 until nHashFuncs) {\n            Utils.setBitLE(filter, MurmurHash3.hash(filter, nTweak, i, bytes))\n        }\n    }\n\n    /**\n     * Serialize the filter\n     */\n    fun toByteArray(): ByteArray {\n        val output = BitcoinOutput()\n        output.writeVarInt(filter.size.toLong())\n        output.write(filter)\n        output.writeInt(nHashFuncs)\n        output.writeUnsignedInt(nTweak)\n        output.writeByte(nFlags)\n\n        return output.toByteArray()\n    }\n\n    override fun toString(): String {\n        return \"Bloom Filter of size ${filter.size} with $nHashFuncs hash functions.\"\n    }\n\n    override fun equals(other: Any?) = when (other) {\n        is BloomFilter -> filter.contentEquals(other.filter)\n        else -> false\n    }\n\n    override fun hashCode(): Int {\n        var result = filter.contentHashCode()\n        result = 31 * result + nHashFuncs\n        result = 31 * result + nTweak.hashCode()\n        result = 31 * result + nFlags\n        return result\n    }\n\n    companion object {\n\n        /** Bloom filter - Filter is not adjusted for matching outputs  */\n        const val UPDATE_NONE = 0\n\n        /** Bloom filter - Filter is adjusted for all matching outputs  */\n        const val UPDATE_ALL = 1\n\n        /** Bloom filter - Filter is adjusted only for pay-to-pubkey or pay-to-multi-sig  */\n        const val UPDATE_P2PUBKEY_ONLY = 2\n\n        /** Maximum filter size  */\n        const val MAX_FILTER_SIZE = 36000\n\n        /** Maximum number of hash functions  */\n        const val MAX_HASH_FUNCS = 50\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/extensions/Array.kt",
    "content": "package io.horizontalsystems.bitcoincore.extensions\n\nfun ByteArray.toHexString(): String {\n    return this.joinToString(separator = \"\") {\n        it.toInt().and(0xff).toString(16).padStart(2, '0')\n    }\n}\n\nfun ByteArray.toReversedHex(): String {\n    return reversedArray().toHexString()\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/extensions/String.kt",
    "content": "package io.horizontalsystems.bitcoincore.extensions\n\nfun String.hexToByteArray(): ByteArray {\n    return ByteArray(this.length / 2) {\n        this.substring(it * 2, it * 2 + 2).toInt(16).toByte()\n    }\n}\n\nfun String.toReversedByteArray(): ByteArray {\n    return hexToByteArray().reversedArray()\n}\n\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/managers/AccountPublicKeyManager.kt",
    "content": "package io.horizontalsystems.bitcoincore.managers\n\nimport io.horizontalsystems.bitcoincore.core.IAccountWallet\nimport io.horizontalsystems.bitcoincore.core.IPublicKeyManager\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.models.PublicKey\nimport io.horizontalsystems.bitcoincore.storage.PublicKeyWithUsedState\n\nclass AccountPublicKeyManager(\n    private val storage: IStorage,\n    private val wallet: IAccountWallet,\n    private val restoreKeyConverter: RestoreKeyConverterChain\n) : IBloomFilterProvider, IPublicKeyManager {\n\n    override var bloomFilterManager: BloomFilterManager? = null\n\n    override fun getBloomFilterElements(): List<ByteArray> {\n        val elements = mutableListOf<ByteArray>()\n\n        for (publicKey in storage.getPublicKeys()) {\n            elements.addAll(restoreKeyConverter.bloomFilterElements(publicKey))\n        }\n\n        return elements\n    }\n\n    @Throws\n    override fun receivePublicKey(): PublicKey {\n        return getPublicKey(external = true)\n    }\n\n    override fun usedExternalPublicKeys(change: Boolean): List<PublicKey> {\n        return storage.getPublicKeysWithUsedState().filter { it.publicKey.external == !change && it.used }.map { it.publicKey }\n    }\n\n    @Throws\n    override fun changePublicKey(): PublicKey {\n        return getPublicKey(external = false)\n    }\n\n    override fun getPublicKeyByPath(path: String): PublicKey {\n        val parts = path.split(\"/\").map { it.toInt() }\n\n        if (parts.size != 2) throw Error.InvalidPath\n\n        return wallet.publicKey(parts[1], parts[0] == 0)\n    }\n\n    override fun fillGap() {\n        fillGap(true)\n        fillGap(false)\n\n        bloomFilterManager?.regenerateBloomFilter()\n    }\n\n    override fun addKeys(keys: List<PublicKey>) {\n        if (keys.isEmpty()) return\n\n        storage.savePublicKeys(keys)\n    }\n\n    override fun gapShifts(): Boolean {\n        val publicKeys = storage.getPublicKeysWithUsedState()\n\n        if (gapKeysCount(publicKeys.filter { it.publicKey.external }) < wallet.gapLimit) {\n            return true\n        }\n\n        if (gapKeysCount(publicKeys.filter { !it.publicKey.external }) < wallet.gapLimit) {\n            return true\n        }\n\n        return false\n    }\n\n    private fun fillGap(external: Boolean) {\n        val publicKeys = storage.getPublicKeysWithUsedState().filter { it.publicKey.external == external }\n        val keysCount = gapKeysCount(publicKeys)\n        val keys = mutableListOf<PublicKey>()\n\n        if (keysCount < wallet.gapLimit) {\n            val lastIndex = publicKeys.maxByOrNull { it.publicKey.index }?.publicKey?.index ?: -1\n\n            val newKeysStartIndex = lastIndex + 1\n            val indices = newKeysStartIndex until (newKeysStartIndex + wallet.gapLimit - keysCount)\n            val newKeys = wallet.publicKeys(indices, external)\n\n            keys.addAll(newKeys)\n        }\n\n        addKeys(keys)\n    }\n\n    private fun gapKeysCount(publicKeys: List<PublicKeyWithUsedState>): Int {\n        return when (val lastUsedKey = publicKeys.filter { it.used }.maxByOrNull { it.publicKey.index }) {\n            null -> publicKeys.size\n            else -> publicKeys.filter { it.publicKey.index > lastUsedKey.publicKey.index }.size\n        }\n    }\n\n    @Throws\n    private fun getPublicKey(external: Boolean): PublicKey {\n        return storage.getPublicKeysUnused()\n            .filter { it.external == external }\n            .sortedWith(compareBy { it.index })\n            .firstOrNull() ?: throw Error.NoUnusedPublicKey\n    }\n\n    companion object {\n        fun create(storage: IStorage, wallet: IAccountWallet, restoreKeyConverter: RestoreKeyConverterChain): AccountPublicKeyManager {\n            val addressManager = AccountPublicKeyManager(storage, wallet, restoreKeyConverter)\n            addressManager.fillGap()\n            return addressManager\n        }\n    }\n\n    object Error {\n        object NoUnusedPublicKey : Exception()\n        object InvalidPath : Exception()\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/managers/ApiManager.kt",
    "content": "package io.horizontalsystems.bitcoincore.managers\n\nimport com.eclipsesource.json.Json\nimport com.eclipsesource.json.JsonValue\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport java.io.BufferedOutputStream\nimport java.io.BufferedWriter\nimport java.io.IOException\nimport java.io.InputStream\nimport java.io.OutputStreamWriter\nimport java.net.HttpURLConnection\nimport java.net.URL\nimport java.util.concurrent.TimeUnit\nimport java.util.logging.Logger\n\nclass ApiManager(private val host: String) {\n    private val logger = Logger.getLogger(\"ApiManager\")\n\n    @Throws\n    fun get(resource: String): InputStream? {\n        val url = \"$host/$resource\"\n\n        logger.info(\"Fetching $url\")\n\n        return try {\n            URL(url)\n                    .openConnection()\n                    .apply {\n                        connectTimeout = 5000\n                        readTimeout = 60000\n                        setRequestProperty(\"Accept\", \"application/json\")\n                        setRequestProperty(\"content-type\", \"application/json\")\n                    }.getInputStream()\n        } catch (exception: IOException) {\n            throw ApiManagerException.Other(\"${exception.javaClass.simpleName}: $host\")\n        }\n    }\n\n    @Throws\n    fun post(resource: String, data: String): JsonValue {\n        try {\n            val path = \"$host/$resource\"\n\n            logger.info(\"Fetching $path\")\n\n            val url = URL(path)\n            val urlConnection = url.openConnection() as HttpURLConnection\n            urlConnection.requestMethod = \"POST\"\n            urlConnection.setRequestProperty(\"Content-Type\", \"application/json\")\n            val out = BufferedOutputStream(urlConnection.outputStream)\n            val writer = BufferedWriter(OutputStreamWriter(out, \"UTF-8\"))\n            writer.write(data)\n            writer.flush()\n            writer.close()\n            out.close()\n\n            return urlConnection.inputStream.use {\n                Json.parse(it.bufferedReader())\n            }\n        } catch (exception: IOException) {\n            throw ApiManagerException.Other(\"${exception.javaClass.simpleName}: $host\")\n        }\n    }\n\n    fun doOkHttpGet(uri: String): JsonValue {\n\n        val url = \"$host/$uri\"\n\n        try {\n            val httpClient: OkHttpClient = OkHttpClient.Builder()\n                .apply {\n                    connectTimeout(5000, TimeUnit.MILLISECONDS)\n                    readTimeout(60000, TimeUnit.MILLISECONDS)\n                }.build()\n\n            httpClient.newCall(Request.Builder().url(url).build())\n                    .execute()\n                    .use { response ->\n\n                        if (response.isSuccessful) {\n                            response.body?.let {\n                                return Json.parse(it.string())\n                            }\n                        }\n\n                    if (response.code == 404) {\n                        throw ApiManagerException.Http404Exception\n                    } else {\n                        throw ApiManagerException.Other(\"Unexpected Error:$response\")\n                    }\n                }\n        } catch (e: ApiManagerException) {\n            throw e\n        }\n        catch (e: Exception) {\n            throw ApiManagerException.Other(\"${e.javaClass.simpleName}: $host, ${e.localizedMessage}\")\n        }\n    }\n\n}\n\nsealed class ApiManagerException : Exception() {\n    object Http404Exception : ApiManagerException()\n    class Other(override val message: String) : ApiManagerException()\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/managers/ApiSyncStateManager.kt",
    "content": "package io.horizontalsystems.bitcoincore.managers\n\nimport io.horizontalsystems.bitcoincore.core.IStorage\n\nclass ApiSyncStateManager(\n        private val storage: IStorage,\n        private val restoreFromApi: Boolean\n) {\n\n    var restored: Boolean\n        get() {\n            if (!restoreFromApi) {\n                return true\n            }\n\n            return storage.initialRestored ?: false\n        }\n        set(value) {\n            storage.setInitialRestored(value)\n        }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/managers/BlockValidatorHelper.kt",
    "content": "package io.horizontalsystems.bitcoincore.managers\n\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.models.Block\n\nopen class BlockValidatorHelper(protected val storage: IStorage) {\n    fun getPreviousChunk(blockHeight: Int, size: Int): List<Block> {\n        return storage.getBlocksChunk(fromHeight = blockHeight - size, toHeight = blockHeight)\n    }\n\n    /**\n     * NOTE: When there is a fork there may be 2 blocks with the same height.\n     *\n     * In this case we need to retrieve block that is related to the current syncing peer.\n     * The blocks of current syncing peer are stored in a database with \"stale\" set to true.\n     * So if there is a 2 blocks with the same height we need to retrieve one with \"stale\" = true.\n     *\n     * Prioritizing stale blocks resolves it:\n     *  - if there are 2 blocks then it will retrieve one with \"stale\" = true\n     *  - if there is only 1 block then it will retrieve it. No matter what is set for \"stale\"\n     *  - if there is no block for the given height then it will return null\n     */\n    fun getPrevious(block: Block, stepBack: Int): Block? {\n        return storage.getBlockByHeightStalePrioritized(block.height - stepBack)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/managers/BloomFilterManager.kt",
    "content": "package io.horizontalsystems.bitcoincore.managers\n\nimport io.horizontalsystems.bitcoincore.crypto.BloomFilter\n\nclass BloomFilterManager {\n\n    object BloomFilterExpired : Exception()\n\n    interface Listener {\n        fun onFilterUpdated(bloomFilter: BloomFilter)\n    }\n\n    var listener: Listener? = null\n    var bloomFilter: BloomFilter? = null\n\n    private val bloomFilterProviders = mutableListOf<IBloomFilterProvider>()\n\n    init {\n        regenerateBloomFilter()\n    }\n\n    fun regenerateBloomFilter() {\n        val elements = mutableListOf<ByteArray>()\n\n        bloomFilterProviders.forEach {\n            elements.addAll(it.getBloomFilterElements())\n        }\n\n        if (elements.isNotEmpty()) {\n            BloomFilter(elements).let {\n                bloomFilter = it\n                listener?.onFilterUpdated(it)\n            }\n        }\n    }\n\n    fun addBloomFilterProvider(provider: IBloomFilterProvider) {\n        provider.bloomFilterManager = this\n        bloomFilterProviders.add(provider)\n    }\n}\n\ninterface IBloomFilterProvider {\n    var bloomFilterManager: BloomFilterManager?\n\n    fun getBloomFilterElements(): List<ByteArray>\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/managers/ConnectionManager.kt",
    "content": "package io.horizontalsystems.bitcoincore.managers\n\nimport android.content.Context\nimport android.net.ConnectivityManager\nimport android.net.Network\nimport android.net.NetworkCapabilities\nimport android.net.NetworkRequest\nimport io.horizontalsystems.bitcoincore.core.IConnectionManager\nimport io.horizontalsystems.bitcoincore.core.IConnectionManagerListener\n\nclass ConnectionManager(context: Context) : IConnectionManager {\n\n    private val connectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager\n\n    override var listener: IConnectionManagerListener? = null\n    override var isConnected: Boolean = false\n\n    private var hasValidInternet = false\n    private var hasConnection = false\n    private var callback = ConnectionStatusCallback()\n\n    override fun onEnterForeground() {\n        setInitialValues()\n        try {\n            connectivityManager.unregisterNetworkCallback(callback)\n        } catch (e: Exception) {\n            //was not registered, or already unregistered\n        }\n        connectivityManager.registerNetworkCallback(NetworkRequest.Builder().build(), callback)\n    }\n\n    override fun onEnterBackground() {\n        try {\n            connectivityManager.unregisterNetworkCallback(callback)\n        } catch (e: Exception) {\n            //already unregistered\n        }\n    }\n\n    private fun setInitialValues() {\n        hasConnection = false\n        hasValidInternet = false\n        isConnected = getInitialConnectionStatus()\n        listener?.onConnectionChange(isConnected)\n    }\n\n    private fun getInitialConnectionStatus(): Boolean {\n        val network = connectivityManager.activeNetwork ?: return false\n\n        hasConnection = true\n        val capabilities = connectivityManager.getNetworkCapabilities(network)\n        hasValidInternet = capabilities?.let {\n            it.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && it.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)\n        } ?: false\n\n        return hasValidInternet\n    }\n\n\n    inner class ConnectionStatusCallback : ConnectivityManager.NetworkCallback() {\n\n        private val activeNetworks: MutableList<Network> = mutableListOf()\n\n        override fun onLost(network: Network) {\n            super.onLost(network)\n            activeNetworks.removeAll { activeNetwork -> activeNetwork == network }\n            hasConnection = activeNetworks.isNotEmpty()\n            updatedConnectionState()\n        }\n\n        override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {\n            super.onCapabilitiesChanged(network, networkCapabilities)\n            hasValidInternet = networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)\n                    && networkCapabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED)\n            updatedConnectionState()\n        }\n\n        override fun onAvailable(network: Network) {\n            super.onAvailable(network)\n            if (activeNetworks.none { activeNetwork -> activeNetwork == network }) {\n                activeNetworks.add(network)\n            }\n            hasConnection = activeNetworks.isNotEmpty()\n            updatedConnectionState()\n        }\n    }\n\n    private fun updatedConnectionState() {\n        val oldValue = isConnected\n        isConnected = hasConnection && hasValidInternet\n        if (oldValue != isConnected) {\n            listener?.onConnectionChange(isConnected)\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/managers/IUnspentOutputProvider.kt",
    "content": "package io.horizontalsystems.bitcoincore.managers\n\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\nimport io.horizontalsystems.bitcoincore.storage.UtxoFilters\n\ninterface IUnspentOutputProvider {\n    fun getSpendableUtxo(filters: UtxoFilters): List<UnspentOutput>\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/managers/IUnspentOutputSelector.kt",
    "content": "package io.horizontalsystems.bitcoincore.managers\n\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\nimport io.horizontalsystems.bitcoincore.storage.UtxoFilters\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\n\ninterface IUnspentOutputSelector {\n    fun select(\n        value: Long,\n        memo: String?,\n        feeRate: Int,\n        outputScriptType: ScriptType = ScriptType.P2PKH,\n        changeType: ScriptType = ScriptType.P2PKH,\n        senderPay: Boolean,\n        pluginDataOutputSize: Int,\n        changeToFirstInput: Boolean,\n        filters: UtxoFilters\n    ): SelectedUnspentOutputInfo\n}\n\ndata class SelectedUnspentOutputInfo(\n        val outputs: List<UnspentOutput>,\n        val recipientValue: Long,\n        val changeValue: Long?\n)\n\nsealed class SendValueErrors : Exception() {\n    object Dust : SendValueErrors()\n    object EmptyOutputs : SendValueErrors()\n    object InsufficientUnspentOutputs : SendValueErrors()\n    object NoSingleOutput : SendValueErrors()\n    object HasOutputFailedToSpend : SendValueErrors()\n}\n\nclass UnspentOutputSelectorChain(private val unspentOutputProvider: IUnspentOutputProvider) : IUnspentOutputSelector {\n    private val concreteSelectors = mutableListOf<IUnspentOutputSelector>()\n\n    fun getAllSpendable(filters: UtxoFilters): List<UnspentOutput> {\n        return unspentOutputProvider.getSpendableUtxo(filters)\n    }\n\n    override fun select(\n        value: Long,\n        memo: String?,\n        feeRate: Int,\n        outputScriptType: ScriptType,\n        changeType: ScriptType,\n        senderPay: Boolean,\n        pluginDataOutputSize: Int,\n        changeToFirstInput: Boolean,\n        filters: UtxoFilters\n    ): SelectedUnspentOutputInfo {\n        var lastError: SendValueErrors? = null\n\n        for (selector in concreteSelectors) {\n            try {\n                return selector.select(\n                    value,\n                    memo,\n                    feeRate,\n                    outputScriptType,\n                    changeType,\n                    senderPay,\n                    pluginDataOutputSize,\n                    changeToFirstInput,\n                    filters\n                )\n            } catch (e: SendValueErrors) {\n                lastError = e\n            }\n        }\n\n        throw lastError ?: Error()\n    }\n\n    fun prependSelector(unspentOutputSelector: IUnspentOutputSelector) {\n        concreteSelectors.add(0, unspentOutputSelector)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/managers/IrregularOutputFinder.kt",
    "content": "package io.horizontalsystems.bitcoincore.managers\n\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport io.horizontalsystems.bitcoincore.utils.Utils\n\ninterface IIrregularOutputFinder {\n    fun hasIrregularOutput(outputs: List<TransactionOutput>): Boolean\n}\n\nclass IrregularOutputFinder(\n    private val storage: IStorage,\n    additionalScriptTypes: List<ScriptType>\n) : IIrregularOutputFinder, IBloomFilterProvider {\n\n    private val irregularScriptTypes = listOf(ScriptType.P2WPKHSH, ScriptType.P2WPKH, ScriptType.P2PK, ScriptType.P2TR) + additionalScriptTypes\n\n    // IIrregularOutputFinder\n\n    override fun hasIrregularOutput(outputs: List<TransactionOutput>): Boolean {\n        return outputs.any { it.publicKeyPath != null && irregularScriptTypes.contains(it.scriptType) }\n    }\n\n    // IBloomFilterProvider\n\n    override var bloomFilterManager: BloomFilterManager? = null\n\n    override fun getBloomFilterElements(): List<ByteArray> {\n        val elements = mutableListOf<ByteArray>()\n\n        val transactionOutputs = storage.lastBlock()?.height?.let { lastBlockHeight ->\n            // get transaction outputs which are unspent or spent in last 100 blocks\n            storage.getOutputsForBloomFilter(lastBlockHeight - 100, irregularScriptTypes)\n        } ?: listOf()\n\n        for (output in transactionOutputs) {\n            val outpoint = output.transactionHash + Utils.intToByteArray(output.index).reversedArray()\n            elements.add(outpoint)\n        }\n\n        return elements\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/managers/PendingOutpointsProvider.kt",
    "content": "package io.horizontalsystems.bitcoincore.managers\n\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.utils.Utils\n\nclass PendingOutpointsProvider(private val storage: IStorage) : IBloomFilterProvider {\n\n    override var bloomFilterManager: BloomFilterManager? = null\n\n    override fun getBloomFilterElements(): List<ByteArray> {\n        val incomingPendingTxHashes = storage.getIncomingPendingTxHashes()\n        val inputs = storage.getTransactionInputs(incomingPendingTxHashes)\n\n        return inputs.map { input -> input.previousOutputTxHash + Utils.intToByteArray(input.previousOutputIndex.toInt()).reversedArray() }\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/managers/PublicKeyManager.kt",
    "content": "package io.horizontalsystems.bitcoincore.managers\n\nimport io.horizontalsystems.bitcoincore.core.IPublicKeyManager\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.core.Wallet\nimport io.horizontalsystems.bitcoincore.models.PublicKey\nimport io.horizontalsystems.bitcoincore.storage.PublicKeyWithUsedState\n\nclass PublicKeyManager(\n        private val storage: IStorage,\n        private val wallet: Wallet,\n        private val restoreKeyConverter: RestoreKeyConverterChain\n) : IBloomFilterProvider, IPublicKeyManager {\n\n    // IBloomFilterProvider\n\n    override var bloomFilterManager: BloomFilterManager? = null\n\n    override fun getBloomFilterElements(): List<ByteArray> {\n        val elements = mutableListOf<ByteArray>()\n\n        for (publicKey in storage.getPublicKeys()) {\n            elements.addAll(restoreKeyConverter.bloomFilterElements(publicKey))\n        }\n\n        return elements\n    }\n\n    @Throws\n    override fun receivePublicKey(): PublicKey {\n        return getPublicKey(true)\n    }\n\n    @Throws\n    override fun changePublicKey(): PublicKey {\n        return getPublicKey(external = false)\n    }\n\n    override fun usedExternalPublicKeys(change: Boolean): List<PublicKey> {\n        return storage.getPublicKeysWithUsedState().filter { it.publicKey.external == !change && it.used }.map { it.publicKey }\n    }\n\n    override fun getPublicKeyByPath(path: String): PublicKey {\n        val parts = path.split(\"/\").map { it.toInt() }\n\n        if (parts.size != 3) throw Error.InvalidPath\n\n        return wallet.publicKey(parts[0], parts[2], parts[1] == 0)\n    }\n\n    override fun fillGap() {\n        val lastUsedAccount = storage.getPublicKeysUsed().map { it.account }.maxOrNull()\n\n        val requiredAccountsCount = if (lastUsedAccount != null) {\n            lastUsedAccount + 1 + 1 //  One because account starts from 0, One because we must have n+1 accounts\n        } else {\n            1\n        }\n\n        repeat(requiredAccountsCount) { account ->\n            fillGap(account, true)\n            fillGap(account, false)\n        }\n\n        bloomFilterManager?.regenerateBloomFilter()\n    }\n\n    override fun addKeys(keys: List<PublicKey>) {\n        if (keys.isEmpty()) return\n\n        storage.savePublicKeys(keys)\n    }\n\n    override fun gapShifts(): Boolean {\n        val publicKeys = storage.getPublicKeysWithUsedState()\n        val lastAccount = publicKeys.map { it.publicKey.account }.max() ?: return false\n\n        for (i in 0..lastAccount) {\n            if (gapKeysCount(publicKeys.filter { it.publicKey.account == i && it.publicKey.external }) < wallet.gapLimit) {\n                return true\n            }\n\n            if (gapKeysCount(publicKeys.filter { it.publicKey.account == i && !it.publicKey.external }) < wallet.gapLimit) {\n                return true\n            }\n        }\n\n        return false\n    }\n\n    private fun fillGap(account: Int, external: Boolean) {\n        val publicKeys = storage.getPublicKeysWithUsedState().filter { it.publicKey.account == account && it.publicKey.external == external }\n        val keysCount = gapKeysCount(publicKeys)\n        val keys = mutableListOf<PublicKey>()\n\n        if (keysCount < wallet.gapLimit) {\n            val lastIndex = publicKeys.maxByOrNull { it.publicKey.index }?.publicKey?.index ?: -1\n\n            val newKeysStartIndex = lastIndex + 1\n            val indices = newKeysStartIndex until (newKeysStartIndex + wallet.gapLimit - keysCount)\n            val newKeys = wallet.publicKeys(account, indices, external)\n\n            keys.addAll(newKeys)\n        }\n\n        addKeys(keys)\n    }\n\n    private fun gapKeysCount(publicKeys: List<PublicKeyWithUsedState>): Int {\n        val lastUsedKey = publicKeys.filter { it.used }.maxByOrNull { it.publicKey.index }\n\n        return when (lastUsedKey) {\n            null -> publicKeys.size\n            else -> publicKeys.filter { it.publicKey.index > lastUsedKey.publicKey.index }.size\n        }\n    }\n\n    @Throws\n    private fun getPublicKey(external: Boolean): PublicKey {\n        return storage.getPublicKeysUnused()\n                .filter { it.account == 0 && it.external == external }\n                .sortedWith(compareBy { it.index })\n                .firstOrNull() ?: throw Error.NoUnusedPublicKey\n    }\n\n    companion object {\n        fun create(storage: IStorage, wallet: Wallet, restoreKeyConverter: RestoreKeyConverterChain): PublicKeyManager {\n            val addressManager = PublicKeyManager(storage, wallet, restoreKeyConverter)\n            addressManager.fillGap()\n            return addressManager\n        }\n    }\n\n    object Error {\n        object NoUnusedPublicKey : Exception()\n        object InvalidPath: Exception()\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/managers/RestoreKeyConverters.kt",
    "content": "package io.horizontalsystems.bitcoincore.managers\n\nimport io.horizontalsystems.bitcoincore.extensions.toHexString\nimport io.horizontalsystems.bitcoincore.models.PublicKey\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport io.horizontalsystems.bitcoincore.utils.IAddressConverter\n\ninterface IRestoreKeyConverter {\n    fun keysForApiRestore(publicKey: PublicKey): List<String>\n    fun bloomFilterElements(publicKey: PublicKey): List<ByteArray>\n}\n\nclass RestoreKeyConverterChain : IRestoreKeyConverter {\n\n    private val converters = mutableListOf<IRestoreKeyConverter>()\n\n    fun add(converter: IRestoreKeyConverter) {\n        converters.add(converter)\n    }\n\n    override fun keysForApiRestore(publicKey: PublicKey): List<String> {\n        val keys = mutableListOf<String>()\n\n        for (converter in converters) {\n            keys.addAll(converter.keysForApiRestore(publicKey))\n        }\n\n        return keys.distinct()\n    }\n\n    override fun bloomFilterElements(publicKey: PublicKey): List<ByteArray> {\n        val keys = mutableListOf<ByteArray>()\n\n        for (converter in converters) {\n            keys.addAll(converter.bloomFilterElements(publicKey))\n        }\n\n        return keys.distinct()\n    }\n}\n\nclass Bip44RestoreKeyConverter(private val addressConverter: IAddressConverter) : IRestoreKeyConverter {\n\n    override fun keysForApiRestore(publicKey: PublicKey): List<String> {\n        val legacyAddress = addressConverter.convert(publicKey, ScriptType.P2PKH).stringValue\n\n        return listOf(legacyAddress)\n    }\n\n    override fun bloomFilterElements(publicKey: PublicKey): List<ByteArray> {\n        return listOf(publicKey.publicKeyHash, publicKey.publicKey)\n    }\n}\n\nclass Bip49RestoreKeyConverter(private val addressConverter: IAddressConverter) : IRestoreKeyConverter {\n\n    override fun keysForApiRestore(publicKey: PublicKey): List<String> {\n        val wpkhShAddress = addressConverter.convert(publicKey, ScriptType.P2WPKHSH).stringValue\n\n        return listOf(wpkhShAddress)\n    }\n\n    override fun bloomFilterElements(publicKey: PublicKey): List<ByteArray> {\n        return listOf(publicKey.scriptHashP2WPKH)\n    }\n}\n\n\nclass Bip84RestoreKeyConverter(private val addressConverter: IAddressConverter) : IRestoreKeyConverter {\n\n    override fun keysForApiRestore(publicKey: PublicKey): List<String> {\n        val segwitAddress = addressConverter.convert(publicKey, ScriptType.P2WPKH).stringValue\n\n        return listOf(segwitAddress)\n    }\n\n    override fun bloomFilterElements(publicKey: PublicKey): List<ByteArray> {\n        return listOf(publicKey.publicKeyHash)\n    }\n}\n\nclass Bip86RestoreKeyConverter(private val addressConverter: IAddressConverter) : IRestoreKeyConverter {\n\n    override fun keysForApiRestore(publicKey: PublicKey): List<String> {\n        val taprootAddress = addressConverter.convert(publicKey, ScriptType.P2TR).stringValue\n\n        return listOf(taprootAddress)\n    }\n\n    override fun bloomFilterElements(publicKey: PublicKey): List<ByteArray> {\n        return listOf(publicKey.convertedForP2TR)\n    }\n}\n\nclass KeyHashRestoreKeyConverter(\n    private val scriptType: ScriptType\n) : IRestoreKeyConverter {\n    override fun keysForApiRestore(publicKey: PublicKey): List<String> {\n        return if (scriptType == ScriptType.P2TR)\n            listOf(publicKey.convertedForP2TR.toHexString())\n        else\n            listOf(publicKey.publicKeyHash.toHexString())\n    }\n\n    override fun bloomFilterElements(publicKey: PublicKey): List<ByteArray> {\n        return if (scriptType == ScriptType.P2TR)\n            listOf(publicKey.convertedForP2TR)\n        else\n            listOf(publicKey.publicKeyHash)\n    }\n}\n\nclass BlockchairCashRestoreKeyConverter(\n    private val addressConverter: IAddressConverter\n) : IRestoreKeyConverter {\n\n    override fun keysForApiRestore(publicKey: PublicKey): List<String> {\n        val legacyAddress = addressConverter.convert(publicKey, ScriptType.P2PKH).stringValue\n\n        return listOf(legacyAddress.split(\":\").last())\n    }\n\n    override fun bloomFilterElements(publicKey: PublicKey): List<ByteArray> {\n        return listOf(publicKey.publicKeyHash, publicKey.publicKey)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/managers/SyncManager.kt",
    "content": "package io.horizontalsystems.bitcoincore.managers\n\nimport io.horizontalsystems.bitcoincore.BitcoinCore\nimport io.horizontalsystems.bitcoincore.BitcoinCore.KitState\nimport io.horizontalsystems.bitcoincore.BitcoinCore.SyncMode\nimport io.horizontalsystems.bitcoincore.core.IApiSyncer\nimport io.horizontalsystems.bitcoincore.core.IApiSyncerListener\nimport io.horizontalsystems.bitcoincore.core.IBlockSyncListener\nimport io.horizontalsystems.bitcoincore.core.IConnectionManagerListener\nimport io.horizontalsystems.bitcoincore.core.IKitStateListener\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.network.peer.PeerGroup\nimport kotlin.math.max\n\nclass SyncManager(\n    private val connectionManager: ConnectionManager,\n    private val apiSyncer: IApiSyncer,\n    private val peerGroup: PeerGroup,\n    private val storage: IStorage,\n    private val syncMode: SyncMode,\n    bestBlockHeight: Int\n) : IApiSyncerListener, IConnectionManagerListener, IBlockSyncListener {\n\n    var listener: IKitStateListener? = null\n\n    var syncState: KitState = KitState.NotSynced(BitcoinCore.StateError.NotStarted())\n        private set(value) {\n            if (value != field) {\n                field = value\n\n                listener?.onKitStateUpdate(field)\n            }\n        }\n\n    private val syncIdle: Boolean\n        get() = syncState.let {\n            it is KitState.NotSynced && it.exception !is BitcoinCore.StateError.NotStarted\n        }\n\n    private var initialBestBlockHeight = bestBlockHeight\n    private var currentBestBlockHeight = bestBlockHeight\n    private var foundTransactionsCount = 0\n    private var forceAddedBlocksTotal: Int = 0\n\n    private fun startSync() {\n        if (apiSyncer.willSync) {\n            startInitialSync()\n        } else {\n            startPeerGroup()\n        }\n    }\n\n    private fun startInitialSync() {\n        syncState = KitState.ApiSyncing(0)\n        apiSyncer.sync()\n    }\n\n    private fun startPeerGroup() {\n        syncState = KitState.Syncing(0.0)\n        peerGroup.start()\n    }\n\n    fun start() {\n        if (syncMode is SyncMode.Blockchair) {\n            when (syncState) {\n                is KitState.ApiSyncing,\n                is KitState.Syncing -> return\n\n                else -> Unit\n            }\n        } else {\n            if (syncState !is KitState.NotSynced) return\n        }\n\n        if (connectionManager.isConnected) {\n            startSync()\n        } else {\n            syncState = KitState.NotSynced(BitcoinCore.StateError.NoInternet())\n        }\n    }\n\n    fun stop() {\n        when (syncState) {\n            is KitState.ApiSyncing -> {\n                apiSyncer.terminate()\n            }\n\n            is KitState.Syncing, is KitState.Synced -> {\n                peerGroup.stop()\n            }\n\n            else -> Unit\n        }\n        syncState = KitState.NotSynced(BitcoinCore.StateError.NotStarted())\n    }\n\n    //\n    // ConnectionManager Listener\n    //\n\n    override fun onConnectionChange(isConnected: Boolean) {\n        if (isConnected && syncIdle) {\n            startSync()\n        } else if (!isConnected && (syncState is KitState.Syncing || syncState is KitState.Synced)) {\n            peerGroup.stop()\n            syncState = KitState.NotSynced(BitcoinCore.StateError.NoInternet())\n        }\n    }\n\n    //\n    // IApiSyncerListener\n    //\n\n    override fun onSyncSuccess() {\n        forceAddedBlocksTotal = storage.getApiBlockHashesCount()\n\n        if (peerGroup.running) {\n            if (foundTransactionsCount > 0) {\n                foundTransactionsCount = 0\n                syncState = KitState.Syncing(0.0)\n                peerGroup.refresh()\n            } else {\n                syncState = KitState.Synced\n            }\n        } else {\n            startPeerGroup()\n        }\n    }\n\n    override fun onSyncFailed(error: Throwable) {\n        syncState = KitState.NotSynced(error)\n    }\n\n    override fun onTransactionsFound(count: Int) {\n        foundTransactionsCount += count\n        syncState = KitState.ApiSyncing(foundTransactionsCount)\n    }\n\n    //\n    // IBlockSyncListener implementations\n    //\n\n    override fun onCurrentBestBlockHeightUpdate(height: Int, maxBlockHeight: Int) {\n        if (!connectionManager.isConnected) return\n\n        currentBestBlockHeight = max(currentBestBlockHeight, height)\n\n        val blocksDownloaded = currentBestBlockHeight - initialBestBlockHeight\n        val allBlocksToDownload = maxBlockHeight - initialBestBlockHeight\n\n        val progress = when {\n            allBlocksToDownload <= 0 -> 1.0\n            else -> blocksDownloaded / allBlocksToDownload.toDouble()\n        }\n\n        syncState = if (progress >= 1) {\n            KitState.Synced\n        } else {\n            val remainingBlocks = allBlocksToDownload - blocksDownloaded\n            KitState.Syncing(progress, remainingBlocks)\n        }\n    }\n\n    override fun onBlockForceAdded() {\n        if (syncMode !is SyncMode.Blockchair) {\n            syncState = KitState.Syncing(0.0)\n            return\n        }\n\n        val forceAddedBlocks = forceAddedBlocksTotal - storage.getApiBlockHashesCount()\n        syncState = when {\n            forceAddedBlocks >= forceAddedBlocksTotal -> {\n                KitState.Synced\n            }\n\n            else -> {\n                val remainingBlocks = forceAddedBlocksTotal - forceAddedBlocks\n                val progress = forceAddedBlocks / forceAddedBlocksTotal.toDouble()\n                KitState.Syncing(progress, remainingBlocks)\n            }\n        }\n    }\n\n    override fun onBlockSyncFinished() {\n        syncState = KitState.Synced\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/managers/UnspentOutputProvider.kt",
    "content": "package io.horizontalsystems.bitcoincore.managers\n\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.core.PluginManager\nimport io.horizontalsystems.bitcoincore.models.BalanceInfo\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\nimport io.horizontalsystems.bitcoincore.storage.UtxoFilters\n\nclass UnspentOutputProvider(\n    private val storage: IStorage,\n    private val confirmationsThreshold: Int = 6,\n    val pluginManager: PluginManager\n) : IUnspentOutputProvider {\n\n    override fun getSpendableUtxo(filters: UtxoFilters): List<UnspentOutput> {\n        return allUtxo().filter {\n            isSpendable(it) && filters.filterUtxo(it, storage)\n        }\n    }\n\n    private fun isSpendable(utxo: UnspentOutput): Boolean {\n        if (!pluginManager.isSpendable(utxo)) {\n            return false\n        }\n\n        if (utxo.transaction.status != Transaction.Status.RELAYED) {\n            return false\n        }\n\n        return true\n    }\n\n    private fun getUnspendableTimeLockedUtxo() = allUtxo().filter {\n        !pluginManager.isSpendable(it)\n    }\n\n    private fun getUnspendableNotRelayedUtxo() = allUtxo().filter {\n        it.transaction.status != Transaction.Status.RELAYED\n    }\n\n    fun getBalance(): BalanceInfo {\n        val spendable = getSpendableUtxo(UtxoFilters()).sumOf { it.output.value }\n        val unspendableTimeLocked = getUnspendableTimeLockedUtxo().sumOf { it.output.value }\n        val unspendableNotRelayed = getUnspendableNotRelayedUtxo().sumOf { it.output.value }\n\n        return BalanceInfo(spendable, unspendableTimeLocked, unspendableNotRelayed)\n    }\n\n    // Only confirmed spendable outputs\n    fun getConfirmedSpendableUtxo(filters: UtxoFilters): List<UnspentOutput> {\n        val lastBlockHeight = storage.lastBlock()?.height ?: 0\n\n        return getSpendableUtxo(filters).filter {\n            val block = it.block ?: return@filter false\n            return@filter block.height <= lastBlockHeight - confirmationsThreshold + 1\n        }\n    }\n\n    private fun allUtxo(): List<UnspentOutput> {\n        val unspentOutputs = storage.getUnspentOutputs()\n\n        if (confirmationsThreshold == 0) return unspentOutputs\n\n        val lastBlockHeight = storage.lastBlock()?.height ?: 0\n\n        return unspentOutputs.filter {\n            // If a transaction is an outgoing transaction, then it can be used\n            // even if it's not included in a block yet\n            if (it.transaction.isOutgoing) {\n                return@filter true\n            }\n\n            // If a transaction is an incoming transaction, then it can be used\n            // only if it's included in a block and has enough number of confirmations\n            val block = it.block ?: return@filter false\n            if (block.height <= lastBlockHeight - confirmationsThreshold + 1) {\n                return@filter true\n            }\n\n            false\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/managers/UnspentOutputQueue.kt",
    "content": "package io.horizontalsystems.bitcoincore.managers\n\nimport io.horizontalsystems.bitcoincore.DustCalculator\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\nimport io.horizontalsystems.bitcoincore.transactions.TransactionSizeCalculator\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\n\nclass UnspentOutputQueue(\n    private val parameters: Parameters,\n    private val sizeCalculator: TransactionSizeCalculator,\n    dustCalculator: DustCalculator,\n) {\n    private val changeType: ScriptType\n        get() {\n            if (parameters.changeToFirstInput) {\n                selectedOutputs.firstOrNull()?.let {\n                    return it.output.scriptType\n                }\n            }\n\n            return parameters.changeType\n        }\n\n    private var selectedOutputs: MutableList<UnspentOutput> = mutableListOf()\n    private var totalValue: Long = 0L\n\n    val recipientOutputDust = dustCalculator.dust(parameters.outputScriptType)\n\n    fun push(output: UnspentOutput) {\n        selectedOutputs.add(output)\n        totalValue += output.output.value\n        enforceOutputsLimit()\n    }\n\n    private fun enforceOutputsLimit() {\n        val limit = parameters.outputsLimit\n        if (limit != null && limit > 0 && selectedOutputs.size > limit) {\n            totalValue -= selectedOutputs.firstOrNull()?.output?.value ?: 0\n            selectedOutputs.removeFirst()\n        }\n    }\n\n    fun set(outputs: List<UnspentOutput>) {\n        selectedOutputs.clear()\n        totalValue = 0\n\n        outputs.forEach { push(it) }\n    }\n\n    @Throws(SendValueErrors::class)\n    fun calculate(): SelectedUnspentOutputInfo {\n        if (selectedOutputs.isEmpty()) {\n            throw SendValueErrors.EmptyOutputs\n        }\n\n        val feeWithoutChange = calculateFeeWithoutChange()\n        val (receiveValue, remainder) = calculateSendValues(feeWithoutChange)\n\n        val changeFee = sizeCalculator.outputSize(changeType) * parameters.fee\n        val actualRemainder = remainder - changeFee\n\n        return if (actualRemainder <= recipientOutputDust) {\n            SelectedUnspentOutputInfo(selectedOutputs, receiveValue, null)\n        } else {\n            SelectedUnspentOutputInfo(selectedOutputs, receiveValue, actualRemainder)\n        }\n    }\n\n    private fun calculateFeeWithoutChange(): Long =\n        sizeCalculator.transactionSize(\n            previousOutputs = selectedOutputs.map { it.output },\n            outputs = listOf(parameters.outputScriptType),\n            memo = parameters.memo,\n            pluginDataOutputSize = parameters.pluginDataOutputSize\n        ) * parameters.fee\n\n    @Throws(SendValueErrors::class)\n    private fun calculateSendValues(feeWithoutChange: Long): Pair<Long, Long> {\n        val sentValue = if (parameters.senderPay) parameters.value + feeWithoutChange else parameters.value\n\n        if (totalValue < sentValue) {\n            throw SendValueErrors.InsufficientUnspentOutputs\n        }\n\n        val receiveValue = if (parameters.senderPay) parameters.value else parameters.value - feeWithoutChange\n        if (receiveValue < recipientOutputDust) {\n            throw SendValueErrors.Dust\n        }\n\n        return Pair(receiveValue, totalValue - receiveValue - feeWithoutChange)\n    }\n\n\n    data class Parameters(\n        val value: Long,\n        val senderPay: Boolean,\n        val memo: String?,\n        val fee: Int,\n        val outputsLimit: Int?,\n        val outputScriptType: ScriptType,\n        val changeType: ScriptType,\n        val pluginDataOutputSize: Int,\n        val changeToFirstInput: Boolean,\n    )\n}"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/managers/UnspentOutputSelector.kt",
    "content": "package io.horizontalsystems.bitcoincore.managers\n\nimport io.horizontalsystems.bitcoincore.DustCalculator\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\nimport io.horizontalsystems.bitcoincore.storage.UtxoFilters\nimport io.horizontalsystems.bitcoincore.transactions.TransactionSizeCalculator\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\n\nclass UnspentOutputSelector(\n    private val calculator: TransactionSizeCalculator,\n    private val dustCalculator: DustCalculator,\n    private val unspentOutputProvider: IUnspentOutputProvider,\n    private val outputsLimit: Int? = null\n) : IUnspentOutputSelector {\n\n    fun getAll(filters: UtxoFilters): List<UnspentOutput> {\n        return unspentOutputProvider.getSpendableUtxo(filters)\n    }\n\n    @Throws(SendValueErrors::class)\n    override fun select(\n        value: Long,\n        memo: String?,\n        feeRate: Int,\n        outputScriptType: ScriptType,\n        changeType: ScriptType,\n        senderPay: Boolean,\n        pluginDataOutputSize: Int,\n        changeToFirstInput: Boolean,\n        filters: UtxoFilters\n    ): SelectedUnspentOutputInfo {\n        val sortedOutputs =\n            unspentOutputProvider.getSpendableUtxo(filters).sortedWith(compareByDescending<UnspentOutput> {\n                it.output.failedToSpend\n            }.thenBy {\n                it.output.value\n            })\n\n        // check if value is not dust. recipientValue may be less, but not more\n        if (value < dustCalculator.dust(outputScriptType)) {\n            throw SendValueErrors.Dust\n        }\n\n        val params = UnspentOutputQueue.Parameters(\n            value = value,\n            senderPay = senderPay,\n            memo = memo,\n            fee = feeRate,\n            outputsLimit = outputsLimit,\n            outputScriptType = outputScriptType,\n            changeType = changeType,\n            pluginDataOutputSize = pluginDataOutputSize,\n            changeToFirstInput = changeToFirstInput,\n        )\n        val queue = UnspentOutputQueue(params, calculator, dustCalculator)\n\n        // select unspentOutputs with the least value until we get the needed value\n        var lastError: SendValueErrors? = null\n        for (unspentOutput in sortedOutputs) {\n            queue.push(unspentOutput)\n\n            try {\n                return queue.calculate()\n            } catch (error: SendValueErrors) {\n                lastError = error\n            }\n        }\n        throw lastError ?: SendValueErrors.InsufficientUnspentOutputs\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/managers/UnspentOutputSelectorSingleNoChange.kt",
    "content": "package io.horizontalsystems.bitcoincore.managers\n\nimport io.horizontalsystems.bitcoincore.DustCalculator\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\nimport io.horizontalsystems.bitcoincore.storage.UtxoFilters\nimport io.horizontalsystems.bitcoincore.transactions.TransactionSizeCalculator\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\n\nclass UnspentOutputSelectorSingleNoChange(\n    private val calculator: TransactionSizeCalculator,\n    private val dustCalculator: DustCalculator,\n    private val unspentOutputProvider: IUnspentOutputProvider\n) : IUnspentOutputSelector {\n\n    override fun select(\n        value: Long,\n        memo: String?,\n        feeRate: Int,\n        outputScriptType: ScriptType,\n        changeType: ScriptType,\n        senderPay: Boolean,\n        pluginDataOutputSize: Int,\n        changeToFirstInput: Boolean,\n        filters: UtxoFilters\n    ): SelectedUnspentOutputInfo {\n        val dust = dustCalculator.dust(outputScriptType)\n        if (value < dust) {\n            throw SendValueErrors.Dust\n        }\n\n        val sortedOutputs =\n            unspentOutputProvider.getSpendableUtxo(filters).sortedWith(compareByDescending<UnspentOutput> {\n                it.output.failedToSpend\n            }.thenBy {\n                it.output.value\n            })\n\n        if (sortedOutputs.isEmpty()) {\n            throw SendValueErrors.EmptyOutputs\n        }\n\n        if (sortedOutputs.any { it.output.failedToSpend }) {\n            throw SendValueErrors.HasOutputFailedToSpend\n        }\n\n        val params = UnspentOutputQueue.Parameters(\n            value = value,\n            senderPay = senderPay,\n            memo = memo,\n            fee = feeRate,\n            outputsLimit = null,\n            outputScriptType = outputScriptType,\n            changeType = changeType,\n            pluginDataOutputSize = pluginDataOutputSize,\n            changeToFirstInput = changeToFirstInput,\n        )\n        val queue = UnspentOutputQueue(params, calculator, dustCalculator)\n\n        //  try to find 1 unspent output with exactly matching value\n        for (unspentOutput in sortedOutputs) {\n            queue.set(listOf(unspentOutput))\n\n            try {\n                val info = queue.calculate()\n                if (info.changeValue == null) {\n                    return info\n                }\n            } catch (error: SendValueErrors) {\n                //  ignore\n            }\n        }\n\n        throw SendValueErrors.NoSingleOutput\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/Address.kt",
    "content": "package io.horizontalsystems.bitcoincore.models\n\nimport io.horizontalsystems.bitcoincore.transactions.scripts.OpCodes\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\n\nenum class AddressType {\n    PubKeyHash,\n    ScriptHash\n}\n\ninterface Address {\n    val scriptType: ScriptType\n    val lockingScriptPayload: ByteArray\n    val stringValue: String\n    val lockingScript: ByteArray\n}\n\nclass LegacyAddress(\n    override val stringValue: String,\n    override val lockingScriptPayload: ByteArray,\n    val type: AddressType\n) : Address {\n\n    override val scriptType: ScriptType\n        get() = when (type) {\n            AddressType.PubKeyHash -> ScriptType.P2PKH\n            AddressType.ScriptHash -> ScriptType.P2SH\n        }\n\n    override val lockingScript: ByteArray\n        get() = when (type) {\n            AddressType.PubKeyHash -> OpCodes.p2pkhStart + OpCodes.push(lockingScriptPayload) + OpCodes.p2pkhEnd\n            AddressType.ScriptHash -> OpCodes.p2pshStart + OpCodes.push(lockingScriptPayload) + OpCodes.p2pshEnd\n        }\n}\n\nclass SegWitV0Address(\n    override val stringValue: String,\n    override val lockingScriptPayload: ByteArray,\n    val type: AddressType\n) : Address {\n\n    override val scriptType: ScriptType\n        get() = when (type) {\n            AddressType.PubKeyHash -> ScriptType.P2WPKH\n            AddressType.ScriptHash -> ScriptType.P2WSH\n        }\n\n    override val lockingScript: ByteArray\n        get() = OpCodes.push(0) + OpCodes.push(lockingScriptPayload)\n}\n\nclass TaprootAddress(\n    override val stringValue: String,\n    override val lockingScriptPayload: ByteArray,\n    val version: Int\n) : Address {\n\n    override val scriptType: ScriptType = ScriptType.P2TR\n\n    override val lockingScript: ByteArray\n        get() = OpCodes.push(version) + OpCodes.push(lockingScriptPayload)\n}\n\nclass CashAddress(\n    override val stringValue: String,\n    override val lockingScriptPayload: ByteArray,\n    val version: Int,\n    val type: AddressType\n) : Address {\n\n    override val scriptType: ScriptType\n        get() = when (type) {\n            AddressType.PubKeyHash -> ScriptType.P2PKH\n            AddressType.ScriptHash -> ScriptType.P2SH\n        }\n\n    override val lockingScript: ByteArray\n        get() = when (type) {\n            AddressType.PubKeyHash -> OpCodes.p2pkhStart + OpCodes.push(lockingScriptPayload) + OpCodes.p2pkhEnd\n            AddressType.ScriptHash -> OpCodes.p2pshStart + OpCodes.push(lockingScriptPayload) + OpCodes.p2pshEnd\n        }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/BitcoinPaymentData.kt",
    "content": "package io.horizontalsystems.bitcoincore.models\n\ndata class BitcoinPaymentData(\n        val address: String,\n        val version: String? = null,\n        val amount: Double? = null,\n        val label: String? = null,\n        val message: String? = null,\n        val parameters: MutableMap<String, String>? = null) {\n\n    val uriPaymentAddress: String\n        get() {\n            val uriAddress = address\n            version?.let {\n                uriAddress.plus(\";version=$version\")\n            }\n            amount?.let {\n                uriAddress.plus(\"?amount=$it\")\n            }\n            label?.let {\n                uriAddress.plus(\"?label=$label\")\n            }\n            message?.let {\n                uriAddress.plus(\"?message=$message\")\n            }\n            parameters?.let {\n                for ((name, value) in it) {\n                    uriAddress.plus(\"?$name=$value\")\n                }\n            }\n\n            return uriAddress\n        }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/BitcoinSendInfo.kt",
    "content": "package io.horizontalsystems.bitcoincore.models\n\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\n\ndata class BitcoinSendInfo(\n    val unspentOutputs: List<UnspentOutput>,\n    val fee: Long,\n    val changeValue: Long?,\n    val changeAddress: Address?\n)"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/Block.kt",
    "content": "package io.horizontalsystems.bitcoincore.models\n\nimport androidx.room.ColumnInfo\nimport androidx.room.Entity\nimport androidx.room.Index\nimport androidx.room.PrimaryKey\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.storage.BlockHeader\n\n/**\n * Block\n *\n *  Size        Field           Description\n *  ====        =====           ===========\n *  80 bytes    Header          Consists of 6 fields that are hashed to calculate the block hash\n *  VarInt      TxCount         Number of transactions in the block\n *  Variable    Transactions    The transactions in the block\n */\n\n@Entity(indices = [Index(\"height\")])\n\nclass Block() {\n\n    //  Header\n    @ColumnInfo(name = \"block_version\")\n    var version: Int = 0\n    var previousBlockHash: ByteArray = byteArrayOf()\n    var merkleRoot: ByteArray = byteArrayOf()\n    @ColumnInfo(name = \"block_timestamp\")\n    var timestamp: Long = 0\n    var bits: Long = 0\n    var nonce: Long = 0\n    var hasTransactions = false\n\n    @PrimaryKey\n    var headerHash: ByteArray = byteArrayOf()\n    var height: Int = 0\n    var stale = false\n    var partial = false\n\n    fun previousBlock(storage: IStorage): Block? {\n        return storage.getBlock(hashHash = previousBlockHash)\n    }\n\n    constructor(header: BlockHeader, previousBlock: Block) : this(header, height = previousBlock.height + 1)\n    constructor(header: BlockHeader, height: Int) : this() {\n        version = header.version\n        previousBlockHash = header.previousBlockHeaderHash\n        merkleRoot = header.merkleRoot\n        timestamp = header.timestamp\n        bits = header.bits\n        nonce = header.nonce\n\n        headerHash = header.hash\n        this.height = height\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/BlockHash.kt",
    "content": "package io.horizontalsystems.bitcoincore.models\n\nimport androidx.room.Entity\nimport androidx.room.PrimaryKey\n\n@Entity\nclass BlockHash(\n        @PrimaryKey\n        val headerHash: ByteArray,\n        val height: Int,\n        val sequence: Int = 0)\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/BlockHashPublicKey.kt",
    "content": "package io.horizontalsystems.bitcoincore.models\n\nimport androidx.room.Entity\nimport androidx.room.ForeignKey\n\n@Entity(\n    primaryKeys = [\"blockHash\", \"publicKeyPath\"],\n    foreignKeys = [\n        ForeignKey(\n            entity = BlockHash::class,\n            parentColumns = [\"headerHash\"],\n            childColumns = [\"blockHash\"],\n            onUpdate = ForeignKey.CASCADE,\n            onDelete = ForeignKey.CASCADE,\n            deferred = true\n        ),\n        ForeignKey(\n            entity = PublicKey::class,\n            parentColumns = [\"path\"],\n            childColumns = [\"publicKeyPath\"],\n            onUpdate = ForeignKey.CASCADE,\n            onDelete = ForeignKey.CASCADE,\n            deferred = true\n        )\n    ]\n)\nclass BlockHashPublicKey(\n    val blockHash: ByteArray,\n    val publicKeyPath: String\n)\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/BlockchainState.kt",
    "content": "package io.horizontalsystems.bitcoincore.models\n\nimport androidx.room.Entity\nimport androidx.room.PrimaryKey\n\n@Entity\ndata class BlockchainState(var initialRestored: Boolean?) {\n\n    @PrimaryKey\n    var primaryKey: String = \"primary-key\"\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/Checkpoint.kt",
    "content": "package io.horizontalsystems.bitcoincore.models\n\nimport io.horizontalsystems.bitcoincore.BitcoinCore\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.extensions.hexToByteArray\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.network.Network\nimport io.horizontalsystems.bitcoincore.storage.BlockHeader\nimport java.io.BufferedReader\nimport java.io.InputStreamReader\nimport java.io.Reader\n\nclass Checkpoint(fileName: String) {\n    val block: Block\n    val additionalBlocks: List<Block>\n\n    init {\n        val stream = javaClass.classLoader?.getResourceAsStream(fileName)\n        val inputStreamReader: Reader = InputStreamReader(stream)\n        val reader = BufferedReader(inputStreamReader)\n        val checkpoints = reader.readLines()\n\n        val blocks = checkpoints.map { readBlock(it) }\n\n        block = blocks.first()\n        additionalBlocks = blocks.drop(1)\n    }\n\n    private fun readBlock(serializedCheckpointBlock: String): Block {\n        BitcoinInputMarkable(serializedCheckpointBlock.hexToByteArray()).use { input ->\n            val version = input.readInt()\n            val prevHash = input.readBytes(32)\n            val merkleHash = input.readBytes(32)\n            val timestamp = input.readUnsignedInt()\n            val bits = input.readUnsignedInt()\n            val nonce = input.readUnsignedInt()\n            val height = input.readInt()\n            val hash = input.readBytes(32)\n\n            return Block(\n                BlockHeader(\n                    version = version,\n                    previousBlockHeaderHash = prevHash,\n                    merkleRoot = merkleHash,\n                    timestamp = timestamp,\n                    bits = bits,\n                    nonce = nonce,\n                    hash = hash\n                ), height\n            )\n        }\n    }\n\n    companion object {\n        fun resolveCheckpoint(syncMode: BitcoinCore.SyncMode, network: Network, storage: IStorage): Checkpoint {\n            val lastBlock = storage.lastBlock()\n\n            val checkpoint = if (syncMode is BitcoinCore.SyncMode.Full) {\n                network.bip44Checkpoint\n            } else {\n                val lastCheckpoint = network.lastCheckpoint\n                if (lastBlock != null && lastBlock.height < lastCheckpoint.block.height) {\n                    // during app updating there may be case when the last block in DB is earlier than new checkpoint block\n                    // in this case we set the very first checkpoint block for bip44,\n                    // since it surely will be earlier than the last block in DB\n                    network.bip44Checkpoint\n                } else {\n                    lastCheckpoint\n                }\n            }\n\n            if (lastBlock == null) {\n                storage.saveBlock(checkpoint.block)\n                checkpoint.additionalBlocks.forEach { block ->\n                    storage.saveBlock(block)\n                }\n            }\n\n            return checkpoint\n        }\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/InvalidTransaction.kt",
    "content": "package io.horizontalsystems.bitcoincore.models\n\nimport androidx.room.Entity\n\n@Entity\nclass InvalidTransaction() : Transaction() {\n\n    constructor(transaction: Transaction, serializedTxInfo: String, rawTransaction: String?) : this() {\n        uid = transaction.uid\n        hash = transaction.hash\n        blockHash = transaction.blockHash\n        version = transaction.version\n        lockTime = transaction.lockTime\n        timestamp = transaction.timestamp\n        order = transaction.order\n        isMine = transaction.isMine\n        isOutgoing = transaction.isOutgoing\n        segwit = transaction.segwit\n        status = Status.INVALID\n        conflictingTxHash = transaction.conflictingTxHash\n\n        this.serializedTxInfo = serializedTxInfo\n        this.rawTransaction = rawTransaction\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/InventoryItem.kt",
    "content": "package io.horizontalsystems.bitcoincore.models\n\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\nimport java.io.IOException\n\n/**\n * InventoryItem\n *\n *   Size       Field   Description\n *   ====       =====   ===========\n *   4 bytes    Type    0=Error, 1=Transaction, 2=Block, 3=Filtered Block\n *  32 bytes    Hash    Object hash\n */\nclass InventoryItem {\n\n    // Uint32\n    var type: Int = 0\n\n    // 32-bytes hash\n    lateinit var hash: ByteArray\n\n    constructor()\n\n    @Throws(IOException::class)\n    constructor(input: BitcoinInputMarkable) {\n        this.type = input.readInt()\n        this.hash = input.readBytes(32)\n    }\n\n    constructor(type: Int, hash: ByteArray) {\n        this.type = type\n        this.hash = hash\n    }\n\n    fun toByteArray(): ByteArray {\n        return BitcoinOutput().writeInt(this.type).write(this.hash).toByteArray()\n    }\n\n    companion object {\n\n        /**\n         * Any data of with this number may be ignored.\n         */\n        const val ERROR = 0\n\n        /**\n         * Hash is related to a transaction.\n         */\n        const val MSG_TX = 1\n\n        /**\n         * Hash is related to a data block.\n         */\n        const val MSG_BLOCK = 2\n\n        /**\n         * Hash of a block header; identical to MSG_BLOCK. Only to be used in\n         * getdata message. Indicates the reply should be a merkleblock message\n         * rather than a block message; this only works if a bloom filter has been\n         * set.\n         */\n        const val MSG_FILTERED_BLOCK = 3\n\n        /**\n         * Hash of a block header; identical to MSG_BLOCK. Only to be used in\n         * getdata message. Indicates the reply should be a cmpctblock message. See\n         * BIP 152 for more info.\n         */\n        const val MSG_CMPCT_BLOCK = 4\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/MerkleBlock.kt",
    "content": "package io.horizontalsystems.bitcoincore.models\n\nimport io.horizontalsystems.bitcoincore.core.HashBytes\nimport io.horizontalsystems.bitcoincore.storage.BlockHeader\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\n\nclass MerkleBlock(val header: BlockHeader, val associatedTransactionHashes: Map<HashBytes, Boolean>) {\n\n    var height: Int? = null\n    var associatedTransactions = mutableListOf<FullTransaction>()\n    val blockHash = header.hash\n\n    val complete: Boolean\n        get() = associatedTransactionHashes.size == associatedTransactions.size\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/NetworkAddress.kt",
    "content": "package io.horizontalsystems.bitcoincore.models\n\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\nimport io.horizontalsystems.bitcoincore.network.Network\nimport io.horizontalsystems.bitcoincore.utils.NetworkUtils\nimport java.io.IOException\nimport java.net.InetAddress\n\n/**\n * Network Address\n *\n *   Size       Field       Description\n *   ====       =====       ===========\n *   4 bytes    Time        Timestamp in seconds since the epoch\n *   8 bytes    Services    Services provided by the node\n *  16 bytes    Address     IPv6 address (IPv4 addresses are encoded as IPv6 addresses)\n *   2 bytes    Port        Port (network byte order)\n */\nclass NetworkAddress {\n\n    // Uint32, the Time (version >= 31402). Not present in version message.\n    var time: Long = 0\n\n    // Uint64, same service(s) listed in version\n    var services: Long = 0\n\n    // 16 bytes IPv6 address. Network byte order. The IPv4 address is 12 bytes\n    // 00 00 00 00 00 00 00 00 00 00 FF FF, followed by the 4 bytes of the IPv4 address\n    lateinit var address: ByteArray\n\n    // Uint16, port number\n    var port: Int = 0\n\n    constructor()\n\n    @Throws(IOException::class)\n    constructor(input: BitcoinInputMarkable, excludeTime: Boolean) {\n        if (!excludeTime) {\n            time = input.readUnsignedInt()\n        }\n        services = input.readLong()\n        address = input.readBytes(16)\n        port = input.readUnsignedShort()\n    }\n\n    constructor(addr: InetAddress, network: Network) {\n        time = System.currentTimeMillis() / 1000\n        services = 1\n        address = NetworkUtils.getIPv6(addr)\n        port = network.port\n    }\n\n    fun toByteArray(excludeTime: Boolean): ByteArray {\n        val output = BitcoinOutput()\n        if (!excludeTime) {\n            output.writeUnsignedInt(time)  // time\n        }\n        output.writeLong(services)         // service\n                .write(address)            // address\n                .writeUnsignedShort(port)  // port\n        return output.toByteArray()\n    }\n\n    companion object {\n\n        @Throws(IOException::class)\n        fun parse(input: BitcoinInputMarkable, excludeTime: Boolean): NetworkAddress {\n            val addr = NetworkAddress()\n            if (!excludeTime) {\n                addr.time = input.readUnsignedInt()\n            }\n            addr.services = input.readLong()\n            addr.address = input.readBytes(16)\n            addr.port = input.readUnsignedShort()\n\n            return addr\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/PeerAddress.kt",
    "content": "package io.horizontalsystems.bitcoincore.models\n\nimport androidx.room.Entity\nimport androidx.room.PrimaryKey\n\n@Entity\ndata class PeerAddress(@PrimaryKey var ip: String, var score: Int = 0, var connectionTime: Long? = null)\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/PublicKey.kt",
    "content": "package io.horizontalsystems.bitcoincore.models\n\nimport androidx.room.ColumnInfo\nimport androidx.room.Entity\nimport androidx.room.Index\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.transactions.scripts.OpCodes\nimport io.horizontalsystems.bitcoincore.utils.Utils\nimport io.horizontalsystems.hdwalletkit.ECKey\n\n@Entity(\n    primaryKeys = [\"path\"],\n    indices = [\n        Index(\"publicKey\"),\n        Index(\"publicKeyHash\"),\n        Index(\"scriptHashP2WPKH\"),\n        Index(\"convertedForP2TR\")\n    ]\n)\n\nopen class PublicKey() {\n    var path: String = \"\"\n\n    var account = 0\n\n    @ColumnInfo(name = \"address_index\")\n    var index = 0\n    var external = true\n\n    var publicKeyHash = byteArrayOf()\n    var publicKey = byteArrayOf()\n    var scriptHashP2WPKH = byteArrayOf()\n    var convertedForP2TR = byteArrayOf()\n\n    fun used(storage: IStorage): Boolean {\n        return storage.getOutputsOfPublicKey(this).isNotEmpty()\n    }\n\n    constructor(account: Int, index: Int, external: Boolean, publicKey: ByteArray, publicKeyHash: ByteArray) : this() {\n        this.path = \"$account/${if (external) 0 else 1}/$index\"\n        this.account = account\n        this.index = index\n        this.external = external\n        this.publicKey = publicKey\n        this.publicKeyHash = publicKeyHash\n\n        val version = 0\n        val redeemScript = OpCodes.push(version) + OpCodes.push(this.publicKeyHash)\n        this.scriptHashP2WPKH = Utils.sha256Hash160(redeemScript)\n        this.convertedForP2TR = ECKey(publicKey).tweakedOutputKey.pubKeyXCoord\n    }\n\n    override fun equals(other: Any?): Boolean {\n        return other is PublicKey && other.path == path\n    }\n\n    override fun hashCode(): Int {\n        return path.hashCode()\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/SentTransaction.kt",
    "content": "package io.horizontalsystems.bitcoincore.models\n\nimport androidx.room.Entity\nimport androidx.room.PrimaryKey\n\n@Entity\nclass SentTransaction() {\n\n    @PrimaryKey\n    var hash = byteArrayOf()\n    var firstSendTime: Long = System.currentTimeMillis()\n    var lastSendTime: Long = System.currentTimeMillis()\n    var retriesCount: Int = 0\n    var sendSuccess: Boolean = false\n\n    constructor(hash: ByteArray) : this() {\n        this.hash = hash\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/Transaction.kt",
    "content": "package io.horizontalsystems.bitcoincore.models\n\nimport androidx.room.Entity\nimport androidx.room.ForeignKey\nimport java.util.*\n\n/**\n * Transaction\n *\n *  Size        Field           Description\n *  ====        =====           ===========\n *  4 bytes     Version         Transaction version\n *  VarInt      InputsCount     Number of inputs\n *  Variable    Inputs          Inputs\n *  VarInt      OutputsCount    Number of outputs\n *  Variable    Outputs         Outputs\n *  4 bytes     LockTime        Transaction lock time\n */\n\n@Entity(primaryKeys = [\"hash\"],\n        foreignKeys = [ForeignKey(\n                entity = Block::class,\n                parentColumns = [\"headerHash\"],\n                childColumns = [\"blockHash\"],\n                onUpdate = ForeignKey.CASCADE,\n                onDelete = ForeignKey.CASCADE,\n                deferred = true)\n        ])\n\nopen class Transaction() {\n\n    var uid: String = UUID.randomUUID().toString()\n\n    var hash: ByteArray = byteArrayOf()\n    var blockHash: ByteArray? = null\n\n    var version: Int = 0\n    var lockTime: Long = 0\n    var timestamp: Long = Date().time / 1000\n    var order: Int = 0 // topological order\n    var isMine = false\n    var isOutgoing = false\n    var segwit = false\n    var status: Int = Status.RELAYED\n    var serializedTxInfo: String = \"\"\n    var conflictingTxHash: ByteArray? = null\n    var rawTransaction: String? = null\n\n    constructor(version: Int = 0, lockTime: Long = 0) : this() {\n        this.version = version\n        this.lockTime = lockTime\n    }\n\n    object Status {\n        const val NEW = 1\n        const val RELAYED = 2\n        const val INVALID = 3\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/TransactionDataSortType.kt",
    "content": "package io.horizontalsystems.bitcoincore.models\n\nenum class TransactionDataSortType {\n    None, Shuffle, Bip69\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/TransactionInfo.kt",
    "content": "package io.horizontalsystems.bitcoincore.models\n\nimport com.eclipsesource.json.Json\nimport com.eclipsesource.json.JsonArray\nimport com.eclipsesource.json.JsonObject\nimport io.horizontalsystems.bitcoincore.core.IPluginOutputData\n\nopen class TransactionInfo {\n    var uid: String = \"\"\n    var transactionHash: String = \"\"\n    var transactionIndex: Int = 0\n    var inputs: List<TransactionInputInfo> = listOf()\n    var outputs: List<TransactionOutputInfo> = listOf()\n    var amount: Long = 0\n    var type: TransactionType\n    var fee: Long? = null\n    var blockHeight: Int? = null\n    var timestamp: Long = 0\n    var status: TransactionStatus = TransactionStatus.NEW\n    var conflictingTxHash: String? = null\n    var rbfEnabled: Boolean = false\n\n    val replaceable: Boolean\n        get() = rbfEnabled && blockHeight == null && conflictingTxHash == null\n\n    constructor(\n        uid: String,\n        transactionHash: String,\n        transactionIndex: Int,\n        inputs: List<TransactionInputInfo>,\n        outputs: List<TransactionOutputInfo>,\n        amount: Long,\n        type: TransactionType,\n        fee: Long?,\n        blockHeight: Int?,\n        timestamp: Long,\n        status: TransactionStatus,\n        conflictingTxHash: String? = null,\n        rbfEnabled: Boolean = false\n    ) {\n        this.uid = uid\n        this.transactionHash = transactionHash\n        this.transactionIndex = transactionIndex\n        this.inputs = inputs\n        this.outputs = outputs\n        this.amount = amount\n        this.type = type\n        this.fee = fee\n        this.blockHeight = blockHeight\n        this.timestamp = timestamp\n        this.status = status\n        this.conflictingTxHash = conflictingTxHash\n        this.rbfEnabled = rbfEnabled\n    }\n\n    @Throws\n    constructor(serialized: String) {\n        val jsonObject = Json.parse(serialized).asObject()\n        uid = jsonObject.get(\"uid\").asString()\n        transactionHash = jsonObject.get(\"transactionHash\").asString()\n        transactionIndex = jsonObject.get(\"transactionIndex\").asInt()\n        inputs = parseInputs(jsonObject.get(\"inputs\").asArray())\n        outputs = parseOutputs(jsonObject.get(\"outputs\").asArray())\n        amount = jsonObject.get(\"amount\").asLong()\n        type = TransactionType.fromValue(jsonObject.get(\"type\").asInt()) ?: TransactionType.Incoming\n        fee = jsonObject.get(\"fee\")?.asLong()\n        blockHeight = jsonObject.get(\"blockHeight\")?.asInt()\n        timestamp = jsonObject.get(\"timestamp\").asLong()\n        status = TransactionStatus.getByCode(jsonObject.get(\"status\").asInt())\n                ?: TransactionStatus.INVALID\n        conflictingTxHash = jsonObject.get(\"conflictingTxHash\")?.asString()\n        rbfEnabled = jsonObject.get(\"rbfEnabled\")?.asBoolean() ?: false\n    }\n\n    private fun parseInputs(jsonArray: JsonArray): List<TransactionInputInfo> {\n        val inputs = mutableListOf<TransactionInputInfo>()\n\n        for (inputJsonValue in jsonArray) {\n            inputJsonValue.asObject().let {\n                val input = TransactionInputInfo(\n                        mine = it.get(\"mine\").asBoolean(),\n                        value = if (it.get(\"value\")?.isNull == false) it.get(\"value\")?.asLong() else null,\n                        address = if (it.get(\"address\")?.isNull == false) it.get(\"address\")?.asString() else null)\n\n                inputs.add(input)\n            }\n        }\n        return inputs\n    }\n\n    private fun parseOutputs(jsonArray: JsonArray): List<TransactionOutputInfo> {\n        val outputs = mutableListOf<TransactionOutputInfo>()\n\n        for (outputJsonValue in jsonArray) {\n            outputJsonValue.asObject().let {\n                val output = TransactionOutputInfo(\n                        mine = it.get(\"mine\").asBoolean(),\n                        changeOutput = it.get(\"changeOutput\").asBoolean(),\n                        value = it.get(\"value\").asLong(),\n                        address = if (it.get(\"address\")?.isNull == false) it.get(\"address\")?.asString() else null,\n                        memo = if (it.get(\"memo\")?.isNull == false) it.get(\"memo\")?.asString() else null,\n                        pluginId = if (it.get(\"pluginId\")?.isNull == false) it.get(\"pluginId\")?.asString()?.toByte() else null,\n                        pluginDataString = if (it.get(\"pluginDataString\")?.isNull == false) it.get(\"pluginDataString\")?.asString() else null)\n\n                outputs.add(output)\n            }\n        }\n        return outputs\n    }\n\n    private fun outputsToJson(outputs: List<TransactionOutputInfo>): JsonArray {\n        val jsonArray = JsonArray()\n        outputs.forEach {\n            val outputObj = JsonObject()\n            outputObj.add(\"mine\", it.mine)\n            outputObj.add(\"changeOutput\", it.changeOutput)\n            outputObj.add(\"value\", it.value)\n            outputObj.add(\"address\", it.address)\n            outputObj.add(\"pluginId\", it.pluginId?.toString())\n            outputObj.add(\"pluginDataString\", it.pluginDataString)\n            outputObj.add(\"memo\", it.memo)\n            jsonArray.add(outputObj)\n        }\n        return jsonArray\n    }\n\n    private fun inputsToJson(inputs: List<TransactionInputInfo>): JsonArray {\n        val jsonArray = JsonArray()\n        inputs.forEach { input ->\n            val inputObj = JsonObject()\n            inputObj.add(\"mine\", input.mine)\n            input.value?.let { inputObj.add(\"value\", it) }\n            inputObj.add(\"address\", input.address)\n            jsonArray.add(inputObj)\n        }\n        return jsonArray\n    }\n\n    protected open fun asJsonObject(): JsonObject {\n        val jsonObject = JsonObject()\n\n        jsonObject.add(\"uid\", uid)\n        jsonObject.add(\"transactionHash\", transactionHash)\n        jsonObject.add(\"transactionIndex\", transactionIndex)\n        jsonObject.add(\"inputs\", inputsToJson(inputs))\n        jsonObject.add(\"outputs\", outputsToJson(outputs))\n        jsonObject.add(\"amount\", amount)\n        jsonObject.add(\"type\", type.value)\n        fee?.let { jsonObject.add(\"fee\", it) }\n        blockHeight?.let { jsonObject.add(\"blockHeight\", it) }\n        jsonObject.add(\"timestamp\", timestamp)\n        jsonObject.add(\"status\", status.code)\n        conflictingTxHash?.let { jsonObject.add(\"conflictingTxHash\", it) }\n\n        return jsonObject\n    }\n\n    fun serialize(): String {\n        return asJsonObject().toString()\n    }\n\n}\n\nenum class TransactionStatus(val code: Int) {\n    NEW(Transaction.Status.NEW),\n    RELAYED(Transaction.Status.RELAYED),\n    INVALID(Transaction.Status.INVALID);\n\n    companion object {\n        private val values = values()\n\n        fun getByCode(code: Int): TransactionStatus? = values.firstOrNull { it.code == code }\n    }\n}\n\ndata class TransactionInputInfo(val mine: Boolean, val value: Long? = null, val address: String? = null)\n\ndata class TransactionOutputInfo(\n    val mine: Boolean,\n    val changeOutput: Boolean,\n    val value: Long,\n    val address: String? = null,\n    val memo: String?,\n    val pluginId: Byte? = null,\n    val pluginData: IPluginOutputData? = null,\n    internal val pluginDataString: String? = null,\n)\n\ndata class BlockInfo(\n        val headerHash: String,\n        val height: Int,\n        val timestamp: Long\n)\n\ndata class BalanceInfo(val spendable: Long, val unspendableTimeLocked: Long, val unspendableNotRelayed: Long)\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/TransactionInput.kt",
    "content": "package io.horizontalsystems.bitcoincore.models\n\nimport androidx.room.Entity\nimport androidx.room.ForeignKey\nimport androidx.room.TypeConverters\nimport io.horizontalsystems.bitcoincore.storage.WitnessConverter\n\n/**\n * Transaction input\n *\n *  Size        Field                Description\n *  ===         =====                ===========\n *  32 bytes    OutputHash           Double SHA-256 hash of the transaction containing the output to be used by this input\n *  4 bytes     OutputIndex          Index of the output within the transaction\n *  VarInt      InputScriptLength    Script length\n *  Variable    InputScript          Script\n *  4 bytes     InputSeqNumber       Input sequence number (irrelevant unless transaction LockTime is non-zero)\n *\n *  Note: In order to enable nLockTime and disable Replace-By-Fee, nSequenceNumber must be set to 0xfffffffe (BIP-125)\n *\n */\n\n@Entity(\n    primaryKeys = [\"previousOutputTxHash\", \"previousOutputIndex\", \"sequence\"],\n    foreignKeys = [ForeignKey(\n        entity = Transaction::class,\n        parentColumns = [\"hash\"],\n        childColumns = [\"transactionHash\"],\n        onUpdate = ForeignKey.CASCADE,\n        onDelete = ForeignKey.CASCADE,\n        deferred = true\n    )\n    ]\n)\n\nclass TransactionInput(\n    val previousOutputTxHash: ByteArray,\n    val previousOutputIndex: Long,\n    var sigScript: ByteArray = byteArrayOf(),\n    var sequence: Long\n) {\n\n    var transactionHash = byteArrayOf()\n    var lockingScriptPayload: ByteArray? = null\n    var address: String? = \"\"\n\n    @TypeConverters(WitnessConverter::class)\n    var witness: List<ByteArray> = listOf()\n}\n\nval TransactionInput.rbfEnabled: Boolean\n    get() = sequence < 0xfffffffe\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/TransactionMetadata.kt",
    "content": "package io.horizontalsystems.bitcoincore.models\n\nimport androidx.room.Entity\nimport androidx.room.PrimaryKey\nimport androidx.room.TypeConverter\n\n@Entity\nclass TransactionMetadata(\n    @PrimaryKey\n    val transactionHash: ByteArray\n) {\n    var amount: Long = 0\n    var type: TransactionType = TransactionType.Incoming\n    var fee: Long? = null\n}\n\nenum class TransactionType(val value: Int) {\n    Incoming(1),\n    Outgoing(2),\n    SentToSelf(3);\n\n    companion object {\n        fun fromValue(value: Int) = values().find { it.value == value }\n    }\n}\n\nenum class TransactionFilterType(val types: List<TransactionType>) {\n    Incoming(listOf(TransactionType.Incoming, TransactionType.SentToSelf)),\n    Outgoing(listOf(TransactionType.Outgoing, TransactionType.SentToSelf))\n}\n\nclass TransactionTypeConverter {\n    @TypeConverter\n    fun fromInt(value: Int?): TransactionType? {\n        return value?.let { TransactionType.fromValue(it) }\n    }\n\n    @TypeConverter\n    fun transactionTypeToInt(transactionTypeToInt: TransactionType?): Int? {\n        return transactionTypeToInt?.value\n    }\n}"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/TransactionOutput.kt",
    "content": "package io.horizontalsystems.bitcoincore.models\n\nimport androidx.room.Entity\nimport androidx.room.ForeignKey\nimport androidx.room.Ignore\nimport androidx.room.TypeConverter\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\n\n/**\n * Transaction output\n *\n *  Size        Field                Description\n *  ====        =====                ===========\n *  8 bytes     OutputValue          Value expressed in Satoshis (0.00000001 BTC)\n *  VarInt      OutputScriptLength   Script length\n *  Variable    OutputScript         Script\n */\n\n@Entity(\n    primaryKeys = [\"transactionHash\", \"index\"],\n    foreignKeys = [\n        ForeignKey(\n            entity = PublicKey::class,\n            parentColumns = [\"path\"],\n            childColumns = [\"publicKeyPath\"],\n            onUpdate = ForeignKey.SET_NULL,\n            onDelete = ForeignKey.SET_NULL,\n            deferred = true\n        ),\n        ForeignKey(\n            entity = Transaction::class,\n            parentColumns = [\"hash\"],\n            childColumns = [\"transactionHash\"],\n            onDelete = ForeignKey.CASCADE,\n            onUpdate = ForeignKey.CASCADE,\n            deferred = true\n        )\n    ]\n)\n\nclass TransactionOutput() {\n\n    var value: Long = 0\n    var lockingScript: ByteArray = byteArrayOf()\n    var redeemScript: ByteArray? = null\n    var index: Int = 0\n\n    var transactionHash = byteArrayOf()\n    var publicKeyPath: String? = null\n    var changeOutput: Boolean = false\n    var scriptType: ScriptType = ScriptType.UNKNOWN\n    var lockingScriptPayload: ByteArray? = null\n    var address: String? = null\n    var failedToSpend = false\n\n    var pluginId: Byte? = null\n    var pluginData: String? = null\n\n    @Ignore\n    var signatureScriptFunction: ((List<ByteArray>) -> ByteArray)? = null\n\n    constructor(\n        value: Long,\n        index: Int,\n        script: ByteArray,\n        type: ScriptType = ScriptType.UNKNOWN,\n        address: String? = null,\n        lockingScriptPayload: ByteArray? = null,\n        publicKey: PublicKey? = null\n    ) : this() {\n        this.value = value\n        this.lockingScript = script\n        this.index = index\n        this.scriptType = type\n        this.address = address\n        this.lockingScriptPayload = lockingScriptPayload\n        publicKey?.let { setPublicKey(it) }\n    }\n\n    constructor(output: TransactionOutput) : this() {\n         value = output.value\n         lockingScript = output.lockingScript\n         redeemScript = output.redeemScript\n         index = output.index\n         transactionHash = output.transactionHash\n         publicKeyPath = output.publicKeyPath\n         changeOutput = output.changeOutput\n         scriptType = output.scriptType\n         lockingScriptPayload = output.lockingScriptPayload\n         address = output.address\n         failedToSpend = output.failedToSpend\n         pluginId = output.pluginId\n         pluginData = output.pluginData\n    }\n\n    fun setPublicKey(publicKey: PublicKey) {\n        this.publicKeyPath = publicKey.path\n        this.changeOutput = !publicKey.external\n    }\n}\n\nclass ScriptTypeConverter {\n    @TypeConverter\n    fun fromInt(value: Int?): ScriptType? {\n        return value?.let { ScriptType.fromValue(it) }\n    }\n\n\n    @TypeConverter\n    fun scriptTypeToInt(scriptType: ScriptType?): Int? {\n        return scriptType?.value\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/UsedAddress.kt",
    "content": "package io.horizontalsystems.bitcoincore.models\n\ndata class UsedAddress(\n    val index: Int,\n    val address: String\n)\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/models/WatchAddressPublicKey.kt",
    "content": "package io.horizontalsystems.bitcoincore.models\n\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\n\nclass WatchAddressPublicKey(\n    data: ByteArray,\n    scriptType: ScriptType\n) : PublicKey() {\n    init {\n        path = \"WatchAddressPublicKey\"\n        when (scriptType) {\n            ScriptType.P2PKH,\n            ScriptType.P2WPKH -> {\n                publicKeyHash = data\n            }\n\n            ScriptType.P2SH,\n            ScriptType.P2WSH,\n            ScriptType.P2WPKHSH -> {\n                scriptHashP2WPKH = data\n            }\n\n            ScriptType.P2TR -> {\n                convertedForP2TR = data\n            }\n\n            ScriptType.P2PK -> {\n                publicKey = data\n            }\n\n            ScriptType.NULL_DATA,\n            ScriptType.UNKNOWN -> {\n                Unit // Not supported yet\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/Network.kt",
    "content": "package io.horizontalsystems.bitcoincore.network\n\nimport io.horizontalsystems.bitcoincore.models.Checkpoint\nimport io.horizontalsystems.bitcoincore.transactions.scripts.Sighash\nimport io.horizontalsystems.bitcoincore.utils.HashUtils\n\nabstract class Network {\n\n    open val protocolVersion = 70014\n    open val syncableFromApi = true\n    val bloomFilterVersion = 70000\n    open val noBloomVersion = 70011\n    val networkServices = 0L\n    val serviceFullNode = 1L\n    val serviceBloomFilter = 4L\n    val zeroHashBytes = HashUtils.toBytesAsLE(\"0000000000000000000000000000000000000000000000000000000000000000\")\n\n    abstract val blockchairChainId: String\n\n    abstract val maxBlockSize: Int\n    abstract val dustRelayTxFee: Int\n\n    abstract var port: Int\n    abstract var magic: Long\n    abstract var bip32HeaderPub: Int\n    abstract var bip32HeaderPriv: Int\n    abstract var coinType: Int\n    abstract var dnsSeeds: List<String>\n    abstract var addressVersion: Int\n    abstract var addressSegwitHrp: String\n    abstract var addressScriptVersion: Int\n\n    open val bip44Checkpoint = Checkpoint(\"${javaClass.simpleName}-bip44.checkpoint\")\n    open val lastCheckpoint = Checkpoint(\"${javaClass.simpleName}.checkpoint\")\n\n    open val sigHashForked: Boolean = false\n    open val sigHashValue = Sighash.ALL\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/AddrMessage.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.messages\n\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.models.NetworkAddress\n\nclass AddrMessage(var addresses: List<NetworkAddress>) : IMessage {\n    override fun toString(): String {\n        return \"AddrMessage(count=${addresses.size})\"\n    }\n}\n\nclass AddrMessageParser : IMessageParser {\n    override val command = \"addr\"\n\n    override fun parseMessage(input: BitcoinInputMarkable): IMessage {\n        val count = input.readVarInt() // do not store count\n\n        val addresses = List(count.toInt()) {\n            NetworkAddress(input, false)\n        }\n\n        return AddrMessage(addresses)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/FilterLoadMessage.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.messages\n\nimport io.horizontalsystems.bitcoincore.crypto.BloomFilter\n\nclass FilterLoadMessage(bloomFilter: BloomFilter) : IMessage {\n    var filter: BloomFilter = bloomFilter\n\n    override fun toString(): String {\n        return \"FilterLoadMessage($filter)\"\n    }\n}\n\nclass FilterLoadMessageSerializer : IMessageSerializer {\n    override val command: String = \"filterload\"\n\n    override fun serialize(message: IMessage): ByteArray? {\n        if (message !is FilterLoadMessage) {\n            return null\n        }\n\n        return message.filter.toByteArray()\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/GetBlocksMessage.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.messages\n\nimport io.horizontalsystems.bitcoincore.extensions.toReversedHex\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\n\nclass GetBlocksMessage(var hashes: List<ByteArray>, var version: Int, var hashStop: ByteArray) : IMessage {\n    override fun toString(): String {\n        val list = hashes\n                .take(10)\n                .map { hash -> hash.toReversedHex() }\n                .joinToString()\n\n        return (\"GetBlocksMessage(\" + hashes.size + \": [\" + list + \"], hashStop=\" + hashStop.toReversedHex() + \")\")\n    }\n}\n\nclass GetBlocksMessageSerializer : IMessageSerializer {\n    override val command: String = \"getblocks\"\n\n    override fun serialize(message: IMessage): ByteArray? {\n        if (message !is GetBlocksMessage) {\n            return null\n        }\n\n        val output = BitcoinOutput()\n        output.writeInt(message.version).writeVarInt(message.hashes.size.toLong())\n        message.hashes.forEach {\n            output.write(it)\n        }\n        output.write(message.hashStop)\n        return output.toByteArray()\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/GetDataMessage.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.messages\n\nimport io.horizontalsystems.bitcoincore.extensions.toReversedHex\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\nimport io.horizontalsystems.bitcoincore.models.InventoryItem\n\nclass GetDataMessage(var inventory: List<InventoryItem>) : IMessage {\n    override fun toString(): String {\n        val invList = inventory.take(10)\n                .map { inv -> \"${inv.type} :${inv.hash.toReversedHex()}\" }\n                .toTypedArray()\n                .joinToString()\n\n        return \"GetDataMessage(${inventory.size}: [$invList])\"\n    }\n}\n\nclass GetDataMessageParser : IMessageParser {\n    override val command: String = \"getdata\"\n\n    override fun parseMessage(input: BitcoinInputMarkable): IMessage {\n        val count = input.readVarInt() // do not store count\n        val inventory = List(count.toInt()) {\n            InventoryItem(input)\n        }\n        return GetDataMessage(inventory)\n    }\n}\n\nclass GetDataMessageSerializer : IMessageSerializer {\n    override val command: String = \"getdata\"\n\n    override fun serialize(message: IMessage): ByteArray? {\n        if (message !is GetDataMessage) {\n            return null\n        }\n\n        val output = BitcoinOutput().writeVarInt(message.inventory.size.toLong())\n        message.inventory.forEach {\n            output.write(it.toByteArray())\n        }\n\n        return output.toByteArray()\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/GetHeadersMessage.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.messages\n\nimport io.horizontalsystems.bitcoincore.extensions.toReversedHex\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\n\nclass GetHeadersMessage(var version: Int, var hashes: List<ByteArray>, var hashStop: ByteArray) : IMessage {\n    override fun toString(): String {\n        return (\"GetHeadersMessage(${hashes.size}: hashStop=${hashStop.toReversedHex()})\")\n    }\n}\n\nclass GetHeadersMessageSerializer : IMessageSerializer {\n    override val command: String = \"getheaders\"\n\n    override fun serialize(message: IMessage): ByteArray? {\n        if (message !is GetHeadersMessage) {\n            return null\n        }\n\n        val output = BitcoinOutput().also {\n            it.writeInt(message.version)\n            it.writeVarInt(message.hashes.size.toLong())\n        }\n\n        message.hashes.forEach {\n            output.write(it)\n        }\n\n        output.write(message.hashStop)\n\n        return output.toByteArray()\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/HeadersMessage.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.messages\n\nimport io.horizontalsystems.bitcoincore.core.IHasher\nimport io.horizontalsystems.bitcoincore.extensions.toReversedHex\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\nimport io.horizontalsystems.bitcoincore.storage.BlockHeader\n\nclass HeadersMessage(val headers: Array<BlockHeader>) : IMessage {\n    override fun toString(): String {\n        return \"HeadersMessage(${headers.size}:[${headers.joinToString { it.hash.toReversedHex() }}])\"\n    }\n}\n\nclass HeadersMessageParser(private val hasher: IHasher) : IMessageParser {\n    override val command: String = \"headers\"\n\n    override fun parseMessage(input: BitcoinInputMarkable): IMessage {\n        val count = input.readVarInt().toInt()\n\n        val headers = Array(count) {\n            val version = input.readInt()\n            val prevHash = input.readBytes(32)\n            val merkleHash = input.readBytes(32)\n            val timestamp = input.readUnsignedInt()\n            val bits = input.readUnsignedInt()\n            val nonce = input.readUnsignedInt()\n            input.readVarInt() // tx count always zero\n\n            val headerPayload = BitcoinOutput().also {\n                it.writeInt(version)\n                it.write(prevHash)\n                it.write(merkleHash)\n                it.writeUnsignedInt(timestamp)\n                it.writeUnsignedInt(bits)\n                it.writeUnsignedInt(nonce)\n            }\n\n            BlockHeader(version, prevHash, merkleHash, timestamp, bits, nonce, hasher.hash(headerPayload.toByteArray()))\n        }\n\n        return HeadersMessage(headers)\n    }\n}\n\nclass HeadersMessageSerializer : IMessageSerializer {\n    override val command: String = \"headers\"\n\n    override fun serialize(message: IMessage): ByteArray? {\n        if (message !is HeadersMessage) {\n            return null\n        }\n\n        val output = BitcoinOutput().also {\n            it.writeInt(message.headers.size)\n        }\n\n        message.headers.forEach {\n            output.writeInt(it.version)\n            output.write(it.previousBlockHeaderHash)\n            output.write(it.merkleRoot)\n            output.writeUnsignedInt(it.timestamp)\n            output.writeUnsignedInt(it.bits)\n            output.writeUnsignedInt(it.nonce)\n        }\n\n        return output.toByteArray()\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/IMessage.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.messages\n\nimport io.horizontalsystems.bitcoincore.exceptions.BitcoinException\nimport io.horizontalsystems.bitcoincore.io.BitcoinInput\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\nimport io.horizontalsystems.bitcoincore.utils.HashUtils\nimport java.io.IOException\nimport java.nio.charset.StandardCharsets\nimport java.util.*\n\ninterface IMessage\n\ninterface IMessageParser {\n    val command: String\n    fun parseMessage(input: BitcoinInputMarkable): IMessage\n}\n\ninterface IMessageSerializer {\n    val command: String\n    fun serialize(message: IMessage): ByteArray?\n}\n\nclass NetworkMessageParser(private val magic: Long) {\n    private var messageParsers = hashMapOf<String, IMessageParser>()\n\n    /**\n     * Parse stream as message.\n     */\n    @Throws(IOException::class)\n    fun parseMessage(input: BitcoinInput): IMessage {\n        val magic = input.readUnsignedInt()\n        if (magic != this.magic) {\n            throw BitcoinException(\"Bad magic. (local) ${this.magic}!=$magic\")\n        }\n\n        val command = getCommandFrom(input.readBytes(12))\n        val payloadLength = input.readInt()\n        val expectedChecksum = ByteArray(4)\n        input.readFully(expectedChecksum)\n        val payload = ByteArray(payloadLength)\n        input.readFully(payload)\n\n        // check:\n        val actualChecksum = getCheckSum(payload)\n        if (!expectedChecksum.contentEquals(actualChecksum)) {\n            throw BitcoinException(\"Checksum failed.\")\n        }\n\n        try {\n            BitcoinInputMarkable(payload).use {\n                return messageParsers[command]?.parseMessage(it) ?: UnknownMessage(command)\n            }\n        } catch (e: Exception) {\n            throw RuntimeException(e)\n        }\n    }\n\n    fun add(messageParser: IMessageParser) {\n        messageParsers[messageParser.command] = messageParser\n    }\n\n    private fun getCommandFrom(cmd: ByteArray): String {\n        var n = cmd.size - 1\n        while (n >= 0) {\n            if (cmd[n].toInt() == 0) {\n                n--\n            } else {\n                break\n            }\n        }\n        if (n <= 0) {\n            throw BitcoinException(\"Bad command bytes.\")\n        }\n        val b = cmd.copyOfRange(0, n + 1)\n        return String(b, StandardCharsets.UTF_8)\n    }\n\n    private fun getCheckSum(payload: ByteArray): ByteArray {\n        val hash = HashUtils.doubleSha256(payload)\n        return hash.copyOfRange(0, 4)\n    }\n}\n\nclass NetworkMessageSerializer(private val magic: Long) {\n    private var messageSerializers = mutableListOf<IMessageSerializer>()\n\n    fun serialize(msg: IMessage): ByteArray {\n        var payload: ByteArray? = null\n        var serializer: IMessageSerializer? = null\n\n        for (item in messageSerializers) {\n            payload = item.serialize(msg)\n\n            if (payload != null) {\n                serializer = item\n                break\n            }\n        }\n\n        if (payload == null || serializer == null) {\n            throw NoSerializer(msg)\n        }\n\n        return BitcoinOutput()\n                .writeInt32(magic)                          // magic\n                .write(getCommandBytes(serializer.command)) // command: char[12]\n                .writeInt(payload.size)         // length: uint32_t\n                .write(getCheckSum(payload))    // checksum: uint32_t\n                .write(payload)                 // payload:\n                .toByteArray()\n    }\n\n    fun add(messageSerializer: IMessageSerializer) {\n        messageSerializers.add(messageSerializer)\n    }\n\n    private fun getCommandBytes(cmd: String): ByteArray {\n        val cmdBytes = cmd.toByteArray()\n        if (cmdBytes.isEmpty() || cmdBytes.size > 12) {\n            throw IllegalArgumentException(\"Bad command: $cmd\")\n        }\n        val buffer = ByteArray(12)\n        System.arraycopy(cmdBytes, 0, buffer, 0, cmdBytes.size)\n        return buffer\n    }\n\n    private fun getCheckSum(payload: ByteArray): ByteArray {\n        val hash = HashUtils.doubleSha256(payload)\n        return Arrays.copyOfRange(hash, 0, 4)\n    }\n}\n\nclass NoSerializer(message: IMessage) : Exception(\"Cannot serialize message=$message\")\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/InvMessage.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.messages\n\nimport io.horizontalsystems.bitcoincore.extensions.toReversedHex\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\nimport io.horizontalsystems.bitcoincore.models.InventoryItem\n\nclass InvMessage : IMessage {\n    var inventory: List<InventoryItem>\n\n    constructor(type: Int, hash: ByteArray) {\n        val inv = InventoryItem()\n        inv.type = type\n        inv.hash = hash\n        inventory = listOf(inv)\n    }\n\n    constructor(inventory: List<InventoryItem>) {\n        this.inventory = inventory\n    }\n\n    override fun toString(): String {\n        val invList = inventory.take(10)\n                .map { inv -> inv.type.toString() + \":\" + inv.hash.toReversedHex() }\n                .toTypedArray()\n                .joinToString()\n\n        return (\"InvMessage(\" + inventory.size + \": [\" + invList + \"])\")\n    }\n}\n\nclass InvMessageParser : IMessageParser {\n    override val command: String = \"inv\"\n\n    override fun parseMessage(input: BitcoinInputMarkable): IMessage {\n        val count = input.readVarInt() // do not store count\n        val inventory = List(count.toInt()) {\n            InventoryItem(input)\n        }\n\n        return InvMessage(inventory)\n    }\n}\n\nclass InvMessageSerializer : IMessageSerializer {\n    override val command: String = \"inv\"\n\n    override fun serialize(message: IMessage): ByteArray? {\n        if (message !is InvMessage) {\n            return null\n        }\n\n        val output = BitcoinOutput()\n        output.writeVarInt(message.inventory.size.toLong())\n        message.inventory.forEach {\n            output.write(it.toByteArray())\n        }\n\n        return output.toByteArray()\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/MempoolMessage.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.messages\n\nclass MempoolMessage : IMessage {\n    override fun toString(): String {\n        return \"MempoolMessage()\"\n    }\n}\n\nclass MempoolMessageSerializer : IMessageSerializer {\n    override val command: String = \"mempool\"\n\n    override fun serialize(message: IMessage): ByteArray? {\n        if (message !is MempoolMessage) {\n            return null\n        }\n\n        return ByteArray(0)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/MerkleBlockMessage.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.messages\n\nimport io.horizontalsystems.bitcoincore.extensions.toReversedHex\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.serializers.BlockHeaderParser\nimport io.horizontalsystems.bitcoincore.storage.BlockHeader\n\n/**\n * MerkleBlock Message\n *\n *  Size        Field           Description\n *  ====        =====           ===========\n *  80 bytes    Header          Consists of 6 fields that are hashed to calculate the block hash\n *  VarInt      hashCount       Number of hashes\n *  Variable    hashes          Hashes in depth-first order\n *  VarInt      flagsCount      Number of bytes of flag bits\n *  Variable    flagsBits       Flag bits packed 8 per byte, least significant bit first\n */\nclass MerkleBlockMessage(\n        var header: BlockHeader,\n        var txCount: Int,\n        var hashCount: Int,\n        var hashes: List<ByteArray>,\n        var flagsCount: Int,\n        var flags: ByteArray) : IMessage {\n\n    private val blockHash: String by lazy {\n        header.hash.toReversedHex()\n    }\n\n    override fun toString(): String {\n        return \"MerkleBlockMessage(blockHash=$blockHash, hashesSize=${hashes.size})\"\n    }\n}\n\nclass MerkleBlockMessageParser(private val blockHeaderParser: BlockHeaderParser) : IMessageParser {\n    override val command = \"merkleblock\"\n\n    override fun parseMessage(input: BitcoinInputMarkable): IMessage {\n        val header = blockHeaderParser.parse(input)\n        val txCount = input.readInt()\n\n        val hashCount = input.readVarInt().toInt()\n        val hashes: MutableList<ByteArray> = mutableListOf()\n        repeat(hashCount) {\n            hashes.add(input.readBytes(32))\n        }\n\n        val flagsCount = input.readVarInt().toInt()\n        val flags = input.readBytes(flagsCount)\n\n        return MerkleBlockMessage(header, txCount, hashCount, hashes, flagsCount, flags)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/PingMessage.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.messages\n\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\n\nclass PingMessage(val nonce: Long) : IMessage {\n    override fun toString(): String {\n        return \"PingMessage(nonce=$nonce)\"\n    }\n}\n\nclass PingMessageParser : IMessageParser {\n    override val command: String = \"ping\"\n\n    override fun parseMessage(input: BitcoinInputMarkable): IMessage {\n        return PingMessage(input.readLong())\n    }\n}\n\nclass PingMessageSerializer : IMessageSerializer {\n    override val command: String = \"ping\"\n\n    override fun serialize(message: IMessage): ByteArray? {\n        if (message !is PingMessage) {\n            return null\n        }\n\n        return BitcoinOutput()\n                .writeLong(message.nonce)\n                .toByteArray()\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/PongMessage.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.messages\n\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\n\nclass PongMessage(val nonce: Long) : IMessage {\n    override fun toString(): String {\n        return \"PongMessage(nonce=$nonce)\"\n    }\n}\n\nclass PongMessageParser : IMessageParser {\n    override val command: String = \"pong\"\n\n    override fun parseMessage(input: BitcoinInputMarkable): IMessage {\n        return PongMessage(input.readLong())\n    }\n}\n\nclass PongMessageSerializer : IMessageSerializer {\n    override val command: String = \"pong\"\n\n    override fun serialize(message: IMessage): ByteArray? {\n        if (message !is PongMessage) {\n            return null\n        }\n\n        return BitcoinOutput()\n                .writeLong(message.nonce)\n                .toByteArray()\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/RejectMessage.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.messages\n\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\n\nclass RejectMessage(private val responseToMessage: String,\n                    private val rejectCode: Byte,\n                    private val reason: String) : IMessage {\n\n    override fun toString(): String {\n        return \"RejectMessage(responseToMessage=$responseToMessage, rejectCode: $rejectCode, reason: $reason)\"\n    }\n}\n\nclass RejectMessageParser : IMessageParser {\n    override val command = \"reject\"\n\n    override fun parseMessage(input: BitcoinInputMarkable): IMessage {\n        val responseToMessage = input.readString()\n        val rejectCode = input.readByte()\n        val reason = input.readString()\n\n        return RejectMessage(responseToMessage, rejectCode, reason)\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/TransactionMessage.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.messages\n\nimport io.horizontalsystems.bitcoincore.extensions.toReversedHex\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.serializers.TransactionSerializer\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\n\nclass TransactionMessage(var transaction: FullTransaction, val size: Int) : IMessage {\n    override fun toString(): String {\n        return \"TransactionMessage(${transaction.header.hash.toReversedHex()})\"\n    }\n}\n\nclass TransactionMessageParser : IMessageParser {\n    override val command: String = \"tx\"\n\n    override fun parseMessage(input: BitcoinInputMarkable): IMessage {\n        val transaction = TransactionSerializer.deserialize(input)\n        return TransactionMessage(transaction, input.count)\n    }\n}\n\nclass TransactionMessageSerializer : IMessageSerializer {\n    override val command: String = \"tx\"\n\n    override fun serialize(message: IMessage): ByteArray? {\n        if (message !is TransactionMessage) {\n            return null\n        }\n\n        return TransactionSerializer.serialize(message.transaction)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/UnknownMessage.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.messages\n\nclass UnknownMessage(val command: String) : IMessage {\n    override fun toString(): String {\n        return \"UnknownMessage(command=$command)\"\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/VerAckMessage.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.messages\n\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\n\nclass VerAckMessage : IMessage {\n    override fun toString(): String {\n        return \"VerAckMessage()\"\n    }\n}\n\nclass VerAckMessageParser : IMessageParser {\n    override val command: String = \"verack\"\n\n    override fun parseMessage(input: BitcoinInputMarkable): IMessage {\n        return VerAckMessage()\n    }\n}\n\nclass VerAckMessageSerializer : IMessageSerializer {\n    override val command: String = \"verack\"\n\n    override fun serialize(message: IMessage): ByteArray? {\n        if (message !is VerAckMessage) {\n            return null\n        }\n\n        return ByteArray(0)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/messages/VersionMessage.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.messages\n\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\nimport io.horizontalsystems.bitcoincore.models.NetworkAddress\nimport io.horizontalsystems.bitcoincore.network.Network\nimport io.horizontalsystems.bitcoincore.utils.NetworkUtils\nimport java.net.InetAddress\n\nclass VersionMessage(val protocolVersion: Int, val services: Long, val timestamp: Long, val recipientAddress: NetworkAddress) : IMessage {\n\n    lateinit var senderAddress: NetworkAddress\n\n    // Random value to identify sending node\n    var nonce = 0L\n\n    // User-Agent as defined in <a href=\"https://github.com/bitcoin/bips/blob/master/bip-0014.mediawiki\">BIP 14</a>.\n    var subVersion = \"/BitcoinKit:0.1.0/\"\n\n    // How many blocks are in the chain, according to the other side.\n    var lastBlock: Int = 0\n\n    // Whether or not to relay tx invs before a filter is received.\n    // See <a href=\"https://github.com/bitcoin/bips/blob/master/bip-0037.mediawiki#extensions-to-existing-messages\">BIP 37</a>.\n    var relay = false\n\n    constructor(bestBlock: Int, recipientAddr: InetAddress, network: Network) : this(network.protocolVersion, network.networkServices, System.currentTimeMillis() / 1000, NetworkAddress(recipientAddr, network)) {\n        lastBlock = bestBlock\n        senderAddress = NetworkAddress(NetworkUtils.getLocalInetAddress(), network)\n        nonce = (Math.random() * java.lang.Long.MAX_VALUE).toLong() //Random node id generated at startup.\n    }\n\n    fun hasBlockChain(network: Network): Boolean {\n        return (services and network.serviceFullNode) == network.serviceFullNode\n    }\n\n    // see https://github.com/bitcoin/bips/blob/master/bip-0111.mediawiki\n    fun supportsBloomFilter(network: Network): Boolean {\n        return when {\n            protocolVersion >= network.noBloomVersion -> {\n                services and network.serviceBloomFilter == network.serviceBloomFilter\n            }\n            else -> protocolVersion >= network.bloomFilterVersion\n        }\n    }\n\n    override fun toString(): String {\n        return (\"VersionMessage(lastBlock=$lastBlock, protocol=$protocolVersion, services=$services, timestamp=$timestamp), userAgent=$subVersion\")\n    }\n}\n\nclass VersionMessageParser : IMessageParser {\n    override val command: String = \"version\"\n\n    override fun parseMessage(input: BitcoinInputMarkable): IMessage {\n        val protocolVersion = input.readInt()\n        val services = input.readLong()\n        val timestamp = input.readLong()\n        val recipientAddress = NetworkAddress.parse(input, true)\n\n        val versionMessage = VersionMessage(protocolVersion, services, timestamp, recipientAddress)\n\n        if (protocolVersion >= 106) {\n            versionMessage.senderAddress = NetworkAddress.parse(input, true)\n            versionMessage.nonce = input.readLong()\n            versionMessage.subVersion = input.readString()\n            versionMessage.lastBlock = input.readInt()\n            if (protocolVersion >= 70001) {\n                versionMessage.relay = input.readByte().toInt() != 0\n            }\n        }\n\n        return versionMessage\n    }\n}\n\nclass VersionMessageSerializer : IMessageSerializer {\n    override val command: String = \"version\"\n\n    override fun serialize(message: IMessage): ByteArray? {\n        if (message !is VersionMessage) {\n            return null\n        }\n\n        val output = BitcoinOutput()\n        output.writeInt(message.protocolVersion)\n                .writeLong(message.services)\n                .writeLong(message.timestamp)\n                .write(message.recipientAddress.toByteArray(true))\n        if (message.protocolVersion >= 106) {\n            output.write(message.senderAddress.toByteArray(true))\n                    .writeLong(message.nonce)\n                    .writeString(message.subVersion)\n                    .writeInt(message.lastBlock)\n            if (message.protocolVersion >= 70001) {\n                output.writeByte(1)\n            }\n        }\n\n        return output.toByteArray()\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/peer/IInventoryItemsHandler.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.peer\n\nimport io.horizontalsystems.bitcoincore.models.InventoryItem\n\ninterface IInventoryItemsHandler {\n    fun handleInventoryItems(peer: Peer, inventoryItems: List<InventoryItem>)\n}\n\nclass InventoryItemsHandlerChain : IInventoryItemsHandler {\n\n    private val concreteHandlers = mutableListOf<IInventoryItemsHandler>()\n\n    override fun handleInventoryItems(peer: Peer, inventoryItems: List<InventoryItem>) {\n        concreteHandlers.forEach {\n            it.handleInventoryItems(peer, inventoryItems)\n        }\n    }\n\n    fun addHandler(h: IInventoryItemsHandler) {\n        concreteHandlers.add(h)\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/peer/IPeerTaskHandler.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.peer\n\nimport io.horizontalsystems.bitcoincore.network.peer.task.PeerTask\n\ninterface IPeerTaskHandler {\n    fun handleCompletedTask(peer: Peer, task: PeerTask): Boolean\n}\n\nclass PeerTaskHandlerChain : IPeerTaskHandler {\n\n    private val concreteHandlers = mutableListOf<IPeerTaskHandler>()\n\n    override fun handleCompletedTask(peer: Peer, task: PeerTask): Boolean {\n        return concreteHandlers.any {\n            it.handleCompletedTask(peer, task)\n        }\n    }\n\n    fun addHandler(h: IPeerTaskHandler) {\n        concreteHandlers.add(h)\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/peer/MempoolTransactions.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.peer\n\nimport io.horizontalsystems.bitcoincore.models.InventoryItem\nimport io.horizontalsystems.bitcoincore.network.peer.task.PeerTask\nimport io.horizontalsystems.bitcoincore.network.peer.task.RequestTransactionsTask\nimport io.horizontalsystems.bitcoincore.transactions.TransactionSender\nimport io.horizontalsystems.bitcoincore.transactions.TransactionSyncer\n\nclass MempoolTransactions(\n        private val transactionSyncer: TransactionSyncer,\n        private val transactionSender: TransactionSender?\n) : IPeerTaskHandler, IInventoryItemsHandler, PeerGroup.Listener {\n\n    private val requestedTransactions = hashMapOf<String, MutableList<ByteArray>>()\n\n    override fun handleCompletedTask(peer: Peer, task: PeerTask): Boolean {\n        return when (task) {\n            is RequestTransactionsTask -> {\n                transactionSyncer.handleRelayed(task.transactions)\n                removeFromRequestedTransactions(peer.host, task.transactions.map { it.header.hash })\n                transactionSender?.transactionsRelayed(task.transactions)\n                true\n            }\n            else -> false\n        }\n    }\n\n    override fun handleInventoryItems(peer: Peer, inventoryItems: List<InventoryItem>) {\n        val transactionHashes = mutableListOf<ByteArray>()\n\n        inventoryItems.forEach { item ->\n            if (item.type == InventoryItem.MSG_TX\n                    && !isTransactionRequested(item.hash)\n                    && transactionSyncer.shouldRequestTransaction(item.hash)) {\n                transactionHashes.add(item.hash)\n            }\n        }\n\n        if (transactionHashes.isNotEmpty()) {\n            peer.addTask(RequestTransactionsTask(transactionHashes))\n\n            addToRequestedTransactions(peer.host, transactionHashes)\n        }\n    }\n\n    override fun onPeerDisconnect(peer: Peer, e: Exception?) {\n        requestedTransactions.remove(peer.host)\n    }\n\n    private fun addToRequestedTransactions(peerHost: String, transactionHashes: List<ByteArray>) {\n        if (!requestedTransactions.containsKey(peerHost)) {\n            requestedTransactions[peerHost] = mutableListOf()\n        }\n\n        requestedTransactions[peerHost]?.addAll(transactionHashes)\n    }\n\n    private fun removeFromRequestedTransactions(peerHost: String, transactionHashes: List<ByteArray>) {\n        transactionHashes.forEach { transactionHash ->\n            val i = requestedTransactions[peerHost]?.indexOfFirst {\n                it.contentEquals(transactionHash)\n            }\n\n            if (i != null && i != -1) {\n                requestedTransactions[peerHost]?.removeAt(i)\n            }\n        }\n    }\n\n    private fun isTransactionRequested(hash: ByteArray): Boolean {\n        return requestedTransactions.any { (_, inventories) ->\n            inventories.any {\n                it.contentEquals(hash)\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/peer/Peer.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.peer\n\nimport io.horizontalsystems.bitcoincore.crypto.BloomFilter\nimport io.horizontalsystems.bitcoincore.network.Network\nimport io.horizontalsystems.bitcoincore.network.messages.*\nimport io.horizontalsystems.bitcoincore.network.peer.task.PeerTask\nimport java.net.InetAddress\nimport java.util.concurrent.ExecutorService\n\nclass Peer(\n        val host: String,\n        private val network: Network,\n        private val listener: Listener,\n        networkMessageParser: NetworkMessageParser,\n        networkMessageSerializer: NetworkMessageSerializer,\n        executorService: ExecutorService)\n    : PeerConnection.Listener, PeerTask.Listener, PeerTask.Requester {\n\n    interface Listener {\n        fun onConnect(peer: Peer)\n        fun onReady(peer: Peer)\n        fun onDisconnect(peer: Peer, e: Exception?)\n        fun onReceiveMessage(peer: Peer, message: IMessage)\n        fun onTaskComplete(peer: Peer, task: PeerTask)\n    }\n\n    var blockHashesSynced = false\n    var announcedLastBlockHeight = 0\n    var localBestBlockHeight = 0\n    var synced = false\n    var connected = false\n    var connectionTime: Long = 1000\n    var tasks = mutableListOf<PeerTask>()\n    var subVersion = \"\"\n\n    private var connectStartTime: Long? = null\n    private val peerConnection = PeerConnection(host, network, this, executorService, networkMessageParser, networkMessageSerializer)\n    private val timer = PeerTimer()\n\n    val ready: Boolean\n        get() = connected && tasks.isEmpty()\n\n    fun start(peerThreadPool: ExecutorService) {\n        peerThreadPool.execute(peerConnection)\n        connectStartTime = System.currentTimeMillis()\n    }\n\n    fun close(disconnectError: Exception? = null) {\n        peerConnection.close(disconnectError)\n    }\n\n    fun addTask(task: PeerTask) {\n        tasks.add(task)\n\n        task.listener = this\n        task.requester = this\n\n        task.start()\n    }\n\n    fun filterLoad(bloomFilter: BloomFilter) {\n        peerConnection.sendMessage(FilterLoadMessage(bloomFilter))\n    }\n\n    fun sendMempoolMessage() {\n        peerConnection.sendMessage(MempoolMessage())\n    }\n\n    override fun equals(other: Any?): Boolean {\n        if (other is Peer) {\n            return host == other.host\n        }\n\n        return false\n    }\n\n    override fun hashCode(): Int {\n        return host.hashCode()\n    }\n\n    //\n    // PeerConnection Listener implementations\n    //\n    override fun onTimePeriodPassed() {\n        try {\n            timer.check()\n\n            tasks.firstOrNull()?.checkTimeout()\n        } catch (e: PeerTimer.Error.Idle) {\n            val nonce = (Math.random() * Long.MAX_VALUE)\n            send(PingMessage(nonce.toLong()))\n            timer.pingSent()\n        } catch (e: PeerTimer.Error.Timeout) {\n            peerConnection.close(e)\n        }\n    }\n\n    override fun onMessage(message: IMessage) {\n        timer.restart()\n\n        if (message is VersionMessage)\n            return handleVersionMessage(message)\n        if (message is VerAckMessage)\n            return handleVerackMessage()\n\n        if (!connected) return\n\n        when (message) {\n            is PingMessage -> peerConnection.sendMessage(PongMessage(message.nonce))\n            is PongMessage -> {\n            }\n            is AddrMessage -> listener.onReceiveMessage(this, message)\n            else -> if (tasks.none { it.handleMessage(message) }) {\n                listener.onReceiveMessage(this, message)\n            }\n        }\n    }\n\n    override fun socketConnected(address: InetAddress) {\n        peerConnection.sendMessage(VersionMessage(localBestBlockHeight, address, network))\n        timer.restart()\n    }\n\n    override fun disconnected(e: Exception?) {\n        connected = false\n        listener.onDisconnect(this, e)\n    }\n\n    private fun handleVersionMessage(message: VersionMessage) = try {\n        validatePeerVersion(message)\n\n        announcedLastBlockHeight = message.lastBlock\n        subVersion = message.subVersion\n\n        peerConnection.sendMessage(VerAckMessage())\n    } catch (e: Error.UnsuitablePeerVersion) {\n        close(e)\n    }\n\n    private fun handleVerackMessage() {\n        if (connectStartTime == null) {\n            return close()\n        }\n\n        connected = true\n        connectStartTime?.let {\n            connectionTime = System.currentTimeMillis() - it\n        }\n        listener.onConnect(this)\n    }\n\n    private fun validatePeerVersion(message: VersionMessage) {\n        when {\n            message.lastBlock <= 0 -> throw Error.UnsuitablePeerVersion(\"Peer last block is not greater than 0.\")\n            message.lastBlock < localBestBlockHeight -> throw Error.UnsuitablePeerVersion(\"Peer has expired blockchain ${message.lastBlock} vs ${localBestBlockHeight}(local)\")\n            !message.hasBlockChain(network) -> throw Error.UnsuitablePeerVersion(\"Peer does not have a copy of the block chain.\")\n            !message.supportsBloomFilter(network) -> throw Error.UnsuitablePeerVersion(\"Peer does not support Bloom Filter.\")\n            message.protocolVersion < network.protocolVersion -> throw Error.UnsuitablePeerVersion(\"Peer protocol version ${message.protocolVersion} vs ${network.protocolVersion}(local)\")\n        }\n    }\n\n    //\n    // PeerTask Listener implementations\n    //\n    override fun onTaskCompleted(task: PeerTask) {\n        tasks.find { it == task }?.let { completedTask ->\n            tasks.remove(completedTask)\n            listener.onTaskComplete(this, task)\n        }\n\n        // Reset timer for the next task in list\n        tasks.firstOrNull()?.resetTimer()\n\n        if (tasks.isEmpty()) {\n            listener.onReady(this)\n        }\n    }\n\n    override fun onTaskFailed(task: PeerTask, e: Exception) {\n        peerConnection.close(e)\n    }\n\n    //\n    // PeerTask Requester implementations\n    //\n\n    override val protocolVersion = network.protocolVersion\n\n    override fun send(message: IMessage) {\n        peerConnection.sendMessage(message)\n    }\n\n    open class Error(message: String) : Exception(message) {\n        class UnsuitablePeerVersion(message: String) : Error(message)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/peer/PeerAddressManager.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.peer\n\nimport io.horizontalsystems.bitcoincore.core.IPeerAddressManager\nimport io.horizontalsystems.bitcoincore.core.IPeerAddressManagerListener\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.models.PeerAddress\nimport io.horizontalsystems.bitcoincore.network.Network\nimport java.util.logging.Logger\n\nclass PeerAddressManager(private val network: Network, private val storage: IStorage) : IPeerAddressManager {\n\n    override var listener: IPeerAddressManagerListener? = null\n\n    private val state = State()\n    private val logger = Logger.getLogger(\"PeerHostManager\")\n    private val peerDiscover = PeerDiscover(this)\n\n    override val hasFreshIps: Boolean\n        get() {\n            getLeastScoreFastestPeer()?.let { peerAddress ->\n                return peerAddress.connectionTime == null\n            }\n\n            return false\n        }\n\n    override fun getIp(): String? {\n        val peerAddress = getLeastScoreFastestPeer()\n        if (peerAddress == null) {\n            peerDiscover.lookup(network.dnsSeeds)\n            return null\n        }\n\n        state.add(peerAddress.ip)\n\n        return peerAddress.ip\n    }\n\n    override fun addIps(ips: List<String>) {\n        storage.setPeerAddresses(ips.map { PeerAddress(it, 0) })\n\n        logger.info(\"Added new addresses: ${ips.size}\")\n\n        listener?.onAddAddress()\n    }\n\n    override fun markFailed(ip: String) {\n        state.remove(ip)\n\n        storage.deletePeerAddress(ip)\n    }\n\n    override fun markSuccess(ip: String) {\n        state.remove(ip)\n    }\n\n    override fun markConnected(peer: Peer) {\n        storage.markConnected(peer.host, peer.connectionTime)\n    }\n\n    private fun getLeastScoreFastestPeer(): PeerAddress? {\n        return storage.getLeastScoreFastestPeerAddressExcludingIps(state.getUsedPeers())\n    }\n\n    private class State {\n        private var usedPeers = mutableListOf<String>()\n\n        @Synchronized\n        fun getUsedPeers(): List<String> {\n            return usedPeers.toList()\n        }\n\n        @Synchronized\n        fun add(ip: String) {\n            usedPeers.add(ip)\n        }\n\n        @Synchronized\n        fun remove(ip: String) {\n            usedPeers.removeAll { it == ip }\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/peer/PeerConnection.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.peer\n\nimport io.horizontalsystems.bitcoincore.io.BitcoinInput\nimport io.horizontalsystems.bitcoincore.network.Network\nimport io.horizontalsystems.bitcoincore.network.messages.IMessage\nimport io.horizontalsystems.bitcoincore.network.messages.NetworkMessageParser\nimport io.horizontalsystems.bitcoincore.network.messages.NetworkMessageSerializer\nimport io.horizontalsystems.bitcoincore.utils.NetworkUtils\nimport java.io.InputStream\nimport java.io.OutputStream\nimport java.net.InetAddress\nimport java.net.InetSocketAddress\nimport java.util.concurrent.ExecutorService\nimport java.util.logging.Logger\n\nclass PeerConnection(\n        private val host: String,\n        private val network: Network,\n        private val listener: Listener,\n        private val sendingExecutor: ExecutorService,\n        private val networkMessageParser: NetworkMessageParser,\n        private val networkMessageSerializer: NetworkMessageSerializer)\n    : Runnable {\n\n    interface Listener {\n        fun socketConnected(address: InetAddress)\n        fun disconnected(e: Exception? = null)\n        fun onTimePeriodPassed() // didn't find better name\n        fun onMessage(message: IMessage)\n    }\n\n    private val socket = NetworkUtils.createSocket()\n\n    private val logger = Logger.getLogger(\"Peer[$host]\")\n    private var outputStream: OutputStream? = null\n    private var inputStream: InputStream? = null\n    private var disconnectError: Exception? = null\n\n    @Volatile\n    private var isRunning = false\n\n    override fun run() {\n        isRunning = true\n        // connect:\n        try {\n            socket.connect(InetSocketAddress(host, network.port), 10000)\n            socket.soTimeout = 10000\n\n            outputStream = socket.getOutputStream()\n            val inputStream = socket.getInputStream()\n            val bitcoinInput = BitcoinInput(inputStream)\n\n            logger.info(\"Socket $host connected.\")\n\n            listener.socketConnected(socket.inetAddress)\n\n            this.inputStream = inputStream\n            // loop:\n            while (isRunning) {\n                listener.onTimePeriodPassed()\n\n                Thread.sleep(1000)\n\n                // try receive message:\n                while (isRunning && inputStream.available() > 0) {\n                    val parsedMsg = networkMessageParser.parseMessage(bitcoinInput)\n                    logger.info(\"<= $parsedMsg\")\n                    listener.onMessage(parsedMsg)\n                }\n            }\n        } catch (e: Exception) {\n            close(e)\n        } finally {\n            outputStream?.close()\n            outputStream = null\n\n            inputStream?.close()\n            inputStream = null\n\n            listener.disconnected(disconnectError)\n        }\n    }\n\n    @Synchronized\n    fun close(error: Exception?) {\n        disconnectError = error\n        isRunning = false\n    }\n\n    @Synchronized\n    fun sendMessage(message: IMessage) {\n        sendingExecutor.execute {\n            if (isRunning) {\n                try {\n                    logger.info(\"=> $message\")\n                    outputStream?.write(networkMessageSerializer.serialize(message))\n                } catch (e: Exception) {\n                    close(e)\n                }\n            }\n        }\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/peer/PeerDiscover.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.peer\n\nimport io.horizontalsystems.bitcoincore.core.IPeerAddressManager\nimport kotlinx.coroutines.GlobalScope\nimport kotlinx.coroutines.launch\nimport java.net.Inet6Address\nimport java.net.InetAddress\nimport java.net.UnknownHostException\nimport java.util.logging.Logger\n\nclass PeerDiscover(private val peerAddressManager: IPeerAddressManager) {\n\n    private val logger = Logger.getLogger(\"PeerDiscover\")\n\n    fun lookup(dnsList: List<String>) {\n        logger.info(\"Lookup peers from DNS seed...\")\n\n        // todo: launch coroutines for each dns resolve\n        GlobalScope.launch {\n            dnsList.forEach { host ->\n                try {\n                    val ips = InetAddress\n                        .getAllByName(host)\n                        .filter { it !is Inet6Address }\n                        .map { it.hostAddress }\n\n                    logger.info(\"Fetched ${ips.size} peer addresses from host: $host\")\n                    peerAddressManager.addIps(ips)\n                } catch (e: UnknownHostException) {\n                    logger.warning(\"Cannot look up host: $host\")\n                }\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/peer/PeerGroup.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.peer\n\nimport io.horizontalsystems.bitcoincore.core.IConnectionManager\nimport io.horizontalsystems.bitcoincore.core.IPeerAddressManager\nimport io.horizontalsystems.bitcoincore.core.IPeerAddressManagerListener\nimport io.horizontalsystems.bitcoincore.network.Network\nimport io.horizontalsystems.bitcoincore.network.messages.AddrMessage\nimport io.horizontalsystems.bitcoincore.network.messages.IMessage\nimport io.horizontalsystems.bitcoincore.network.messages.InvMessage\nimport io.horizontalsystems.bitcoincore.network.messages.NetworkMessageParser\nimport io.horizontalsystems.bitcoincore.network.messages.NetworkMessageSerializer\nimport io.horizontalsystems.bitcoincore.network.peer.task.PeerTask\nimport java.net.Inet6Address\nimport java.net.InetAddress\nimport java.util.concurrent.Executors\nimport java.util.logging.Logger\n\nclass PeerGroup(\n    private val hostManager: IPeerAddressManager,\n    private val network: Network,\n    private val peerManager: PeerManager,\n    peerSize: Int,\n    private val networkMessageParser: NetworkMessageParser,\n    private val networkMessageSerializer: NetworkMessageSerializer,\n    private val connectionManager: IConnectionManager,\n    private val localDownloadedBestBlockHeight: Int,\n    private val handleAddrMessage: Boolean\n) : Peer.Listener, IPeerAddressManagerListener {\n\n    interface Listener {\n        fun onStart() = Unit\n        fun onStop() = Unit\n        fun onRefresh() = Unit\n        fun onPeerCreate(peer: Peer) = Unit\n        fun onPeerConnect(peer: Peer) = Unit\n        fun onPeerDisconnect(peer: Peer, e: Exception?) = Unit\n        fun onPeerReady(peer: Peer) = Unit\n    }\n\n    var inventoryItemsHandler: IInventoryItemsHandler? = null\n    var peerTaskHandler: IPeerTaskHandler? = null\n\n    var running = false\n        private set\n\n    private val logger = Logger.getLogger(\"PeerGroup\")\n    private val peerGroupListeners = mutableListOf<Listener>()\n    private val executorService = Executors.newCachedThreadPool()\n    private val peerThreadPool = Executors.newCachedThreadPool()\n\n    private val acceptableBlockHeightDifference = 50_000\n    private var peerCountToConnectMax = 100\n    private var peerCountToConnect: Int? = null // number of peers to connect to\n    private val peerCountToHold = peerSize      // number of peers held\n    private var peerCountConnected = 0          // number of peers connected to\n\n    fun start() {\n        if (running) {\n            return\n        }\n\n        running = true\n        peerCountConnected = 0\n        peerGroupListeners.forEach { it.onStart() }\n        connectPeersIfRequired()\n    }\n\n    fun stop() {\n        running = false\n        peerManager.disconnectAll()\n        peerGroupListeners.forEach { it.onStop() }\n    }\n\n    fun refresh() {\n        if (running) {\n            peerGroupListeners.forEach { it.onRefresh() }\n        }\n    }\n\n    fun addPeerGroupListener(listener: Listener) {\n        peerGroupListeners.add(listener)\n    }\n\n    //\n    // PeerListener implementations\n    //\n    override fun onConnect(peer: Peer) {\n        hostManager.markConnected(peer)\n        peerGroupListeners.forEach { it.onPeerConnect(peer) }\n\n        peerCountToConnect?.let { disconnectSlowestPeer(it) } ?: setPeerCountToConnect(peer)\n    }\n\n    override fun onReady(peer: Peer) {\n        peerGroupListeners.forEach { it.onPeerReady(peer) }\n    }\n\n    override fun onDisconnect(peer: Peer, e: Exception?) {\n        peerManager.remove(peer)\n\n        when (e) {\n            null -> {\n                logger.info(\"Peer ${peer.host} disconnected.\")\n                hostManager.markSuccess(peer.host)\n            }\n\n            is PeerTimer.Error.Timeout -> {\n                logger.warning(\"Peer ${peer.host} disconnected. Warning: ${e.javaClass.simpleName}, ${e.message}.\")\n                // since the peer can be normally interacted after awhile we should not remove it from list\n                // that is why we mark it as disconnected with no error\n                hostManager.markSuccess(peer.host)\n            }\n\n            else -> {\n                logger.warning(\"Peer ${peer.host} disconnected. Error: ${e.javaClass.simpleName}, ${e.message}.\")\n                hostManager.markFailed(peer.host)\n            }\n        }\n\n        peerGroupListeners.forEach { it.onPeerDisconnect(peer, e) }\n        connectPeersIfRequired()\n    }\n\n    override fun onReceiveMessage(peer: Peer, message: IMessage) {\n        if (message is AddrMessage && handleAddrMessage) {\n            val peerIps = message.addresses\n                // exclude peers those don't support bloom filter\n                .filter {\n                    it.services and network.serviceBloomFilter == network.serviceBloomFilter\n                }\n                .map {\n                    InetAddress.getByAddress(it.address)\n                }\n                // exclude ipv6 addresses\n                .filter {\n                    it !is Inet6Address\n                }\n                .map {\n                    it.hostAddress\n                }\n\n            hostManager.addIps(peerIps)\n        } else if (message is InvMessage) {\n            inventoryItemsHandler?.handleInventoryItems(peer, message.inventory)\n        }\n    }\n\n    override fun onTaskComplete(peer: Peer, task: PeerTask) {\n        peerTaskHandler?.handleCompletedTask(peer, task)\n    }\n\n    //\n    // PeerAddressManager Listener\n    //\n    override fun onAddAddress() {\n        connectPeersIfRequired()\n    }\n\n    //\n    // Private methods\n    //\n\n    private fun setPeerCountToConnect(peer: Peer) {\n        peerCountToConnect = if (peer.announcedLastBlockHeight - localDownloadedBestBlockHeight > acceptableBlockHeightDifference) {\n            peerCountToConnectMax\n        } else {\n            0\n        }\n    }\n\n    private fun disconnectSlowestPeer(peerCountToConnect: Int) {\n        if (peerCountToConnect > peerCountConnected && peerCountToHold > 1 && hostManager.hasFreshIps) {\n            val sortedPeers = peerManager.sorted()\n            if (sortedPeers.size >= peerCountToHold) {\n                sortedPeers.lastOrNull()?.close()\n            }\n        }\n    }\n\n    @Synchronized\n    private fun connectPeersIfRequired() {\n        if (!running || !connectionManager.isConnected) {\n            return\n        }\n\n        for (i in peerManager.peersCount until peerCountToHold) {\n            val ip = hostManager.getIp() ?: break\n            val peer = Peer(ip, network, this, networkMessageParser, networkMessageSerializer, executorService)\n            peerCountConnected += 1\n            peerGroupListeners.forEach { it.onPeerCreate(peer) }\n            peerManager.add(peer)\n            peer.start(peerThreadPool)\n        }\n    }\n\n    //\n    // PeerGroup Exceptions\n    //\n    class Error(message: String) : Exception(message)\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/peer/PeerManager.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.peer\n\nimport java.util.concurrent.ConcurrentHashMap\n\nclass PeerManager {\n\n    private var peers = ConcurrentHashMap<String, Peer>()\n\n    val peersCount: Int\n        get() = peers.size\n\n    fun add(peer: Peer) {\n        peers[peer.host] = peer\n    }\n\n    fun remove(peer: Peer) {\n        peers.remove(peer.host)\n    }\n\n    fun disconnectAll() {\n        peers.values.forEach { it.close() }\n        peers.clear()\n    }\n\n    fun connected(): List<Peer> {\n        return peers.values.filter { it.connected }\n    }\n\n    fun sorted(): List<Peer> {\n        return connected().sortedBy { it.connectionTime }\n    }\n\n    fun readyPears(): List<Peer> {\n        return peers.values.filter { it.connected && it.ready }\n    }\n\n    fun hasSyncedPeer(): Boolean {\n        return peers.values.any { it.connected && it.synced }\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/peer/PeerTimer.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.peer\n\nimport java.util.concurrent.TimeUnit\n\nclass PeerTimer {\n\n    private val maxIdleTime = TimeUnit.SECONDS.toMillis(60)\n    private val pingTimeout = TimeUnit.SECONDS.toMillis(5)\n\n    private var messageLastReceivedTime: Long? = null\n    private var lastPingTime: Long? = null\n\n    fun check() {\n        lastPingTime?.let {\n            if (System.currentTimeMillis() - it > pingTimeout) {\n                throw Error.Timeout(pingTimeout)\n            }\n        }\n\n        messageLastReceivedTime?.let {\n            if (lastPingTime == null && System.currentTimeMillis() - it > maxIdleTime) {\n                throw Error.Idle()\n            }\n        }\n    }\n\n    fun pingSent() {\n        lastPingTime = System.currentTimeMillis()\n    }\n\n    fun restart() {\n        messageLastReceivedTime = System.currentTimeMillis()\n        lastPingTime = null\n    }\n\n    open class Error(message: String) : Exception(message) {\n        class Idle: Error(\"Idle\")\n        class Timeout(time: Long) : Error(\"No response within $time milliseconds\")\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/peer/task/GetBlockHashesTask.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.peer.task\n\nimport io.horizontalsystems.bitcoincore.models.InventoryItem\nimport io.horizontalsystems.bitcoincore.network.messages.GetBlocksMessage\nimport io.horizontalsystems.bitcoincore.network.messages.IMessage\nimport io.horizontalsystems.bitcoincore.network.messages.InvMessage\nimport java.util.concurrent.TimeUnit\nimport kotlin.math.max\nimport kotlin.math.min\n\nclass GetBlockHashesTask(private val blockLocatorHashes: List<ByteArray>, expectedHashesMinCount: Int) : PeerTask() {\n\n    var blockHashes = listOf<ByteArray>()\n\n    private val maxAllowedIdleTime = TimeUnit.SECONDS.toMillis(10)\n    private val minAllowedIdleTime = TimeUnit.SECONDS.toMillis(1)\n    private val maxExpectedBlockHashesCount: Int = 500\n    private val minExpectedBlockHashesCount: Int = 6\n\n    private val expectedHashesMinCount: Int\n\n    init {\n        this.expectedHashesMinCount = min(max(minExpectedBlockHashesCount, expectedHashesMinCount), maxExpectedBlockHashesCount)\n\n        val resolvedAllowedIdleTime = maxAllowedIdleTime * this.expectedHashesMinCount / maxExpectedBlockHashesCount.toDouble()\n        allowedIdleTime = max(minAllowedIdleTime, resolvedAllowedIdleTime.toLong())\n    }\n\n    override val state: String\n        get() = \"expectedHashesMinCount: $expectedHashesMinCount; allowedIdleTime: $allowedIdleTime\"\n\n    override fun start() {\n        requester?.let { it.send(GetBlocksMessage(blockLocatorHashes, it.protocolVersion, ByteArray(32))) }\n        resetTimer()\n    }\n\n    override fun handleMessage(message: IMessage): Boolean {\n        if (message !is InvMessage) {\n            return false\n        }\n\n        val newBlockHashes = message.inventory.filter { it.type == InventoryItem.MSG_BLOCK }.map { it.hash }\n        if (newBlockHashes.isEmpty()) return false\n\n        // When we send getblocks message the remote peer responds with 2 inv messages:\n        //  - one of them is the message we are awaiting\n        //  - another is the last block in the peer\n        // Based on bitcoin protocol it should respond with only one inv message.\n        // It can send the second inv message only if it has a stale block.\n        // That is why we take the inv message with the last block as the stale block.\n        // When we in the last iteration of syncing we send the last block in the block locator hashes.\n        // And if the remote peer sends us the inv message with the last block we should ignore it.\n        newBlockHashes.forEach { newBlockHash ->\n            blockLocatorHashes.forEach { blockHash ->\n                if (blockHash.contentEquals(newBlockHash)) {\n                    return true\n                }\n            }\n        }\n\n        if (newBlockHashes.size > blockHashes.size) {\n            blockHashes = newBlockHashes\n        }\n\n        if (newBlockHashes.size >= expectedHashesMinCount) {\n            listener?.onTaskCompleted(this)\n        }\n\n        return true\n    }\n\n    override fun handleTimeout() {\n        listener?.onTaskCompleted(this)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/peer/task/GetBlockHeadersTask.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.peer.task\n\nimport io.horizontalsystems.bitcoincore.network.messages.GetHeadersMessage\nimport io.horizontalsystems.bitcoincore.network.messages.HeadersMessage\nimport io.horizontalsystems.bitcoincore.network.messages.IMessage\nimport io.horizontalsystems.bitcoincore.storage.BlockHeader\n\nclass GetBlockHeadersTask(private val blockLocatorHashes: List<ByteArray>) : PeerTask() {\n\n    var blockHeaders = arrayOf<BlockHeader>()\n\n    override fun start() {\n        requester?.let { it.send(GetHeadersMessage(it.protocolVersion, blockLocatorHashes, ByteArray(32))) }\n        resetTimer()\n    }\n\n    override fun handleMessage(message: IMessage): Boolean {\n        if (message !is HeadersMessage) {\n            return false\n        }\n\n        blockHeaders = message.headers\n        listener?.onTaskCompleted(this)\n\n        return true\n    }\n\n    override fun handleTimeout() {\n        listener?.onTaskCompleted(this)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/peer/task/GetMerkleBlocksTask.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.peer.task\n\nimport io.horizontalsystems.bitcoincore.blocks.MerkleBlockExtractor\nimport io.horizontalsystems.bitcoincore.core.HashBytes\nimport io.horizontalsystems.bitcoincore.models.BlockHash\nimport io.horizontalsystems.bitcoincore.models.InventoryItem\nimport io.horizontalsystems.bitcoincore.models.MerkleBlock\nimport io.horizontalsystems.bitcoincore.network.messages.GetDataMessage\nimport io.horizontalsystems.bitcoincore.network.messages.IMessage\nimport io.horizontalsystems.bitcoincore.network.messages.MerkleBlockMessage\nimport io.horizontalsystems.bitcoincore.network.messages.TransactionMessage\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport kotlin.math.roundToInt\n\nclass GetMerkleBlocksTask(\n        hashes: List<BlockHash>,\n        private val merkleBlockHandler: MerkleBlockHandler,\n        private val merkleBlockExtractor: MerkleBlockExtractor,\n        private val minMerkleBlocks: Double,\n        private val minTransactions: Double,\n        private val minReceiveBytes: Double) : PeerTask() {\n\n    interface MerkleBlockHandler {\n        fun handleMerkleBlock(merkleBlock: MerkleBlock)\n    }\n\n    private var blockHashes = hashes.toMutableList()\n    private var pendingMerkleBlocks = mutableListOf<MerkleBlock>()\n\n    // Following fields used to calculate peer speed\n    private var receivedMerkleBlocks = 0\n    private var receivedTransactions = 0\n    private var receivedBytes = 0\n\n    private var totalWaitingTime: Long = 0\n    private var waitingStartTime: Long = 0\n    private var maxWarningCount = 10\n    private var firstResponseReceived = false\n\n    override val state: String\n        get() = \"minMerkleBlocksCount: ${minMerkleBlocks.roundToInt()}; minTransactionsCount: ${minTransactions.roundToInt()}; minTransactionsSize: ${minTransactions.roundToInt()}\"\n\n    override fun start() {\n        val items = blockHashes.map { hash ->\n            InventoryItem(InventoryItem.MSG_FILTERED_BLOCK, hash.headerHash)\n        }\n\n        requester?.send(GetDataMessage(items))\n        resumeWaiting()\n        resetTimer()\n    }\n\n    override fun handleTimeout() {\n        if (blockHashes.isEmpty()) {\n            listener?.onTaskCompleted(this)\n        } else {\n            listener?.onTaskFailed(this, MerkleBlockNotReceived())\n        }\n    }\n\n    override fun checkTimeout() {\n        pauseWaiting()\n\n        if (totalWaitingTime < 1000) {\n            return resumeWaiting()\n        }\n\n        val awaitingSeconds = (totalWaitingTime / 1000.0)\n        val minMerkleBlocks = (minMerkleBlocks * awaitingSeconds).roundToInt()\n        val minTransactions = (minTransactions * awaitingSeconds).roundToInt()\n        val minReceiveBytes = (minReceiveBytes * awaitingSeconds).roundToInt()\n\n        if (minMerkleBlocks > receivedMerkleBlocks && minTransactions > receivedTransactions && minReceiveBytes > receivedBytes) {\n            maxWarningCount -= 1\n        }\n\n        if (maxWarningCount < 1) {\n            listener?.onTaskFailed(this, PeerTooSlow(\n                    \"Received ${receivedMerkleBlocks / totalWaitingTime} blocks, \" +\n                            \"${receivedTransactions / totalWaitingTime} txs and $receivedBytes bytes per second; \" +\n                            \"Required ${minMerkleBlocks / totalWaitingTime} blocks, \" +\n                            \"${minTransactions / totalWaitingTime} txs or ${minReceiveBytes / totalWaitingTime} bytes per second\"\n            ))\n        }\n\n        resumeWaiting()\n        totalWaitingTime = 0\n        receivedMerkleBlocks = 0\n        receivedTransactions = 0\n        receivedBytes = 0\n    }\n\n    override fun handleMessage(message: IMessage): Boolean {\n        pauseWaiting()\n\n        val canHandleMessage = when (message) {\n            is MerkleBlockMessage -> {\n                receivedMerkleBlocks += 1\n                receivedTransactions += message.txCount\n                handleMerkleBlock(merkleBlockExtractor.extract(message))\n            }\n            is TransactionMessage -> {\n                receivedBytes += message.size\n                handleTransaction(message.transaction)\n            }\n            else -> false\n        }\n\n        resumeWaiting()\n\n        return canHandleMessage\n    }\n\n    private fun handleMerkleBlock(merkleBlock: MerkleBlock): Boolean {\n        val blockHash = blockHashes.find { merkleBlock.blockHash.contentEquals(it.headerHash) }\n                ?: return false\n\n        merkleBlock.height = if (blockHash.height > 0) blockHash.height else null\n\n        if (merkleBlock.complete) {\n            handleCompletedMerkleBlock(merkleBlock)\n        } else {\n            pendingMerkleBlocks.add(merkleBlock)\n        }\n\n        return true\n    }\n\n    private fun handleTransaction(transaction: FullTransaction): Boolean {\n        val block = pendingMerkleBlocks.find { it.associatedTransactionHashes[HashBytes(transaction.header.hash)] == true }\n                ?: return false\n\n        block.associatedTransactions.add(transaction)\n\n        if (block.complete) {\n            pendingMerkleBlocks.remove(block)\n            handleCompletedMerkleBlock(block)\n        }\n\n        return true\n    }\n\n    private fun handleCompletedMerkleBlock(merkleBlock: MerkleBlock) {\n        blockHashes.find { it.headerHash.contentEquals(merkleBlock.blockHash) }?.let {\n            blockHashes.remove(it)\n        }\n\n        try {\n            merkleBlockHandler.handleMerkleBlock(merkleBlock)\n        } catch (e: Exception) {\n            listener?.onTaskFailed(this, e)\n        }\n\n        if (blockHashes.isEmpty()) {\n            listener?.onTaskCompleted(this)\n        }\n    }\n\n    private fun pauseWaiting() {\n        totalWaitingTime += System.currentTimeMillis() - waitingStartTime\n\n        if (pendingMerkleBlocks.size > 0 && !firstResponseReceived) {\n            totalWaitingTime /= 2\n            firstResponseReceived = true\n        }\n    }\n\n    private fun resumeWaiting() {\n        waitingStartTime = System.currentTimeMillis()\n    }\n\n    class MerkleBlockNotReceived : Exception(\"Merkle blocks are not received\")\n    class PeerTooSlow(e: String) : Exception(e)\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/peer/task/PeerTask.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.peer.task\n\nimport io.horizontalsystems.bitcoincore.network.messages.IMessage\n\nopen class PeerTask {\n\n    interface Listener {\n        fun onTaskCompleted(task: PeerTask)\n        fun onTaskFailed(task: PeerTask, e: Exception)\n    }\n\n    interface Requester {\n        val protocolVersion: Int\n        fun send(message: IMessage)\n    }\n\n    var requester: Requester? = null\n    var listener: Listener? = null\n    open val state: String = \"\"\n\n    protected var lastActiveTime: Long? = null\n    protected var allowedIdleTime: Long? = null\n\n    open fun start() = Unit\n    open fun handleTimeout() = Unit\n\n    open fun checkTimeout() {\n        allowedIdleTime?.let { allowedIdleTime ->\n            lastActiveTime?.let { lastActiveTime ->\n                if (System.currentTimeMillis() - lastActiveTime > allowedIdleTime) {\n                    handleTimeout()\n                }\n            }\n        }\n    }\n\n    fun resetTimer() {\n        lastActiveTime = System.currentTimeMillis()\n    }\n\n    open fun handleMessage(message: IMessage): Boolean {\n        return false\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/peer/task/RequestTransactionsTask.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.peer.task\n\nimport io.horizontalsystems.bitcoincore.models.InventoryItem\nimport io.horizontalsystems.bitcoincore.network.messages.GetDataMessage\nimport io.horizontalsystems.bitcoincore.network.messages.IMessage\nimport io.horizontalsystems.bitcoincore.network.messages.TransactionMessage\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\n\nclass RequestTransactionsTask(hashes: List<ByteArray>) : PeerTask() {\n    val hashes = hashes.toMutableList()\n    var transactions = mutableListOf<FullTransaction>()\n\n    override val state: String\n        get() =\n            \"hashesCount: ${hashes.size}; receivedTransactionsCount: ${transactions.size}\"\n\n    override fun start() {\n        val items = hashes.map { hash ->\n            InventoryItem(InventoryItem.MSG_TX, hash)\n        }\n\n        requester?.send(GetDataMessage(items))\n        resetTimer()\n    }\n\n    override fun handleMessage(message: IMessage): Boolean {\n        if (message !is TransactionMessage) {\n            return false\n        }\n\n        val transaction = message.transaction\n        val hash = hashes.firstOrNull { it.contentEquals(transaction.header.hash) } ?: return false\n\n        hashes.remove(hash)\n        transactions.add(transaction)\n\n        if (hashes.isEmpty()) {\n            listener?.onTaskCompleted(this)\n        }\n\n        return true\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/network/peer/task/SendTransactionTask.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.peer.task\n\nimport io.horizontalsystems.bitcoincore.extensions.toReversedHex\nimport io.horizontalsystems.bitcoincore.models.InventoryItem\nimport io.horizontalsystems.bitcoincore.network.messages.GetDataMessage\nimport io.horizontalsystems.bitcoincore.network.messages.IMessage\nimport io.horizontalsystems.bitcoincore.network.messages.InvMessage\nimport io.horizontalsystems.bitcoincore.network.messages.TransactionMessage\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport java.util.concurrent.TimeUnit\n\nclass SendTransactionTask(val transaction: FullTransaction) : PeerTask() {\n\n    init {\n        allowedIdleTime = TimeUnit.SECONDS.toMillis(30)\n    }\n\n    override val state: String\n        get() = \"transaction: ${transaction.header.hash.toReversedHex()}\"\n\n    override fun start() {\n        requester?.send(InvMessage(InventoryItem.MSG_TX, transaction.header.hash))\n        resetTimer()\n    }\n\n    override fun handleMessage(message: IMessage): Boolean {\n        val transactionRequested =\n                message is GetDataMessage &&\n                message.inventory.any { it.type == InventoryItem.MSG_TX && it.hash.contentEquals(transaction.header.hash) }\n\n        if (transactionRequested) {\n            requester?.send(TransactionMessage(transaction, 0))\n            listener?.onTaskCompleted(this)\n        }\n\n        return transactionRequested\n    }\n\n    override fun handleTimeout() {\n        listener?.onTaskCompleted(this)\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/rbf/ReplacementTransaction.kt",
    "content": "package io.horizontalsystems.bitcoincore.rbf\n\nimport io.horizontalsystems.bitcoincore.models.TransactionInfo\nimport io.horizontalsystems.bitcoincore.transactions.builder.MutableTransaction\n\ndata class ReplacementTransaction(\n    internal val mutableTransaction: MutableTransaction,\n    val info: TransactionInfo,\n    val replacedTransactionHashes: List<String>\n)\n\ndata class ReplacementTransactionInfo(\n    val replacementTxMinSize: Long,\n    val feeRange: LongRange\n)\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/rbf/ReplacementTransactionBuilder.kt",
    "content": "package io.horizontalsystems.bitcoincore.rbf\n\nimport io.horizontalsystems.bitcoincore.DustCalculator\nimport io.horizontalsystems.bitcoincore.core.IPublicKeyManager\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.core.PluginManager\nimport io.horizontalsystems.bitcoincore.extensions.toReversedByteArray\nimport io.horizontalsystems.bitcoincore.extensions.toReversedHex\nimport io.horizontalsystems.bitcoincore.managers.UnspentOutputProvider\nimport io.horizontalsystems.bitcoincore.models.Address\nimport io.horizontalsystems.bitcoincore.models.PublicKey\nimport io.horizontalsystems.bitcoincore.models.TransactionInput\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.models.TransactionType\nimport io.horizontalsystems.bitcoincore.models.rbfEnabled\nimport io.horizontalsystems.bitcoincore.storage.FullTransactionInfo\nimport io.horizontalsystems.bitcoincore.storage.InputToSign\nimport io.horizontalsystems.bitcoincore.storage.InputWithPreviousOutput\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\nimport io.horizontalsystems.bitcoincore.storage.UtxoFilters\nimport io.horizontalsystems.bitcoincore.transactions.TransactionConflictsResolver\nimport io.horizontalsystems.bitcoincore.transactions.TransactionSizeCalculator\nimport io.horizontalsystems.bitcoincore.transactions.builder.LockTimeSetter\nimport io.horizontalsystems.bitcoincore.transactions.builder.MutableTransaction\nimport io.horizontalsystems.bitcoincore.transactions.extractors.TransactionMetadataExtractor\nimport io.horizontalsystems.bitcoincore.utils.ShuffleSorter\nimport kotlin.math.min\n\nclass ReplacementTransactionBuilder(\n    private val storage: IStorage,\n    private val sizeCalculator: TransactionSizeCalculator,\n    private val dustCalculator: DustCalculator,\n    private val metadataExtractor: TransactionMetadataExtractor,\n    private val pluginManager: PluginManager,\n    private val unspentOutputProvider: UnspentOutputProvider,\n    private val publicKeyManager: IPublicKeyManager,\n    private val conflictsResolver: TransactionConflictsResolver,\n    private val lockTimeSetter: LockTimeSetter\n) {\n\n    private fun replacementTransaction(\n        minFee: Long,\n        minFeeRate: Int,\n        utxo: List<TransactionOutput>,\n        fixedOutputs: List<TransactionOutput>,\n        outputs: List<TransactionOutput>\n    ): Pair<List<TransactionOutput>, /*Fee*/Long>? {\n\n        var minFee = minFee\n        val size = sizeCalculator.transactionSize(previousOutputs = utxo, outputs = fixedOutputs + outputs)\n\n        val inputsValue = utxo.sumOf { it.value }\n        val outputsValue = (fixedOutputs + outputs).sumOf { it.value }\n        val fee = inputsValue - outputsValue\n        val feeRate = fee / size\n\n        if (feeRate < minFeeRate) {\n            minFee = minFeeRate * size\n        }\n\n        if (fee >= minFee) {\n            return Pair(outputs, fee)\n        }\n\n        if (outputs.isEmpty()) {\n            return null\n        }\n\n        val output = TransactionOutput(outputs.first())\n        output.value = output.value - (minFee - fee)\n\n        if (output.value > dustCalculator.dust(output.scriptType)) {\n            return Pair(listOf(output) + outputs.drop(1), fee)\n        }\n\n        return null\n    }\n\n    private fun incrementedSequence(inputWithPreviousOutput: InputWithPreviousOutput): Long {\n        val input = inputWithPreviousOutput.input\n\n        if (inputWithPreviousOutput.previousOutput?.pluginId != null) {\n            return pluginManager.incrementedSequence(inputWithPreviousOutput)\n        }\n\n        return min(input.sequence + 1, 0xFFFFFFFF)\n    }\n\n    private fun inputToSign(previousOutput: TransactionOutput, prevOutputPublicKey: PublicKey, sequence: Long): InputToSign {\n        val transactionInput = TransactionInput(previousOutput.transactionHash, previousOutput.index.toLong(), sequence = sequence)\n\n        return InputToSign(transactionInput, previousOutput, prevOutputPublicKey)\n    }\n\n    private fun setInputs(\n        mutableTransaction: MutableTransaction,\n        originalInputs: List<InputWithPreviousOutput>,\n        additionalInputs: List<UnspentOutput>\n    ) {\n        additionalInputs.map { utxo ->\n            mutableTransaction.addInput(inputToSign(previousOutput = utxo.output, prevOutputPublicKey = utxo.publicKey, sequence = 0x0))\n        }\n\n        pluginManager.processInputs(mutableTransaction)\n\n        originalInputs.map { inputWithPreviousOutput ->\n            val previousOutput =\n                inputWithPreviousOutput.previousOutput ?: throw BuildError.InvalidTransaction(\"No previous output of original transaction\")\n            val publicKey = previousOutput.publicKeyPath?.let { publicKeyManager.getPublicKeyByPath(it) }\n                ?: throw BuildError.InvalidTransaction(\"No public key of original transaction\")\n            mutableTransaction.addInput(inputToSign(previousOutput = previousOutput, publicKey, incrementedSequence(inputWithPreviousOutput)))\n        }\n    }\n\n    private fun setOutputs(mutableTransaction: MutableTransaction, outputs: List<TransactionOutput>) {\n        val sorted = ShuffleSorter().sortOutputs(outputs)\n        sorted.forEachIndexed { index, transactionOutput ->\n            transactionOutput.index = index\n        }\n\n        mutableTransaction.outputs = sorted\n    }\n\n    private fun speedUpReplacement(\n        originalFullInfo: FullTransactionInfo,\n        minFee: Long,\n        originalFeeRate: Int,\n        fixedUtxo: List<TransactionOutput>\n    ): MutableTransaction? {\n        // If an output has a pluginId, it most probably has a time-locked value and it shouldn't be altered.\n        var fixedOutputs = originalFullInfo.outputs.filter { it.publicKeyPath == null || it.pluginId != null }\n        val myOutputs = originalFullInfo.outputs.filter { it.publicKeyPath != null && it.pluginId == null }\n        val myChangeOutputs = myOutputs.filter { it.changeOutput }.sortedBy { it.value }\n        val myExternalOutputs = myOutputs.filter { !it.changeOutput }.sortedBy { it.value }\n\n        var sortedOutputs = myChangeOutputs + myExternalOutputs\n        if (fixedOutputs.isEmpty() && sortedOutputs.isNotEmpty()) {\n            fixedOutputs = listOf(sortedOutputs.last())\n            sortedOutputs = sortedOutputs.dropLast(1)\n        }\n        val unusedUtxo = unspentOutputProvider.getConfirmedSpendableUtxo(UtxoFilters()).sortedBy { it.output.value }\n        var optimalReplacement: Triple</*inputs*/ List<UnspentOutput>, /*outputs*/ List<TransactionOutput>, /*fee*/ Long>? = null\n\n        var utxoCount = 0\n        do {\n            val utxo = unusedUtxo.take(utxoCount)\n            var outputsCount = sortedOutputs.size\n\n            do {\n                val outputs = sortedOutputs.takeLast(outputsCount)\n\n                replacementTransaction(\n                    minFee = minFee,\n                    minFeeRate = originalFeeRate,\n                    utxo = fixedUtxo + utxo.map { it.output },\n                    fixedOutputs = fixedOutputs,\n                    outputs = outputs\n                )?.let { (outputs, fee) ->\n                    optimalReplacement.let { _optimalReplacement ->\n                        if (_optimalReplacement != null) {\n                            if (_optimalReplacement.third > fee) {\n                                optimalReplacement = Triple(utxo, outputs, fee)\n                            }\n                        } else {\n                            optimalReplacement = Triple(utxo, outputs, fee)\n                        }\n                    }\n                }\n\n                outputsCount--\n            } while (outputsCount >= 0)\n\n            utxoCount++\n        } while (utxoCount <= unusedUtxo.size)\n\n        return optimalReplacement?.let { (inputs, outputs, _) ->\n            val mutableTransaction = MutableTransaction()\n            setInputs(\n                mutableTransaction = mutableTransaction,\n                originalInputs = originalFullInfo.inputs,\n                additionalInputs = inputs\n            )\n            setOutputs(\n                mutableTransaction = mutableTransaction,\n                outputs = fixedOutputs + outputs\n            )\n            lockTimeSetter.setLockTime(mutableTransaction)\n            mutableTransaction\n        }\n    }\n\n    private fun cancelReplacement(\n        originalFullInfo: FullTransactionInfo,\n        minFee: Long,\n        originalFeeRate: Int,\n        fixedUtxo: List<TransactionOutput>,\n        userAddress: Address,\n        publicKey: PublicKey\n    ): MutableTransaction? {\n        val unusedUtxo = unspentOutputProvider.getConfirmedSpendableUtxo(UtxoFilters()).sortedBy { it.output.value }\n        val originalInputsValue = fixedUtxo.sumOf { it.value }\n\n        var optimalReplacement: Triple</*inputs*/ List<UnspentOutput>, /*outputs*/ List<TransactionOutput>, /*fee*/ Long>? = null\n\n        var utxoCount = 0\n        val outputs = listOf(\n            TransactionOutput(\n                value = originalInputsValue - minFee,\n                index = 0,\n                script = userAddress.lockingScript,\n                type = userAddress.scriptType,\n                address = userAddress.stringValue,\n                lockingScriptPayload = userAddress.lockingScriptPayload,\n                publicKey = publicKey\n            )\n        )\n        do {\n            if (originalInputsValue - minFee < dustCalculator.dust(userAddress.scriptType)) {\n                utxoCount++\n                continue\n            }\n\n            val utxo = unusedUtxo.take(utxoCount)\n\n            replacementTransaction(\n                minFee = minFee,\n                minFeeRate = originalFeeRate,\n                utxo = fixedUtxo + utxo.map { it.output },\n                fixedOutputs = listOf(),\n                outputs = outputs\n            )?.let { (outputs, fee) ->\n                optimalReplacement.let { _optimalReplacement ->\n                    if (_optimalReplacement != null) {\n                        if (_optimalReplacement.third > fee) {\n                            optimalReplacement = Triple(utxo, outputs, fee)\n                        }\n                    } else {\n                        optimalReplacement = Triple(utxo, outputs, fee)\n                    }\n                }\n            }\n\n            utxoCount++\n        } while (utxoCount <= unusedUtxo.size)\n\n\n        return optimalReplacement?.let { (inputs, outputs, _) ->\n            val mutableTransaction = MutableTransaction()\n            setInputs(\n                mutableTransaction = mutableTransaction,\n                originalInputs = originalFullInfo.inputs,\n                additionalInputs = inputs\n            )\n            setOutputs(\n                mutableTransaction = mutableTransaction,\n                outputs = outputs\n            )\n            mutableTransaction\n        }\n    }\n\n    fun replacementTransaction(\n        transactionHash: String,\n        minFee: Long,\n        type: ReplacementType\n    ): Triple<MutableTransaction, FullTransactionInfo, List<String>> {\n        val originalFullInfo = storage.getFullTransactionInfo(transactionHash.toReversedByteArray())\n            ?: throw BuildError.InvalidTransaction(\"No FullTransactionInfo\")\n        check(originalFullInfo.block == null) { throw BuildError.InvalidTransaction(\"Transaction already in block\") }\n\n        val originalFee = originalFullInfo.metadata.fee\n        checkNotNull(originalFee) { throw BuildError.InvalidTransaction(\"No fee for original transaction\") }\n\n        check(originalFullInfo.metadata.type != TransactionType.Incoming) { throw BuildError.InvalidTransaction(\"Can replace only outgoing transaction\") }\n\n        val fixedUtxo = originalFullInfo.inputs.mapNotNull { it.previousOutput }\n\n        check(originalFullInfo.inputs.size == fixedUtxo.size) { throw BuildError.NoPreviousOutput }\n\n        check(originalFullInfo.inputs.any { it.input.rbfEnabled }) { throw BuildError.RbfNotEnabled }\n\n        val originalSize = sizeCalculator.transactionSize(previousOutputs = fixedUtxo, outputs = originalFullInfo.outputs)\n\n        val originalFeeRate = (originalFee / originalSize).toInt()\n        val descendantTransactions = storage.getDescendantTransactionsFullInfo(transactionHash.toReversedByteArray())\n        val absoluteFee = descendantTransactions.sumOf { it.metadata.fee ?: 0 }\n\n        check(\n            descendantTransactions.all { it.header.conflictingTxHash == null } &&\n                    !conflictsResolver.isTransactionReplaced(originalFullInfo.fullTransaction)\n        ) {\n            throw BuildError.InvalidTransaction(\"Already replaced\")\n        }\n\n        check(absoluteFee <= minFee) { throw BuildError.FeeTooLow }\n\n        val mutableTransaction = when (type) {\n            ReplacementType.SpeedUp -> speedUpReplacement(\n                originalFullInfo,\n                minFee,\n                originalFeeRate,\n                fixedUtxo\n            )\n            is ReplacementType.Cancel -> cancelReplacement(\n                originalFullInfo,\n                minFee,\n                originalFeeRate,\n                fixedUtxo,\n                type.address,\n                type.publicKey\n            )\n        }\n\n        checkNotNull(mutableTransaction) { throw BuildError.UnableToReplace }\n\n        val fullTransaction = mutableTransaction.build()\n        metadataExtractor.extract(fullTransaction)\n        val metadata = fullTransaction.metadata\n\n        return Triple(\n            mutableTransaction,\n            FullTransactionInfo(\n                block = null,\n                header = mutableTransaction.transaction,\n                inputs = mutableTransaction.inputsToSign.map {\n                    InputWithPreviousOutput(it.input, it.previousOutput)\n                },\n                outputs = mutableTransaction.outputs,\n                metadata = metadata\n            ),\n            descendantTransactions.map { it.metadata.transactionHash.toReversedHex() }\n        )\n    }\n\n    fun replacementInfo(\n        transactionHash: String,\n        type: ReplacementType\n    ): ReplacementTransactionInfo? {\n        val originalFullInfo = storage.getFullTransactionInfo(transactionHash.toReversedByteArray()) ?: return null\n        check(originalFullInfo.block == null) { throw BuildError.InvalidTransaction(\"Transaction already in block\") }\n        check(originalFullInfo.metadata.type != TransactionType.Incoming) { throw BuildError.InvalidTransaction(\"Can replace only outgoing transaction\") }\n\n        val originalFee = originalFullInfo.metadata.fee\n        checkNotNull(originalFee) { throw BuildError.InvalidTransaction(\"No fee for original transaction\") }\n\n        val fixedUtxo = originalFullInfo.inputs.mapNotNull { it.previousOutput }\n        check(originalFullInfo.inputs.size == fixedUtxo.size) { throw BuildError.NoPreviousOutput }\n\n        val descendantTransactions = storage.getDescendantTransactionsFullInfo(transactionHash.toReversedByteArray())\n        val absoluteFee = descendantTransactions.sumOf { it.metadata.fee ?: 0 }\n\n        check(\n            descendantTransactions.all { it.header.conflictingTxHash == null } &&\n                    !conflictsResolver.isTransactionReplaced(originalFullInfo.fullTransaction)\n        ) {\n            return null\n        }\n\n        val replacementTxMinSize: Long\n        val removableOutputsValue: Long\n\n        when (type) {\n            ReplacementType.SpeedUp -> {\n                var fixedOutputs = originalFullInfo.outputs.filter { it.publicKeyPath == null || it.pluginId != null }\n                val myOutputs = originalFullInfo.outputs.filter { it.publicKeyPath != null && it.pluginId == null }\n                val myChangeOutputs = myOutputs.filter { it.changeOutput }.sortedBy { it.value }\n                val myExternalOutputs = myOutputs.filter { !it.changeOutput }.sortedBy { it.value }\n\n                var sortedOutputs = myChangeOutputs + myExternalOutputs\n                if (fixedOutputs.isEmpty() && sortedOutputs.isNotEmpty()) {\n                    fixedOutputs = listOf(sortedOutputs.last())\n                    sortedOutputs = sortedOutputs.dropLast(1)\n                }\n\n                replacementTxMinSize = sizeCalculator.transactionSize(fixedUtxo, fixedOutputs)\n                removableOutputsValue = sortedOutputs.sumOf { it.value }\n            }\n\n            is ReplacementType.Cancel -> {\n                val dustValue = dustCalculator.dust(type.address.scriptType).toLong()\n                val fixedOutputs = listOf(\n                    TransactionOutput(\n                        value = dustValue,\n                        index = 0,\n                        script = type.address.lockingScript,\n                        type = type.address.scriptType,\n                        address = type.address.stringValue,\n                        lockingScriptPayload = type.address.lockingScriptPayload\n                    )\n                )\n                replacementTxMinSize = sizeCalculator.transactionSize(fixedUtxo, fixedOutputs)\n                removableOutputsValue = originalFullInfo.outputs.sumOf { it.value } - dustValue\n            }\n        }\n\n        val confirmedUtxoTotalValue = unspentOutputProvider.getConfirmedSpendableUtxo(UtxoFilters()).sumOf { it.output.value }\n        val maxFeeAmount = originalFee + removableOutputsValue + confirmedUtxoTotalValue\n\n        return if (absoluteFee > maxFeeAmount) {\n            null\n        } else {\n            ReplacementTransactionInfo(replacementTxMinSize, LongRange(absoluteFee, maxFeeAmount))\n        }\n    }\n\n    sealed class BuildError : Throwable() {\n        class InvalidTransaction(override val message: String) : BuildError()\n        object NoPreviousOutput : BuildError()\n        object FeeTooLow : BuildError()\n        object RbfNotEnabled : BuildError()\n        object UnableToReplace : BuildError()\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/rbf/ReplacementType.kt",
    "content": "package io.horizontalsystems.bitcoincore.rbf\n\nimport io.horizontalsystems.bitcoincore.models.Address\nimport io.horizontalsystems.bitcoincore.models.PublicKey\n\nsealed class ReplacementType {\n    object SpeedUp : ReplacementType()\n    data class Cancel(val address: Address, val publicKey: PublicKey) : ReplacementType()\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/serializers/BlockHeaderParser.kt",
    "content": "package io.horizontalsystems.bitcoincore.serializers\n\nimport io.horizontalsystems.bitcoincore.core.IHasher\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.storage.BlockHeader\n\nclass BlockHeaderParser(private val hasher: IHasher) {\n\n    fun parse(input: BitcoinInputMarkable): BlockHeader {\n        input.mark()\n        val payload = input.readBytes(80)\n        val hash = hasher.hash(payload)\n        input.reset()\n\n        val version = input.readInt()\n        val previousBlockHeaderHash = input.readBytes(32)\n        val merkleRoot = input.readBytes(32)\n        val timestamp = input.readUnsignedInt()\n        val bits = input.readUnsignedInt()\n        val nonce = input.readUnsignedInt()\n\n        return BlockHeader(version, previousBlockHeaderHash, merkleRoot, timestamp, bits, nonce, hash)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/serializers/InputSerializer.kt",
    "content": "package io.horizontalsystems.bitcoincore.serializers\n\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\nimport io.horizontalsystems.bitcoincore.models.TransactionInput\nimport io.horizontalsystems.bitcoincore.storage.InputToSign\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\n\nobject InputSerializer {\n\n    fun serialize(input: TransactionInput): ByteArray {\n        return BitcoinOutput()\n                .write(input.previousOutputTxHash)\n                .writeUnsignedInt(input.previousOutputIndex)\n                .writeVarInt(input.sigScript.size.toLong())\n                .write(input.sigScript)\n                .writeUnsignedInt(input.sequence)\n                .toByteArray()\n    }\n\n    fun deserialize(input: BitcoinInputMarkable): TransactionInput {\n        val previousOutputHash = input.readBytes(32)\n        val previousOutputIndex = input.readUnsignedInt()\n        val sigScriptLength = input.readVarInt()\n        val sigScript = input.readBytes(sigScriptLength.toInt())\n        val sequence = input.readUnsignedInt()\n\n        return TransactionInput(previousOutputHash, previousOutputIndex, sigScript, sequence)\n    }\n\n    fun serializeOutpoint(input: InputToSign): ByteArray {\n        return BitcoinOutput()\n                .write(input.previousOutput.transactionHash)\n                .writeInt(input.previousOutput.index)\n                .toByteArray()\n    }\n\n    fun serializeForSignature(inputToSign: InputToSign, forCurrentInputSignature: Boolean): ByteArray {\n        val previousOutput = inputToSign.previousOutput\n        val output = BitcoinOutput()\n                .write(previousOutput.transactionHash)\n                .writeUnsignedInt(previousOutput.index.toLong())\n\n        if (forCurrentInputSignature) {\n            val script = when (previousOutput.scriptType) {\n                ScriptType.P2SH -> {\n                    previousOutput.redeemScript ?: throw Exception(\"no previous output script\")\n                }\n                else -> previousOutput.lockingScript\n            }\n            output.writeVarInt(script.size.toLong())\n                    .write(script)\n        } else {\n            output.writeVarInt(0L)\n        }\n\n        return output.writeUnsignedInt(inputToSign.input.sequence).toByteArray()\n    }\n\n    fun serializeWitness(witness: List<ByteArray>): ByteArray {\n        val output = BitcoinOutput()\n                .writeVarInt(witness.size.toLong())\n\n        witness.forEach { data ->\n            output.writeVarInt(data.size.toLong())\n            output.write(data)\n        }\n\n        return output.toByteArray()\n    }\n\n    fun deserializeWitness(input: BitcoinInputMarkable): MutableList<ByteArray> {\n        val stackSize = input.readVarInt()\n        val witnessData = mutableListOf<ByteArray>()\n\n        for (i in 0 until stackSize) {\n            val dataSize = input.readVarInt()\n            val data = input.readBytes(dataSize.toInt())\n            witnessData.add(data)\n        }\n\n        return witnessData\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/serializers/OutputSerializer.kt",
    "content": "package io.horizontalsystems.bitcoincore.serializers\n\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\n\nobject OutputSerializer {\n    fun deserialize(input: BitcoinInputMarkable, vout: Long): TransactionOutput {\n        val value = input.readLong()\n        val scriptLength = input.readVarInt() // do not store\n        val lockingScript = input.readBytes(scriptLength.toInt())\n        val index = vout.toInt()\n\n        return TransactionOutput(value, index, lockingScript)\n    }\n\n    fun serialize(output: TransactionOutput): ByteArray {\n        return BitcoinOutput()\n                .writeLong(output.value)\n                .writeVarInt(output.lockingScript.size.toLong())\n                .write(output.lockingScript)\n                .toByteArray()\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/serializers/TransactionSerializer.kt",
    "content": "package io.horizontalsystems.bitcoincore.serializers\n\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.models.TransactionInput\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.bitcoincore.storage.InputToSign\nimport io.horizontalsystems.bitcoincore.transactions.scripts.OpCodes\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport io.horizontalsystems.bitcoincore.transactions.scripts.Sighash\nimport io.horizontalsystems.bitcoincore.utils.HashUtils\n\nobject TransactionSerializer {\n    fun deserialize(input: BitcoinInputMarkable): FullTransaction {\n        val transaction = Transaction()\n        val inputs = mutableListOf<TransactionInput>()\n        val outputs = mutableListOf<TransactionOutput>()\n\n        transaction.version = input.readInt()\n        input.mark()\n        val marker = 0xff and input.readUnsignedByte()\n        val inputCount = if (marker == 0) {  // segwit marker: 0x00\n            input.read()  // skip segwit flag: 0x01\n            transaction.segwit = true\n            input.readVarInt()\n        } else {\n            input.reset()\n            input.readVarInt()\n        }\n\n        //  inputs\n        for (i in 0 until inputCount) {\n            inputs.add(InputSerializer.deserialize(input))\n        }\n\n        //  outputs\n        val outputCount = input.readVarInt()\n        for (i in 0 until outputCount) {\n            outputs.add(OutputSerializer.deserialize(input, i))\n        }\n\n        //  extract witness data\n        if (transaction.segwit) {\n            inputs.forEach {\n                it.witness = InputSerializer.deserializeWitness(input)\n            }\n        }\n\n        transaction.lockTime = input.readUnsignedInt()\n\n        return FullTransaction(transaction, inputs, outputs)\n    }\n\n    fun serialize(transaction: FullTransaction, withWitness: Boolean = true): ByteArray {\n        val header = transaction.header\n        val buffer = BitcoinOutput()\n        buffer.writeInt(header.version)\n\n        if (header.segwit && withWitness) {\n            buffer.writeByte(0) // marker 0x00\n            buffer.writeByte(1) // flag 0x01\n        }\n\n        // inputs\n        buffer.writeVarInt(transaction.inputs.size.toLong())\n        transaction.inputs.forEach { buffer.write(InputSerializer.serialize(it)) }\n\n        // outputs\n        buffer.writeVarInt(transaction.outputs.size.toLong())\n        transaction.outputs.forEach { buffer.write(OutputSerializer.serialize(it)) }\n\n        //  serialize witness data\n        if (header.segwit && withWitness) {\n            transaction.inputs.forEach { buffer.write(InputSerializer.serializeWitness(it.witness)) }\n        }\n\n        buffer.writeUnsignedInt(header.lockTime)\n        return buffer.toByteArray()\n    }\n\n    fun serializeForSignature(\n        transaction: Transaction,\n        inputsToSign: List<InputToSign>,\n        outputs: List<TransactionOutput>,\n        inputIndex: Int,\n        isWitness: Boolean = false\n    ): ByteArray {\n        val buffer = BitcoinOutput().writeInt(transaction.version)\n        if (isWitness) {\n            val outpoints = BitcoinOutput()\n            val sequences = BitcoinOutput()\n\n            for (inputToSign in inputsToSign) {\n                outpoints.write(InputSerializer.serializeOutpoint(inputToSign))\n                sequences.writeInt32(inputToSign.input.sequence)\n            }\n\n            buffer.write(HashUtils.doubleSha256(outpoints.toByteArray())) // hash prevouts\n            buffer.write(HashUtils.doubleSha256(sequences.toByteArray())) // hash sequence\n\n            val inputToSign = inputsToSign[inputIndex]\n            val previousOutput = checkNotNull(inputToSign.previousOutput) { throw Exception(\"no previous output\") }\n\n            buffer.write(InputSerializer.serializeOutpoint(inputToSign))\n\n            when (previousOutput.scriptType) {\n                ScriptType.P2SH -> {\n                    val script = previousOutput.redeemScript ?: throw Exception(\"no previous output script\")\n                    buffer.writeVarInt(script.size.toLong())\n                    buffer.write(script)\n                }\n                else -> {\n                    buffer.write(OpCodes.push(OpCodes.p2pkhStart + OpCodes.push(inputToSign.previousOutputPublicKey.publicKeyHash) + OpCodes.p2pkhEnd))\n                }\n            }\n\n            buffer.writeLong(previousOutput.value)\n            buffer.writeInt32(inputToSign.input.sequence)\n\n            val hashOutputs = BitcoinOutput()\n            for (output in outputs) {\n                hashOutputs.write(OutputSerializer.serialize(output))\n            }\n\n            buffer.write(HashUtils.doubleSha256(hashOutputs.toByteArray()))\n        } else {\n            // inputs\n            buffer.writeVarInt(inputsToSign.size.toLong())\n            inputsToSign.forEachIndexed { index, input ->\n                buffer.write(InputSerializer.serializeForSignature(input, index == inputIndex))\n            }\n\n            // outputs\n            buffer.writeVarInt(outputs.size.toLong())\n            outputs.forEach { buffer.write(OutputSerializer.serialize(it)) }\n        }\n\n        buffer.writeUnsignedInt(transaction.lockTime)\n        return buffer.toByteArray()\n    }\n\n    fun serializeForTaprootSignature(\n        transaction: Transaction,\n        inputsToSign: List<InputToSign>,\n        outputs: List<TransactionOutput>,\n        inputIndex: Int\n    ): ByteArray {\n        val buffer = BitcoinOutput()\n            .writeByte(0)\n            .writeByte(Sighash.DEFAULT)\n            .writeInt(transaction.version)\n            .writeUnsignedInt(transaction.lockTime)\n\n        // sha_prevouts\n        val outpoints = BitcoinOutput()\n        for (inputToSign in inputsToSign) {\n            outpoints.write(InputSerializer.serializeOutpoint(inputToSign))\n        }\n        buffer.write(HashUtils.sha256(outpoints.toByteArray()))\n\n        // sha_amounts\n        val outputValues = BitcoinOutput()\n        for (inputToSign in inputsToSign) {\n            outputValues.writeLong(inputToSign.previousOutput.value)\n        }\n        buffer.write(HashUtils.sha256(outputValues.toByteArray()))\n\n        // sha_scriptpubkeys\n        val outputLockingScripts = BitcoinOutput()\n        for (inputToSign in inputsToSign) {\n            outputLockingScripts.write(OpCodes.push(inputToSign.previousOutput.lockingScript))\n        }\n        buffer.write(HashUtils.sha256(outputLockingScripts.toByteArray()))\n\n        // sha_sequences\n        val sequences = BitcoinOutput()\n        for (inputToSign in inputsToSign) {\n            sequences.writeInt32(inputToSign.input.sequence)\n        }\n        buffer.write(HashUtils.sha256(sequences.toByteArray()))\n\n        // sha_outputs\n        val hashOutputs = BitcoinOutput()\n        for (output in outputs) {\n            hashOutputs.write(OutputSerializer.serialize(output))\n        }\n        buffer.write(HashUtils.sha256(hashOutputs.toByteArray()))\n\n        buffer.writeByte(0) // spendType (no annex, no scriptPath)\n        buffer.writeInt(inputIndex)\n\n        return buffer.toByteArray()\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/BlockDao.kt",
    "content": "package io.horizontalsystems.bitcoincore.storage\n\nimport androidx.room.*\nimport io.horizontalsystems.bitcoincore.models.Block\n\n@Dao\ninterface BlockDao {\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    fun insert(block: Block)\n\n    @Update(onConflict = OnConflictStrategy.REPLACE)\n    fun update(block: Block)\n\n    @Query(\"UPDATE Block SET partial = 1 WHERE headerHash = :headerHash\")\n    fun setBlockPartial(headerHash: ByteArray)\n\n    @Query(\"SELECT * FROM Block WHERE stale = :stale ORDER BY height DESC limit 1\")\n    fun getLast(stale: Boolean): Block?\n\n    @Query(\"SELECT * FROM Block WHERE stale = :stale ORDER BY height ASC limit 1\")\n    fun getFirst(stale: Boolean): Block?\n\n    @Query(\"SELECT * FROM Block ORDER BY height DESC limit 1\")\n    fun getLastBlock(): Block?\n\n    @Query(\"SELECT * FROM Block WHERE hasTransactions = 1 ORDER BY height DESC limit 1\")\n    fun getLastBlockWithTransactions(): Block?\n\n    @Query(\"SELECT * FROM Block WHERE headerHash IN (:hashes)\")\n    fun getBlocks(hashes: List<ByteArray>): List<Block>\n\n    @Query(\"SELECT COUNT(headerHash) FROM Block WHERE headerHash IN (:hashes)\")\n    fun getBlocksCount(hashes: List<ByteArray>): Int\n\n    @Query(\"SELECT * FROM Block WHERE height >= :heightGreaterOrEqualTo AND stale = :stale\")\n    fun getBlocks(heightGreaterOrEqualTo: Int, stale: Boolean): List<Block>\n\n    @Query(\"SELECT * FROM Block WHERE stale = :stale\")\n    fun getBlocksByStale(stale: Boolean): List<Block>\n\n    @Query(\"SELECT * FROM Block WHERE height > :heightGreaterThan ORDER BY height DESC LIMIT :limit\")\n    fun getBlocks(heightGreaterThan: Int, limit: Int): List<Block>\n\n    @Query(\"SELECT * FROM Block WHERE height >= :fromHeight AND height <= :toHeight ORDER BY height ASC\")\n    fun getBlocksChunk(fromHeight: Int, toHeight: Int): List<Block>\n\n    @Query(\"SELECT * FROM Block WHERE headerHash = :hash limit 1\")\n    fun getBlockByHash(hash: ByteArray): Block?\n\n    @Query(\"SELECT * FROM Block WHERE height = :height limit 1\")\n    fun getBlockByHeight(height: Int): Block?\n\n    @Query(\"SELECT * FROM Block WHERE height = :height ORDER by stale DESC limit 1\")\n    fun getBlockByHeightStalePrioritized(height: Int): Block?\n\n    @Query(\"SELECT COUNT(headerHash) FROM Block\")\n    fun count(): Int\n\n    @Query(\"DELETE FROM Block WHERE height < :toHeight AND hasTransactions = 0\")\n    fun deleteBlocksWithoutTransactions(toHeight: Int)\n\n    @Query(\"UPDATE Block SET stale = 0\")\n    fun unstaleAllBlocks()\n\n    @Delete\n    fun delete(block: Block)\n\n    @Delete\n    fun deleteAll(blocks: List<Block>)\n\n    @Query(\"SELECT block_timestamp FROM Block WHERE height >= :from AND height <= :to ORDER BY block_timestamp ASC\")\n    fun getTimestamps(from: Int, to: Int) : List<Long>\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/BlockHashDao.kt",
    "content": "package io.horizontalsystems.bitcoincore.storage\n\nimport androidx.room.*\nimport io.horizontalsystems.bitcoincore.models.BlockHash\n\n@Dao\ninterface BlockHashDao {\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    fun insert(blockHash: BlockHash)\n\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    fun insertAll(users: List<BlockHash>)\n\n    @Query(\"SELECT headerHash FROM BlockHash\")\n    fun allBlockHashes(): List<ByteArray>\n\n    @Query(\"SELECT headerHash FROM BlockHash WHERE headerHash NOT IN(:excludedHash)\")\n    fun allBlockHashes(excludedHash: List<ByteArray>): List<ByteArray>\n\n    @Query(\"SELECT * FROM BlockHash ORDER BY sequence ASC, height ASC LIMIT :limit\")\n    fun getBlockHashesSortedSequenceHeight(limit: Int): List<BlockHash>\n\n    @Query(\"SELECT * FROM BlockHash WHERE height = 0\")\n    fun getBlockchainBlockHashes(): List<BlockHash>\n\n    @Query(\"SELECT COUNT(*) FROM BlockHash WHERE height > 0\")\n    fun getApiBlockHashesCount(): Int\n\n    @Query(\"SELECT * FROM BlockHash ORDER BY sequence DESC LIMIT 1\")\n    fun getLastBlockHash(): BlockHash?\n\n    @Query(\"SELECT * FROM BlockHash ORDER BY height DESC LIMIT 1\")\n    fun getLastBlockHashByHeight(): BlockHash?\n\n    @Query(\"SELECT * FROM BlockHash WHERE height = 0 ORDER BY sequence DESC LIMIT 1\")\n    fun getLastBlockchainBlockHash(): BlockHash?\n\n    @Delete\n    fun delete(blockHash: BlockHash)\n\n    @Query(\"DELETE FROM BlockHash WHERE height = :height\")\n    fun delete(height: Int)\n\n    @Query(\"DELETE FROM BlockHash WHERE headerHash = :hash\")\n    fun delete(hash: ByteArray)\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/BlockHashPublicKeyDao.kt",
    "content": "package io.horizontalsystems.bitcoincore.storage\n\nimport androidx.room.Dao\nimport androidx.room.Insert\nimport androidx.room.OnConflictStrategy\nimport io.horizontalsystems.bitcoincore.models.BlockHashPublicKey\n\n@Dao\ninterface BlockHashPublicKeyDao {\n\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    fun insertAll(users: List<BlockHashPublicKey>)\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/BlockchainStateDao.kt",
    "content": "package io.horizontalsystems.bitcoincore.storage\n\nimport androidx.room.*\nimport io.horizontalsystems.bitcoincore.models.BlockchainState\n\n@Dao\ninterface BlockchainStateDao {\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    fun insert(state: BlockchainState)\n\n    @Query(\"SELECT * FROM BlockchainState LIMIT 1\")\n    fun getState(): BlockchainState?\n\n    @Delete\n    fun delete(state: BlockchainState)\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/CoreDatabase.kt",
    "content": "package io.horizontalsystems.bitcoincore.storage\n\nimport androidx.sqlite.db.SupportSQLiteDatabase\nimport androidx.room.Database\nimport androidx.room.Room\nimport androidx.room.RoomDatabase\nimport androidx.room.TypeConverters\nimport androidx.room.migration.Migration\nimport android.content.Context\nimport io.horizontalsystems.bitcoincore.models.*\nimport io.horizontalsystems.bitcoincore.storage.migrations.*\n\n@Database(version = 19, exportSchema = false, entities = [\n    BlockchainState::class,\n    PeerAddress::class,\n    BlockHash::class,\n    BlockHashPublicKey::class,\n    Block::class,\n    SentTransaction::class,\n    Transaction::class,\n    TransactionInput::class,\n    TransactionOutput::class,\n    PublicKey::class,\n    InvalidTransaction::class,\n    TransactionMetadata::class\n])\n@TypeConverters(\n    ScriptTypeConverter::class,\n    TransactionTypeConverter::class\n)\nabstract class CoreDatabase : RoomDatabase() {\n\n    abstract val blockchainState: BlockchainStateDao\n    abstract val peerAddress: PeerAddressDao\n    abstract val blockHash: BlockHashDao\n    abstract val blockHashPublicKey: BlockHashPublicKeyDao\n    abstract val block: BlockDao\n    abstract val sentTransaction: SentTransactionDao\n    abstract val transaction: TransactionDao\n    abstract val transactionMetadata: TransactionMetadataDao\n    abstract val input: TransactionInputDao\n    abstract val output: TransactionOutputDao\n    abstract val publicKey: PublicKeyDao\n    abstract val invalidTransaction: InvalidTransactionDao\n\n    companion object {\n\n        fun getInstance(context: Context, dbName: String): CoreDatabase {\n            return Room.databaseBuilder(context, CoreDatabase::class.java, dbName)\n                    .allowMainThreadQueries()\n                    .addMigrations(\n                            Migration_18_19,\n                            Migration_17_18,\n                            Migration_16_17,\n                            Migration_15_16,\n                            Migration_14_15,\n                            Migration_13_14,\n                            Migration_12_13,\n                            Migration_11_12,\n                            Migration_10_11,\n                            add_rawTransaction_to_Transaction,\n                            add_conflictingTxHash_to_Transaction,\n                            add_table_InvalidTransaction,\n                            update_transaction_output,\n                            update_block_timestamp,\n                            add_hasTransaction_to_Block,\n                            add_connectionTime_to_PeerAddress\n                    )\n                    .fallbackToDestructiveMigration()\n                    .build()\n        }\n\n        private val add_rawTransaction_to_Transaction = object : Migration(9, 10) {\n            override fun migrate(database: SupportSQLiteDatabase) {\n                database.execSQL(\"ALTER TABLE `Transaction` ADD COLUMN `rawTransaction` TEXT\")\n                database.execSQL(\"ALTER TABLE `InvalidTransaction` ADD COLUMN `rawTransaction` TEXT\")\n            }\n        }\n\n        private val add_conflictingTxHash_to_Transaction = object : Migration(8, 9) {\n            override fun migrate(database: SupportSQLiteDatabase) {\n                database.execSQL(\"ALTER TABLE `Transaction` ADD COLUMN `conflictingTxHash` BLOB\")\n                database.execSQL(\"ALTER TABLE `InvalidTransaction` ADD COLUMN `conflictingTxHash` BLOB\")\n            }\n        }\n\n        private val add_table_InvalidTransaction = object : Migration(7, 8) {\n            override fun migrate(database: SupportSQLiteDatabase) {\n                database.execSQL(\"CREATE TABLE IF NOT EXISTS `InvalidTransaction` (`hash` BLOB NOT NULL, `blockHash` BLOB, `version` INTEGER NOT NULL, `lockTime` INTEGER NOT NULL, `timestamp` INTEGER NOT NULL, `order` INTEGER NOT NULL, `isMine` INTEGER NOT NULL, `isOutgoing` INTEGER NOT NULL, `segwit` INTEGER NOT NULL, `status` INTEGER NOT NULL, `serializedTxInfo` TEXT NOT NULL, PRIMARY KEY(`hash`))\")\n                database.execSQL(\"ALTER TABLE SentTransaction ADD COLUMN sendSuccess INTEGER DEFAULT 0 NOT NULL\")\n                database.execSQL(\"ALTER TABLE `Transaction` ADD COLUMN serializedTxInfo TEXT DEFAULT '' NOT NULL\")\n            }\n        }\n\n        private val update_transaction_output = object : Migration(6, 7) {\n            override fun migrate(database: SupportSQLiteDatabase) {\n                database.execSQL(\"ALTER TABLE TransactionOutput ADD COLUMN `pluginId` INTEGER\")\n                database.execSQL(\"ALTER TABLE TransactionOutput ADD COLUMN `pluginData` TEXT\")\n            }\n        }\n\n        private val update_block_timestamp = object : Migration(3, 4) {\n            override fun migrate(database: SupportSQLiteDatabase) {\n                database.execSQL(\"UPDATE Block SET block_timestamp = 1559256184 WHERE height = 578592 AND block_timestamp = 1559277784\")\n            }\n        }\n\n        private val add_hasTransaction_to_Block = object : Migration(2, 3) {\n            override fun migrate(database: SupportSQLiteDatabase) {\n                database.execSQL(\"ALTER TABLE Block ADD COLUMN hasTransactions INTEGER DEFAULT 0 NOT NULL\")\n                database.execSQL(\"UPDATE Block SET hasTransactions = 1\")\n            }\n        }\n\n        private val add_connectionTime_to_PeerAddress = object : Migration(1, 2) {\n            override fun migrate(database: SupportSQLiteDatabase) {\n                database.execSQL(\"ALTER TABLE PeerAddress ADD COLUMN connectionTime INTEGER\")\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/DataObjects.kt",
    "content": "package io.horizontalsystems.bitcoincore.storage\n\nimport androidx.room.Embedded\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.extensions.toHexString\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.bitcoincore.models.PublicKey\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.models.TransactionInput\nimport io.horizontalsystems.bitcoincore.models.TransactionMetadata\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.serializers.TransactionSerializer\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport io.horizontalsystems.bitcoincore.utils.HashUtils\n\nclass BlockHeader(\n        val version: Int,\n        val previousBlockHeaderHash: ByteArray,\n        val merkleRoot: ByteArray,\n        val timestamp: Long,\n        val bits: Long,\n        val nonce: Long,\n        val hash: ByteArray)\n\nopen class FullTransaction(\n    val header: Transaction,\n    val inputs: List<TransactionInput>,\n    val outputs: List<TransactionOutput>,\n    val forceHashUpdate: Boolean = true\n) {\n\n    lateinit var metadata: TransactionMetadata\n\n    init {\n        if (forceHashUpdate) {\n            setHash(HashUtils.doubleSha256(TransactionSerializer.serialize(this, withWitness = false)))\n        }\n    }\n\n    fun setHash(hash: ByteArray) {\n        header.hash = hash\n\n        metadata = TransactionMetadata(header.hash)\n\n        inputs.forEach {\n            it.transactionHash = header.hash\n        }\n        outputs.forEach {\n            it.transactionHash = header.hash\n        }\n    }\n\n}\n\nclass InputToSign(\n        val input: TransactionInput,\n        val previousOutput: TransactionOutput,\n        val previousOutputPublicKey: PublicKey)\n\nclass TransactionWithBlock(\n        @Embedded val transaction: Transaction,\n        @Embedded val block: Block?)\n\nclass PublicKeyWithUsedState(\n        @Embedded val publicKey: PublicKey,\n        val usedCount: Int) {\n\n    val used: Boolean\n        get() = usedCount > 0\n}\n\nclass InputWithPreviousOutput(\n    val input: TransactionInput,\n    val previousOutput: TransactionOutput?\n)\n\nclass UnspentOutput(\n    @Embedded val output: TransactionOutput,\n    @Embedded val publicKey: PublicKey,\n    @Embedded val transaction: Transaction,\n    @Embedded val block: Block?\n)\n\ndata class UtxoFilters(\n    val scriptTypes: List<ScriptType>? = null,\n    val maxOutputsCountForInputs: Int? = null\n) {\n    fun isEmpty() = scriptTypes == null && maxOutputsCountForInputs == null\n\n    fun filterUtxo(utxo: UnspentOutput, storage: IStorage): Boolean {\n        if (\n            scriptTypes != null &&\n            !scriptTypes.contains(utxo.output.scriptType)\n        ) {\n            return false\n        }\n\n        if (\n            maxOutputsCountForInputs != null &&\n            storage.getTransactionOutputsCount(utxo.transaction.hash) > maxOutputsCountForInputs\n        ) {\n            return false\n        }\n\n\n        return true\n    }\n}\n\nclass UnspentOutputInfo(\n    val outputIndex: Int,\n    val transactionHash: ByteArray,\n    val timestamp: Long,\n    val address: String?,\n    val value: Long\n) {\n    companion object {\n        fun fromUnspentOutput(unspentOutput: UnspentOutput): UnspentOutputInfo {\n            return UnspentOutputInfo(\n                unspentOutput.output.index,\n                unspentOutput.output.transactionHash,\n                unspentOutput.transaction.timestamp,\n                unspentOutput.output.address,\n                unspentOutput.output.value,\n            )\n        }\n    }\n}\n\nclass FullTransactionInfo(\n        val block: Block?,\n        val header: Transaction,\n        val inputs: List<InputWithPreviousOutput>,\n        val outputs: List<TransactionOutput>,\n        val metadata: TransactionMetadata\n) {\n\n    val rawTransaction: String\n        get() {\n            return TransactionSerializer.serialize(fullTransaction).toHexString()\n        }\n\n    val fullTransaction: FullTransaction\n        get() = FullTransaction(header, inputs.map { it.input }, outputs)\n\n}\n\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/DataTypeConverters.kt",
    "content": "package io.horizontalsystems.bitcoincore.storage\n\nimport androidx.room.TypeConverter\nimport io.horizontalsystems.bitcoincore.extensions.hexToByteArray\nimport io.horizontalsystems.bitcoincore.extensions.toHexString\n\nclass WitnessConverter {\n\n    @TypeConverter\n    fun fromWitness(list: List<ByteArray>): String {\n        return list.joinToString(\", \") {\n            it.toHexString()\n        }\n    }\n\n    @TypeConverter\n    fun toWitness(data: String): List<ByteArray> = when {\n        data.isEmpty() -> listOf()\n        else -> data.split(\", \").map {\n            it.hexToByteArray()\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/InvalidTransactionDao.kt",
    "content": "package io.horizontalsystems.bitcoincore.storage\n\nimport androidx.room.Dao\nimport androidx.room.Insert\nimport androidx.room.OnConflictStrategy\nimport androidx.room.Query\nimport io.horizontalsystems.bitcoincore.models.InvalidTransaction\n\n@Dao\ninterface InvalidTransactionDao {\n\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    fun insert(transaction: InvalidTransaction)\n\n    @Query(\"DELETE FROM InvalidTransaction\")\n    fun deleteAll()\n\n    @Query(\"DELETE FROM InvalidTransaction where uid = :uid\")\n    fun delete(uid: String)\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/PeerAddressDao.kt",
    "content": "package io.horizontalsystems.bitcoincore.storage\n\nimport androidx.room.*\nimport io.horizontalsystems.bitcoincore.models.PeerAddress\n\n@Dao\ninterface PeerAddressDao {\n    @Insert(onConflict = OnConflictStrategy.IGNORE)\n    fun insertAll(peers: List<PeerAddress>)\n\n    @Query(\"SELECT * FROM PeerAddress WHERE ip NOT IN(:ips) ORDER BY score ASC, connectionTime ASC LIMIT 1\")\n    fun getLeastScoreFastest(ips: List<String>): PeerAddress?\n\n    @Query(\"UPDATE PeerAddress SET connectionTime = :time, score = score + 1 WHERE ip = :ip\")\n    fun setSuccessConnectionTime(time: Long, ip: String)\n\n    @Delete\n    fun delete(peerAddress: PeerAddress)\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/PublicKeyDao.kt",
    "content": "package io.horizontalsystems.bitcoincore.storage\n\nimport androidx.room.*\nimport io.horizontalsystems.bitcoincore.models.PublicKey\n\n@Dao\ninterface PublicKeyDao {\n\n    @Insert(onConflict = OnConflictStrategy.IGNORE)\n    fun insertOrIgnore(keys: List<PublicKey>)\n\n    @Delete\n    fun delete(publicKey: PublicKey)\n\n    @Query(\"select * from PublicKey\")\n    fun getAll(): List<PublicKey>\n\n    @Query(\"\"\"\n        SELECT k.*, COUNT(o.publicKeyPath) c1, COUNT(bhp.publicKeyPath) c2  FROM PublicKey AS k \n        LEFT JOIN TransactionOutput o ON o.publicKeyPath = k.path \n        LEFT JOIN BlockHashPublicKey bhp on bhp.publicKeyPath = k.path\n        GROUP BY k.path \n        HAVING c1 > 0 OR c2 > 0\n        \"\"\")\n    fun getAllUsed(): List<PublicKey>\n\n    @Query(\"\"\"\n        SELECT k.*, COUNT(o.publicKeyPath) c1, COUNT(bhp.publicKeyPath) c2 FROM PublicKey AS k \n        LEFT JOIN TransactionOutput o ON o.publicKeyPath = k.path \n        LEFT JOIN BlockHashPublicKey bhp on bhp.publicKeyPath = k.path\n        GROUP BY k.path \n        HAVING c1 = 0 AND c2 = 0\n        \"\"\")\n    fun getAllUnused(): List<PublicKey>\n\n    @Query(\"\"\"\n        SELECT k.*, (COUNT(o.publicKeyPath) + COUNT(bhp.publicKeyPath)) usedCount FROM PublicKey AS k \n        LEFT JOIN TransactionOutput o ON o.publicKeyPath = k.path \n        LEFT JOIN BlockHashPublicKey bhp on bhp.publicKeyPath = k.path\n        GROUP BY k.path \n        \"\"\")\n    fun getAllWithUsedState(): List<PublicKeyWithUsedState>\n\n    @Query(\"SELECT * from PublicKey where scriptHashP2WPKH = :keyHash limit 1\")\n    fun getByScriptHashWPKH(keyHash: ByteArray): PublicKey?\n\n    @Query(\"SELECT * from PublicKey where publicKey = :keyHash or publicKeyHash =:keyHash limit 1\")\n    fun getByKeyOrKeyHash(keyHash: ByteArray): PublicKey?\n\n    @Query(\"SELECT * from PublicKey where convertedForP2TR = :hashP2TR limit 1\")\n    fun getByHashP2TR(hashP2TR: ByteArray): PublicKey?\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/SentTransactionDao.kt",
    "content": "package io.horizontalsystems.bitcoincore.storage\n\nimport androidx.room.*\nimport io.horizontalsystems.bitcoincore.models.SentTransaction\n\n@Dao\ninterface SentTransactionDao {\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    fun insert(transaction: SentTransaction)\n\n    @Update(onConflict = OnConflictStrategy.REPLACE)\n    fun update(transaction: SentTransaction)\n\n    @Query(\"select * from SentTransaction where hash = :hash limit 1\")\n    fun getTransaction(hash: ByteArray): SentTransaction?\n\n    @Delete\n    fun delete(transaction: SentTransaction)\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/Storage.kt",
    "content": "package io.horizontalsystems.bitcoincore.storage\n\nimport androidx.sqlite.db.SimpleSQLiteQuery\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.extensions.hexToByteArray\nimport io.horizontalsystems.bitcoincore.extensions.toHexString\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.bitcoincore.models.BlockHash\nimport io.horizontalsystems.bitcoincore.models.BlockHashPublicKey\nimport io.horizontalsystems.bitcoincore.models.BlockchainState\nimport io.horizontalsystems.bitcoincore.models.InvalidTransaction\nimport io.horizontalsystems.bitcoincore.models.PeerAddress\nimport io.horizontalsystems.bitcoincore.models.PublicKey\nimport io.horizontalsystems.bitcoincore.models.SentTransaction\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.models.TransactionFilterType\nimport io.horizontalsystems.bitcoincore.models.TransactionInput\nimport io.horizontalsystems.bitcoincore.models.TransactionMetadata\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport kotlin.math.max\n\nopen class Storage(protected open val store: CoreDatabase) : IStorage {\n\n    override fun downloadedTransactionsBestBlockHeight(): Int {\n        val maxDownloadedHeight = store.block.getLastBlockWithTransactions()?.height ?: 0\n        val maxDiscoveredHeight = store.blockHash.getLastBlockHashByHeight()?.height ?: 0\n        return max(maxDownloadedHeight, maxDiscoveredHeight)\n    }\n\n    // RestoreState\n\n    override val initialRestored: Boolean?\n        get() = store.blockchainState.getState()?.initialRestored\n\n    override fun setInitialRestored(isRestored: Boolean) {\n        store.blockchainState.insert(BlockchainState(initialRestored = isRestored))\n    }\n\n    // PeerAddress\n\n    override fun getLeastScoreFastestPeerAddressExcludingIps(ips: List<String>): PeerAddress? {\n        return store.peerAddress.getLeastScoreFastest(ips)\n    }\n\n    override fun deletePeerAddress(ip: String) {\n        store.peerAddress.delete(PeerAddress(ip))\n    }\n\n    override fun setPeerAddresses(list: List<PeerAddress>) {\n        store.peerAddress.insertAll(list)\n    }\n\n    override fun markConnected(ip: String, time: Long) {\n        store.peerAddress.setSuccessConnectionTime(time, ip)\n    }\n\n    // BlockHash\n\n    override fun getBlockHashesSortedBySequenceAndHeight(limit: Int): List<BlockHash> {\n        return store.blockHash.getBlockHashesSortedSequenceHeight(limit)\n    }\n\n    override fun getBlockHashHeaderHashes(): List<ByteArray> {\n        return store.blockHash.allBlockHashes()\n    }\n\n    override fun getBlockHashHeaderHashes(except: List<ByteArray>): List<ByteArray> {\n        return store.blockHash.allBlockHashes(except)\n    }\n\n    override fun getLastBlockHash(): BlockHash? {\n        return store.blockHash.getLastBlockHash()\n    }\n\n    override fun getApiBlockHashesCount(): Int {\n        return store.blockHash.getApiBlockHashesCount()\n    }\n\n    override fun getBlockchainBlockHashes(): List<BlockHash> {\n        return store.blockHash.getBlockchainBlockHashes()\n    }\n\n    override fun deleteBlockHash(hash: ByteArray) {\n        store.blockHash.delete(hash)\n    }\n\n    override fun addBlockHashes(hashes: List<BlockHash>) {\n        store.blockHash.insertAll(hashes)\n    }\n\n    override fun addBockHashPublicKeys(blockHashPublicKeys: List<BlockHashPublicKey>) {\n        store.blockHashPublicKey.insertAll(blockHashPublicKeys)\n    }\n\n    override fun getLastBlockchainBlockHash(): BlockHash? {\n        return store.blockHash.getLastBlockchainBlockHash()\n    }\n\n    override fun deleteBlockchainBlockHashes() {\n        store.blockHash.delete(height = 0)\n    }\n\n    // Block\n\n    override fun getBlockByHeightStalePrioritized(height: Int): Block? {\n        return store.block.getBlockByHeightStalePrioritized(height)\n    }\n\n    override fun getBlock(height: Int): Block? {\n        return store.block.getBlockByHeight(height)\n    }\n\n    override fun getBlock(hashHash: ByteArray): Block? {\n        return store.block.getBlockByHash(hashHash)\n    }\n\n    override fun getBlock(stale: Boolean, sortedHeight: String): Block? {\n        return if (sortedHeight == \"DESC\") {\n            store.block.getLast(stale)\n        } else {\n            store.block.getFirst(stale)\n        }\n    }\n\n    override fun getBlocks(stale: Boolean): List<Block> {\n        return store.block.getBlocksByStale(stale)\n    }\n\n    override fun getBlocks(heightGreaterThan: Int, sortedBy: String, limit: Int): List<Block> {\n        return store.block.getBlocks(heightGreaterThan, limit)\n    }\n\n    override fun getBlocks(heightGreaterOrEqualTo: Int, stale: Boolean): List<Block> {\n        return store.block.getBlocks(heightGreaterOrEqualTo, stale)\n    }\n\n    override fun getBlocks(hashes: List<ByteArray>): List<Block> {\n        return store.block.getBlocks(hashes)\n    }\n\n    override fun getBlocksChunk(fromHeight: Int, toHeight: Int): List<Block> {\n        return store.block.getBlocksChunk(fromHeight, toHeight)\n    }\n\n    override fun blocksCount(headerHashes: List<ByteArray>?): Int {\n        return if (headerHashes == null) {\n            store.block.count()\n        } else {\n            store.block.getBlocksCount(headerHashes)\n        }\n    }\n\n    override fun addBlock(block: Block) {\n        store.block.insert(block)\n    }\n\n    override fun saveBlock(block: Block) {\n        store.block.insert(block)\n    }\n\n    override fun setBlockPartial(headerHash: ByteArray) {\n        store.block.setBlockPartial(headerHash)\n    }\n\n    override fun lastBlock(): Block? {\n        return store.block.getLastBlock()\n    }\n\n    override fun updateBlock(staleBlock: Block) {\n        store.block.update(staleBlock)\n    }\n\n    override fun deleteBlocks(blocks: List<Block>) {\n        blocks.forEach { block ->\n            val transactions = store.transaction.getBlockTransactions(block.headerHash)\n\n            transactions.forEach {\n                store.input.deleteAll(getTransactionInputs(it))\n                store.output.deleteAll(getTransactionOutputs(it))\n            }\n\n            store.transaction.deleteAll(transactions)\n        }\n\n        store.block.deleteAll(blocks)\n    }\n\n    override fun deleteBlocksWithoutTransactions(toHeight: Int) {\n        store.block.deleteBlocksWithoutTransactions(toHeight)\n    }\n\n    override fun unstaleAllBlocks() {\n        store.block.unstaleAllBlocks()\n    }\n\n    override fun timestamps(from: Int, to: Int): List<Long> {\n        return store.block.getTimestamps(from, to)\n    }\n\n    // Transaction\n\n    override fun getFullTransactionInfo(transactions: List<TransactionWithBlock>): List<FullTransactionInfo> {\n        val txHashes = transactions.map { it.transaction.hash }\n        val inputs = store.input.getInputsWithPrevouts(txHashes)\n        val outputs = store.output.getTransactionsOutputs(txHashes)\n        val metadata = store.transactionMetadata.getTransactionMetadata(txHashes)\n\n        return transactions.map { tx ->\n            FullTransactionInfo(\n                tx.block,\n                if (tx.transaction.status == Transaction.Status.INVALID) InvalidTransaction(\n                    tx.transaction,\n                    tx.transaction.serializedTxInfo,\n                    tx.transaction.rawTransaction\n                ) else tx.transaction,\n                inputs.filter { it.input.transactionHash.contentEquals(tx.transaction.hash) },\n                outputs.filter { it.transactionHash.contentEquals(tx.transaction.hash) },\n                metadata.first { it.transactionHash.contentEquals(tx.transaction.hash) }\n            )\n        }\n    }\n\n    override fun getFullTransactionInfo(fromTransaction: Transaction?, type: TransactionFilterType?, limit: Int?): List<FullTransactionInfo> {\n        var query = \"SELECT transactions.*, Block.*\" +\n                \" FROM (SELECT * FROM `Transaction` UNION ALL SELECT * FROM InvalidTransaction) as transactions\" +\n                \" LEFT JOIN Block ON transactions.blockHash = Block.headerHash\" +\n                \" LEFT JOIN TransactionMetadata ON transactions.hash = TransactionMetadata.transactionHash\"\n\n        val whereConditions = mutableListOf<String>()\n        if (fromTransaction != null) {\n            whereConditions.add(\"(transactions.timestamp < ${fromTransaction.timestamp} OR (transactions.timestamp = ${fromTransaction.timestamp} AND transactions.`order` < ${fromTransaction.order}))\")\n        }\n\n        type?.let { filterType ->\n            val types = filterType.types.map { it.value }.joinToString(\", \")\n            whereConditions.add(\"TransactionMetadata.type IN ($types)\")\n        }\n\n        if (whereConditions.isNotEmpty()) {\n            query += \" WHERE \" + whereConditions.joinToString(\" AND \")\n        }\n\n        query += \" ORDER BY timestamp DESC, `order` DESC\"\n\n        if (limit != null) {\n            query += \" LIMIT $limit\"\n        }\n\n        return getFullTransactionInfo(store.transaction.getTransactionWithBlockBySql(SimpleSQLiteQuery(query)))\n    }\n\n    override fun getFullTransactionInfo(txHash: ByteArray): FullTransactionInfo? {\n        return store.transaction.getTransactionWithBlock(txHash)?.let { tx ->\n            val inputs = store.input.getInputsWithPrevouts(listOf(txHash))\n            val outputs = store.output.getTransactionsOutputs(listOf(txHash))\n            val metadata = store.transactionMetadata.getTransactionMetadata(listOf(txHash))\n\n            FullTransactionInfo(\n                tx.block,\n                tx.transaction,\n                inputs.filter { it.input.transactionHash.contentEquals(tx.transaction.hash) },\n                outputs.filter { it.transactionHash.contentEquals(tx.transaction.hash) },\n                metadata.first { it.transactionHash.contentEquals(tx.transaction.hash) }\n            )\n        }\n    }\n\n    override fun getTransaction(hash: ByteArray): Transaction? {\n        return store.transaction.getByHash(hash)\n    }\n\n    override fun getFullTransaction(hash: ByteArray): FullTransaction? {\n        return store.transaction.getByHash(hash)?.let { convertToFullTransaction(it) }\n    }\n\n    override fun getFullTransactions(transactions: List<Transaction>): List<FullTransaction> {\n        val hashes = transactions.map { it.hash }\n        val inputsByTransaction = store.input.getTransactionInputs(hashes).groupBy { it.transactionHash.toHexString() }\n        val outputsByTransaction = store.output.getTransactionsOutputs(hashes).groupBy { it.transactionHash.toHexString() }\n        val metadataByTransaction = store.transactionMetadata.getTransactionMetadata(hashes).associateBy { it.transactionHash.toHexString() }\n\n        return transactions.map { transaction ->\n            val inputs = inputsByTransaction[transaction.hash.toHexString()] ?: listOf()\n            val outputs = outputsByTransaction[transaction.hash.toHexString()] ?: listOf()\n            FullTransaction(transaction, inputs, outputs, false).apply {\n                metadata = metadataByTransaction[transaction.hash.toHexString()] ?: TransactionMetadata(transaction.hash)\n            }\n        }\n    }\n\n    override fun getValidOrInvalidTransaction(uid: String): Transaction? {\n        return store.transaction.getValidOrInvalidByUid(uid)\n    }\n\n    override fun getTransactionOfOutput(output: TransactionOutput): Transaction? {\n        return store.transaction.getByHash(output.transactionHash)\n    }\n\n    override fun addTransaction(transaction: FullTransaction) {\n        store.runInTransaction {\n            addWithoutTransaction(transaction)\n        }\n    }\n\n    override fun updateTransaction(transaction: Transaction) {\n        store.transaction.update(transaction)\n    }\n\n    override fun updateTransaction(transaction: FullTransaction) {\n        store.runInTransaction {\n            store.transaction.update(transaction.header)\n            transaction.inputs.forEach {\n                store.input.update(it)\n            }\n\n            transaction.outputs.forEach {\n                store.output.update(it)\n            }\n        }\n\n    }\n\n    override fun getBlockTransactions(block: Block): List<Transaction> {\n        return store.transaction.getBlockTransactions(block.headerHash)\n    }\n\n    override fun getNewTransaction(hash: ByteArray): Transaction? {\n        return store.transaction.getNewTransaction(hash)\n    }\n\n    override fun getNewTransactions(): List<FullTransaction> {\n        return store.transaction.getNewTransactions().map { convertToFullTransaction(it) }\n    }\n\n    override fun isRelayedTransactionExists(hash: ByteArray): Boolean {\n        return store.transaction.getByHashAndStatus(hash, Transaction.Status.RELAYED) != null\n    }\n\n    override fun isTransactionExists(hash: ByteArray): Boolean {\n        return store.transaction.getByHash(hash) != null\n    }\n\n    override fun getConflictingTransactions(transaction: FullTransaction): List<Transaction> {\n        val txHashes = HashSet<String>()\n        transaction.inputs.forEach { input ->\n            store.input.getInput(input.previousOutputTxHash, input.previousOutputIndex)?.transactionHash?.let { txHash ->\n                txHashes.add(txHash.toHexString())\n            }\n        }\n\n        txHashes.remove(transaction.header.hash.toHexString())\n\n        return if (txHashes.isNotEmpty()) {\n            txHashes.mapNotNull {\n                store.transaction.getByHash(it.hexToByteArray())\n            }\n        } else {\n            listOf()\n        }\n    }\n\n    override fun getIncomingPendingTxHashes(): List<ByteArray> {\n        return store.transaction.getIncomingPendingTxHashes()\n    }\n\n    override fun incomingPendingTransactionsExist(): Boolean {\n        return store.transaction.getIncomingPendingTxCount() > 0\n    }\n\n    private fun convertToFullTransaction(transaction: Transaction): FullTransaction {\n        return FullTransaction(header = transaction, inputs = getTransactionInputs(transaction), outputs = getTransactionOutputs(transaction))\n    }\n\n    private fun addWithoutTransaction(transaction: FullTransaction) {\n        store.transaction.insert(transaction.header)\n        store.transactionMetadata.insert(transaction.metadata)\n\n        transaction.inputs.forEach {\n            store.input.insert(it)\n        }\n\n        transaction.outputs.forEach {\n            store.output.insert(it)\n        }\n    }\n\n    // InvalidTransaction\n\n    override fun getInvalidTransaction(hash: ByteArray): InvalidTransaction? {\n        return store.transaction.getInvalidTransaction(hash)\n    }\n\n    override fun getDescendantTransactionsFullInfo(txHash: ByteArray): List<FullTransactionInfo> {\n        val fullTransactionInfo = getFullTransactionInfo(txHash) ?: return listOf()\n        val list = mutableListOf(fullTransactionInfo)\n\n        val inputs = getTransactionInputsByPrevOutputTxHash(fullTransactionInfo.header.hash)\n\n        inputs.forEach { input ->\n            val descendantTxs = getDescendantTransactionsFullInfo(input.transactionHash)\n            list.addAll(descendantTxs)\n        }\n\n        return list\n    }\n\n    override fun getDescendantTransactions(txHash: ByteArray): List<Transaction> {\n        val transaction = getTransaction(txHash) ?: return listOf()\n        val list = mutableListOf(transaction)\n\n        val inputs = getTransactionInputsByPrevOutputTxHash(txHash)\n\n        inputs.forEach { input ->\n            val descendantTxs = getDescendantTransactions(input.transactionHash)\n            list.addAll(descendantTxs)\n        }\n\n        return list\n    }\n\n    override fun moveTransactionToInvalidTransactions(invalidTransactions: List<InvalidTransaction>) {\n        store.runInTransaction {\n            invalidTransactions.forEach { invalidTransaction ->\n                store.invalidTransaction.insert(invalidTransaction)\n\n                val inputs = store.input.getInputsWithPrevouts(listOf(invalidTransaction.hash))\n                inputs.forEach { input ->\n                    input.previousOutput?.let {\n                        store.output.markFailedToSpend(it.transactionHash, it.index)\n                    }\n                }\n\n                store.input.deleteByTxHash(invalidTransaction.hash)\n\n                store.output.deleteByTxHash(invalidTransaction.hash)\n\n                store.transaction.deleteByHash(invalidTransaction.hash)\n            }\n        }\n    }\n\n    override fun moveInvalidTransactionToTransactions(invalidTransaction: InvalidTransaction, toTransactions: FullTransaction) {\n        store.runInTransaction {\n            addWithoutTransaction(toTransactions)\n            store.invalidTransaction.delete(invalidTransaction.uid)\n        }\n    }\n\n    override fun deleteAllInvalidTransactions() {\n        store.invalidTransaction.deleteAll()\n    }\n\n    // TransactionOutput\n\n    override fun getUnspentOutputs(): List<UnspentOutput> {\n        return store.output.getUnspents()\n    }\n\n    override fun getPreviousOutput(input: TransactionInput): TransactionOutput? {\n        return store.output.getPreviousOutput(input.previousOutputTxHash, input.previousOutputIndex.toInt())\n    }\n\n    override fun getOutput(transactionHash: ByteArray, index: Int): TransactionOutput? {\n        return store.output.getPreviousOutput(transactionHash, index)\n    }\n\n    override fun getTransactionOutputs(transaction: Transaction): List<TransactionOutput> {\n        return store.output.getByHash(transaction.hash)\n    }\n\n    override fun getTransactionOutputsCount(hash: ByteArray): Int {\n        return store.output.getCountByHash(hash)\n    }\n\n    override fun getOutputsOfPublicKey(publicKey: PublicKey): List<TransactionOutput> {\n        return store.output.getListByPath(publicKey.path)\n    }\n\n    override fun getMyOutputs(): List<TransactionOutput> {\n        return store.output.getMyOutputs()\n    }\n\n    override fun getOutputsForBloomFilter(blockHeight: Int, irregularScriptTypes: List<ScriptType>): List<TransactionOutput> {\n        return store.output.getOutputsForBloomFilter(blockHeight, irregularScriptTypes.map { it.value })\n    }\n\n    // TransactionInput\n\n    override fun getTransactionInputs(transaction: Transaction): List<TransactionInput> {\n        return store.input.getTransactionInputs(transaction.hash)\n    }\n\n    override fun getTransactionInputs(txHash: ByteArray): List<TransactionInput> {\n        return store.input.getTransactionInputs(txHash)\n    }\n\n    override fun getTransactionInputs(txHashes: List<ByteArray>): List<TransactionInput> {\n        return store.input.getTransactionInputs(txHashes)\n    }\n\n    override fun getTransactionInput(previousOutputTxHash: ByteArray, previousOutputIndex: Long): TransactionInput? {\n        return store.input.getInput(previousOutputTxHash, previousOutputIndex)\n    }\n\n    override fun getTransactionInputsByPrevOutputTxHash(txHash: ByteArray): List<TransactionInput> {\n        return store.input.getInputsByPrevOutputTxHash(txHash)\n    }\n\n    // PublicKey\n\n    override fun getPublicKeyByHashP2TR(hashP2TR: ByteArray): PublicKey? {\n        return store.publicKey.getByHashP2TR(hashP2TR)\n    }\n\n    override fun getPublicKeyByScriptHashForP2PWKH(keyHash: ByteArray): PublicKey? {\n        return store.publicKey.getByScriptHashWPKH(keyHash)\n    }\n\n    override fun getPublicKeyByKeyOrKeyHash(keyHash: ByteArray): PublicKey? {\n        return store.publicKey.getByKeyOrKeyHash(keyHash)\n    }\n\n    override fun getPublicKeys(): List<PublicKey> {\n        return store.publicKey.getAll()\n    }\n\n    override fun getPublicKeysUsed(): List<PublicKey> {\n        return store.publicKey.getAllUsed()\n    }\n\n    override fun getPublicKeysUnused(): List<PublicKey> {\n        return store.publicKey.getAllUnused()\n    }\n\n    override fun getPublicKeysWithUsedState(): List<PublicKeyWithUsedState> {\n        return store.publicKey.getAllWithUsedState()\n    }\n\n    override fun savePublicKeys(keys: List<PublicKey>) {\n        store.publicKey.insertOrIgnore(keys)\n    }\n\n    // SentTransaction\n\n    override fun getSentTransaction(hash: ByteArray): SentTransaction? {\n        return store.sentTransaction.getTransaction(hash)\n    }\n\n    override fun addSentTransaction(transaction: SentTransaction) {\n        store.sentTransaction.insert(transaction)\n    }\n\n    override fun updateSentTransaction(transaction: SentTransaction) {\n        store.sentTransaction.insert(transaction)\n    }\n\n    override fun deleteSentTransaction(transaction: SentTransaction) {\n        store.sentTransaction.delete(transaction)\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/TransactionDao.kt",
    "content": "package io.horizontalsystems.bitcoincore.storage\n\nimport androidx.sqlite.db.SupportSQLiteQuery\nimport androidx.room.*\nimport io.horizontalsystems.bitcoincore.models.InvalidTransaction\nimport io.horizontalsystems.bitcoincore.models.Transaction\n\n@Dao\ninterface TransactionDao {\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    fun insert(transaction: Transaction)\n\n    @Update(onConflict = OnConflictStrategy.REPLACE)\n    fun update(transaction: Transaction)\n\n    @Query(\"select * from `Transaction` where hash = :hash and status = :status\")\n    fun getByHashAndStatus(hash: ByteArray, status: Int): Transaction?\n\n    @Query(\"select * from `Transaction` where hash = :hash\")\n    fun getByHash(hash: ByteArray): Transaction?\n\n    @Query(\"select * from (SELECT * FROM `Transaction` UNION ALL SELECT * FROM InvalidTransaction) where uid = :uid\")\n    fun getValidOrInvalidByUid(uid: String): Transaction?\n\n    @Query(\"select * from `Transaction` where blockHash = :blockHash\")\n    fun getBlockTransactions(blockHash: ByteArray): List<Transaction>\n\n    @Query(\"select * from `Transaction` where hash = :hash and status = 1 limit 1\")\n    fun getNewTransaction(hash: ByteArray): Transaction?\n\n    @Query(\"select * from `Transaction` where status = 1\")\n    fun getNewTransactions(): List<Transaction>\n\n    @RawQuery\n    fun getTransactionWithBlockBySql(query: SupportSQLiteQuery): List<TransactionWithBlock>\n\n    @Query(\"SELECT * FROM `Transaction` t LEFT JOIN Block b ON t.blockHash = b.headerHash WHERE hash = :hash\")\n    fun getTransactionWithBlock(hash: ByteArray): TransactionWithBlock?\n\n    @Query(\"SELECT hash FROM `Transaction` WHERE blockHash IS NULL AND isOutgoing = 0 AND isMine = 1\")\n    fun getIncomingPendingTxHashes(): List<ByteArray>\n\n    @Query(\"SELECT COUNT(*) FROM `Transaction` WHERE blockHash IS NULL AND isOutgoing = 0 AND isMine = 1\")\n    fun getIncomingPendingTxCount(): Int\n\n    @Query(\"SELECT * FROM InvalidTransaction WHERE hash = :hash\")\n    fun getInvalidTransaction(hash: ByteArray): InvalidTransaction?\n\n    @Delete\n    fun delete(transaction: Transaction)\n\n    @Query(\"DELETE FROM `Transaction` WHERE hash=:hash\")\n    fun deleteByHash(hash: ByteArray)\n\n    @Delete\n    fun deleteAll(transactions: List<Transaction>)\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/TransactionInputDao.kt",
    "content": "package io.horizontalsystems.bitcoincore.storage\n\nimport androidx.room.*\nimport io.horizontalsystems.bitcoincore.models.TransactionInput\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\n\n@Dao\ninterface TransactionInputDao {\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    fun insert(input: TransactionInput)\n\n    @Update(onConflict = OnConflictStrategy.REPLACE)\n    fun update(input: TransactionInput)\n\n    @Delete\n    fun delete(input: TransactionInput)\n\n    @Delete\n    fun deleteAll(inputs: List<TransactionInput>)\n\n    @Query(\"DELETE FROM TransactionInput WHERE transactionHash = :txHash\")\n    fun deleteByTxHash(txHash: ByteArray)\n\n    @Query(\"select * from TransactionInput where transactionHash = :hash order by rowId\")\n    fun getTransactionInputs(hash: ByteArray): List<TransactionInput>\n\n    @Query(\"select * from TransactionInput where transactionHash IN (:hashes)\")\n    fun getTransactionInputs(hashes: List<ByteArray>): List<TransactionInput>\n\n    @Query(\"select * from TransactionOutput where transactionHash=:transactionHash AND `index`=:index limit 1\")\n    fun output(transactionHash: ByteArray, index: Long): TransactionOutput?\n\n    @Transaction\n    fun getInputsWithPrevouts(txHashes: List<ByteArray>) =\n        getTransactionInputs(txHashes).map { input ->\n            val prevOutput = output(input.previousOutputTxHash, input.previousOutputIndex)\n            InputWithPreviousOutput(input, prevOutput)\n        }\n\n    @Query(\"SELECT * FROM TransactionInput WHERE previousOutputTxHash = :txHash\")\n    fun getInputsByPrevOutputTxHash(txHash: ByteArray): List<TransactionInput>\n\n    @Query(\"SELECT * FROM TransactionInput where TransactionInput.previousOutputTxHash = :prevOutputTxHash and TransactionInput.previousOutputIndex = :prevOutputIndex\")\n    fun getInput(prevOutputTxHash: ByteArray, prevOutputIndex: Long): TransactionInput?\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/TransactionMetadataDao.kt",
    "content": "package io.horizontalsystems.bitcoincore.storage\n\nimport androidx.room.Dao\nimport androidx.room.Insert\nimport androidx.room.OnConflictStrategy\nimport androidx.room.Query\nimport io.horizontalsystems.bitcoincore.models.TransactionMetadata\n\n@Dao\ninterface TransactionMetadataDao {\n\n    @Query(\"SELECT * FROM `TransactionMetadata` WHERE `transactionHash` IN (:txHashes)\")\n    fun getTransactionMetadata(txHashes: List<ByteArray>): List<TransactionMetadata>\n\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    fun insert(metadata: TransactionMetadata)\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/TransactionOutputDao.kt",
    "content": "package io.horizontalsystems.bitcoincore.storage\n\nimport androidx.room.Dao\nimport androidx.room.Delete\nimport androidx.room.Insert\nimport androidx.room.OnConflictStrategy\nimport androidx.room.Query\nimport androidx.room.Update\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\n\n@Dao\ninterface TransactionOutputDao {\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    fun insert(output: TransactionOutput)\n\n    @Update(onConflict = OnConflictStrategy.REPLACE)\n    fun update(output: TransactionOutput)\n\n    @Query(\"select * from transactionOutput where transactionHash in (:txHashes)\")\n    fun getTransactionsOutputs(txHashes: List<ByteArray>): List<TransactionOutput>\n\n    @Delete\n    fun delete(output: TransactionOutput)\n\n    @Delete\n    fun deleteAll(outputs: List<TransactionOutput>)\n\n    @Query(\"DELETE FROM TransactionOutput WHERE transactionHash = :txHash\")\n    fun deleteByTxHash(txHash: ByteArray)\n\n    @Query(\"\"\"\n        SELECT TransactionOutput.*, PublicKey.*, `Transaction`.*, Block.*\n        FROM TransactionOutput\n          INNER JOIN PublicKey ON TransactionOutput.publicKeyPath = PublicKey.path\n          INNER JOIN `Transaction` ON TransactionOutput.transactionHash = `Transaction`.hash\n          LEFT JOIN Block ON `Transaction`.blockHash = Block.headerHash\n        WHERE TransactionOutput.scriptType != 0 AND `Transaction`.conflictingTxHash IS NULL\n          AND NOT EXISTS (SELECT previousOutputIndex FROM TransactionInput where TransactionInput.previousOutputTxHash = TransactionOutput.transactionHash and TransactionInput.previousOutputIndex = TransactionOutput.`index`)\n    \"\"\")\n    fun getUnspents(): List<UnspentOutput>\n\n    @Query(\"select * from TransactionOutput where transactionHash = :previousOutputTxHash and `index` = :previousOutputIndex limit 1\")\n    fun getPreviousOutput(previousOutputTxHash: ByteArray, previousOutputIndex: Int): TransactionOutput?\n\n    @Query(\"select * from TransactionOutput where transactionHash = :hash order by rowId\")\n    fun getByHash(hash: ByteArray): List<TransactionOutput>\n\n    @Query(\"select count(*) from TransactionOutput where transactionHash = :hash\")\n    fun getCountByHash(hash: ByteArray): Int\n\n    @Query(\"UPDATE `transactionOutput` SET failedToSpend = 1 WHERE transactionHash = :transactionHash AND `index` = :index\")\n    fun markFailedToSpend(transactionHash: ByteArray, index: Int)\n\n    @Query(\"select * from TransactionOutput where publicKeyPath = :path\")\n    fun getListByPath(path: String): List<TransactionOutput>\n\n    @Query(\"SELECT * FROM TransactionOutput WHERE publicKeyPath IS NOT NULL\")\n    fun getMyOutputs(): List<TransactionOutput>\n\n    @Query(\"\"\"\n        SELECT outputs.*\n        FROM TransactionOutput AS outputs\n        INNER JOIN PublicKey as publicKey ON outputs.publicKeyPath = publicKey.path\n        LEFT JOIN (\n          SELECT\n            inputs.previousOutputIndex,\n            inputs.previousOutputTxHash,\n            inputs.transactionHash AS txHash,\n            Block.*\n          FROM TransactionInput AS inputs\n          INNER JOIN `Transaction` AS transactions ON inputs.transactionHash = transactions.hash\n          LEFT JOIN Block ON transactions.blockHash = Block.headerHash\n        )\n        AS input ON input.previousOutputTxHash = outputs.transactionHash AND input.previousOutputIndex = outputs.`index`\n        WHERE outputs.scriptType IN(:irregularScriptTypes) AND (height IS NULL OR height > :blockHeight)\n    \"\"\")\n    fun getOutputsForBloomFilter(blockHeight: Int, irregularScriptTypes: List<Int>): List<TransactionOutput>\n\n}\n\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/migrations/Migration_10_11.kt",
    "content": "package io.horizontalsystems.bitcoincore.storage.migrations\n\nimport androidx.room.migration.Migration\nimport androidx.sqlite.db.SupportSQLiteDatabase\n\nobject Migration_10_11 : Migration(10, 11) {\n    override fun migrate(database: SupportSQLiteDatabase) {\n        database.execSQL(\"ALTER TABLE `TransactionOutput` ADD COLUMN `failedToSpend` INTEGER DEFAULT 0 NOT NULL\")\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/migrations/Migration_11_12.kt",
    "content": "package io.horizontalsystems.bitcoincore.storage.migrations\n\nimport androidx.room.migration.Migration\nimport androidx.sqlite.db.SupportSQLiteDatabase\n\nobject Migration_11_12 : Migration(11, 12) {\n    override fun migrate(database: SupportSQLiteDatabase) {\n        database.execSQL(\"DELETE FROM `PeerAddress`\")\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/migrations/Migration_12_13.kt",
    "content": "package io.horizontalsystems.bitcoincore.storage.migrations\n\nimport android.database.Cursor\nimport androidx.room.migration.Migration\nimport androidx.sqlite.db.SupportSQLiteDatabase\nimport io.horizontalsystems.bitcoincore.models.*\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.bitcoincore.storage.WitnessConverter\nimport io.horizontalsystems.bitcoincore.transactions.extractors.ITransactionOutputProvider\nimport io.horizontalsystems.bitcoincore.transactions.extractors.MyOutputsCache\nimport io.horizontalsystems.bitcoincore.transactions.extractors.TransactionMetadataExtractor\n\nobject Migration_12_13 : Migration(12, 13) {\n\n    private val __witnessConverter = WitnessConverter()\n    private val __scriptTypeConverter = ScriptTypeConverter()\n\n    override fun migrate(database: SupportSQLiteDatabase) {\n        createTableTransactionMetadata(database)\n        createMetadataForExistingTransactions(database)\n        deleteInvalidTransactions(database)\n    }\n\n    private fun deleteInvalidTransactions(database: SupportSQLiteDatabase) {\n        database.execSQL(\"DELETE FROM `InvalidTransaction`\")\n    }\n\n    private fun createTableTransactionMetadata(database: SupportSQLiteDatabase) {\n        database.execSQL(\"CREATE TABLE IF NOT EXISTS `TransactionMetadata` (`amount` INTEGER NOT NULL, `type` INTEGER NOT NULL, `fee` INTEGER, `transactionHash` BLOB NOT NULL, PRIMARY KEY(`transactionHash`))\")\n    }\n\n    private fun createMetadataForExistingTransactions(database: SupportSQLiteDatabase) {\n        val transactions = getTransactions(database)\n        val inputs = getTransactionInputs(database)\n        val outputs = getTransactionOutputs(database)\n\n        val myOutputsCache = MyOutputsCache().apply {\n            add(outputs)\n        }\n\n        val outputProvider = object : ITransactionOutputProvider {\n            override fun get(transactionHash: ByteArray, index: Int): TransactionOutput? {\n                return outputs.find {\n                    it.transactionHash.contentEquals(transactionHash) && it.index == index\n                }\n            }\n        }\n\n        val metadataExtractor = TransactionMetadataExtractor(myOutputsCache, outputProvider)\n        transactions.forEach { transaction ->\n            val transactionInputs = inputs.filter {\n                it.transactionHash.contentEquals(transaction.hash)\n            }\n\n            val transactionOutputs = outputs.filter {\n                it.transactionHash.contentEquals(transaction.hash)\n            }\n\n            val fullTransaction =\n                FullTransaction(transaction, transactionInputs, transactionOutputs)\n            metadataExtractor.extract(fullTransaction)\n\n            insertTransactionMetadata(database, fullTransaction.metadata)\n        }\n    }\n\n    private fun insertTransactionMetadata(database: SupportSQLiteDatabase, metadata: TransactionMetadata) {\n        database.execSQL(\n            \"INSERT OR REPLACE INTO `TransactionMetadata` (transactionHash,amount,type,fee) VALUES(?, ?, ?, ?)\",\n            arrayOf(metadata.transactionHash, metadata.amount, metadata.type.value, metadata.fee)\n        )\n    }\n\n    private fun getTransactionOutputs(database: SupportSQLiteDatabase): List<TransactionOutput> {\n        val _cursor = database.query(\"SELECT * FROM TransactionOutput ORDER BY rowId\")\n        return try {\n            val _cursorIndexOfValue = getColumnIndexOrThrow(_cursor, \"value\")\n            val _cursorIndexOfLockingScript =\n                getColumnIndexOrThrow(_cursor, \"lockingScript\")\n            val _cursorIndexOfRedeemScript =\n                getColumnIndexOrThrow(_cursor, \"redeemScript\")\n            val _cursorIndexOfIndex = getColumnIndexOrThrow(_cursor, \"index\")\n            val _cursorIndexOfTransactionHash =\n                getColumnIndexOrThrow(_cursor, \"transactionHash\")\n            val _cursorIndexOfPublicKeyPath =\n                getColumnIndexOrThrow(_cursor, \"publicKeyPath\")\n            val _cursorIndexOfChangeOutput =\n                getColumnIndexOrThrow(_cursor, \"changeOutput\")\n            val _cursorIndexOfScriptType = getColumnIndexOrThrow(_cursor, \"scriptType\")\n            val _cursorIndexOfKeyHash = getColumnIndexOrThrow(_cursor, \"keyHash\")\n            val _cursorIndexOfAddress = getColumnIndexOrThrow(_cursor, \"address\")\n            val _cursorIndexOfFailedToSpend =\n                getColumnIndexOrThrow(_cursor, \"failedToSpend\")\n            val _cursorIndexOfPluginId = getColumnIndexOrThrow(_cursor, \"pluginId\")\n            val _cursorIndexOfPluginData = getColumnIndexOrThrow(_cursor, \"pluginData\")\n            val _result: MutableList<TransactionOutput> = ArrayList(_cursor.count)\n            while (_cursor.moveToNext()) {\n                val _item: TransactionOutput\n                _item = TransactionOutput()\n                _item.value = _cursor.getLong(_cursorIndexOfValue)\n                _item.lockingScript = _cursor.getBlob(_cursorIndexOfLockingScript)\n                _item.redeemScript = _cursor.getBlob(_cursorIndexOfRedeemScript)\n                _item.index = _cursor.getInt(_cursorIndexOfIndex)\n                _item.transactionHash = _cursor.getBlob(_cursorIndexOfTransactionHash)\n                _item.publicKeyPath = _cursor.getString(_cursorIndexOfPublicKeyPath)\n                _item.changeOutput = _cursor.getInt(_cursorIndexOfChangeOutput) != 0\n                val _tmp_1 = if (_cursor.isNull(_cursorIndexOfScriptType)) {\n                    null\n                } else {\n                    _cursor.getInt(_cursorIndexOfScriptType)\n                }\n                __scriptTypeConverter.fromInt(_tmp_1)?.let {\n                    _item.scriptType = it\n                }\n                _item.lockingScriptPayload = _cursor.getBlob(_cursorIndexOfKeyHash)\n                _item.address = _cursor.getString(_cursorIndexOfAddress)\n                _item.failedToSpend = _cursor.getInt(_cursorIndexOfFailedToSpend) != 0\n                _item.pluginId = if (_cursor.isNull(_cursorIndexOfPluginId)) {\n                    null\n                } else {\n                    _cursor.getShort(_cursorIndexOfPluginId).toByte()\n                }\n                _item.pluginData = _cursor.getString(_cursorIndexOfPluginData)\n                _result.add(_item)\n            }\n            _result\n        } finally {\n            _cursor.close()\n        }\n    }\n\n    private fun getTransactionInputs(database: SupportSQLiteDatabase): List<TransactionInput> {\n        val _cursor = database.query(\"SELECT * FROM TransactionInput\")\n        return try {\n            val _cursorIndexOfTransactionHash =\n                getColumnIndexOrThrow(_cursor, \"transactionHash\")\n            val _cursorIndexOfKeyHash = getColumnIndexOrThrow(_cursor, \"keyHash\")\n            val _cursorIndexOfAddress = getColumnIndexOrThrow(_cursor, \"address\")\n            val _cursorIndexOfWitness = getColumnIndexOrThrow(_cursor, \"witness\")\n            val _cursorIndexOfPreviousOutputTxHash =\n                getColumnIndexOrThrow(_cursor, \"previousOutputTxHash\")\n            val _cursorIndexOfPreviousOutputIndex =\n                getColumnIndexOrThrow(_cursor, \"previousOutputIndex\")\n            val _cursorIndexOfSigScript = getColumnIndexOrThrow(_cursor, \"sigScript\")\n            val _cursorIndexOfSequence = getColumnIndexOrThrow(_cursor, \"sequence\")\n            val _result: MutableList<TransactionInput> = ArrayList(_cursor.count)\n            while (_cursor.moveToNext()) {\n                val _item: TransactionInput\n                _item = TransactionInput(\n                    _cursor.getBlob(_cursorIndexOfPreviousOutputTxHash),\n                    _cursor.getLong(_cursorIndexOfPreviousOutputIndex),\n                    _cursor.getBlob(_cursorIndexOfSigScript),\n                    _cursor.getLong(_cursorIndexOfSequence)\n                )\n                _item.transactionHash = _cursor.getBlob(_cursorIndexOfTransactionHash)\n                _item.lockingScriptPayload = _cursor.getBlob(_cursorIndexOfKeyHash)\n                _item.address = _cursor.getString(_cursorIndexOfAddress)\n                _item.witness = __witnessConverter.toWitness(_cursor.getString(_cursorIndexOfWitness))\n                _result.add(_item)\n            }\n            _result\n        } finally {\n            _cursor.close()\n        }\n    }\n\n    private fun getTransactions(database: SupportSQLiteDatabase): List<Transaction> {\n        val _cursor = database.query(\"SELECT * FROM `Transaction`\")\n        return try {\n            val _cursorIndexOfUid = getColumnIndexOrThrow(_cursor, \"uid\")\n            val _cursorIndexOfHash = getColumnIndexOrThrow(_cursor, \"hash\")\n            val _cursorIndexOfBlockHash = getColumnIndexOrThrow(_cursor, \"blockHash\")\n            val _cursorIndexOfVersion = getColumnIndexOrThrow(_cursor, \"version\")\n            val _cursorIndexOfLockTime = getColumnIndexOrThrow(_cursor, \"lockTime\")\n            val _cursorIndexOfTimestamp = getColumnIndexOrThrow(_cursor, \"timestamp\")\n            val _cursorIndexOfOrder = getColumnIndexOrThrow(_cursor, \"order\")\n            val _cursorIndexOfIsMine = getColumnIndexOrThrow(_cursor, \"isMine\")\n            val _cursorIndexOfIsOutgoing = getColumnIndexOrThrow(_cursor, \"isOutgoing\")\n            val _cursorIndexOfSegwit = getColumnIndexOrThrow(_cursor, \"segwit\")\n            val _cursorIndexOfStatus = getColumnIndexOrThrow(_cursor, \"status\")\n            val _cursorIndexOfSerializedTxInfo =\n                getColumnIndexOrThrow(_cursor, \"serializedTxInfo\")\n            val _cursorIndexOfConflictingTxHash =\n                getColumnIndexOrThrow(_cursor, \"conflictingTxHash\")\n            val _cursorIndexOfRawTransaction =\n                getColumnIndexOrThrow(_cursor, \"rawTransaction\")\n            val _result: MutableList<Transaction> = ArrayList(_cursor.count)\n            while (_cursor.moveToNext()) {\n                val _item: Transaction\n                _item = Transaction()\n                _item.uid = _cursor.getString(_cursorIndexOfUid)\n                _item.hash = _cursor.getBlob(_cursorIndexOfHash)\n                _item.blockHash = _cursor.getBlob(_cursorIndexOfBlockHash)\n                _item.version = _cursor.getInt(_cursorIndexOfVersion)\n                _item.lockTime = _cursor.getLong(_cursorIndexOfLockTime)\n                _item.timestamp = _cursor.getLong(_cursorIndexOfTimestamp)\n                _item.order = _cursor.getInt(_cursorIndexOfOrder)\n                _item.isMine = _cursor.getInt(_cursorIndexOfIsMine) != 0\n                _item.isOutgoing = _cursor.getInt(_cursorIndexOfIsOutgoing) != 0\n                _item.segwit = _cursor.getInt(_cursorIndexOfSegwit) != 0\n                _item.status = _cursor.getInt(_cursorIndexOfStatus)\n                _item.serializedTxInfo = _cursor.getString(_cursorIndexOfSerializedTxInfo)\n                _item.conflictingTxHash = _cursor.getBlob(_cursorIndexOfConflictingTxHash)\n                _item.rawTransaction = _cursor.getString(_cursorIndexOfRawTransaction)\n                _result.add(_item)\n            }\n            _result\n        } finally {\n            _cursor.close()\n        }\n    }\n\n    private fun getColumnIndexOrThrow(c: Cursor, name: String): Int {\n        val index = c.getColumnIndex(name)\n        return when {\n            index >= 0 -> index\n            else -> c.getColumnIndexOrThrow(\"`$name`\")\n        }\n    }\n\n\n}"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/migrations/Migration_13_14.kt",
    "content": "package io.horizontalsystems.bitcoincore.storage.migrations\n\nimport androidx.room.migration.Migration\nimport androidx.sqlite.db.SupportSQLiteDatabase\n\nobject Migration_13_14 : Migration(13, 14) {\n\n    override fun migrate(database: SupportSQLiteDatabase) {\n        database.execSQL(\"ALTER TABLE Block ADD COLUMN partial INTEGER DEFAULT 0 NOT NULL\")\n        database.execSQL(\"UPDATE Block SET partial = 1 WHERE headerHash IN (SELECT headerHash FROM BlockHash)\")\n    }\n\n}"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/migrations/Migration_14_15.kt",
    "content": "package io.horizontalsystems.bitcoincore.storage.migrations\n\nimport androidx.room.migration.Migration\nimport androidx.sqlite.db.SupportSQLiteDatabase\n\nobject Migration_14_15 : Migration(14, 15) {\n\n    override fun migrate(database: SupportSQLiteDatabase) {\n        addColumnToPublicKey(database)\n        renameColumnsInTransactionInput(database)\n        renameColumnsInTransactionOutput(database)\n    }\n\n    private fun addColumnToPublicKey(database: SupportSQLiteDatabase) {\n        database.execSQL(\"ALTER TABLE PublicKey ADD COLUMN convertedForP2TR BLOB DEFAULT '' NOT NULL\")\n        database.execSQL(\"CREATE INDEX IF NOT EXISTS `index_PublicKey_convertedForP2TR` ON `PublicKey` (`convertedForP2TR`)\")\n    }\n\n    private fun renameColumnsInTransactionInput(database: SupportSQLiteDatabase) {\n        database.execSQL(\"ALTER TABLE `TransactionInput` RENAME TO `TmpTransactionInput`\")\n\n        database.execSQL(\"CREATE TABLE IF NOT EXISTS `TransactionInput` (`transactionHash` BLOB NOT NULL, `lockingScriptPayload` BLOB, `address` TEXT, `witness` TEXT NOT NULL, `previousOutputTxHash` BLOB NOT NULL, `previousOutputIndex` INTEGER NOT NULL, `sigScript` BLOB NOT NULL, `sequence` INTEGER NOT NULL, PRIMARY KEY(`previousOutputTxHash`, `previousOutputIndex`), FOREIGN KEY(`transactionHash`) REFERENCES `Transaction`(`hash`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)\")\n        database.execSQL(\"INSERT OR REPLACE INTO `TransactionInput` (`transactionHash`,`lockingScriptPayload`,`address`,`witness`,`previousOutputTxHash`,`previousOutputIndex`,`sigScript`,`sequence`) SELECT `transactionHash`,`keyHash`,`address`,`witness`,`previousOutputTxHash`,`previousOutputIndex`,`sigScript`,`sequence` FROM `TmpTransactionInput`\")\n\n        database.execSQL(\"DROP TABLE IF EXISTS `TmpTransactionInput`\")\n    }\n\n    private fun renameColumnsInTransactionOutput(database: SupportSQLiteDatabase) {\n        database.execSQL(\"ALTER TABLE `TransactionOutput` RENAME TO `TmpTransactionOutput`\")\n\n        database.execSQL(\"CREATE TABLE IF NOT EXISTS `TransactionOutput` (`value` INTEGER NOT NULL, `lockingScript` BLOB NOT NULL, `redeemScript` BLOB, `index` INTEGER NOT NULL, `transactionHash` BLOB NOT NULL, `publicKeyPath` TEXT, `changeOutput` INTEGER NOT NULL, `scriptType` INTEGER NOT NULL, `lockingScriptPayload` BLOB, `address` TEXT, `failedToSpend` INTEGER NOT NULL, `pluginId` INTEGER, `pluginData` TEXT, PRIMARY KEY(`transactionHash`, `index`), FOREIGN KEY(`publicKeyPath`) REFERENCES `PublicKey`(`path`) ON UPDATE SET NULL ON DELETE SET NULL DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY(`transactionHash`) REFERENCES `Transaction`(`hash`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)\")\n        database.execSQL(\"INSERT OR REPLACE INTO `TransactionOutput` (`value`,`lockingScript`,`redeemScript`,`index`,`transactionHash`,`publicKeyPath`,`changeOutput`,`scriptType`,`lockingScriptPayload`,`address`,`failedToSpend`,`pluginId`,`pluginData`) SELECT `value`,`lockingScript`,`redeemScript`,`index`,`transactionHash`,`publicKeyPath`,`changeOutput`,`scriptType`,`keyHash`,`address`,`failedToSpend`,`pluginId`,`pluginData` FROM `TmpTransactionOutput`\")\n\n        database.execSQL(\"DROP TABLE IF EXISTS `TmpTransactionOutput`\")\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/migrations/Migration_15_16.kt",
    "content": "package io.horizontalsystems.bitcoincore.storage.migrations\n\nimport androidx.room.migration.Migration\nimport androidx.sqlite.db.SupportSQLiteDatabase\n\nobject Migration_15_16 : Migration(15, 16) {\n\n    override fun migrate(database: SupportSQLiteDatabase) {\n        database.execSQL(\"UPDATE `TransactionOutput` SET `lockingScriptPayload` = SUBSTR(`lockingScriptPayload`, 3) WHERE scriptType = 4 and publicKeyPath is not null\")\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/migrations/Migration_16_17.kt",
    "content": "package io.horizontalsystems.bitcoincore.storage.migrations\n\nimport androidx.room.migration.Migration\nimport androidx.sqlite.db.SupportSQLiteDatabase\n\nobject Migration_16_17 : Migration(16, 17) {\n\n    override fun migrate(db: SupportSQLiteDatabase) {\n        db.execSQL(\"CREATE TABLE IF NOT EXISTS `BlockHashPublicKey` (`blockHash` BLOB NOT NULL, `publicKeyPath` TEXT NOT NULL, PRIMARY KEY(`blockHash`, `publicKeyPath`), FOREIGN KEY(`blockHash`) REFERENCES `BlockHash`(`headerHash`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED, FOREIGN KEY(`publicKeyPath`) REFERENCES `PublicKey`(`path`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)\")\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/migrations/Migration_17_18.kt",
    "content": "package io.horizontalsystems.bitcoincore.storage.migrations\n\nimport androidx.room.migration.Migration\nimport androidx.sqlite.db.SupportSQLiteDatabase\n\nobject Migration_17_18 : Migration(17, 18) {\n\n    override fun migrate(database: SupportSQLiteDatabase) {\n        database.execSQL(\"ALTER TABLE `TransactionInput` RENAME TO `TmpTransactionInput`\")\n\n        database.execSQL(\"CREATE TABLE IF NOT EXISTS `TransactionInput` (`previousOutputTxHash` BLOB NOT NULL, `previousOutputIndex` INTEGER NOT NULL, `sigScript` BLOB NOT NULL, `sequence` INTEGER NOT NULL, `transactionHash` BLOB NOT NULL, `lockingScriptPayload` BLOB, `address` TEXT, `witness` TEXT NOT NULL, PRIMARY KEY(`previousOutputTxHash`, `previousOutputIndex`, `sequence`), FOREIGN KEY(`transactionHash`) REFERENCES `Transaction`(`hash`) ON UPDATE CASCADE ON DELETE CASCADE DEFERRABLE INITIALLY DEFERRED)\")\n        database.execSQL(\"INSERT OR REPLACE INTO `TransactionInput` (`transactionHash`,`lockingScriptPayload`,`address`,`witness`,`previousOutputTxHash`,`previousOutputIndex`,`sigScript`,`sequence`) SELECT `transactionHash`,`lockingScriptPayload`,`address`,`witness`,`previousOutputTxHash`,`previousOutputIndex`,`sigScript`,`sequence` FROM `TmpTransactionInput`\")\n\n        database.execSQL(\"DROP TABLE IF EXISTS `TmpTransactionInput`\")\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/storage/migrations/Migration_18_19.kt",
    "content": "package io.horizontalsystems.bitcoincore.storage.migrations\n\nimport android.content.ContentValues\nimport android.database.sqlite.SQLiteDatabase\nimport android.util.Log\nimport androidx.room.migration.Migration\nimport androidx.sqlite.db.SupportSQLiteDatabase\nimport io.horizontalsystems.bitcoincore.managers.PublicKeyManager\n\nobject Migration_18_19 : Migration(18, 19) {\n\n    override fun migrate(database: SupportSQLiteDatabase) {\n        try {\n            database.beginTransaction()\n\n            migratePublicKeyPath(database)\n            migrateTransactionOutput(database)\n            migrateBlockHashPublicKey(database)\n\n            database.setTransactionSuccessful()\n        } catch (error: Throwable) {\n            Log.e(\"e\", \"error in migration\", error)\n        } finally {\n            database.endTransaction()\n        }\n    }\n\n    private fun migratePublicKeyPath(database: SupportSQLiteDatabase) {\n        var cursor = database.query(\"SELECT * FROM `PublicKey`\")\n        val publicKeyPathIndex = cursor.getColumnIndex(\"path\")\n\n        if (publicKeyPathIndex >= 0) {\n\n            while (cursor.moveToNext()) {\n                val path = cursor.getString(publicKeyPathIndex)\n                val fixedPath = fixedPath(path)\n\n                database.update(\n                    /* table = */ \"PublicKey\",\n                    /* conflictAlgorithm = */ SQLiteDatabase.CONFLICT_IGNORE,\n                    /* values = */ ContentValues().apply { put(\"path\", \"tmp-$fixedPath\") },\n                    /* whereClause = */ \"path = ?\",\n                    /* whereArgs = */ arrayOf(path)\n                )\n            }\n\n            cursor = database.query(\"SELECT * FROM `PublicKey`\")\n\n            while (cursor.moveToNext()) {\n                val path = cursor.getString(publicKeyPathIndex)\n                val fixedPath = path.removePrefix(\"tmp-\")\n\n                database.update(\n                    /* table = */ \"PublicKey\",\n                    /* conflictAlgorithm = */ SQLiteDatabase.CONFLICT_IGNORE,\n                    /* values = */ ContentValues().apply { put(\"path\", fixedPath) },\n                    /* whereClause = */ \"path = ?\",\n                    /* whereArgs = */ arrayOf(path)\n                )\n            }\n        }\n    }\n\n    private fun migrateTransactionOutput(database: SupportSQLiteDatabase) {\n        val cursor = database.query(\"SELECT * FROM `TransactionOutput` WHERE publicKeyPath IS NOT NULL\")\n        val publicKeyPathIndex = cursor.getColumnIndex(\"publicKeyPath\")\n        val transactionHashIndex = cursor.getColumnIndex(\"transactionHash\")\n        val indexIndex = cursor.getColumnIndex(\"index\")\n\n        if (publicKeyPathIndex >= 0 && transactionHashIndex >= 0 && indexIndex >= 0) {\n            while (cursor.moveToNext()) {\n                val transactionHash = cursor.getBlob(transactionHashIndex)\n                val index = cursor.getString(indexIndex)\n                val path = cursor.getString(publicKeyPathIndex)\n                val fixedPath = fixedPath(path)\n\n                database.update(\n                    /* table = */ \"TransactionOutput\",\n                    /* conflictAlgorithm = */ SQLiteDatabase.CONFLICT_IGNORE,\n                    /* values = */ ContentValues().apply { put(\"publicKeyPath\", fixedPath) },\n                    /* whereClause = */ \"transactionHash = ? AND `index` = ?\",\n                    /* whereArgs = */ arrayOf(transactionHash, index)\n                )\n            }\n        }\n    }\n\n    private fun migrateBlockHashPublicKey(database: SupportSQLiteDatabase) {\n        val cursor = database.query(\"SELECT * FROM `BlockHashPublicKey` WHERE publicKeyPath IS NOT NULL\")\n        val publicKeyPathIndex = cursor.getColumnIndex(\"publicKeyPath\")\n        val blockHashIndex = cursor.getColumnIndex(\"blockHash\")\n\n        if (publicKeyPathIndex >= 0 && blockHashIndex >= 0) {\n            while (cursor.moveToNext()) {\n                val blockHash = cursor.getBlob(blockHashIndex)\n                val path = cursor.getString(publicKeyPathIndex)\n                val fixedPath = fixedPath(path)\n\n                database.update(\n                    /* table = */ \"BlockHashPublicKey\",\n                    /* conflictAlgorithm = */ SQLiteDatabase.CONFLICT_IGNORE,\n                    /* values = */ ContentValues().apply {\n                        put(\"publicKeyPath\", fixedPath)\n                    },\n                    /* whereClause = */ \"blockHash = ? AND publicKeyPath = ?\",\n                    /* whereArgs = */ arrayOf(blockHash, path)\n                )\n            }\n        }\n    }\n\n    private fun fixedPath(path: String): String {\n        val parts = path.split(\"/\").map { it.toInt() }\n        if (parts.size != 3) return path\n        val account = parts[0]\n        val change = if (parts[1] == 0) 1 else 0\n        val index = parts[2]\n        return \"$account/$change/$index\"\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/BlockTransactionProcessor.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions\n\nimport io.horizontalsystems.bitcoincore.WatchedTransactionManager\nimport io.horizontalsystems.bitcoincore.blocks.IBlockchainDataListener\nimport io.horizontalsystems.bitcoincore.core.IPublicKeyManager\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.core.inTopologicalOrder\nimport io.horizontalsystems.bitcoincore.managers.BloomFilterManager\nimport io.horizontalsystems.bitcoincore.managers.IIrregularOutputFinder\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.bitcoincore.transactions.extractors.TransactionExtractor\n\nclass BlockTransactionProcessor(\n    private val storage: IStorage,\n    private val extractor: TransactionExtractor,\n    private val publicKeyManager: IPublicKeyManager,\n    private val irregularOutputFinder: IIrregularOutputFinder,\n    private val dataListener: IBlockchainDataListener,\n    private val conflictsResolver: TransactionConflictsResolver,\n    private val invalidator: TransactionInvalidator\n) {\n\n    var transactionListener: WatchedTransactionManager? = null\n\n    private fun resolveConflicts(fullTransaction: FullTransaction) {\n        for (transaction in conflictsResolver.getTransactionsConflictingWithInBlockTransaction(fullTransaction)) {\n            transaction.conflictingTxHash = fullTransaction.header.hash\n            invalidator.invalidate(transaction)\n        }\n    }\n\n    @Throws(BloomFilterManager.BloomFilterExpired::class)\n    fun processReceived(transactions: List<FullTransaction>, block: Block, skipCheckBloomFilter: Boolean) {\n        var needToUpdateBloomFilter = false\n\n        val inserted = mutableListOf<Transaction>()\n        val updated = mutableListOf<Transaction>()\n\n        // when the same transaction came in merkle block and from another peer's mempool we need to process it serial\n        synchronized(this) {\n            for ((index, fullTransaction) in transactions.inTopologicalOrder().withIndex()) {\n                val transaction = fullTransaction.header\n                val existingTransaction = storage.getFullTransaction(transaction.hash)\n                if (existingTransaction != null) {\n                    extractor.extract(existingTransaction)\n                    transactionListener?.onTransactionReceived(existingTransaction)\n                    relay(existingTransaction.header, index, block)\n                    resolveConflicts(fullTransaction)\n\n                    storage.updateTransaction(existingTransaction)\n                    updated.add(existingTransaction.header)\n\n                    continue\n                }\n\n                extractor.extract(fullTransaction)\n                transactionListener?.onTransactionReceived(fullTransaction)\n\n                if (!transaction.isMine) {\n                    conflictsResolver.getIncomingPendingTransactionsConflictingWith(fullTransaction).forEach { tx ->\n                        tx.conflictingTxHash = fullTransaction.header.hash\n                        invalidator.invalidate(tx)\n                        needToUpdateBloomFilter = true\n                    }\n\n                    continue\n                }\n\n                relay(transaction, index, block)\n                resolveConflicts(fullTransaction)\n\n\n                val invalidTransaction = storage.getInvalidTransaction(transaction.hash)\n                if (invalidTransaction != null) {\n                    storage.moveInvalidTransactionToTransactions(invalidTransaction, fullTransaction)\n                    updated.add(transaction)\n                } else {\n                    storage.addTransaction(fullTransaction)\n                    inserted.add(transaction)\n                }\n\n                if (!skipCheckBloomFilter) {\n                    needToUpdateBloomFilter = needToUpdateBloomFilter ||\n                            publicKeyManager.gapShifts() ||\n                            irregularOutputFinder.hasIrregularOutput(fullTransaction.outputs)\n                }\n            }\n        }\n\n        if (inserted.isNotEmpty() || updated.isNotEmpty()) {\n            if (!block.hasTransactions) {\n                block.hasTransactions = true\n                storage.updateBlock(block)\n\n            }\n            dataListener.onTransactionsUpdate(inserted, updated, block)\n        }\n\n        if (needToUpdateBloomFilter) {\n            throw BloomFilterManager.BloomFilterExpired\n        }\n    }\n\n\n    private fun relay(transaction: Transaction, order: Int, block: Block) {\n        transaction.blockHash = block.headerHash\n        transaction.timestamp = block.timestamp\n        transaction.conflictingTxHash = null\n        transaction.status = Transaction.Status.RELAYED\n        transaction.order = order\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/PendingTransactionProcessor.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions\n\nimport io.horizontalsystems.bitcoincore.WatchedTransactionManager\nimport io.horizontalsystems.bitcoincore.blocks.IBlockchainDataListener\nimport io.horizontalsystems.bitcoincore.core.IPublicKeyManager\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.core.inTopologicalOrder\nimport io.horizontalsystems.bitcoincore.extensions.toReversedHex\nimport io.horizontalsystems.bitcoincore.managers.BloomFilterManager\nimport io.horizontalsystems.bitcoincore.managers.IIrregularOutputFinder\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.models.TransactionType\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.bitcoincore.transactions.extractors.TransactionExtractor\n\nclass PendingTransactionProcessor(\n    private val storage: IStorage,\n    private val extractor: TransactionExtractor,\n    private val publicKeyManager: IPublicKeyManager,\n    private val irregularOutputFinder: IIrregularOutputFinder,\n    private val dataListener: IBlockchainDataListener,\n    private val conflictsResolver: TransactionConflictsResolver,\n    private val ignorePendingIncoming: Boolean\n) {\n\n    private val notMineTransactions = HashSet<ByteArray>()\n\n    var transactionListener: WatchedTransactionManager? = null\n\n    private fun resolveConflicts(transaction: FullTransaction, updated: MutableList<Transaction>) {\n        val conflictingTransactions = conflictsResolver.getTransactionsConflictingWithPendingTransaction(transaction)\n\n        for (conflictingTransaction in conflictingTransactions) {\n            for (descendantTransaction in storage.getDescendantTransactions(conflictingTransaction.hash)) {\n                descendantTransaction.conflictingTxHash = transaction.header.hash\n                storage.updateTransaction(descendantTransaction)\n                updated.add(descendantTransaction)\n            }\n        }\n    }\n\n    fun processCreated(transaction: FullTransaction) {\n        if (storage.getTransaction(transaction.header.hash) != null) {\n            throw TransactionCreator.TransactionAlreadyExists(\"hash = ${transaction.header.hash.toReversedHex()}\")\n        }\n\n        extractor.extract(transaction)\n        storage.addTransaction(transaction)\n\n        try {\n            dataListener.onTransactionsUpdate(listOf(transaction.header), listOf(), null)\n        } catch (e: Exception) {\n            // ignore any exception since the tx is inserted to the db\n        }\n\n        if (irregularOutputFinder.hasIrregularOutput(transaction.outputs)) {\n            throw BloomFilterManager.BloomFilterExpired\n        }\n    }\n\n    @Throws(BloomFilterManager.BloomFilterExpired::class)\n    fun processReceived(transactions: List<FullTransaction>, skipCheckBloomFilter: Boolean) {\n        var needToUpdateBloomFilter = false\n\n        val inserted = mutableListOf<Transaction>()\n        val updated = mutableListOf<Transaction>()\n\n        // when the same transaction came in merkle block and from another peer's mempool we need to process it serial\n        synchronized(this) {\n            for ((index, transaction) in transactions.inTopologicalOrder().withIndex()) {\n                if (notMineTransactions.any { it.contentEquals(transaction.header.hash) }) {\n                    // already processed this transaction with same state\n                    continue\n                }\n\n                val invalidTransaction = storage.getInvalidTransaction(transaction.header.hash)\n                if (invalidTransaction != null) {\n                    // if some peer send us transaction after it's invalidated, we must ignore it\n                    continue\n                }\n\n                val existingTransaction = storage.getTransaction(transaction.header.hash)\n                if (existingTransaction != null) {\n                    resolveConflicts(transaction, updated)\n\n                    if (existingTransaction.status == Transaction.Status.RELAYED) {\n                        // if comes again from memPool we don't need to update it\n                        continue\n                    }\n\n                    relay(existingTransaction, index)\n\n                    storage.updateTransaction(existingTransaction)\n                    updated.add(existingTransaction)\n\n                    continue\n                }\n\n                relay(transaction.header, index)\n                extractor.extract(transaction)\n                transactionListener?.onTransactionReceived(transaction)\n\n                if (!transaction.header.isMine) {\n                    notMineTransactions.add(transaction.header.hash)\n\n                    conflictsResolver.getIncomingPendingTransactionsConflictingWith(transaction).forEach { tx ->\n                        // Former incoming transaction is conflicting with current transaction\n                        tx.conflictingTxHash = transaction.header.hash\n                        storage.updateTransaction(tx)\n                        updated.add(tx)\n                    }\n\n                    continue\n                }\n\n                resolveConflicts(transaction, updated)\n                if (ignorePendingIncoming && transaction.metadata.type == TransactionType.Incoming) {\n                    continue\n                }\n                storage.addTransaction(transaction)\n                inserted.add(transaction.header)\n\n                if (!skipCheckBloomFilter) {\n                    val checkDoubleSpend = !transaction.header.isOutgoing\n                    needToUpdateBloomFilter = needToUpdateBloomFilter ||\n                            checkDoubleSpend ||\n                            publicKeyManager.gapShifts() ||\n                            irregularOutputFinder.hasIrregularOutput(transaction.outputs)\n                }\n            }\n        }\n\n        if (inserted.isNotEmpty() || updated.isNotEmpty()) {\n            dataListener.onTransactionsUpdate(inserted, updated, null)\n        }\n\n        if (needToUpdateBloomFilter) {\n            throw BloomFilterManager.BloomFilterExpired\n        }\n    }\n\n    private fun relay(transaction: Transaction, order: Int) {\n        transaction.status = Transaction.Status.RELAYED\n        transaction.order = order\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/SendTransactionsOnPeersSynced.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions\n\nimport io.horizontalsystems.bitcoincore.blocks.IPeerSyncListener\n\nclass SendTransactionsOnPeersSynced(var transactionSender: TransactionSender) : IPeerSyncListener {\n\n    override fun onAllPeersSynced() {\n        transactionSender.sendPendingTransactions()\n    }\n\n}\n\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/TransactionConflictsResolver.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions\n\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\n\nclass TransactionConflictsResolver(private val storage: IStorage) {\n\n    // Only pending transactions may be conflicting with a transaction in block. No need to check that\\\n    fun getTransactionsConflictingWithInBlockTransaction(transaction: FullTransaction): List<Transaction> {\n        return getConflictingTransactionsForTransaction(transaction)\n    }\n\n    fun getTransactionsConflictingWithPendingTransaction(transaction: FullTransaction): List<Transaction> {\n        val conflictingTransactions = getConflictingTransactionsForTransaction(transaction)\n\n        if (conflictingTransactions.isEmpty()) return listOf()\n\n        // If any of conflicting transactions is already in a block, then current transaction is invalid and non of them is conflicting with it.\n        if (conflictingTransactions.any { it.blockHash != null }) return listOf()\n\n        val conflictingFullTransactions = storage.getFullTransactions(conflictingTransactions)\n        return conflictingFullTransactions\n            // If an existing transaction has a conflicting input with higher sequence,\n            // then mempool transaction most probably has been received before\n            // and the existing transaction is a replacement transaction that is not relayed in mempool yet.\n            // Other cases are theoretically possible, but highly unlikely\n            .filter { !existingHasHigherSequence(mempoolTransaction = transaction, existingTransaction = it) }\n            .map { it.header }\n    }\n\n    private fun existingHasHigherSequence(mempoolTransaction: FullTransaction, existingTransaction: FullTransaction): Boolean {\n        existingTransaction.inputs.forEach { existingInput ->\n            val mempoolInput = mempoolTransaction.inputs.firstOrNull { mempoolInput ->\n                mempoolInput.previousOutputTxHash.contentEquals(existingInput.previousOutputTxHash)\n                        && mempoolInput.previousOutputIndex == existingInput.previousOutputIndex\n            }\n            if (mempoolInput != null && mempoolInput.sequence < existingInput.sequence)\n                return true\n        }\n\n        return false\n    }\n\n    fun getIncomingPendingTransactionsConflictingWith(transaction: FullTransaction): List<Transaction> {\n        val incomingPendingTxHashes = storage.getIncomingPendingTxHashes()\n\n        if (incomingPendingTxHashes.isEmpty()) return listOf()\n\n        val conflictingTransactionHashes = storage\n            .getTransactionInputs(incomingPendingTxHashes)\n            .filter { input ->\n                transaction.inputs.any { it.previousOutputIndex == input.previousOutputIndex && it.previousOutputTxHash.contentEquals(input.previousOutputTxHash) }\n            }\n            .map {\n                it.transactionHash\n            }\n\n        if (conflictingTransactionHashes.isEmpty()) return listOf()\n\n        return conflictingTransactionHashes.mapNotNull {\n            storage.getTransaction(it)\n        }.filter {\n            it.blockHash == null\n        }\n    }\n\n    // Checks if the transactions has a conflicting input with higher sequence\n    fun isTransactionReplaced(transaction: FullTransaction): Boolean {\n        val conflictingTransactions = getConflictingTransactionsForTransaction(transaction)\n\n        if (conflictingTransactions.isEmpty() || conflictingTransactions.any { it.blockHash == null }) {\n            return false\n        }\n\n        val conflictingFullTransactions = storage.getFullTransactions(conflictingTransactions)\n\n        return conflictingFullTransactions\n            .any { existingHasHigherSequence(mempoolTransaction = transaction, existingTransaction = it) }\n    }\n\n    private fun getConflictingTransactionsForTransaction(transaction: FullTransaction): List<Transaction> {\n        return transaction.inputs.mapNotNull { input ->\n            val conflictingTxHash = storage.getTransactionInput(input.previousOutputTxHash, input.previousOutputIndex)?.transactionHash\n            when {\n                conflictingTxHash == null -> null\n                conflictingTxHash.contentEquals(transaction.header.hash) -> null\n                else -> conflictingTxHash\n            }\n        }.mapNotNull {\n            storage.getTransaction(it)\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/TransactionCreator.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions\n\nimport io.horizontalsystems.bitcoincore.core.IPluginData\nimport io.horizontalsystems.bitcoincore.managers.BloomFilterManager\nimport io.horizontalsystems.bitcoincore.models.TransactionDataSortType\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\nimport io.horizontalsystems.bitcoincore.storage.UtxoFilters\nimport io.horizontalsystems.bitcoincore.transactions.builder.MutableTransaction\nimport io.horizontalsystems.bitcoincore.transactions.builder.TransactionBuilder\nimport io.horizontalsystems.bitcoincore.transactions.builder.TransactionSigner\n\nclass TransactionCreator(\n    private val builder: TransactionBuilder,\n    private val processor: PendingTransactionProcessor,\n    private val transactionSender: TransactionSender,\n    private val transactionSigner: TransactionSigner,\n    private val bloomFilterManager: BloomFilterManager\n) {\n\n    @Throws\n    fun create(\n        toAddress: String,\n        memo: String?,\n        value: Long,\n        feeRate: Int,\n        senderPay: Boolean,\n        sortType: TransactionDataSortType,\n        unspentOutputs: List<UnspentOutput>?,\n        pluginData: Map<Byte, IPluginData>,\n        rbfEnabled: Boolean,\n        changeToFirstInput: Boolean,\n        filters: UtxoFilters\n    ): FullTransaction {\n        val mutableTransaction = builder.buildTransaction(\n            toAddress = toAddress,\n            memo = memo,\n            value = value,\n            feeRate = feeRate,\n            senderPay = senderPay,\n            sortType = sortType,\n            unspentOutputs = unspentOutputs,\n            pluginData = pluginData,\n            rbfEnabled = rbfEnabled,\n            changeToFirstInput = changeToFirstInput,\n            filters = filters,\n        )\n\n        return create(mutableTransaction)\n    }\n\n    @Throws\n    fun create(\n        unspentOutput: UnspentOutput,\n        toAddress: String,\n        memo: String?,\n        feeRate: Int,\n        sortType: TransactionDataSortType,\n        rbfEnabled: Boolean\n    ): FullTransaction {\n        val mutableTransaction = builder.buildTransaction(unspentOutput, toAddress, memo, feeRate, sortType, rbfEnabled)\n\n        return create(mutableTransaction)\n    }\n\n    fun create(mutableTransaction: MutableTransaction): FullTransaction {\n        transactionSigner.sign(mutableTransaction)\n\n        val fullTransaction = mutableTransaction.build()\n        processAndSend(fullTransaction)\n\n        return fullTransaction\n    }\n\n    private fun processAndSend(transaction: FullTransaction): FullTransaction {\n        transactionSender.canSendTransaction()\n\n        try {\n            processor.processCreated(transaction)\n        } catch (ex: BloomFilterManager.BloomFilterExpired) {\n            bloomFilterManager.regenerateBloomFilter()\n        }\n\n        try {\n            transactionSender.sendPendingTransactions()\n        } catch (e: Exception) {\n            // ignore any exception since the tx is inserted to the db\n        }\n\n        return transaction\n    }\n\n    open class TransactionCreationException(msg: String) : Exception(msg)\n    class TransactionAlreadyExists(msg: String) : TransactionCreationException(msg)\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/TransactionFeeCalculator.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions\n\nimport io.horizontalsystems.bitcoincore.core.IPluginData\nimport io.horizontalsystems.bitcoincore.core.IPublicKeyManager\nimport io.horizontalsystems.bitcoincore.core.IRecipientSetter\nimport io.horizontalsystems.bitcoincore.models.BitcoinSendInfo\nimport io.horizontalsystems.bitcoincore.models.TransactionDataSortType\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\nimport io.horizontalsystems.bitcoincore.storage.UtxoFilters\nimport io.horizontalsystems.bitcoincore.transactions.builder.InputSetter\nimport io.horizontalsystems.bitcoincore.transactions.builder.MutableTransaction\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport io.horizontalsystems.bitcoincore.utils.AddressConverterChain\n\nclass TransactionFeeCalculator(\n    private val recipientSetter: IRecipientSetter,\n    private val inputSetter: InputSetter,\n    private val addressConverter: AddressConverterChain,\n    private val publicKeyManager: IPublicKeyManager,\n    private val changeScriptType: ScriptType,\n) {\n\n    fun sendInfo(\n        value: Long,\n        feeRate: Int,\n        senderPay: Boolean,\n        toAddress: String?,\n        memo: String?,\n        unspentOutputs: List<UnspentOutput>?,\n        pluginData: Map<Byte, IPluginData>,\n        changeToFirstInput: Boolean,\n        filters: UtxoFilters\n    ): BitcoinSendInfo {\n        val mutableTransaction = MutableTransaction()\n\n        recipientSetter.setRecipient(\n            mutableTransaction = mutableTransaction,\n            toAddress = toAddress ?: sampleAddress(),\n            value = value,\n            pluginData = pluginData,\n            skipChecking = true,\n            memo = memo\n        )\n\n        val outputInfo = inputSetter.setInputs(\n            mutableTransaction = mutableTransaction,\n            feeRate = feeRate,\n            senderPay = senderPay,\n            unspentOutputs = unspentOutputs,\n            sortType = TransactionDataSortType.None,\n            rbfEnabled = false,\n            changeToFirstInput = changeToFirstInput,\n            filters = filters,\n        )\n\n        val inputsTotalValue = mutableTransaction.inputsToSign.sumOf { it.previousOutput.value }\n        val outputsTotalValue = mutableTransaction.recipientValue + mutableTransaction.changeValue\n\n        return BitcoinSendInfo(\n            unspentOutputs = outputInfo.unspentOutputs,\n            fee = inputsTotalValue - outputsTotalValue,\n            changeValue = outputInfo.changeInfo?.value,\n            changeAddress = outputInfo.changeInfo?.address\n        )\n    }\n\n    private fun sampleAddress(): String {\n        return addressConverter.convert(\n            publicKey = publicKeyManager.changePublicKey(),\n            scriptType = changeScriptType\n        ).stringValue\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/TransactionInvalidator.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions\n\nimport io.horizontalsystems.bitcoincore.blocks.IBlockchainDataListener\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.core.ITransactionInfoConverter\nimport io.horizontalsystems.bitcoincore.models.InvalidTransaction\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.storage.FullTransactionInfo\n\nclass TransactionInvalidator(\n        private val storage: IStorage,\n        private val transactionInfoConverter: ITransactionInfoConverter,\n        private val listener: IBlockchainDataListener\n) {\n\n    fun invalidate(transaction: Transaction) {\n        val invalidTransactionsFullInfo = storage.getDescendantTransactionsFullInfo(transaction.hash)\n\n        if (invalidTransactionsFullInfo.isEmpty()) return\n\n        invalidTransactionsFullInfo.forEach { fullTxInfo ->\n            fullTxInfo.header.status = Transaction.Status.INVALID\n        }\n\n        val invalidTransactions = invalidTransactionsFullInfo.map { fullTxInfo ->\n            val txInfo = transactionInfoConverter.transactionInfo(fullTxInfo)\n            val serializedTxInfo = txInfo.serialize()\n            InvalidTransaction(fullTxInfo.header, serializedTxInfo, fullTxInfo.rawTransaction)\n        }\n\n        storage.moveTransactionToInvalidTransactions(invalidTransactions)\n        listener.onTransactionsUpdate(listOf(), invalidTransactions, null)\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/TransactionSendTimer.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions\n\nimport java.util.concurrent.Executors\nimport java.util.concurrent.ScheduledFuture\nimport java.util.concurrent.TimeUnit\n\nclass TransactionSendTimer(private val period: Long) {\n\n    interface Listener {\n        fun onTimePassed()\n    }\n\n    var listener: Listener? = null\n\n    private var executor = Executors.newSingleThreadScheduledExecutor()\n    private var task: ScheduledFuture<*>? = null\n\n    @Synchronized\n    fun startIfNotRunning() {\n        if (task == null) {\n            task = executor.scheduleAtFixedRate({ listener?.onTimePassed() }, period, period, TimeUnit.SECONDS)\n        }\n    }\n\n    @Synchronized\n    fun stop() {\n        task?.let {\n            it.cancel(true)\n            task = null\n        }\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/TransactionSender.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions\n\nimport io.horizontalsystems.bitcoincore.BitcoinCore\nimport io.horizontalsystems.bitcoincore.apisync.blockchair.BlockchairApi\nimport io.horizontalsystems.bitcoincore.core.IInitialDownload\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.extensions.toHexString\nimport io.horizontalsystems.bitcoincore.models.SentTransaction\nimport io.horizontalsystems.bitcoincore.network.peer.IPeerTaskHandler\nimport io.horizontalsystems.bitcoincore.network.peer.Peer\nimport io.horizontalsystems.bitcoincore.network.peer.PeerGroup\nimport io.horizontalsystems.bitcoincore.network.peer.PeerManager\nimport io.horizontalsystems.bitcoincore.network.peer.task.PeerTask\nimport io.horizontalsystems.bitcoincore.network.peer.task.SendTransactionTask\nimport io.horizontalsystems.bitcoincore.serializers.TransactionSerializer\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\n\nclass TransactionSender(\n    private val transactionSyncer: TransactionSyncer,\n    private val peerManager: PeerManager,\n    private val initialBlockDownload: IInitialDownload,\n    private val storage: IStorage,\n    private val timer: TransactionSendTimer,\n    private val sendType: BitcoinCore.SendType,\n    private val transactionSerializer: TransactionSerializer,\n    private val maxRetriesCount: Int = 3,\n    private val retriesPeriod: Int = 60\n) : IPeerTaskHandler, TransactionSendTimer.Listener {\n\n    fun sendPendingTransactions() {\n        try {\n            val transactions = transactionSyncer.getNewTransactions()\n            if (transactions.isEmpty()) {\n                timer.stop()\n                return\n            }\n\n            val transactionsToSend = getTransactionsToSend(transactions)\n            if (transactionsToSend.isNotEmpty()) {\n                send(transactionsToSend)\n            }\n\n        } catch (e: PeerGroup.Error) {\n//            logger.warning(\"Handling pending transactions failed with: ${e.message}\")\n        }\n    }\n\n    fun canSendTransaction() {\n        if (getPeersToSend().isEmpty()) {\n            throw PeerGroup.Error(\"peers not synced\")\n        }\n    }\n\n    fun transactionsRelayed(transactions: List<FullTransaction>) {\n        transactions.forEach { transaction ->\n            storage.getSentTransaction(transaction.header.hash)?.let { sentTransaction ->\n                storage.deleteSentTransaction(sentTransaction)\n            }\n        }\n    }\n\n    private fun getTransactionsToSend(transactions: List<FullTransaction>): List<FullTransaction> {\n        return transactions.filter { transaction ->\n            storage.getSentTransaction(transaction.header.hash)?.let { sentTransaction ->\n                sentTransaction.retriesCount < maxRetriesCount && sentTransaction.lastSendTime < (System.currentTimeMillis() - retriesPeriod * 1000)\n            } ?: true\n        }\n    }\n\n    private fun getPeersToSend(): List<Peer> {\n        if (peerManager.peersCount < minConnectedPeerSize) {\n            return emptyList()\n        }\n\n        val freeSyncedPeer = initialBlockDownload.syncedPeers\n            .sortedBy { it.ready } // not ready first\n            .firstOrNull()\n            ?: return emptyList()\n\n        val readyPeers = peerManager.readyPears()\n            .filter { it != freeSyncedPeer }\n            .sortedBy { it.synced } // not synced first\n\n        if (readyPeers.size == 1) {\n            return readyPeers\n        }\n\n        return readyPeers.take(readyPeers.size / 2)\n    }\n\n    private fun send(transactions: List<FullTransaction>) {\n        when (sendType) {\n            BitcoinCore.SendType.P2P -> {\n                sendViaP2P(transactions)\n            }\n\n            is BitcoinCore.SendType.API -> {\n                sendViaAPI(transactions, sendType.blockchairApi)\n            }\n        }\n    }\n\n    private fun sendViaAPI(transactions: List<FullTransaction>, blockchairApi: BlockchairApi) {\n        transactions.forEach { transaction ->\n            try {\n                val hex = transactionSerializer.serialize(transaction).toHexString()\n                blockchairApi.broadcastTransaction(hex)\n\n                transactionSyncer.handleRelayed(listOf(transaction))\n            } catch (error: Throwable) {\n                transactionSyncer.handleInvalid(transaction)\n            }\n        }\n    }\n\n    private fun sendViaP2P(transactions: List<FullTransaction>) {\n        val peers = getPeersToSend()\n        if (peers.isEmpty()) {\n            return\n        }\n\n        timer.startIfNotRunning()\n\n        transactions.forEach { transaction ->\n            transactionSendStart(transaction)\n\n            peers.forEach { peer ->\n                peer.addTask(SendTransactionTask(transaction))\n            }\n        }\n    }\n\n    private fun transactionSendStart(transaction: FullTransaction) {\n        val sentTransaction = storage.getSentTransaction(transaction.header.hash)\n\n        if (sentTransaction == null) {\n            storage.addSentTransaction(SentTransaction(transaction.header.hash))\n        } else {\n            sentTransaction.lastSendTime = System.currentTimeMillis()\n            sentTransaction.sendSuccess = false\n            storage.updateSentTransaction(sentTransaction)\n        }\n    }\n\n    @Synchronized\n    private fun transactionSendSuccess(transaction: FullTransaction) {\n        val sentTransaction = storage.getSentTransaction(transaction.header.hash)\n\n        if (sentTransaction == null || sentTransaction.sendSuccess) {\n            return\n        }\n\n        sentTransaction.retriesCount++\n        sentTransaction.sendSuccess = true\n\n        if (sentTransaction.retriesCount >= maxRetriesCount) {\n            transactionSyncer.handleInvalid(transaction)\n            storage.deleteSentTransaction(sentTransaction)\n        } else {\n            storage.updateSentTransaction(sentTransaction)\n        }\n    }\n\n    // IPeerTaskHandler\n\n    override fun handleCompletedTask(peer: Peer, task: PeerTask): Boolean {\n        return when (task) {\n            is SendTransactionTask -> {\n                transactionSendSuccess(task.transaction)\n                true\n            }\n\n            else -> false\n        }\n    }\n\n    // TransactionSendTimer.Listener\n\n    override fun onTimePassed() {\n        sendPendingTransactions()\n    }\n\n    companion object {\n        const val minConnectedPeerSize = 2\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/TransactionSizeCalculator.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions\n\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.transactions.scripts.OP_RETURN\nimport io.horizontalsystems.bitcoincore.transactions.scripts.OpCodes\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\n\nclass TransactionSizeCalculator {\n    val ecdsaSignatureLength = 72 + 1      // signature length + pushByte\n    val schnorrSignatureLength = 64 + 1     // signature length + pushByte\n    val pubKeyLength = 33 + 1         // pubKey length + pushByte\n    private val p2wpkhShLength = 22 + 1       // 0014<20-byte-script-hash> + pushByte\n\n    private val legacyTx = 16 + 4 + 4 + 16    // 40 Version + number of inputs + number of outputs + locktime\n    private val witnessTx = legacyTx + 1 + 1  // 42 segwit marker + segwit flag\n    private val legacyWitnessData = 1         // 1 Only 0x00 for legacy input\n    // P2WPKH or P2WPKH(SH)\n    val p2wpkhWitnessData = 1 + ecdsaSignatureLength + pubKeyLength   // 108 Number of stack items for input + Size of stack item 0 + Stack item 0, signature + Size of stack item 1 + Stack item 1, pubkey\n    val p2trWitnessData = 1 + schnorrSignatureLength\n\n    private val sigLengths = mapOf(\n            ScriptType.P2PKH to ecdsaSignatureLength + pubKeyLength,\n            ScriptType.P2PK to ecdsaSignatureLength,\n            ScriptType.P2WPKHSH to p2wpkhShLength\n    )\n\n    private val lockingScriptSizes = mapOf(\n            ScriptType.P2PK to 35,\n            ScriptType.P2PKH to 25,\n            ScriptType.P2SH to 23,\n            ScriptType.P2WPKH to 22,\n            ScriptType.P2WSH to 34,\n            ScriptType.P2WPKHSH to 23,\n            ScriptType.P2TR to 34\n    )\n\n    fun outputSize(scripType: ScriptType): Int {\n        return outputSize(lockingScriptSize = getLockingScriptSize(scripType))\n    }\n\n    fun outputSize(lockingScriptSize: Int): Int {\n        return 8 + 1 + lockingScriptSize\n    }\n\n    fun inputSize(scriptType: ScriptType): Int {\n        val sigLength = sigLengths[scriptType] ?: 0\n\n        return 32 + 4 + 1 + sigLength + 4 // PreviousOutputHex + OutputIndex + sigLength + sigScript + sequence\n    }\n\n    /**\n     * Calculate size only for those inputs, which we can sign later in TransactionSigner.\n     * Any other inputs will fail to sign later, so no need to calculate size here.\n     */\n    private fun inputSize(output: TransactionOutput): Int {\n        val scriptSigLength = when (output.scriptType) {\n            ScriptType.P2PK -> ecdsaSignatureLength\n            ScriptType.P2PKH -> ecdsaSignatureLength + pubKeyLength\n            ScriptType.P2WPKHSH -> p2wpkhShLength\n            ScriptType.P2SH -> {\n                output.redeemScript?.let { redeemScript ->\n                    val signatureScriptFunction = output.signatureScriptFunction\n                    if (signatureScriptFunction != null) {\n                        // non-standard P2SH signature script\n                        val emptySignature = ByteArray(ecdsaSignatureLength)\n                        val emptyPublicKey = ByteArray(pubKeyLength)\n\n                        signatureScriptFunction(listOf(emptySignature, emptyPublicKey)).size\n                    } else {\n                        // standard (signature, publicKey, redeemScript) signature script\n                        ecdsaSignatureLength + pubKeyLength + OpCodes.push(redeemScript).size\n                    }\n                } ?: 0\n            }\n            else -> 0\n        }\n\n        return 32 + 4 + 1 + scriptSigLength + 4 // PreviousOutputHex + InputIndex + sigLength + scriptSig + sequence\n    }\n\n    private fun getMemoSize(memo: String?): Int {\n        if (memo == null) return 0\n\n        val memoData = memo.toByteArray(Charsets.UTF_8)\n        val lockingScript = byteArrayOf(OP_RETURN.toByte()) + OpCodes.push(memoData)\n        return outputSize(lockingScriptSize = lockingScript.size) * 4\n    }\n\n    fun transactionSize(\n        previousOutputs: List<TransactionOutput>,\n        outputs: List<TransactionOutput>,\n        memo: String? = null,\n    ): Long {\n        val txIsWitness = previousOutputs.any { it.scriptType.isWitness }\n        val txWeight = if (txIsWitness) witnessTx else legacyTx\n        val inputWeight = previousOutputs.sumOf { inputSize(it) * 4 + if (txIsWitness) witnessSize(it.scriptType) else 0 }\n\n        var outputWeight = 0\n        for (output in outputs) {\n            outputWeight += when (output.scriptType) {\n                ScriptType.NULL_DATA -> outputSize(lockingScriptSize = output.lockingScript.size) * 4\n                ScriptType.UNKNOWN -> throw IllegalStateException(\"Unknown output script type\")\n                else -> outputSize(output.scriptType) * 4\n            }\n        }\n\n        outputWeight += getMemoSize(memo)\n\n        return toBytes(txWeight + inputWeight + outputWeight).toLong()\n    }\n\n    fun transactionSize(\n        previousOutputs: List<TransactionOutput>,\n        outputs: List<ScriptType>,\n        memo: String?,\n        pluginDataOutputSize: Int,\n    ): Long {\n        val txIsWitness = previousOutputs.any { it.scriptType.isWitness }\n        val txWeight = if (txIsWitness) witnessTx else legacyTx\n\n        val inputWeight = previousOutputs.map { inputSize(it) * 4 + if (txIsWitness) witnessSize(it.scriptType) else 0 }.sum()\n        var outputWeight = outputs.map { outputSize(it) }.sum() * 4 // to vbytes\n\n        outputWeight += getMemoSize(memo)\n\n        if (pluginDataOutputSize > 0) {\n            outputWeight += outputSizeByScriptSize(pluginDataOutputSize) * 4\n        }\n\n        return toBytes(txWeight + inputWeight + outputWeight).toLong()\n    }\n\n    private fun outputSizeByScriptSize(size: Int): Int {\n        return 8 + 1 + size            // spentValue + scriptLength + script\n    }\n\n    fun witnessSize(type: ScriptType): Int {  // in vbytes\n        // We assume that only single-key outputs can be here (P2PKH, P2PKH(SH), P2TR)\n        return when(type) {\n            ScriptType.P2WPKH, ScriptType.P2WPKHSH -> p2wpkhWitnessData\n            ScriptType.P2TR -> p2trWitnessData\n            else -> legacyWitnessData\n        }\n\n    }\n\n    private fun toBytes(fee: Int): Int {\n        return (fee / 4) + if (fee % 4 == 0) 0 else 1\n    }\n\n    private fun getLockingScriptSize(scriptType: ScriptType): Int {\n        return lockingScriptSizes[scriptType] ?: 0\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/TransactionSyncer.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions\n\nimport io.horizontalsystems.bitcoincore.core.IPublicKeyManager\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.managers.BloomFilterManager\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\n\nclass TransactionSyncer(\n    private val storage: IStorage,\n    private val transactionProcessor: PendingTransactionProcessor,\n    private val invalidator: TransactionInvalidator,\n    private val publicKeyManager: IPublicKeyManager\n) {\n\n    fun getNewTransactions(): List<FullTransaction> {\n        return storage.getNewTransactions()\n    }\n\n    fun handleRelayed(transactions: List<FullTransaction>) {\n        if (transactions.isEmpty()) return\n\n        var needToUpdateBloomFilter = false\n\n        try {\n            transactionProcessor.processReceived(transactions, false)\n        } catch (e: BloomFilterManager.BloomFilterExpired) {\n            needToUpdateBloomFilter = true\n        }\n\n        if (needToUpdateBloomFilter) {\n            publicKeyManager.fillGap()\n        }\n    }\n\n    fun shouldRequestTransaction(hash: ByteArray): Boolean {\n        return !storage.isRelayedTransactionExists(hash)\n    }\n\n    fun handleInvalid(fullTransaction: FullTransaction) {\n        invalidator.invalidate(fullTransaction.header)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/builder/ECKey.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions.builder\n\nimport io.horizontalsystems.bitcoincore.crypto.schnorr.Schnorr\nimport io.horizontalsystems.hdwalletkit.ECKey\n\nfun ECKey.signSchnorr(input: ByteArray, auxRand: ByteArray = ByteArray(32)): ByteArray {\n    return Schnorr.sign(input, privKeyBytes, auxRand)\n}\n\nfun ECKey.verifySchnorr(input: ByteArray, signature: ByteArray): Boolean {\n    return Schnorr.verify(input, pubKeyXCoord, signature)\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/builder/EcdsaInputSigner.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions.builder\n\nimport io.horizontalsystems.bitcoincore.core.IPrivateWallet\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.network.Network\nimport io.horizontalsystems.bitcoincore.serializers.TransactionSerializer\nimport io.horizontalsystems.bitcoincore.storage.InputToSign\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\n\nclass EcdsaInputSigner(\n    private val hdWallet: IPrivateWallet,\n    private val network: Network\n) {\n\n    fun sigScriptData(transaction: Transaction, inputsToSign: List<InputToSign>, outputs: List<TransactionOutput>, index: Int): List<ByteArray> {\n\n        val input = inputsToSign[index]\n        val prevOutput = input.previousOutput\n        val publicKey = input.previousOutputPublicKey\n\n        val privateKey = checkNotNull(hdWallet.privateKey(publicKey.account, publicKey.index, publicKey.external)) {\n            throw Error.NoPrivateKey()\n        }\n\n        val txContent = TransactionSerializer.serializeForSignature(\n            transaction = transaction,\n            inputsToSign = inputsToSign,\n            outputs = outputs,\n            inputIndex = index,\n            isWitness = prevOutput.scriptType.isWitness || network.sigHashForked\n        ) + byteArrayOf(network.sigHashValue, 0, 0, 0)\n        val signature = privateKey.createSignature(txContent) + network.sigHashValue\n\n        return when (prevOutput.scriptType) {\n            ScriptType.P2PK -> listOf(signature)\n            else -> listOf(signature, publicKey.publicKey)\n        }\n    }\n\n    open class Error : Exception() {\n        class NoPrivateKey : Error()\n        class NoPreviousOutput : Error()\n        class NoPreviousOutputAddress : Error()\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/builder/InputSetter.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions.builder\n\nimport io.horizontalsystems.bitcoincore.DustCalculator\nimport io.horizontalsystems.bitcoincore.core.IPublicKeyManager\nimport io.horizontalsystems.bitcoincore.core.ITransactionDataSorterFactory\nimport io.horizontalsystems.bitcoincore.core.PluginManager\nimport io.horizontalsystems.bitcoincore.managers.IUnspentOutputSelector\nimport io.horizontalsystems.bitcoincore.managers.SelectedUnspentOutputInfo\nimport io.horizontalsystems.bitcoincore.managers.SendValueErrors\nimport io.horizontalsystems.bitcoincore.managers.UnspentOutputQueue\nimport io.horizontalsystems.bitcoincore.models.Address\nimport io.horizontalsystems.bitcoincore.models.TransactionDataSortType\nimport io.horizontalsystems.bitcoincore.models.TransactionInput\nimport io.horizontalsystems.bitcoincore.storage.InputToSign\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\nimport io.horizontalsystems.bitcoincore.storage.UtxoFilters\nimport io.horizontalsystems.bitcoincore.transactions.TransactionSizeCalculator\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport io.horizontalsystems.bitcoincore.utils.IAddressConverter\n\nclass InputSetter(\n    private val unspentOutputSelector: IUnspentOutputSelector,\n    private val publicKeyManager: IPublicKeyManager,\n    private val addressConverter: IAddressConverter,\n    private val changeScriptType: ScriptType,\n    private val transactionSizeCalculator: TransactionSizeCalculator,\n    private val pluginManager: PluginManager,\n    private val dustCalculator: DustCalculator,\n    private val transactionDataSorterFactory: ITransactionDataSorterFactory\n) {\n    fun setInputs(\n        mutableTransaction: MutableTransaction,\n        unspentOutput: UnspentOutput,\n        feeRate: Int,\n        rbfEnabled: Boolean\n    ) {\n        if (unspentOutput.output.scriptType != ScriptType.P2SH) {\n            throw TransactionBuilder.BuilderException.NotSupportedScriptType()\n        }\n\n        // Calculate fee\n        val transactionSize =\n            transactionSizeCalculator.transactionSize(\n                previousOutputs = listOf(unspentOutput.output),\n                outputs = listOf(mutableTransaction.recipientAddress.scriptType),\n                memo = mutableTransaction.memo,\n                pluginDataOutputSize = 0\n            )\n\n        val fee = transactionSize * feeRate\n        val value = unspentOutput.output.value\n        if (value < fee) {\n            throw TransactionBuilder.BuilderException.FeeMoreThanValue()\n        }\n        mutableTransaction.addInput(inputToSign(unspentOutput, rbfEnabled))\n        mutableTransaction.recipientValue = value - fee\n    }\n\n    @Throws(SendValueErrors::class)\n    fun setInputs(\n        mutableTransaction: MutableTransaction,\n        feeRate: Int,\n        senderPay: Boolean,\n        unspentOutputs: List<UnspentOutput>?,\n        sortType: TransactionDataSortType,\n        rbfEnabled: Boolean,\n        changeToFirstInput: Boolean,\n        filters: UtxoFilters\n    ): OutputInfo {\n        val unspentOutputInfo: SelectedUnspentOutputInfo\n        if (unspentOutputs != null) {\n            val params = UnspentOutputQueue.Parameters(\n                value = mutableTransaction.recipientValue,\n                senderPay = senderPay,\n                memo = mutableTransaction.memo,\n                fee = feeRate,\n                outputsLimit = null,\n                outputScriptType = mutableTransaction.recipientAddress.scriptType,\n                changeType = changeScriptType,  // Assuming changeScriptType is defined somewhere\n                pluginDataOutputSize = mutableTransaction.getPluginDataOutputSize(),\n                changeToFirstInput = changeToFirstInput\n            )\n            val queue = UnspentOutputQueue(\n                params,\n                transactionSizeCalculator,\n                dustCalculator,\n            )\n            queue.set(unspentOutputs)\n            unspentOutputInfo = queue.calculate()\n        } else {\n            val value = mutableTransaction.recipientValue\n            unspentOutputInfo = unspentOutputSelector.select(\n                value,\n                mutableTransaction.memo,\n                feeRate,\n                mutableTransaction.recipientAddress.scriptType,  // Assuming changeScriptType is defined somewhere\n                changeScriptType,\n                senderPay,\n                mutableTransaction.getPluginDataOutputSize(),\n                changeToFirstInput,\n                filters\n            )\n        }\n\n        val sortedUnspentOutputs =\n            transactionDataSorterFactory.sorter(sortType).sortUnspents(unspentOutputInfo.outputs)\n\n        for (unspentOutput in sortedUnspentOutputs) {\n            mutableTransaction.addInput(inputToSign(unspentOutput, rbfEnabled))\n        }\n\n        mutableTransaction.recipientValue = unspentOutputInfo.recipientValue\n\n        // Add change output if needed\n        var changeInfo: ChangeInfo? = null\n        unspentOutputInfo.changeValue?.let { changeValue ->\n            val firstOutput = unspentOutputInfo.outputs.firstOrNull()\n            val changeAddress = if (changeToFirstInput && firstOutput != null) {\n                addressConverter.convert(firstOutput.publicKey, firstOutput.output.scriptType)\n            } else {\n                val changePubKey = publicKeyManager.changePublicKey()\n                addressConverter.convert(changePubKey, changeScriptType)\n            }\n\n            mutableTransaction.changeAddress = changeAddress\n            mutableTransaction.changeValue = changeValue\n            changeInfo = ChangeInfo(address = changeAddress, value = changeValue)\n        }\n\n        pluginManager.processInputs(mutableTransaction)\n        return OutputInfo(\n            unspentOutputs = sortedUnspentOutputs,\n            changeInfo = changeInfo\n        )\n    }\n\n    private fun inputToSign(unspentOutput: UnspentOutput, rbfEnabled: Boolean): InputToSign {\n        val previousOutput = unspentOutput.output\n        val sequence = if (rbfEnabled) {\n            0x00\n        } else {\n            0xfffffffe\n        }\n        val transactionInput = TransactionInput(previousOutput.transactionHash, previousOutput.index.toLong(), sequence = sequence)\n\n        return InputToSign(transactionInput, previousOutput, unspentOutput.publicKey)\n    }\n\n    data class ChangeInfo(\n        val address: Address,\n        val value: Long\n    )\n\n    data class OutputInfo(\n        val unspentOutputs: List<UnspentOutput>,\n        val changeInfo: ChangeInfo?\n    )\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/builder/LockTimeSetter.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions.builder\n\nimport io.horizontalsystems.bitcoincore.core.IStorage\n\nclass LockTimeSetter(private val storage: IStorage) {\n\n    fun setLockTime(transaction: MutableTransaction) {\n        transaction.transaction.lockTime = storage.lastBlock()?.height?.toLong() ?: 0\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/builder/MutableTransaction.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions.builder\n\nimport io.horizontalsystems.bitcoincore.models.Address\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.bitcoincore.storage.InputToSign\n\nclass MutableTransaction(isOutgoing: Boolean = true) {\n\n    val transaction = Transaction(2, 0)\n    val inputsToSign = mutableListOf<InputToSign>()\n    var outputs = listOf<TransactionOutput>()\n\n    lateinit var recipientAddress: Address\n    var recipientValue = 0L\n    var memo: String? = null\n    var changeAddress: Address? = null\n    var changeValue = 0L\n\n    private val pluginData = mutableMapOf<Byte, ByteArray>()\n\n    init {\n        transaction.status = Transaction.Status.NEW\n        transaction.isMine = true\n        transaction.isOutgoing = isOutgoing\n    }\n\n    fun getPluginDataOutputSize(): Int {\n        return if (pluginData.isNotEmpty()) {\n            1 + pluginData.map { 1 + it.value.size }.sum()\n        } else {\n            0\n        }\n    }\n\n    fun addInput(inputToSign: InputToSign) {\n        inputsToSign.add(inputToSign)\n    }\n\n    fun addPluginData(id: Byte, data: ByteArray) {\n        pluginData[id] = data\n    }\n\n    fun getPluginData(): Map<Byte, ByteArray> {\n        return pluginData\n    }\n\n    fun build(): FullTransaction {\n        return FullTransaction(transaction, inputsToSign.map { it.input }, outputs)\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/builder/OutputSetter.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions.builder\n\nimport io.horizontalsystems.bitcoincore.core.ITransactionDataSorterFactory\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\nimport io.horizontalsystems.bitcoincore.models.TransactionDataSortType\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.transactions.scripts.OP_RETURN\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\n\nclass OutputSetter(private val transactionDataSorterFactory: ITransactionDataSorterFactory) {\n\n    fun setOutputs(transaction: MutableTransaction, sortType: TransactionDataSortType) {\n        val list = mutableListOf<TransactionOutput>()\n\n        transaction.recipientAddress.let {\n            list.add(TransactionOutput(transaction.recipientValue, 0, it.lockingScript, it.scriptType, it.stringValue, it.lockingScriptPayload))\n        }\n\n        transaction.changeAddress?.let {\n            list.add(TransactionOutput(transaction.changeValue, 0, it.lockingScript, it.scriptType, it.stringValue, it.lockingScriptPayload))\n        }\n\n        if (transaction.getPluginData().isNotEmpty()) {\n            var data = byteArrayOf(OP_RETURN.toByte())\n            transaction.getPluginData().forEach {\n                data += byteArrayOf(it.key) + it.value\n            }\n\n            list.add(TransactionOutput(0, 0, data, ScriptType.NULL_DATA))\n        }\n\n        val sorted = transactionDataSorterFactory.sorter(sortType).sortOutputs(list).toMutableList()\n\n        transaction.memo?.let { memo ->\n            val data = BitcoinOutput()\n                .writeByte(OP_RETURN)\n                .writeString(memo)\n                .toByteArray()\n\n            sorted.add(TransactionOutput(0, 0, data, ScriptType.NULL_DATA))\n        }\n\n        sorted.forEachIndexed { index, transactionOutput ->\n            transactionOutput.index = index\n        }\n\n        transaction.outputs = sorted\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/builder/RecipientSetter.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions.builder\n\nimport io.horizontalsystems.bitcoincore.core.IPluginData\nimport io.horizontalsystems.bitcoincore.core.IRecipientSetter\nimport io.horizontalsystems.bitcoincore.core.PluginManager\nimport io.horizontalsystems.bitcoincore.utils.IAddressConverter\n\nclass RecipientSetter(\n        private val addressConverter: IAddressConverter,\n        private val pluginManager: PluginManager\n) : IRecipientSetter {\n\n    override fun setRecipient(\n        mutableTransaction: MutableTransaction,\n        toAddress: String,\n        value: Long,\n        pluginData: Map<Byte, IPluginData>,\n        skipChecking: Boolean,\n        memo: String?\n    ) {\n        mutableTransaction.recipientAddress = addressConverter.convert(toAddress)\n        mutableTransaction.recipientValue = value\n        mutableTransaction.memo = memo\n\n        pluginManager.processOutputs(mutableTransaction, pluginData, skipChecking)\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/builder/SchnorrInputSigner.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions.builder\n\nimport io.horizontalsystems.bitcoincore.core.IPrivateWallet\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.serializers.TransactionSerializer\nimport io.horizontalsystems.bitcoincore.storage.InputToSign\nimport io.horizontalsystems.hdwalletkit.Utils\n\nclass SchnorrInputSigner(\n    private val hdWallet: IPrivateWallet\n) {\n    fun sigScriptData(\n        transaction: Transaction,\n        inputsToSign: List<InputToSign>,\n        outputs: List<TransactionOutput>,\n        index: Int\n    ): List<ByteArray> {\n        val input = inputsToSign[index]\n        val publicKey = input.previousOutputPublicKey\n        val tweakedPrivateKey = checkNotNull(hdWallet.privateKey(publicKey.account, publicKey.index, publicKey.external).tweakedOutputKey) {\n            throw Error.NoPrivateKey()\n        }\n        val serializedTransaction = TransactionSerializer.serializeForTaprootSignature(transaction, inputsToSign, outputs, index)\n\n        val signatureHash = Utils.taggedHash(\"TapSighash\", serializedTransaction)\n        val signature = tweakedPrivateKey.signSchnorr(signatureHash)\n\n        return listOf(signature)\n    }\n\n    open class Error : Exception() {\n        class NoPrivateKey : Error()\n        class NoPreviousOutput : Error()\n        class NoPreviousOutputAddress : Error()\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/builder/TransactionBuilder.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions.builder\n\nimport io.horizontalsystems.bitcoincore.core.IPluginData\nimport io.horizontalsystems.bitcoincore.core.IRecipientSetter\nimport io.horizontalsystems.bitcoincore.models.TransactionDataSortType\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\nimport io.horizontalsystems.bitcoincore.storage.UtxoFilters\n\nclass TransactionBuilder(\n    private val recipientSetter: IRecipientSetter,\n    private val outputSetter: OutputSetter,\n    private val inputSetter: InputSetter,\n    private val lockTimeSetter: LockTimeSetter\n) {\n\n    fun buildTransaction(\n        toAddress: String,\n        memo: String?,\n        value: Long,\n        feeRate: Int,\n        senderPay: Boolean,\n        sortType: TransactionDataSortType,\n        unspentOutputs: List<UnspentOutput>?,\n        pluginData: Map<Byte, IPluginData>,\n        rbfEnabled: Boolean,\n        changeToFirstInput: Boolean,\n        filters: UtxoFilters\n    ): MutableTransaction {\n        val mutableTransaction = MutableTransaction()\n\n        recipientSetter.setRecipient(mutableTransaction, toAddress, value, pluginData, false, memo)\n        inputSetter.setInputs(\n            mutableTransaction,\n            feeRate,\n            senderPay,\n            unspentOutputs,\n            sortType,\n            rbfEnabled,\n            changeToFirstInput,\n            filters,\n        )\n        lockTimeSetter.setLockTime(mutableTransaction)\n\n        outputSetter.setOutputs(mutableTransaction, sortType)\n\n        return mutableTransaction\n    }\n\n    fun buildTransaction(\n        unspentOutput: UnspentOutput,\n        toAddress: String,\n        memo: String?,\n        feeRate: Int,\n        sortType: TransactionDataSortType,\n        rbfEnabled: Boolean\n    ): MutableTransaction {\n        val mutableTransaction = MutableTransaction(false)\n\n        recipientSetter.setRecipient(\n            mutableTransaction,\n            toAddress,\n            unspentOutput.output.value,\n            mapOf(),\n            false,\n            memo\n        )\n        inputSetter.setInputs(mutableTransaction, unspentOutput, feeRate, rbfEnabled)\n        lockTimeSetter.setLockTime(mutableTransaction)\n\n        outputSetter.setOutputs(mutableTransaction, sortType)\n\n        return mutableTransaction\n    }\n\n    open class BuilderException : Exception() {\n        class FeeMoreThanValue : BuilderException()\n        class NotSupportedScriptType : BuilderException()\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/builder/TransactionSigner.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions.builder\n\nimport io.horizontalsystems.bitcoincore.transactions.scripts.OpCodes\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\n\nclass TransactionSigner(\n    private val ecdsaInputSigner: EcdsaInputSigner,\n    private val schnorrInputSigner: SchnorrInputSigner\n) {\n\n    fun sign(mutableTransaction: MutableTransaction) {\n        mutableTransaction.inputsToSign.forEachIndexed { index, inputToSign ->\n            if (inputToSign.previousOutput.scriptType == ScriptType.P2TR) {\n                schnorrSign(index, mutableTransaction)\n            } else {\n                ecdsaSign(index, mutableTransaction)\n            }\n        }\n    }\n\n    private fun schnorrSign(index: Int, mutableTransaction: MutableTransaction) {\n        val inputToSign = mutableTransaction.inputsToSign[index]\n        val previousOutput = inputToSign.previousOutput\n\n        if (previousOutput.scriptType != ScriptType.P2TR) {\n            throw TransactionBuilder.BuilderException.NotSupportedScriptType()\n        }\n\n        val witnessData = schnorrInputSigner.sigScriptData(\n            mutableTransaction.transaction,\n            mutableTransaction.inputsToSign,\n            mutableTransaction.outputs,\n            index\n        )\n\n        mutableTransaction.transaction.segwit = true\n        inputToSign.input.witness = witnessData\n    }\n\n    private fun ecdsaSign(index: Int, mutableTransaction: MutableTransaction) {\n        val inputToSign = mutableTransaction.inputsToSign[index]\n        val previousOutput = inputToSign.previousOutput\n        val publicKey = inputToSign.previousOutputPublicKey\n        val sigScriptData = ecdsaInputSigner.sigScriptData(\n            mutableTransaction.transaction,\n            mutableTransaction.inputsToSign,\n            mutableTransaction.outputs,\n            index\n        )\n\n        when (previousOutput.scriptType) {\n            ScriptType.P2PKH -> {\n                inputToSign.input.sigScript = signatureScript(sigScriptData)\n            }\n\n            ScriptType.P2WPKH -> {\n                mutableTransaction.transaction.segwit = true\n                inputToSign.input.witness = sigScriptData\n            }\n\n            ScriptType.P2WPKHSH -> {\n                mutableTransaction.transaction.segwit = true\n                val witnessProgram = OpCodes.scriptWPKH(publicKey.publicKeyHash)\n\n                inputToSign.input.sigScript = signatureScript(listOf(witnessProgram))\n                inputToSign.input.witness = sigScriptData\n            }\n\n            ScriptType.P2SH -> {\n                val redeemScript = previousOutput.redeemScript ?: throw NoRedeemScriptException()\n                val signatureScriptFunction = previousOutput.signatureScriptFunction\n\n                if (signatureScriptFunction != null) {\n                    // non-standard P2SH signature script\n                    inputToSign.input.sigScript = signatureScriptFunction(sigScriptData)\n                } else {\n                    // standard (signature, publicKey, redeemScript) signature script\n                    inputToSign.input.sigScript = signatureScript(sigScriptData + redeemScript)\n                }\n            }\n\n            else -> throw TransactionBuilder.BuilderException.NotSupportedScriptType()\n        }\n    }\n\n    private fun signatureScript(params: List<ByteArray>): ByteArray {\n        return params.fold(byteArrayOf()) { acc, bytes -> acc + OpCodes.push(bytes) }\n    }\n}\n\nclass NoRedeemScriptException : Exception()\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/extractors/MyOutputsCache.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions.extractors\n\nimport io.horizontalsystems.bitcoincore.core.HashBytes\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.models.TransactionInput\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\n\nclass MyOutputsCache {\n    private val outputsCache = mutableMapOf<HashBytes, MutableMap<Int, Long>>()\n\n    fun add(outputs: List<TransactionOutput>) {\n        for (output in outputs) {\n            if (output.publicKeyPath != null) {\n                if (!outputsCache.containsKey(HashBytes(output.transactionHash))) {\n                    outputsCache[HashBytes(output.transactionHash)] = mutableMapOf()\n                }\n\n                outputsCache[HashBytes(output.transactionHash)]?.set(output.index, output.value)\n            }\n        }\n    }\n\n    fun valueSpentBy(input: TransactionInput): Long? {\n        return outputsCache[HashBytes(input.previousOutputTxHash)]?.get(input.previousOutputIndex.toInt())\n    }\n\n    companion object {\n        fun create(storage: IStorage): MyOutputsCache {\n            val outputsCache = MyOutputsCache()\n            val outputs = storage.getMyOutputs()\n\n            outputsCache.add(outputs)\n\n            return outputsCache\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/extractors/TransactionExtractor.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions.extractors\n\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.core.PluginManager\nimport io.horizontalsystems.bitcoincore.models.PublicKey\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.bitcoincore.transactions.scripts.OP_CHECKMULTISIG\nimport io.horizontalsystems.bitcoincore.transactions.scripts.OP_CHECKMULTISIGVERIFY\nimport io.horizontalsystems.bitcoincore.transactions.scripts.OP_CHECKSIG\nimport io.horizontalsystems.bitcoincore.transactions.scripts.OP_CHECKSIGVERIFY\nimport io.horizontalsystems.bitcoincore.transactions.scripts.OP_DUP\nimport io.horizontalsystems.bitcoincore.transactions.scripts.OP_ENDIF\nimport io.horizontalsystems.bitcoincore.transactions.scripts.OP_EQUAL\nimport io.horizontalsystems.bitcoincore.transactions.scripts.OP_EQUALVERIFY\nimport io.horizontalsystems.bitcoincore.transactions.scripts.OP_HASH160\nimport io.horizontalsystems.bitcoincore.transactions.scripts.OP_RETURN\nimport io.horizontalsystems.bitcoincore.transactions.scripts.OpCodes\nimport io.horizontalsystems.bitcoincore.transactions.scripts.Script\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport io.horizontalsystems.bitcoincore.utils.IAddressConverter\nimport io.horizontalsystems.bitcoincore.utils.Utils\nimport java.util.Arrays\n\nclass TransactionExtractor(\n    private val addressConverter: IAddressConverter,\n    private val storage: IStorage,\n    private val pluginManager: PluginManager,\n    private val metadataExtractor: TransactionMetadataExtractor\n) {\n\n    fun extractOutputs(transaction: FullTransaction) {\n        var nullDataOutput: TransactionOutput? = null\n        for (output in transaction.outputs) {\n            val payload: ByteArray\n            val scriptType: ScriptType\n\n            val lockingScript = output.lockingScript\n\n            if (isP2PKH(lockingScript)) {\n                payload = lockingScript.copyOfRange(3, 23)\n                scriptType = ScriptType.P2PKH\n            } else if (isP2PK(lockingScript)) {\n                payload = lockingScript.copyOfRange(1, lockingScript.size - 1)\n                scriptType = ScriptType.P2PK\n            } else if (isP2SH(lockingScript)) {\n                payload = lockingScript.copyOfRange(2, lockingScript.size - 1)\n                scriptType = ScriptType.P2SH\n            } else if (isP2WPKH(lockingScript)) {\n                payload = lockingScript.copyOfRange(2, lockingScript.size)\n                scriptType = ScriptType.P2WPKH\n            } else if (isP2TR(lockingScript)) {\n                payload = lockingScript.copyOfRange(2, lockingScript.size)\n                scriptType = ScriptType.P2TR\n            } else if (isP2WSH(lockingScript)) {\n                payload = lockingScript.copyOfRange(2, lockingScript.size)\n                scriptType = ScriptType.P2WSH\n            } else if (isNullData(lockingScript)) {\n                payload = lockingScript\n                scriptType = ScriptType.NULL_DATA\n                nullDataOutput = output\n            } else continue\n\n            output.scriptType = scriptType\n            output.lockingScriptPayload = payload\n\n            // Set public key if exist\n            getPublicKey(output)?.let {\n                output.setPublicKey(it)\n            }\n        }\n\n        nullDataOutput?.let {\n            pluginManager.processTransactionWithNullData(transaction, it)\n        }\n\n    }\n\n    fun extractInputs(transaction: FullTransaction) {\n        for (input in transaction.inputs) {\n            val previousOutput = storage.getPreviousOutput(input)\n            if (previousOutput != null) {\n                input.address = previousOutput.address\n                input.lockingScriptPayload = previousOutput.lockingScriptPayload\n                continue\n            }\n\n            val payload: ByteArray\n            val scriptType: ScriptType\n            val sigScript = input.sigScript\n\n            //  P2SH script {push-sig}{signature}{push-redeem}{script}\n            val scriptHash = getScriptHash(sigScript)\n            if (scriptHash != null) {\n                payload = scriptHash\n                scriptType = ScriptType.P2SH\n            }\n            //  P2PKH {signature}{public-key} script is 107±1 bytes\n            else if (sigScript.size >= 106 && sigScript[0] in 71..74) {\n                val signOffset = sigScript[0].toInt()\n                val pubKeySize = sigScript[signOffset + 1].toInt()\n\n                if ((pubKeySize == 33 || pubKeySize == 65) && sigScript.size == signOffset + pubKeySize + 2) {\n                    scriptType = ScriptType.P2PKH\n                    payload = Arrays.copyOfRange(sigScript, signOffset + 2, sigScript.size)\n                } else continue\n            }\n            //  P2WPKHSH 0x16 00 14 {20-byte-key-hash}\n            else if (sigScript.size == 23 && sigScript[0] == 0x16.toByte() &&\n                (sigScript[1] == 0.toByte() || sigScript[1] in 0x50..0x61) &&\n                sigScript[2] == 0x14.toByte()\n            ) {\n                payload = sigScript.drop(1).toByteArray()\n                scriptType = ScriptType.P2WPKHSH\n            } else continue\n\n            try {\n                val keyHash = Utils.sha256Hash160(payload)\n                val address = addressConverter.convert(keyHash, scriptType)\n                input.lockingScriptPayload = address.lockingScriptPayload\n                input.address = address.stringValue\n\n            } catch (e: Exception) {\n            }\n        }\n    }\n\n    fun extractAddress(transaction: FullTransaction) {\n        for (output in transaction.outputs) {\n            val payload = output.lockingScriptPayload ?: continue\n            val scriptType = output.scriptType\n\n            val pubkeyHash = when (scriptType) {\n                ScriptType.P2PK -> Utils.sha256Hash160(payload)\n                ScriptType.P2WPKHSH -> Utils.sha256Hash160(OpCodes.scriptWPKH(payload))\n                else -> payload\n            }\n\n            try {\n                output.address = addressConverter.convert(pubkeyHash, scriptType).stringValue\n            } catch (e: Exception) {\n            }\n        }\n    }\n\n    private fun getPublicKey(output: TransactionOutput): PublicKey? {\n        val payload = output.lockingScriptPayload ?: return null\n\n        return when (output.scriptType) {\n            ScriptType.P2PK,\n            ScriptType.P2PKH,\n            ScriptType.P2WPKH -> {\n                storage.getPublicKeyByKeyOrKeyHash(payload)\n            }\n            ScriptType.P2SH -> {\n                storage.getPublicKeyByScriptHashForP2PWKH(payload)?.apply {\n                    output.scriptType = ScriptType.P2WPKHSH\n                    output.lockingScriptPayload = publicKeyHash\n                }\n            }\n            ScriptType.P2TR -> {\n                storage.getPublicKeyByHashP2TR(payload)\n            }\n            else -> null\n        }\n    }\n\n    //\n    // Parse Outputs\n    //\n\n    // 25 bytes script: 76 A9 14 {20-byte-key-hash} 88 AC\n    private fun isP2PKH(lockingScript: ByteArray): Boolean {\n        return (lockingScript.size == 25 &&\n                lockingScript[0] == OP_DUP.toByte() &&\n                lockingScript[1] == OP_HASH160.toByte() &&\n                lockingScript[2] == 20.toByte() &&\n                lockingScript[23] == OP_EQUALVERIFY.toByte() &&\n                lockingScript[24] == OP_CHECKSIG.toByte())\n    }\n\n    // 35/67 bytes script: {push-length-byte 33/65}{33/65-byte-public-key} AC\n    private fun isP2PK(lockingScript: ByteArray): Boolean {\n        return ((lockingScript.size == 35 || lockingScript.size == 67) &&\n                (lockingScript[0] == 33.toByte() || lockingScript[0] == 65.toByte()) &&\n                lockingScript[lockingScript.size - 1] == OP_CHECKSIG.toByte())\n    }\n\n    // 23 bytes script: A9 14 {20-byte-script-hash} 87\n    private fun isP2SH(lockingScript: ByteArray): Boolean {\n        return (lockingScript.size == 23 &&\n                lockingScript[0] == OP_HASH160.toByte() &&\n                lockingScript[1] == 20.toByte() &&\n                lockingScript[lockingScript.size - 1] == OP_EQUAL.toByte())\n    }\n\n    // 22 bytes script: {version-byte 00} 14 {20-byte-key-hash}\n    private fun isP2WPKH(lockingScript: ByteArray): Boolean {\n        return (lockingScript.size == 22 &&\n                lockingScript[0] == 0.toByte() &&\n                lockingScript[1] == 20.toByte())\n    }\n\n    // 34 bytes script: {version-byte 51} 20 {32-byte-public-key}\n    private fun isP2TR(lockingScript: ByteArray): Boolean {\n        return lockingScript.size == 34 &&\n                lockingScript[0] == 0x51.toByte() &&\n                lockingScript[1] == 32.toByte()\n    }\n\n    private fun isP2WSH(lockingScript: ByteArray): Boolean {\n        return lockingScript.size == 34 &&\n                lockingScript[0] == 0.toByte() &&\n                lockingScript[1] == 32.toByte()\n    }\n\n    private fun isNullData(lockingScript: ByteArray): Boolean {\n        return lockingScript.isNotEmpty() && lockingScript[0] == OP_RETURN.toByte()\n    }\n\n    //\n    // Parse Inputs\n    //\n\n    // P2SH input {push-sig}{signature}{push-redeem}{script}\n    private fun getScriptHash(bytes: ByteArray): ByteArray? {\n        val script = Script(bytes)\n        if (script.chunks.isEmpty())\n            return null\n\n        //  Grab the raw redeem script bytes\n        val redeemChunk = script.chunks.last()\n        if (redeemChunk.data == null)\n            return null\n\n        val redeemScript = Script(redeemChunk.data)\n        if (redeemScript.chunks.isEmpty())\n            return null\n\n        var chunkLast = redeemScript.chunks.last()\n        if (chunkLast.opcode == OP_ENDIF && redeemScript.chunks.size > 1) {\n            val chunk = redeemScript.chunks.takeLast(2).firstOrNull()\n            if (chunk != null) {\n                chunkLast = chunk\n            }\n        }\n\n        val checks = listOf(OP_CHECKSIG, OP_CHECKSIGVERIFY, OP_CHECKMULTISIGVERIFY, OP_CHECKMULTISIG)\n        if (checks.contains(chunkLast.opcode)) {\n            return redeemChunk.data\n        }\n\n        return null\n    }\n\n    fun extract(transaction: FullTransaction) {\n        extractOutputs(transaction)\n        metadataExtractor.extract(transaction)\n\n        if (transaction.header.isMine) {\n            extractAddress(transaction)\n            extractInputs(transaction)\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/extractors/TransactionMetadataExtractor.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions.extractors\n\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.models.*\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport kotlin.math.absoluteValue\n\nclass TransactionMetadataExtractor(\n    private val myOutputsCache: MyOutputsCache,\n    private val outputProvider: ITransactionOutputProvider\n) {\n\n    fun extract(transaction: FullTransaction) {\n\n        var myInputsTotalValue = 0L\n        var myOutputsTotalValue = 0L\n        var myChangeOutputsTotalValue = 0L\n        var outputsTotalValue = 0L\n        var allInputsMine = true\n\n        for (input in transaction.inputs) {\n            val value = myOutputsCache.valueSpentBy(input)\n            if (value != null) {\n                myInputsTotalValue += value\n            } else {\n                allInputsMine = false\n            }\n        }\n\n        for (output in transaction.outputs) {\n            if (output.value <= 0) continue\n\n            outputsTotalValue += output.value\n\n            if (output.publicKeyPath != null) {\n                myOutputsTotalValue += output.value\n                if (output.changeOutput) {\n                    myChangeOutputsTotalValue += output.value\n                }\n            }\n        }\n\n        if (myInputsTotalValue == 0L && myOutputsTotalValue == 0L) {\n            return\n        }\n\n        transaction.header.isMine = true\n        if (myInputsTotalValue > 0) {\n            transaction.header.isOutgoing = true\n        }\n\n        var amount = myOutputsTotalValue - myInputsTotalValue\n        var fee: Long? = null\n\n        if (allInputsMine) {\n            fee = myInputsTotalValue - outputsTotalValue\n            amount += fee\n        } else {\n            var inputsTotalValue = 0L\n            var allInputsHaveValue = true\n            for (input in transaction.inputs) {\n                val previousOutput = outputProvider.get(input.previousOutputTxHash, input.previousOutputIndex.toInt())\n                if (previousOutput != null) {\n                    inputsTotalValue += previousOutput.value\n                } else {\n                    allInputsHaveValue = false\n                    break\n                }\n            }\n\n            fee = if (allInputsHaveValue) inputsTotalValue - outputsTotalValue else null\n        }\n\n        if (amount > 0) {\n            transaction.metadata.amount = amount\n            transaction.metadata.type = TransactionType.Incoming\n        } else if (amount < 0) {\n            transaction.metadata.amount = amount.absoluteValue\n            transaction.metadata.type = TransactionType.Outgoing\n        } else {\n            transaction.metadata.amount = (myOutputsTotalValue - myChangeOutputsTotalValue).absoluteValue\n            transaction.metadata.type = TransactionType.SentToSelf\n        }\n        transaction.metadata.fee = fee\n\n        if (myOutputsTotalValue > 0) {\n            myOutputsCache.add(transaction.outputs)\n        }\n    }\n}\n\ninterface ITransactionOutputProvider {\n    fun get(transactionHash: ByteArray, index: Int): TransactionOutput?\n}\n\nclass TransactionOutputProvider(private val storage: IStorage): ITransactionOutputProvider {\n    override fun get(transactionHash: ByteArray, index: Int): TransactionOutput? {\n        return storage.getOutput(transactionHash, index)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/scripts/OpCodes.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions.scripts\n\nimport io.horizontalsystems.bitcoincore.utils.Utils\n\n// push value\nconst val OP_0 = 0x00 // push empty vector\nconst val OP_FALSE = OP_0\nconst val OP_PUSHDATA1 = 0x4c\nconst val OP_PUSHDATA2 = 0x4d\nconst val OP_PUSHDATA4 = 0x4e\nconst val OP_1NEGATE = 0x4f\nconst val OP_RESERVED = 0x50\nconst val OP_1 = 0x51\nconst val OP_TRUE = OP_1\nconst val OP_2 = 0x52\nconst val OP_3 = 0x53\nconst val OP_4 = 0x54\nconst val OP_5 = 0x55\nconst val OP_6 = 0x56\nconst val OP_7 = 0x57\nconst val OP_8 = 0x58\nconst val OP_9 = 0x59\nconst val OP_10 = 0x5a\nconst val OP_11 = 0x5b\nconst val OP_12 = 0x5c\nconst val OP_13 = 0x5d\nconst val OP_14 = 0x5e\nconst val OP_15 = 0x5f\nconst val OP_16 = 0x60\n\n// control\nconst val OP_NOP = 0x61\nconst val OP_VER = 0x62\nconst val OP_IF = 0x63\nconst val OP_NOTIF = 0x64\nconst val OP_VERIF = 0x65\nconst val OP_VERNOTIF = 0x66\nconst val OP_ELSE = 0x67\nconst val OP_ENDIF = 0x68\nconst val OP_VERIFY = 0x69\nconst val OP_RETURN = 0x6a\n\n// stack ops\nconst val OP_TOALTSTACK = 0x6b\nconst val OP_FROMALTSTACK = 0x6c\nconst val OP_2DROP = 0x6d\nconst val OP_2DUP = 0x6e\nconst val OP_3DUP = 0x6f\nconst val OP_2OVER = 0x70\nconst val OP_2ROT = 0x71\nconst val OP_2SWAP = 0x72\nconst val OP_IFDUP = 0x73\nconst val OP_DEPTH = 0x74\nconst val OP_DROP = 0x75\nconst val OP_DUP = 0x76\nconst val OP_NIP = 0x77\nconst val OP_OVER = 0x78\nconst val OP_PICK = 0x79\nconst val OP_ROLL = 0x7a\nconst val OP_ROT = 0x7b\nconst val OP_SWAP = 0x7c\nconst val OP_TUCK = 0x7d\n\n// splice ops\nconst val OP_CAT = 0x7e\nconst val OP_SUBSTR = 0x7f\nconst val OP_LEFT = 0x80\nconst val OP_RIGHT = 0x81\nconst val OP_SIZE = 0x82\n\n// bit logic\nconst val OP_INVERT = 0x83\nconst val OP_AND = 0x84\nconst val OP_OR = 0x85\nconst val OP_XOR = 0x86\nconst val OP_EQUAL = 0x87\nconst val OP_EQUALVERIFY = 0x88\nconst val OP_RESERVED1 = 0x89\nconst val OP_RESERVED2 = 0x8a\n\n// numeric\nconst val OP_1ADD = 0x8b\nconst val OP_1SUB = 0x8c\nconst val OP_2MUL = 0x8d\nconst val OP_2DIV = 0x8e\nconst val OP_NEGATE = 0x8f\nconst val OP_ABS = 0x90\nconst val OP_NOT = 0x91\nconst val OP_0NOTEQUAL = 0x92\nconst val OP_ADD = 0x93\nconst val OP_SUB = 0x94\nconst val OP_MUL = 0x95\nconst val OP_DIV = 0x96\nconst val OP_MOD = 0x97\nconst val OP_LSHIFT = 0x98\nconst val OP_RSHIFT = 0x99\nconst val OP_BOOLAND = 0x9a\nconst val OP_BOOLOR = 0x9b\nconst val OP_NUMEQUAL = 0x9c\nconst val OP_NUMEQUALVERIFY = 0x9d\nconst val OP_NUMNOTEQUAL = 0x9e\nconst val OP_LESSTHAN = 0x9f\nconst val OP_GREATERTHAN = 0xa0\nconst val OP_LESSTHANOREQUAL = 0xa1\nconst val OP_GREATERTHANOREQUAL = 0xa2\nconst val OP_MIN = 0xa3\nconst val OP_MAX = 0xa4\nconst val OP_WITHIN = 0xa5\n\n// crypto\nconst val OP_RIPEMD160 = 0xa6\nconst val OP_SHA1 = 0xa7\nconst val OP_SHA256 = 0xa8\nconst val OP_HASH160 = 0xa9\nconst val OP_HASH256 = 0xaa\nconst val OP_CODESEPARATOR = 0xab\nconst val OP_CHECKSIG = 0xac\nconst val OP_CHECKSIGVERIFY = 0xad\nconst val OP_CHECKMULTISIG = 0xae\nconst val OP_CHECKMULTISIGVERIFY = 0xaf\n\n// block state\n/** Check lock time of the block. Introduced in BIP 65, replacing OP_NOP2  */\nconst val OP_CHECKLOCKTIMEVERIFY = 0xb1\nconst val OP_CHECKSEQUENCEVERIFY = 0xb2\n\n// expansion\nconst val OP_NOP1 = 0xb0\n/** Deprecated by BIP 65  */\n@Deprecated(\"\")\nconst val OP_NOP2 = OP_CHECKLOCKTIMEVERIFY\n/** Deprecated by BIP 112  */\n@Deprecated(\"\")\nconst val OP_NOP3 = OP_CHECKSEQUENCEVERIFY\nconst val OP_NOP4 = 0xb3\nconst val OP_NOP5 = 0xb4\nconst val OP_NOP6 = 0xb5\nconst val OP_NOP7 = 0xb6\nconst val OP_NOP8 = 0xb7\nconst val OP_NOP9 = 0xb8\nconst val OP_NOP10 = 0xb9\nconst val OP_INVALIDOPCODE = 0xff\n\n/** Sighash Types */\nobject Sighash {\n    const val DEFAULT = 0  // https://github.com/bitcoin/bips/blob/master/bip-0341.mediawiki\n    const val ALL: Byte = 1              // Sign all outputs\n    const val NONE = 2             // Do not sign outputs (zero sequences)\n    const val SINGLE = 3           // Sign output at the same index (zero sequences)\n    const val FORKID: Byte = 0x40        // Bitcoin Cash SIGHASH_FORKID\n    const val ANYONECANPAY = 0x80  // Sign only the current input (mask)\n}\n\nobject OpCodes {\n\n    private val opCodeMap = hashMapOf(\n            OP_0 to \"0\",\n            OP_PUSHDATA1 to \"PUSHDATA1\",\n            OP_PUSHDATA2 to \"PUSHDATA2\",\n            OP_PUSHDATA4 to \"PUSHDATA4\",\n            OP_1NEGATE to \"1NEGATE\",\n            OP_RESERVED to \"RESERVED\",\n            OP_1 to \"1\",\n            OP_2 to \"2\",\n            OP_3 to \"3\",\n            OP_4 to \"4\",\n            OP_5 to \"5\",\n            OP_6 to \"6\",\n            OP_7 to \"7\",\n            OP_8 to \"8\",\n            OP_9 to \"9\",\n            OP_10 to \"10\",\n            OP_11 to \"11\",\n            OP_12 to \"12\",\n            OP_13 to \"13\",\n            OP_14 to \"14\",\n            OP_15 to \"15\",\n            OP_16 to \"16\",\n            OP_NOP to \"NOP\",\n            OP_VER to \"VER\",\n            OP_IF to \"IF\",\n            OP_NOTIF to \"NOTIF\",\n            OP_VERIF to \"VERIF\",\n            OP_VERNOTIF to \"VERNOTIF\",\n            OP_ELSE to \"ELSE\",\n            OP_ENDIF to \"ENDIF\",\n            OP_VERIFY to \"VERIFY\",\n            OP_RETURN to \"RETURN\",\n            OP_TOALTSTACK to \"TOALTSTACK\",\n            OP_FROMALTSTACK to \"FROMALTSTACK\",\n            OP_2DROP to \"2DROP\",\n            OP_2DUP to \"2DUP\",\n            OP_3DUP to \"3DUP\",\n            OP_2OVER to \"2OVER\",\n            OP_2ROT to \"2ROT\",\n            OP_2SWAP to \"2SWAP\",\n            OP_IFDUP to \"IFDUP\",\n            OP_DEPTH to \"DEPTH\",\n            OP_DROP to \"DROP\",\n            OP_DUP to \"DUP\",\n            OP_NIP to \"NIP\",\n            OP_OVER to \"OVER\",\n            OP_PICK to \"PICK\",\n            OP_ROLL to \"ROLL\",\n            OP_ROT to \"ROT\",\n            OP_SWAP to \"SWAP\",\n            OP_TUCK to \"TUCK\",\n            OP_CAT to \"CAT\",\n            OP_SUBSTR to \"SUBSTR\",\n            OP_LEFT to \"LEFT\",\n            OP_RIGHT to \"RIGHT\",\n            OP_SIZE to \"SIZE\",\n            OP_INVERT to \"INVERT\",\n            OP_AND to \"AND\",\n            OP_OR to \"OR\",\n            OP_XOR to \"XOR\",\n            OP_EQUAL to \"EQUAL\",\n            OP_EQUALVERIFY to \"EQUALVERIFY\",\n            OP_RESERVED1 to \"RESERVED1\",\n            OP_RESERVED2 to \"RESERVED2\",\n            OP_1ADD to \"1ADD\",\n            OP_1SUB to \"1SUB\",\n            OP_2MUL to \"2MUL\",\n            OP_2DIV to \"2DIV\",\n            OP_NEGATE to \"NEGATE\",\n            OP_ABS to \"ABS\",\n            OP_NOT to \"NOT\",\n            OP_0NOTEQUAL to \"0NOTEQUAL\",\n            OP_ADD to \"ADD\",\n            OP_SUB to \"SUB\",\n            OP_MUL to \"MUL\",\n            OP_DIV to \"DIV\",\n            OP_MOD to \"MOD\",\n            OP_LSHIFT to \"LSHIFT\",\n            OP_RSHIFT to \"RSHIFT\",\n            OP_BOOLAND to \"BOOLAND\",\n            OP_BOOLOR to \"BOOLOR\",\n            OP_NUMEQUAL to \"NUMEQUAL\",\n            OP_NUMEQUALVERIFY to \"NUMEQUALVERIFY\",\n            OP_NUMNOTEQUAL to \"NUMNOTEQUAL\",\n            OP_LESSTHAN to \"LESSTHAN\",\n            OP_GREATERTHAN to \"GREATERTHAN\",\n            OP_LESSTHANOREQUAL to \"LESSTHANOREQUAL\",\n            OP_GREATERTHANOREQUAL to \"GREATERTHANOREQUAL\",\n            OP_MIN to \"MIN\",\n            OP_MAX to \"MAX\",\n            OP_WITHIN to \"WITHIN\",\n            OP_RIPEMD160 to \"RIPEMD160\",\n            OP_SHA1 to \"SHA1\",\n            OP_SHA256 to \"SHA256\",\n            OP_HASH160 to \"HASH160\",\n            OP_HASH256 to \"HASH256\",\n            OP_CODESEPARATOR to \"CODESEPARATOR\",\n            OP_CHECKSIG to \"CHECKSIG\",\n            OP_CHECKSIGVERIFY to \"CHECKSIGVERIFY\",\n            OP_CHECKMULTISIG to \"CHECKMULTISIG\",\n            OP_CHECKMULTISIGVERIFY to \"CHECKMULTISIGVERIFY\",\n            OP_NOP1 to \"NOP1\",\n            OP_CHECKLOCKTIMEVERIFY to \"CHECKLOCKTIMEVERIFY\",\n            OP_CHECKSEQUENCEVERIFY to \"CHECKSEQUENCEVERIFY\",\n            OP_NOP4 to \"NOP4\",\n            OP_NOP5 to \"NOP5\",\n            OP_NOP6 to \"NOP6\",\n            OP_NOP7 to \"NOP7\",\n            OP_NOP8 to \"NOP8\",\n            OP_NOP9 to \"NOP9\",\n            OP_NOP10 to \"NOP10\"\n    )\n\n    val p2pkhStart = byteArrayOf(OP_DUP.toByte(), OP_HASH160.toByte())\n    val p2pkhEnd = byteArrayOf(OP_EQUALVERIFY.toByte(), OP_CHECKSIG.toByte())\n    val p2pshStart = byteArrayOf(OP_HASH160.toByte())\n    val p2pshEnd = byteArrayOf(OP_EQUAL.toByte())\n\n    //  Converts the given OpCode into a string (eg \"0\", \"PUSHDATA\", or \"NON_OP(10)\")\n    fun getOpCodeName(opcode: Int): String {\n        return opCodeMap[opcode] ?: \"NON_OP($opcode)\"\n    }\n\n    //  Converts the given pushdata OpCode into a string (eg \"PUSHDATA2\", or \"PUSHDATA(23)\")\n    fun getPushDataName(opcode: Int): String {\n        return opCodeMap[opcode] ?: \"PUSHDATA($opcode)\"\n    }\n\n    fun push(value: Int) = when (value) {\n        0 -> byteArrayOf(0)\n        in 1..16 -> byteArrayOf((value.toByte() + 0x50).toByte())\n        else -> byteArrayOf()\n    }\n\n    fun push(data: ByteArray): ByteArray {\n        val bytes = when (val length = data.size) {\n            in 0x00..0x4b -> byteArrayOf(length.toByte())\n            in 0x4c..0xff -> byteArrayOf(OP_PUSHDATA1.toByte(), length.toByte())\n            in 0x0100..0xffff -> {\n                val lengthBytes = Utils.intToByteArray(length)\n                val length16InLE = byteArrayOf(lengthBytes[3], lengthBytes[2])\n                byteArrayOf(OP_PUSHDATA2.toByte()) + length16InLE\n            }\n            in 0x10000..0xffffffff -> {\n                val lengthBytesInLE = Utils.intToByteArray(length).reversedArray()\n                byteArrayOf(OP_PUSHDATA2.toByte()) + lengthBytesInLE\n            }\n            else -> byteArrayOf()\n        }\n        return bytes + data\n    }\n\n    fun scriptWPKH(data: ByteArray, versionByte: Int = 0): ByteArray {\n        return push(versionByte) + push(data)\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/transactions/scripts/Script.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions.scripts\n\nimport io.horizontalsystems.bitcoincore.extensions.toHexString\nimport io.horizontalsystems.bitcoincore.utils.Utils\nimport java.io.ByteArrayInputStream\n\nenum class ScriptType(val value: Int) {\n    P2PKH(1),     // pay to pubkey hash (aka pay to address)\n    P2PK(2),      // pay to pubkey\n    P2SH(3),      // pay to script hash\n    P2WPKH(4),    // pay to witness pubkey hash\n    P2WSH(5),     // pay to witness script hash\n    P2WPKHSH(6),  // P2WPKH nested in P2SH\n    P2TR(8),\n    NULL_DATA(7),\n    UNKNOWN(0);\n\n    companion object {\n        fun fromValue(value: Int): ScriptType? {\n            return values().find { it.value == value }\n        }\n    }\n\n    val isWitness: Boolean\n        get() = this in arrayOf(P2WPKH, P2WSH, P2WPKHSH, P2TR)\n}\n\nclass Script(bytes: ByteArray) {\n    val chunks = try {\n        parseChunks(bytes)\n    } catch (e: Exception) {\n        listOf<Chunk>()\n    }\n\n    private fun parseChunks(bytes: ByteArray): List<Chunk> {\n        val chunks = mutableListOf<Chunk>()\n        val stream = ByteArrayInputStream(bytes)\n\n        while (stream.available() > 0) {\n            var dataToRead: Long = -1\n\n            val opcode = stream.read()\n            when (opcode) {\n                in 0 until OP_PUSHDATA1 -> {\n                    // Read some bytes of data, where how many is the opcode value itself.\n                    dataToRead = opcode.toLong()\n                }\n                OP_PUSHDATA1 -> {\n                    if (stream.available() < 1) throw Exception(\"Unexpected end of script\")\n\n                    dataToRead = stream.read().toLong()\n                }\n                OP_PUSHDATA2 -> {\n                    // Read a short, then read that many bytes of data.\n                    if (stream.available() < 2) throw Exception(\"Unexpected end of script\")\n\n                    dataToRead = Utils.readUint16FromStream(stream).toLong()\n                }\n                OP_PUSHDATA4 -> {\n                    // Read a uint32, then read that many bytes of data.\n                    // Though this is allowed, because its value cannot be > 520, it should never actually be used\n                    if (stream.available() < 4) throw Exception(\"Unexpected end of script\")\n\n                    dataToRead = Utils.readUint32FromStream(stream)\n                }\n            }\n\n            val chunk = when {\n                dataToRead < 0 -> {\n                    Chunk(opcode)\n                }\n                dataToRead > stream.available() -> {\n                    throw Exception(\"Push of data element that is larger than remaining data\")\n                }\n                else -> {\n                    val data = ByteArray(dataToRead.toInt())\n                    check(dataToRead == 0L || stream.read(data, 0, dataToRead.toInt()).toLong() == dataToRead)\n                    Chunk(opcode, data)\n                }\n            }\n\n            chunks.add(chunk)\n        }\n\n        return chunks\n    }\n\n    class Chunk(val opcode: Int, val data: ByteArray? = null) {\n\n        override fun toString(): String {\n            val buf = StringBuilder()\n            //  opcode is a single byte of non-pushdata content\n            when {\n                opcode > OP_PUSHDATA4 -> {\n                    buf.append(OpCodes.getOpCodeName(opcode))\n                }\n                data != null -> {\n                    // Data chunk\n                    buf.append(OpCodes.getPushDataName(opcode))\n                            .append(\"[\")\n                            .append(data.toHexString())\n                            .append(\"]\")\n                }\n                else -> {\n                    // Small num\n                    buf.append(opcode)\n                }\n            }\n\n            return buf.toString()\n        }\n    }\n\n    override fun toString() = chunks.joinToString(\" \")\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/utils/Base58AddressConverter.kt",
    "content": "package io.horizontalsystems.bitcoincore.utils\n\nimport io.horizontalsystems.bitcoincore.crypto.Base58\nimport io.horizontalsystems.bitcoincore.exceptions.AddressFormatException\nimport io.horizontalsystems.bitcoincore.models.Address\nimport io.horizontalsystems.bitcoincore.models.AddressType\nimport io.horizontalsystems.bitcoincore.models.LegacyAddress\nimport io.horizontalsystems.bitcoincore.models.PublicKey\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport java.util.*\n\nclass Base58AddressConverter(private val addressVersion: Int, private val addressScriptVersion: Int) : IAddressConverter {\n\n    override fun convert(addressString: String): Address {\n        val data = Base58.decodeChecked(addressString)\n        if (data.size != 20 + 1) {\n            throw AddressFormatException(\"Address length is not 20 hash\")\n        }\n\n        val bytes = Arrays.copyOfRange(data, 1, data.size)\n        val addressType = when (data[0].toInt() and 0xFF) {\n            addressScriptVersion -> AddressType.ScriptHash\n            addressVersion -> AddressType.PubKeyHash\n            else -> throw AddressFormatException(\"Wrong address prefix\")\n        }\n\n        return LegacyAddress(addressString, bytes, addressType)\n\n    }\n\n    override fun convert(lockingScriptPayload: ByteArray, scriptType: ScriptType): Address {\n        val addressType: AddressType\n        val addressVersion: Int\n\n        when (scriptType) {\n            ScriptType.P2PK,\n            ScriptType.P2PKH -> {\n                addressType = AddressType.PubKeyHash\n                addressVersion = this.addressVersion\n            }\n            ScriptType.P2SH,\n            ScriptType.P2WPKHSH -> {\n                addressType = AddressType.ScriptHash\n                addressVersion = addressScriptVersion\n            }\n\n            else -> throw AddressFormatException(\"Unknown Address Type\")\n        }\n\n        val addressBytes = byteArrayOf(addressVersion.toByte()) + lockingScriptPayload\n        val doubleSHA256 = Utils.doubleDigest(addressBytes)\n        val addrChecksum = Arrays.copyOfRange(doubleSHA256, 0, 4)\n\n        val addressString = Base58.encode(addressBytes + addrChecksum)\n\n        return LegacyAddress(addressString, lockingScriptPayload, addressType)\n    }\n\n    override fun convert(publicKey: PublicKey, scriptType: ScriptType): Address {\n        var keyhash = publicKey.publicKeyHash\n\n        if (scriptType == ScriptType.P2WPKHSH) {\n            keyhash = publicKey.scriptHashP2WPKH\n        }\n\n        return convert(keyhash, scriptType)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/utils/Bech32AddressConverter.kt",
    "content": "package io.horizontalsystems.bitcoincore.utils\n\nimport io.horizontalsystems.bitcoincore.crypto.Bech32.Encoding\nimport io.horizontalsystems.bitcoincore.crypto.Bech32Cash\nimport io.horizontalsystems.bitcoincore.crypto.Bech32Segwit\nimport io.horizontalsystems.bitcoincore.exceptions.AddressFormatException\nimport io.horizontalsystems.bitcoincore.models.*\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport io.horizontalsystems.hdwalletkit.ECKey\n\nabstract class Bech32AddressConverter(var hrp: String) : IAddressConverter\n\nclass SegwitAddressConverter(addressSegwitHrp: String) : Bech32AddressConverter(addressSegwitHrp) {\n    override fun convert(addressString: String): Address {\n        val decoded = Bech32Segwit.decode(addressString)\n        if (decoded.hrp != hrp) {\n            throw AddressFormatException(\"Address HRP ${decoded.hrp} is not correct\")\n        }\n        val data = decoded.data\n        val stringValue = Bech32Segwit.encode(hrp, decoded.encoding, data)\n        val program = Bech32Segwit.convertBits(data, 1, data.size - 1, 5, 8, false)\n\n        return when (val version = data[0].toInt()) {\n            0 -> {\n                val type = when (program.size) {\n                    20 -> AddressType.PubKeyHash\n                    32 -> AddressType.ScriptHash\n                    else -> throw AddressFormatException(\"Unknown address type\")\n                }\n                SegWitV0Address(stringValue, program, type)\n            }\n            1 -> {\n                TaprootAddress(stringValue, program, version)\n            }\n            else -> throw AddressFormatException(\"Unknown address type\")\n        }\n    }\n\n    override fun convert(lockingScriptPayload: ByteArray, scriptType: ScriptType): Address {\n        val witnessScript = Bech32Segwit.convertBits(lockingScriptPayload, 0, lockingScriptPayload.size, 8, 5, true)\n\n        return when (scriptType) {\n            ScriptType.P2WPKH -> {\n                val addressString = Bech32Segwit.encode(hrp, Encoding.BECH32, byteArrayOf(0.toByte()) + witnessScript)\n                SegWitV0Address(addressString, lockingScriptPayload, AddressType.PubKeyHash)\n            }\n            ScriptType.P2WSH -> {\n                val addressString = Bech32Segwit.encode(hrp, Encoding.BECH32, byteArrayOf(0.toByte()) + witnessScript)\n                SegWitV0Address(addressString, lockingScriptPayload, AddressType.ScriptHash)\n            }\n            ScriptType.P2TR -> {\n                val addressString = Bech32Segwit.encode(hrp, Encoding.BECH32M, byteArrayOf(1.toByte()) + witnessScript)\n                TaprootAddress(addressString, lockingScriptPayload, 1)\n            }\n            else -> throw AddressFormatException(\"Unknown Address Type\")\n        }\n    }\n\n    override fun convert(publicKey: PublicKey, scriptType: ScriptType) = when (scriptType) {\n        ScriptType.P2WPKH, ScriptType.P2WSH -> {\n            convert(publicKey.publicKeyHash, scriptType)\n        }\n        ScriptType.P2TR -> {\n            convert(publicKey.convertedForP2TR, scriptType)\n        }\n        else -> throw AddressFormatException(\"Unknown Address Type\")\n    }\n}\n\nclass CashAddressConverter(addressSegwitHrp: String) : Bech32AddressConverter(addressSegwitHrp) {\n    override fun convert(addressString: String): CashAddress {\n        var correctedAddress = addressString\n        if (addressString.indexOf(\":\") < 0) {\n            correctedAddress = \"$hrp:$addressString\"\n        }\n\n        val decoded = Bech32Cash.decode(correctedAddress, hrp)\n        if (decoded.hrp != hrp) {\n            throw AddressFormatException(\"Invalid prefix for network: ${decoded.hrp} != $hrp (expected)\")\n        }\n\n        val payload = decoded.data\n        if (payload.isEmpty()) {\n            throw AddressFormatException(\"No payload\")\n        }\n\n        // Check that the padding is zero.\n        val extraBits = (payload.size * 5 % 8).toByte()\n        if (extraBits >= 5) {\n            throw AddressFormatException(\"More than allowed padding\")\n        }\n\n        val data = ByteArray(payload.size * 5 / 8)\n        Bech32Cash.convertBits(data, payload, 5, 8, false)\n\n        // Decode type and size from the version.\n        val version = data[0].toInt()\n        if (version and 0x80 != 0) {\n            throw AddressFormatException(\"First bit is reserved\")\n        }\n\n        val addressType = when ((version shr 3) and 0x1f) {\n            0 -> AddressType.PubKeyHash\n            1 -> AddressType.ScriptHash\n            else -> throw AddressFormatException(\"Unknown Address Type\")\n        }\n\n        var hashSize = 20 + 4 * (version and 0x03)\n        if (version and 0x04 != 0) {\n            hashSize *= 2\n        }\n\n        // Check that we decoded the exact number of bytes we expected.\n        if (data.size != hashSize + 1) {\n            throw AddressFormatException(\"Data length ${data.size} != hash size $hashSize\")\n        }\n\n        return CashAddress(correctedAddress, data.copyOfRange(1, data.size), version, addressType)\n    }\n\n    override fun convert(lockingScriptPayload: ByteArray, scriptType: ScriptType): CashAddress {\n        val addressType = when (scriptType) {\n            ScriptType.P2PKH,\n            ScriptType.P2PK -> AddressType.PubKeyHash\n            ScriptType.P2SH -> AddressType.ScriptHash\n            else -> throw AddressFormatException(\"Unknown Address Type\")\n        }\n\n        val payloadSize = lockingScriptPayload.size\n        val encodedSize = when (payloadSize * 8) {\n            160 -> 0\n            192 -> 1\n            224 -> 2\n            256 -> 3\n            320 -> 4\n            384 -> 5\n            448 -> 6\n            512 -> 7\n            else -> throw AddressFormatException(\"Invalid address length\")\n        }\n\n        val version = (addressType.ordinal shl 3) or encodedSize\n        val data = byteArrayOf(version.toByte()) + lockingScriptPayload\n\n        // Reserve the number of bytes required for a 5-bit packed version of a\n        // hash, with version byte.  Add half a byte(4) so integer math provides\n        // the next multiple-of-5 that would fit all the data.\n\n        val addressBytes = ByteArray(((payloadSize + 1) * 8 + 4) / 5)\n        Bech32Cash.convertBits(addressBytes, data, 8, 5, true)\n\n        val addressString = Bech32Cash.encode(hrp, addressBytes)\n\n        return CashAddress(addressString, lockingScriptPayload, version, addressType)\n    }\n\n    override fun convert(publicKey: PublicKey, scriptType: ScriptType): Address {\n        return convert(publicKey.publicKeyHash, scriptType)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/utils/Bip69.kt",
    "content": "package io.horizontalsystems.bitcoincore.utils\n\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\n\nobject Bip69 {\n\n    private const val before = -1\n    private const val equal = 0\n    private const val after = 1\n\n    val outputComparator = kotlin.Comparator<TransactionOutput> { o1, o2 ->\n        //sort by amount first\n        val valueCompareResult = o1.value.compareTo(o2.value)\n        if (valueCompareResult != equal) {\n            return@Comparator valueCompareResult\n        }\n\n        val keyHash1 = o1.lockingScriptPayload ?: return@Comparator after\n        val keyHash2 = o2.lockingScriptPayload ?: return@Comparator before\n\n        //when amounts are equal, sort by hash\n        val hashCompareResult = compareByteArrays(keyHash1, keyHash2)\n        if (hashCompareResult != equal) {\n            return@Comparator hashCompareResult\n        }\n\n        //sort by hash size\n        return@Comparator keyHash1.size.compareTo(keyHash2.size)\n    }\n\n    val inputComparator = kotlin.Comparator<UnspentOutput> { o1, o2 ->\n        //sort by hash first\n        val result = compareByteArrays(o1.output.transactionHash, o2.output.transactionHash)\n        if (result != equal) {\n            return@Comparator result\n        }\n\n        //sort by index\n        return@Comparator o1.output.index.compareTo(o2.output.index)\n    }\n\n    private fun compareByteArrays(b1: ByteArray, b2: ByteArray): Int {\n        var pos = 0\n\n        while (pos < b1.size && pos < b2.size) {\n            val result = (b1[pos].toInt() and 0xff).compareTo(b2[pos].toInt() and 0xff)\n            if (result == equal) {\n                pos++\n            } else {\n                return result\n            }\n        }\n\n        return equal\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/utils/DirectExecutor.kt",
    "content": "package io.horizontalsystems.bitcoincore.utils\n\nimport java.util.concurrent.Executor\n\nclass DirectExecutor : Executor {\n\n    override fun execute(command: Runnable) {\n        command.run()\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/utils/IAddressConverter.kt",
    "content": "package io.horizontalsystems.bitcoincore.utils\n\nimport io.horizontalsystems.bitcoincore.exceptions.AddressFormatException\nimport io.horizontalsystems.bitcoincore.models.Address\nimport io.horizontalsystems.bitcoincore.models.PublicKey\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\n\ninterface IAddressConverter {\n    @Throws\n    fun convert(addressString: String): Address\n\n    @Throws\n    fun convert(lockingScriptPayload: ByteArray, scriptType: ScriptType = ScriptType.P2PKH): Address\n\n    @Throws\n    fun convert(publicKey: PublicKey, scriptType: ScriptType = ScriptType.P2PKH): Address\n}\n\nclass AddressConverterChain : IAddressConverter {\n    private val concreteConverters = mutableListOf<IAddressConverter>()\n\n    fun prependConverter(converter: IAddressConverter) {\n        concreteConverters.add(0, converter)\n    }\n\n    override fun convert(addressString: String): Address {\n        val exceptions = mutableListOf<Exception>()\n\n        for (converter in concreteConverters) {\n            try {\n                return converter.convert(addressString)\n            } catch (e: Exception) {\n                exceptions.add(e)\n            }\n        }\n\n        val exception = AddressFormatException(\"No converter in chain could process the address\")\n        exceptions.forEach {\n            exception.addSuppressed(it)\n        }\n\n        throw exception\n    }\n\n    override fun convert(lockingScriptPayload: ByteArray, scriptType: ScriptType): Address {\n        val exceptions = mutableListOf<Exception>()\n\n        for (converter in concreteConverters) {\n            try {\n                return converter.convert(lockingScriptPayload, scriptType)\n            } catch (e: Exception) {\n                exceptions.add(e)\n            }\n        }\n\n        val exception = AddressFormatException(\"No converter in chain could process the address\")\n        exceptions.forEach {\n            exception.addSuppressed(it)\n        }\n\n        throw exception\n    }\n\n    override fun convert(publicKey: PublicKey, scriptType: ScriptType): Address {\n        val exceptions = mutableListOf<Exception>()\n\n        for (converter in concreteConverters) {\n            try {\n                return converter.convert(publicKey, scriptType)\n            } catch (e: Exception) {\n                exceptions.add(e)\n            }\n        }\n\n        val exception = AddressFormatException(\"No converter in chain could process the address\").also {\n            exceptions.forEach { it.addSuppressed(it) }\n        }\n\n        throw exception\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/utils/MainThreadExecutor.kt",
    "content": "package io.horizontalsystems.bitcoincore.utils\n\nimport android.os.Handler\nimport android.os.Looper\nimport java.util.concurrent.Executor\n\nclass MainThreadExecutor : Executor {\n\n    private val uiHandler = Handler(Looper.getMainLooper())\n\n    override fun execute(command: Runnable) {\n        uiHandler.post(command)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/utils/MerkleBranch.kt",
    "content": "package io.horizontalsystems.bitcoincore.utils\n\nimport io.horizontalsystems.bitcoincore.blocks.InvalidMerkleBlockException\nimport io.horizontalsystems.bitcoincore.core.HashBytes\n\nclass MerkleBranch {\n    private var txCount = 0\n    private var hashes = listOf<ByteArray>()\n    private var flags = byteArrayOf()\n\n    /** Bits used while traversing the tree  */\n    private var bitsUsed: Int = 0\n\n    /** Hashes used while traversing the tree  */\n    private var hashesUsed: Int = 0\n\n    @Throws(InvalidMerkleBlockException::class)\n    fun calculateMerkleRoot(txCount: Int, hashes: List<ByteArray>, flags: ByteArray, matchedHashes: MutableMap<HashBytes, Boolean>): ByteArray {\n        this.txCount = txCount\n        this.hashes = hashes\n        this.flags = flags\n\n        matchedHashes.clear()\n        bitsUsed = 0\n        hashesUsed = 0\n        //\n        // Start at the root and travel down to the leaf node\n        //\n        var height = 0\n        while (getTreeWidth(height) > 1)\n            height++\n        val merkleRoot = parseBranch(height, 0, matchedHashes)\n        //\n        // Verify that all bits and hashes were consumed\n        //\n        if ((bitsUsed + 7) / 8 != flags.size)\n            throw InvalidMerkleBlockException(\"Merkle branch did not use all of its bits\")\n        if (hashesUsed != hashes.size)\n            throw InvalidMerkleBlockException(String.format(\"Merkle branch used %d of %d hashes\",\n                    hashesUsed, hashes.size))\n        return merkleRoot\n    }\n\n    @Throws(InvalidMerkleBlockException::class)\n    private fun parseBranch(height: Int, pos: Int, matchedHashes: MutableMap<HashBytes, Boolean>): ByteArray {\n        if (bitsUsed >= flags.size * 8)\n            throw InvalidMerkleBlockException(\"Merkle branch overflowed the bits array\")\n        val parentOfMatch = Utils.checkBitLE(flags, bitsUsed++)\n        if (height == 0 || !parentOfMatch) {\n            //\n            // If at height 0 or nothing interesting below, use the stored hash and do not descend\n            // to the next level.  If we have a match at height 0, it is a matching transaction.\n            //\n            if (hashesUsed >= hashes.size)\n                throw InvalidMerkleBlockException(\"Merkle branch overflowed the hash array\")\n            if (height == 0 && parentOfMatch)\n                matchedHashes[HashBytes(hashes[hashesUsed])] = true\n            return hashes[hashesUsed++]\n        }\n        //\n        // Continue down to the next level\n        //\n        val right: ByteArray\n        val left = parseBranch(height - 1, pos * 2, matchedHashes)\n        right = if (pos * 2 + 1 < getTreeWidth(height - 1))\n            parseBranch(height - 1, pos * 2 + 1, matchedHashes)\n        else\n            left\n\n        return Utils.doubleDigestTwoBuffers(left, 0, 32, right, 0, 32)\n    }\n\n    private fun getTreeWidth(height: Int): Int {\n        return txCount + (1 shl height) - 1 shr height\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/utils/NetworkUtils.kt",
    "content": "package io.horizontalsystems.bitcoincore.utils\n\nimport io.horizontalsystems.bitcoincore.extensions.toHexString\nimport java.net.*\n\nobject NetworkUtils {\n\n    fun getLocalInetAddress(): InetAddress {\n        try {\n            return InetAddress.getLocalHost()\n        } catch (e: UnknownHostException) {\n            throw RuntimeException(e)\n        }\n    }\n\n    fun getIPv6(inetAddr: InetAddress): ByteArray {\n        val ip = inetAddr.address\n        if (ip.size == 16) {\n            return ip\n        }\n\n        if (ip.size == 4) {\n            val ipv6 = ByteArray(16)\n            ipv6[10] = -1\n            ipv6[11] = -1\n            System.arraycopy(ip, 0, ipv6, 12, 4)\n            return ipv6\n        }\n\n        throw RuntimeException(\"Bad IP: \" + ip.toHexString())\n    }\n\n    fun createSocket(): Socket {\n\n        val socksProxyHost = System.getProperty(\"socksProxyHost\")\n        val socksProxyPort = System.getProperty(\"socksProxyPort\")?.toIntOrNull()\n\n        return if (socksProxyHost != null && socksProxyPort != null) {\n            val socketAddress = InetSocketAddress.createUnresolved(socksProxyHost, socksProxyPort)\n            val proxy = Proxy(Proxy.Type.SOCKS, socketAddress)\n            Socket(proxy)\n        } else {\n            Socket()\n        }\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/utils/PaymentAddressParser.kt",
    "content": "package io.horizontalsystems.bitcoincore.utils\n\nimport io.horizontalsystems.bitcoincore.models.BitcoinPaymentData\n\nclass PaymentAddressParser(private val validScheme: String, private val removeScheme: Boolean) {\n    private val parameterVersion = \"version\"\n    private val parameterAmount = \"amount\"\n    private val parameterLabel = \"label\"\n    private val parameterMessage = \"message\"\n\n    fun parse(paymentAddress: String): BitcoinPaymentData {\n        var parsedString = paymentAddress\n        val address: String\n\n        var version: String? = null\n        var amount: Double? = null\n        var label: String? = null\n        var message: String? = null\n\n        val parameters = mutableMapOf<String, String>()\n\n        val schemeSeparatedParts = paymentAddress.split(\":\")\n        // check exist scheme. If scheme equal network scheme (Bitcoin or bitcoincash), remove scheme for bitcoin or leave for cash. Otherwise, leave wrong scheme to make throw in validator\n        if (schemeSeparatedParts.size >= 2) {\n            parsedString = if (schemeSeparatedParts[0] == validScheme) {\n                if (removeScheme) schemeSeparatedParts[1] else paymentAddress\n            } else {\n                paymentAddress\n            }\n        }\n\n        // check exist params\n        val versionSeparatedParts = parsedString.split(\";\",\"?\")\n\n        if (versionSeparatedParts.size < 2) {\n            address = parsedString\n\n            return BitcoinPaymentData(address = address)\n        }\n\n        address = versionSeparatedParts[0]\n        val strippedList = versionSeparatedParts.drop(0)\n        for (parameter in strippedList) {\n            val parts = parameter.split(\"=\")\n            if (parts.size == 2) {\n                when (parts[0]) {\n                    parameterVersion -> version = parts [1]\n                    parameterAmount-> amount = parts[1].toDoubleOrNull() //todo add try catch for exception\n                    parameterLabel -> label = parts [1]\n                    parameterMessage -> message = parts[1]\n                    else -> parameters[parts[0]] = parts[1]\n                }\n            }\n        }\n\n        return BitcoinPaymentData(address = address, version = version, amount = amount, label = label, message = message, parameters = if (parameters.isEmpty()) null else parameters)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/kotlin/io/horizontalsystems/bitcoincore/utils/TransactionDataSorters.kt",
    "content": "package io.horizontalsystems.bitcoincore.utils\n\nimport io.horizontalsystems.bitcoincore.core.ITransactionDataSorter\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\nimport java.util.*\n\nclass Bip69Sorter : ITransactionDataSorter {\n    override fun sortOutputs(outputs: List<TransactionOutput>): List<TransactionOutput> {\n        Collections.sort(outputs, Bip69.outputComparator)\n        return outputs\n    }\n\n    override fun sortUnspents(unspents: List<UnspentOutput>): List<UnspentOutput> {\n        Collections.sort(unspents, Bip69.inputComparator)\n        return unspents\n    }\n}\n\nclass ShuffleSorter : ITransactionDataSorter {\n    override fun sortOutputs(outputs: List<TransactionOutput>): List<TransactionOutput> {\n        return outputs.shuffled()\n    }\n\n    override fun sortUnspents(unspents: List<UnspentOutput>): List<UnspentOutput> {\n        return unspents.shuffled()\n    }\n}\n\nclass StraightSorter : ITransactionDataSorter {\n    override fun sortOutputs(outputs: List<TransactionOutput>): List<TransactionOutput> {\n        return outputs\n    }\n\n    override fun sortUnspents(unspents: List<UnspentOutput>): List<UnspentOutput> {\n        return unspents\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">Bitcoin Core</string>\n</resources>\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/Fixtures.kt",
    "content": "package io.horizontalsystems.bitcoincore\n\nimport com.nhaarman.mockitokotlin2.mock\nimport com.nhaarman.mockitokotlin2.whenever\nimport io.horizontalsystems.bitcoincore.extensions.hexToByteArray\nimport io.horizontalsystems.bitcoincore.extensions.toReversedByteArray\nimport io.horizontalsystems.bitcoincore.models.*\nimport io.horizontalsystems.bitcoincore.storage.BlockHeader\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport io.horizontalsystems.bitcoincore.utils.HashUtils\n\nobject Fixtures {\n\n    val checkpointBlock1\n        get() = Block(\n            height = 0, // 536256\n            header = BlockHeader(\n                version = 536870912,\n                previousBlockHeaderHash = HashUtils.toBytesAsLE(\"00000000000000000000943de85f4495f053ff55f27d135edc61c27990c2eec5\"),\n                merkleRoot = HashUtils.toBytesAsLE(\"167bf70981d49388d07881b1a448ff9b79cf2a32716e45c535345823d8cdd541\"),\n                timestamp = 1533980459,\n                bits = 388763047,\n                nonce = 1545867530,\n                hash = \"000000000000000000262e508512ce2e6a018e181fb2e5efe048a4e01d21fa7a\".toReversedByteArray()\n            )\n        )\n\n    val block1\n        get() = Block(\n            height = 2013, // 538269\n            header = BlockHeader(\n                version = 536870912,\n                previousBlockHeaderHash = HashUtils.toBytesAsLE(\"00000000000000000011206e641083b68ffc41b7fe6ee1af4a5d69995d1b2d0e\"),\n                merkleRoot = HashUtils.toBytesAsLE(\"5510c0c3d1fd9d2b56a34aab98c29860015caf248fa62a1907b197ddec17c788\"),\n                timestamp = 1535128609,\n                bits = 388763047,\n                nonce = 2295801359,\n                hash = \"0000000000000000000a876dbca5804f792afa90b6dc7946dedb5866245d0c55\".toReversedByteArray()\n            )\n        )\n\n    val block2\n        get() = Block(\n            height = 2014, // 538270\n            header = BlockHeader(\n                version = 536870912,\n                previousBlockHeaderHash = HashUtils.toBytesAsLE(\"0000000000000000000a876dbca5804f792afa90b6dc7946dedb5866245d0c55\"),\n                merkleRoot = HashUtils.toBytesAsLE(\"ccf2737e44e435e2e11481755b00d161815a24e605d605a17bf20da49320ad7d\"),\n                timestamp = 1535128839,\n                bits = 388763047,\n                nonce = 3401296263,\n                hash = \"000000000000000000124a73e879fd66a1b29d1b4b3f1a81de3cbcbe579e21a8\".toReversedByteArray()\n            )\n        )\n\n    val block3\n        get() = Block(\n            height = 2015, // 538271\n            header = BlockHeader(\n                version = 536870912,\n                previousBlockHeaderHash = HashUtils.toBytesAsLE(\"000000000000000000124a73e879fd66a1b29d1b4b3f1a81de3cbcbe579e21a8\"),\n                merkleRoot = HashUtils.toBytesAsLE(\"7904930640df999005df3b57f9c6f542088af33c3d773dcec2939f55ced359b8\"),\n                timestamp = 1535129301,\n                bits = 388763047,\n                nonce = 59591417,\n                hash = \"0000000000000000001d9d48d93793aaa85b5f6d17c176d4ef905c7e7112b1cf\".toReversedByteArray()\n            )\n        )\n\n    val checkpointBlock2\n        get() = Block(\n            height = 2016, // 538272\n            header = BlockHeader(\n                version = 536870912,\n                previousBlockHeaderHash = HashUtils.toBytesAsLE(\"0000000000000000001d9d48d93793aaa85b5f6d17c176d4ef905c7e7112b1cf\"),\n                merkleRoot = HashUtils.toBytesAsLE(\"3ad0fa0e8c100db5831ebea7cabf6addae2c372e6e1d84f6243555df5bbfa351\"),\n                timestamp = 1535129431,\n                bits = 388618029,\n                nonce = 2367954839,\n                hash = \"00000000000000000004f11858464cc6113248537a01e628324968b499848a60\".toReversedByteArray()\n            )\n        )\n\n    //    P2PKH: TestNet tx => 68f297d8a8c9af30cd5a9d6d1eeec5ed3df7be1e4b62f2ced135af6ffe7814c2\n    val transactionP2PKH\n        get() = FullTransaction(\n            header = Transaction(),\n            inputs = listOf(\n                TransactionInput(\n                    previousOutputTxHash = \"093f5f5c5e57ae2ae9728147547e183e2ef5c9e6e879a78bee6ceb59db2b4797\".toReversedByteArray(),\n                    previousOutputIndex = 1,\n                    sigScript = \"473044022018f03676d057a3cb350d9778697ff61da47b813c82fe9fb0f2ea87b231fb865b02200706f5cbbc5ebae6f7bd77e346767bce11c8476aea607671d7321e86a3186ec1012102ce0ef85579f055e2184c935e75e71458db8c4b759cd455b0aa5d91761794eef0\".hexToByteArray(),\n                    sequence = 4294967295\n                )\n            ),\n            outputs = listOf(\n                TransactionOutput(\n                    value = 94734191,\n                    index = 0,\n                    script = \"76a91437a9bfe84d9e4883ace248509bbf14c9d72af01788ac\".hexToByteArray(),\n                    type = ScriptType.UNKNOWN\n                ),\n                TransactionOutput(\n                    value = 100000,\n                    index = 1,\n                    script = \"76a91437a9bfe84d9e4883ace248509bbf14c9d72af01788ac\".hexToByteArray(),\n                    type = ScriptType.UNKNOWN\n                )\n            )\n        )\n\n    //  P2SH: TestNet tx => 761cc7102efe24f4353ae7dc816fbed5e15963d11ca93e36449d521bda21ac4d\n    val transactionP2SH\n        get() = FullTransaction(\n            header = Transaction(),\n            inputs = listOf(\n                TransactionInput(\n                    previousOutputTxHash = \"b6f0ede9cc38cdbceb91936619f89b648bb912f4c42773567037ea5de164873d\".toReversedByteArray(),\n                    previousOutputIndex = 0,\n                    sigScript = \"004830450221008c203a0881f75c731d9a3a2e6d2ffa37da7095b7dde61a9e7a906659219cd0fa02202677097ca7f7e164f73924fe8f84e1e6fc6611450efcda360ce771e98af9f73d0147304402201cba9b641483476f67a4cef08d7280f51de8d7615fcce76642d944dc07132a990220323d13175477bbf67c8c36fb243bec0e4c410bc9173a186d9f8e98ce3445363601475221025b64f7c63e30f315259393f64dcca269d18386997b1cc93da1388c4021e3ea8e210386d42d5d7027ac08ddcbb066e2140575091fe7dc1d202a008eb5e036725e975652ae\".hexToByteArray(),\n                    sequence = 4294967295\n                )\n            ),\n            outputs = listOf(\n                TransactionOutput(\n                    value = 617795422,\n                    index = 0,\n                    script = \"a914cdfb2eb01489e9fe8bd9b878ce4a7084dd88776487\".hexToByteArray(),\n                    type = ScriptType.UNKNOWN\n                ),\n                TransactionOutput(\n                    value = 1407000,\n                    index = 1,\n                    script = \"a914aed6f804c63da80800892f8fd4cdbad0d3ad6d1287\".hexToByteArray(),\n                    type = ScriptType.UNKNOWN\n                )\n            )\n        )\n\n    //  P2PK: TestNet tx => 75b84cb54351866cb5248158735e801d9b2c56592633157ba10d08affa2ffbab\n    val transactionP2PK\n        get() = FullTransaction(\n            header = Transaction(),\n            inputs = listOf(\n                TransactionInput(\n                    previousOutputTxHash = \"978530798f3979322351c190856d17b9e9e7e470c5be4ce87a60bd7a9f7756ac\".toReversedByteArray(),\n                    previousOutputIndex = 0,\n                    sigScript = \"473044022003f9d150b4e291de2825af19dbe1846cc80caf3535d7e9fa03743b2ad019cc47022073294e520c508f702e3ad7a085ecce4a4b311d43faa1e6eb685ec78c002e795d01\".hexToByteArray(),\n                    sequence = 4294967295\n                )\n            ),\n            outputs = listOf(\n                TransactionOutput(\n                    value = 1000000000,\n                    index = 0,\n                    script = \"4104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac\".hexToByteArray(),\n                    type = ScriptType.UNKNOWN\n                ),\n                TransactionOutput(\n                    value = 4000000000,\n                    index = 1,\n                    script = \"410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac\".hexToByteArray(),\n                    type = ScriptType.UNKNOWN\n                )\n            )\n        )\n\n    val transactionP2WPKH\n        get() = FullTransaction(\n            header = Transaction(version = 1),\n            inputs = listOf(\n                TransactionInput(\n                    previousOutputTxHash = \"a6d1ce683f38a84cfd88a9d48b0ba2d7a8def00f8517e3da02c86fce6c7863d7\".toReversedByteArray(),\n                    previousOutputIndex = 0,\n                    sigScript = \"4730440220302e597d74aebcb0bf7f372be156252017af190bd586466104b079fba4b7efa7022037ebbf84e096ef3d966123a93a83586012353c1d2c11c967d21acf1c94c45df001210347235e12207d21b6093d9fd93a0df4d589a0d44252b98b2e934a8da5ab1d1654\".hexToByteArray(),\n                    sequence = 4294967295\n                )\n            ),\n            outputs = listOf(\n                TransactionOutput(\n                    value = 10792000,\n                    index = 0,\n                    script = \"00148749115073ad59a6f3587f1f9e468adedf01473f\".hexToByteArray(),\n                    type = ScriptType.P2WPKH,\n                    lockingScriptPayload = byteArrayOf()\n                ),\n                TransactionOutput(\n                    value = 0,\n                    index = 0,\n                    script = \"6a4c500000b919000189658af37cd16dbd16e4186ea13c5d8e1f40c5b5a0958326067dd923b8fc8f0767f62eb9a7fd57df4f3e775a96ca5b5eabf5057dff98997a3bbd011366703f5e45075f397f7f3c8465da\".hexToByteArray(),\n                    type = ScriptType.P2PK,\n                    lockingScriptPayload = byteArrayOf()\n                )\n            )\n        )\n\n    //  test public key\n    val publicKey = PublicKey().apply {\n        this.publicKey = \"037d56797fbe9aa506fc263751abf23bb46c9770181a6059096808923f0a64cb15\".hexToByteArray()\n        this.publicKeyHash = \"e4de5d630c5cacd7af96418a8f35c411c8ff3c06\".hexToByteArray()\n    }\n\n    fun unspentOutput(value: Long): UnspentOutput {\n        val utxo = mock<UnspentOutput>()\n        val txOutput = mock<TransactionOutput>()\n\n        whenever(utxo.output).thenReturn(txOutput)\n        whenever(txOutput.value).thenReturn(value)\n\n        return utxo\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/RxTestRule.kt",
    "content": "package io.horizontalsystems.bitcoincore\n\nimport io.reactivex.plugins.RxJavaPlugins\nimport io.reactivex.schedulers.Schedulers\n\nobject RxTestRule {\n\n    fun setup() {\n        //https://medium.com/@fabioCollini/testing-asynchronous-rxjava-code-using-mockito-8ad831a16877\n        RxJavaPlugins.setIoSchedulerHandler { Schedulers.trampoline() }\n        RxJavaPlugins.setComputationSchedulerHandler { Schedulers.trampoline() }\n        RxJavaPlugins.setNewThreadSchedulerHandler { Schedulers.trampoline() }\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/TestHelpers.kt",
    "content": "package io.horizontalsystems.bitcoincore\n\nimport com.nhaarman.mockitokotlin2.eq\nimport com.nhaarman.mockitokotlin2.whenever\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.extensions.toReversedHex\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.storage.BlockHeader\nimport org.mockito.Mockito\nimport java.security.SecureRandom\n\nval random = SecureRandom()\n\nfun randomBytes(size: Int = 32): ByteArray {\n    return ByteArray(size).also { random.nextBytes(it) }\n}\n\nclass MockedBlocks(private val storage: IStorage, private val blockHeader: BlockHeader) {\n    var newBlocks = mutableListOf<Block>()\n    var blocksInChain = mutableListOf<Block>()\n    var newBlocksTransactionHashes = mutableListOf<String>()\n    var blocksInChainTransactionHashes = mutableListOf<String>()\n\n    fun create(_blocksInChain: Map<Int, String>, _newBlocks: Map<Int, String>): MockedBlocks {\n        _blocksInChain.forEach { height, id ->\n            val block = Block(blockHeader, height)\n            block.stale = false\n            val transaction = mockTransaction(block)\n\n            whenever(storage.getBlockTransactions(block)).thenReturn(listOf(transaction))\n\n            blocksInChain.add(block)\n            blocksInChainTransactionHashes.add(transaction.hash.toReversedHex())\n        }\n\n        _newBlocks.forEach { height, id ->\n            val block = Block(blockHeader, height)\n            block.stale = false\n\n            val transaction = mockTransaction(block)\n            whenever(storage.getBlockTransactions(block)).thenReturn(listOf(transaction))\n\n            newBlocks.add(block)\n            newBlocksTransactionHashes.add(transaction.hash.toReversedHex())\n        }\n\n        whenever(storage.getBlocks(stale = true)).thenReturn(newBlocks)\n\n        newBlocks.firstOrNull()?.let { firstStale ->\n            whenever(storage.getBlock(stale = true, sortedHeight = \"ASC\"))\n                    .thenReturn(firstStale)\n\n            newBlocks.lastOrNull()?.let { lastStale ->\n                whenever(storage.getBlock(stale = true, sortedHeight = \"DESC\")).thenReturn(lastStale)\n                whenever(storage.getBlock(stale = true, sortedHeight = \"DESC\")).thenReturn(lastStale)\n\n                val inChainBlocksAfterForkPoint = blocksInChain.filter { it.height >= firstStale.height }\n                whenever(storage.getBlocks(heightGreaterOrEqualTo = firstStale.height, stale = false))\n                        .thenReturn(inChainBlocksAfterForkPoint)\n            }\n        }\n\n        blocksInChain.lastOrNull()?.let {\n            whenever(storage.getBlock(stale = eq(false), sortedHeight = eq(\"DESC\"))).thenReturn(it)\n        }\n\n        return this\n    }\n\n    private fun mockTransaction(block: Block): Transaction {\n        val transaction = Mockito.mock(Transaction::class.java)\n\n        whenever(transaction.hash).thenReturn(block.headerHash)\n\n        return transaction\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/blocks/BlockSyncerTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.blocks\n\nimport com.nhaarman.mockitokotlin2.*\nimport io.horizontalsystems.bitcoincore.BitcoinCore\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.extensions.hexToByteArray\nimport io.horizontalsystems.bitcoincore.managers.BloomFilterManager\nimport io.horizontalsystems.bitcoincore.managers.PublicKeyManager\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.bitcoincore.models.BlockHash\nimport io.horizontalsystems.bitcoincore.models.Checkpoint\nimport io.horizontalsystems.bitcoincore.models.MerkleBlock\nimport io.horizontalsystems.bitcoincore.network.Network\nimport io.horizontalsystems.bitcoincore.transactions.BlockTransactionProcessor\nimport org.junit.Assert.assertEquals\nimport org.mockito.Mockito.mock\nimport org.mockito.Mockito.reset\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nobject BlockSyncerTest : Spek({\n\n    lateinit var blockSyncer: BlockSyncer\n\n    val storage = mock(IStorage::class.java)\n    val blockchain = mock(Blockchain::class.java)\n    val transactionProcessor = mock(BlockTransactionProcessor::class.java)\n    val publicKeyManager = mock(PublicKeyManager::class.java)\n    val network = mock(Network::class.java)\n\n    val state = mock(BlockSyncer.State::class.java)\n    val checkpointBlock = mock(Block::class.java)\n    val checkpoint = mock(Checkpoint::class.java)\n\n    beforeEachTest {\n        whenever(network.lastCheckpoint).thenReturn(checkpoint)\n        whenever(checkpoint.block).thenReturn(checkpointBlock)\n        whenever(checkpoint.additionalBlocks).thenReturn(listOf())\n\n        whenever(checkpointBlock.height).thenReturn(1)\n\n        whenever(storage.blocksCount()).thenReturn(1)\n        whenever(storage.lastBlock()).thenReturn(null)\n\n        blockSyncer = BlockSyncer(storage, blockchain, transactionProcessor, publicKeyManager, network.lastCheckpoint, state)\n    }\n\n    afterEachTest {\n        reset(storage, blockchain, transactionProcessor, publicKeyManager, network, state)\n    }\n\n    describe(\"#init\") {\n        beforeEach {\n            reset(storage)\n        }\n\n        context(\"when there are some saved blocks\") {\n            beforeEach {\n                whenever(storage.blocksCount()).thenReturn(1)\n                whenever(storage.lastBlock()).thenReturn(checkpointBlock)\n\n                BlockSyncer(storage, blockchain, transactionProcessor, publicKeyManager, network.lastCheckpoint, state)\n            }\n\n            it(\"does not saves block to storage\") {\n                verify(storage, never()).saveBlock(checkpointBlock)\n            }\n        }\n    }\n\n    describe(\"#localDownloadedBestBlockHeight\") {\n        context(\"when there is no block in storage\") {\n            it(\"returns 0 as default height\") {\n                whenever(storage.lastBlock()).thenReturn(null)\n\n                assertEquals(0, blockSyncer.localDownloadedBestBlockHeight)\n            }\n        }\n\n        context(\"when there are some blocks in storage\") {\n            it(\"returns block height\") {\n                whenever(storage.lastBlock()).thenReturn(checkpointBlock)\n\n                assertEquals(checkpointBlock.height, blockSyncer.localDownloadedBestBlockHeight)\n            }\n        }\n    }\n\n    describe(\"#localKnownBestBlockHeight\") {\n        val blockHash = BlockHash(\"abc\".hexToByteArray(), 1)\n\n        context(\"when no BlockHashes\") {\n            beforeEach {\n                whenever(storage.getBlockchainBlockHashes()).thenReturn(listOf())\n                whenever(storage.blocksCount(listOf())).thenReturn(0)\n            }\n\n            context(\"when no blocks\") {\n                it(\"returns 0\") {\n                    assertEquals(0, blockSyncer.localKnownBestBlockHeight)\n                }\n            }\n\n            context(\"with some blocks\") {\n                it(\"returns last block height + blocks count\") {\n                    whenever(storage.lastBlock()).thenReturn(checkpointBlock)\n\n                    assertEquals(checkpointBlock.height, blockSyncer.localKnownBestBlockHeight)\n                }\n            }\n        }\n\n        context(\"when there are some BlockHashes which haven't downloaded blocks\") {\n            beforeEach {\n                whenever(storage.getBlockchainBlockHashes()).thenReturn(listOf(blockHash))\n                whenever(storage.blocksCount(listOf(blockHash.headerHash))).thenReturn(0)\n            }\n\n            it(\"returns lastBLock + BlockHashes count\") {\n                assertEquals(1, blockSyncer.localKnownBestBlockHeight)\n\n                whenever(storage.lastBlock()).thenReturn(checkpointBlock)\n                assertEquals(checkpointBlock.height + 1, blockSyncer.localKnownBestBlockHeight)\n            }\n        }\n\n        context(\"when there are some BlockHashes which have downloaded blocks\") {\n            beforeEach {\n                whenever(storage.getBlockchainBlockHashes()).thenReturn(listOf(blockHash))\n                whenever(storage.blocksCount(listOf(blockHash.headerHash))).thenReturn(1)\n            }\n\n            it(\"returns lastBLock height\") {\n                assertEquals(0, blockSyncer.localKnownBestBlockHeight)\n\n                whenever(storage.lastBlock()).thenReturn(checkpointBlock)\n                assertEquals(checkpointBlock.height, blockSyncer.localKnownBestBlockHeight)\n            }\n        }\n    }\n\n    describe(\"#prepareForDownload\") {\n        val emptyBlocks = mock<List<Block>>()\n        val blocksHashes = listOf(byteArrayOf(1))\n\n        beforeEach {\n            whenever(storage.getBlocks(any(), any())).thenReturn(emptyBlocks)\n            whenever(storage.getBlockHashHeaderHashes(listOf(checkpointBlock.headerHash))).thenReturn(blocksHashes)\n\n            blockSyncer.prepareForDownload()\n        }\n\n        it(\"handles partial blocks\") {\n            verify(publicKeyManager).fillGap()\n            verify(state).iterationHasPartialBlocks = false\n        }\n\n        it(\"clears partial blocks\") {\n            verify(storage).getBlockHashHeaderHashes(listOf(checkpointBlock.headerHash))\n            verify(storage).getBlocks(blocksHashes)\n            verify(blockchain).deleteBlocks(any())\n        }\n\n        it(\"clears block hashes\") {\n            verify(storage).deleteBlockchainBlockHashes()\n        }\n\n        it(\"handles fork\") {\n            verify(blockchain).handleFork()\n        }\n    }\n\n    describe(\"#downloadIterationCompleted\") {\n\n        context(\"when iteration has partial blocks\") {\n\n            it(\"handles partial blocks\") {\n                whenever(state.iterationHasPartialBlocks).thenReturn(true)\n                blockSyncer.downloadIterationCompleted()\n\n                verify(publicKeyManager).fillGap()\n                verify(state).iterationHasPartialBlocks = false\n            }\n        }\n\n        context(\"when iteration has no partial blocks\") {\n\n            it(\"does not handles partial blocks\") {\n                whenever(state.iterationHasPartialBlocks).thenReturn(false)\n                blockSyncer.downloadIterationCompleted()\n\n                verify(state).iterationHasPartialBlocks\n\n                verifyNoMoreInteractions(state)\n                verifyNoMoreInteractions(publicKeyManager)\n            }\n        }\n    }\n\n    describe(\"#downloadCompleted\") {\n        it(\"handles fork\") {\n            blockSyncer.downloadCompleted()\n\n            verify(blockchain).handleFork()\n        }\n    }\n\n    describe(\"#downloadFailed\") {\n        val emptyBlocks = mock<List<Block>>()\n        val blocksHashes = listOf(byteArrayOf(1))\n\n        beforeEach {\n            whenever(storage.getBlocks(any(), any())).thenReturn(emptyBlocks)\n            whenever(storage.getBlockHashHeaderHashes(listOf(checkpointBlock.headerHash))).thenReturn(blocksHashes)\n\n            blockSyncer.downloadFailed()\n        }\n\n        it(\"handles partial blocks\") {\n            verify(publicKeyManager).fillGap()\n            verify(state).iterationHasPartialBlocks = false\n        }\n\n        it(\"clears partial blocks\") {\n            verify(storage).getBlockHashHeaderHashes(listOf(checkpointBlock.headerHash))\n            verify(storage).getBlocks(blocksHashes)\n            verify(blockchain).deleteBlocks(any())\n        }\n\n        it(\"clears block hashes\") {\n            verify(storage).deleteBlockchainBlockHashes()\n        }\n\n        it(\"handles fork\") {\n            verify(blockchain).handleFork()\n        }\n    }\n\n//    describe(\"#getBlockHashes\") {\n//        val listOfBlockHashes = listOf(BlockHash(\"abc\".hexToByteArray(), 1))\n//\n//        it(\"returns first 500 block hashes\") {\n//            whenever(storage.getBlockHashesSortedBySequenceAndHeight(limit = 500))\n//                    .thenReturn(listOfBlockHashes)\n//\n//            assertEquals(listOfBlockHashes, blockSyncer.getBlockHashes())\n//        }\n//    }\n\n    describe(\"#getBlockLocatorHashes\") {\n        val peerLastBlockHeight = 99\n\n        beforeEach {\n            whenever(storage.getLastBlockchainBlockHash()).thenReturn(null)\n            whenever(storage.getBlocks(checkpointBlock.height, \"height\", 10)).thenReturn(listOf())\n            whenever(storage.getBlock(peerLastBlockHeight)).thenReturn(null)\n        }\n\n        context(\"when there's no block or block hashes\") {\n            it(\"return checkpoint's header hash\") {\n                assertEquals(listOf(checkpointBlock.headerHash), blockSyncer.getBlockLocatorHashes(peerLastBlockHeight))\n            }\n        }\n\n        context(\"when there's blockchain block hashes\") {\n            val blockHash = BlockHash(\"cba\".hexToByteArray(), 1)\n\n            beforeEach {\n                whenever(storage.getLastBlockchainBlockHash()).thenReturn(blockHash)\n            }\n\n            it(\"return last block hash and checkpoint's header hash\") {\n                assertEquals(listOf(blockHash.headerHash, checkpointBlock.headerHash), blockSyncer.getBlockLocatorHashes(peerLastBlockHeight))\n            }\n        }\n\n        context(\"when there's no blockHashes but there are blocks\") {\n            val block1 = mock(Block::class.java)\n            val block2 = mock(Block::class.java)\n            val blocks = listOf(block1, block2)\n\n            beforeEach {\n                whenever(storage.getBlocks(heightGreaterThan = checkpointBlock.height, sortedBy = \"height\", limit = 10)).thenReturn(blocks)\n            }\n\n            it(\"returns blocks\") {\n                val locatorHashes = blockSyncer.getBlockLocatorHashes(peerLastBlockHeight)\n\n                assertEquals(3, locatorHashes.size)\n                assertEquals(block1.headerHash, locatorHashes[0])\n                assertEquals(block2.headerHash, locatorHashes[1])\n                assertEquals(checkpointBlock.headerHash, locatorHashes[2])\n            }\n        }\n\n        context(\"when peers last block already exists in storage\") {\n            val peerBlock = mock(Block::class.java)\n\n            val headerHash = byteArrayOf(1, 2, 3)\n            val blockHash = BlockHash(headerHash, 1)\n\n            beforeEach {\n                whenever(peerBlock.headerHash).thenReturn(headerHash)\n\n                whenever(storage.getLastBlockchainBlockHash()).thenReturn(blockHash)\n                whenever(storage.getBlock(peerLastBlockHeight)).thenReturn(peerBlock)\n            }\n\n            it(\"returns only one header hash\") {\n                assertEquals(listOf(blockHash.headerHash), blockSyncer.getBlockLocatorHashes(peerLastBlockHeight))\n            }\n        }\n    }\n\n    describe(\"#addBlockHashes\") {\n        val existingHashes = listOf(\n                byteArrayOf(1, 2, 3),\n                byteArrayOf(4, 5, 6))\n\n        val newBlockHashes = listOf(\n                byteArrayOf(4, 5, 6),\n                byteArrayOf(5, 6, 7))\n\n        beforeEach {\n            whenever(storage.getBlockHashHeaderHashes()).thenReturn(existingHashes)\n        }\n\n        context(\"when there's some block hashes\") {\n            val blockHash = BlockHash(byteArrayOf(1), 99, sequence = 99)\n\n            beforeEach {\n                whenever(storage.getLastBlockHash()).thenReturn(blockHash)\n            }\n\n            it(\"set sequence of given block hashes staring from last block hash sequence\") {\n                blockSyncer.addBlockHashes(newBlockHashes)\n\n                verify(storage).addBlockHashes(argThat {\n                    this.size == 1 &&\n                            this[0].sequence == blockHash.sequence + 1 &&\n                            this[0].headerHash.contentEquals(newBlockHashes[1])\n                })\n            }\n        }\n\n        context(\"when there's no block hashes\") {\n            beforeEach {\n                whenever(storage.getLastBlockHash()).thenReturn(null)\n            }\n\n            it(\"set sequence of given block hashes staring from 0\") {\n                blockSyncer.addBlockHashes(newBlockHashes)\n\n                verify(storage).addBlockHashes(argThat {\n                    this.size == 1 &&\n                            this[0].sequence == 1 &&\n                            this[0].headerHash.contentEquals(newBlockHashes[1])\n                })\n            }\n        }\n    }\n\n    describe(\"#handleMerkleBlock\") {\n        val maxBlockHeight = 100\n        val block = mock(Block::class.java)\n        val merkleBlock = mock(MerkleBlock::class.java)\n        val merkleHeight = 1\n\n        beforeEach {\n            whenever(merkleBlock.height).thenReturn(null)\n            whenever(merkleBlock.associatedTransactions).thenReturn(mutableListOf())\n            whenever(block.height).thenReturn(merkleHeight)\n            whenever(block.headerHash).thenReturn(byteArrayOf(1, 2, 3))\n            whenever(state.iterationHasPartialBlocks).thenReturn(true)\n\n            whenever(blockchain.connect(merkleBlock)).thenReturn(block)\n            whenever(blockchain.forceAdd(merkleBlock, merkleHeight)).thenReturn(block)\n        }\n\n        afterEach {\n            reset(merkleBlock)\n        }\n\n        it(\"handles merkle block\") {\n            blockSyncer.handleMerkleBlock(merkleBlock, maxBlockHeight)\n\n            verify(blockchain).connect(merkleBlock)\n            verify(transactionProcessor).processReceived(merkleBlock.associatedTransactions, block, state.iterationHasPartialBlocks)\n        }\n\n        context(\"when merkle block height it not null\") {\n\n            beforeEach {\n                whenever(merkleBlock.height).thenReturn(merkleHeight)\n            }\n\n            it(\"force adds merkle block\") {\n                blockSyncer.handleMerkleBlock(merkleBlock, maxBlockHeight)\n\n                verify(blockchain).forceAdd(merkleBlock, merkleHeight)\n            }\n        }\n\n        context(\"when bloom filter expired while processing transaction\") {\n            beforeEach {\n                whenever(transactionProcessor.processReceived(merkleBlock.associatedTransactions, block, state.iterationHasPartialBlocks))\n                        .thenThrow(BloomFilterManager.BloomFilterExpired)\n            }\n\n            it(\"sets state as it left partially handled blocks\") {\n                blockSyncer.handleMerkleBlock(merkleBlock, maxBlockHeight)\n\n                verify(state).iterationHasPartialBlocks = true\n            }\n        }\n\n        context(\"when iteration not have partial blocks\") {\n            beforeEach {\n                whenever(state.iterationHasPartialBlocks).thenReturn(false)\n            }\n\n            it(\"delete block hash\") {\n                blockSyncer.handleMerkleBlock(merkleBlock, maxBlockHeight)\n\n                verify(storage).deleteBlockHash(block.headerHash)\n            }\n        }\n    }\n\n    describe(\"#shouldRequest\") {\n        val block = mock(Block::class.java)\n        val hashHash = byteArrayOf(1)\n\n        it(\"returns true if block exists\") {\n            whenever(storage.getBlock(hashHash)).thenReturn(block)\n\n            assertEquals(false, blockSyncer.shouldRequest(hashHash))\n        }\n    }\n\n//    describe(\"#getCheckpointBlock\") {\n//        val bip44Checkpoint = mock<Checkpoint>()\n//        val bip44CheckpointBlock = mock<Block>()\n//        val lastCheckpoint = mock<Checkpoint>()\n//        val lastCheckpointBlock = mock<Block>()\n//        val lastBlockInDB = mock<Block>()\n//\n//        beforeEach {\n//            whenever(network.bip44Checkpoint).thenReturn(bip44Checkpoint)\n//            whenever(bip44Checkpoint.block).thenReturn(bip44CheckpointBlock)\n//            whenever(bip44Checkpoint.additionalBlocks).thenReturn(listOf())\n//\n//            whenever(network.lastCheckpoint).thenReturn(lastCheckpoint)\n//            whenever(lastCheckpoint.block).thenReturn(lastCheckpointBlock)\n//            whenever(lastCheckpoint.additionalBlocks).thenReturn(listOf())\n//\n//            whenever(storage.lastBlock()).thenReturn(lastBlockInDB)\n//        }\n//\n//        context(\"when sync mode is Full\") {\n//            val syncMode = BitcoinCore.SyncMode.Full()\n//\n//            it(\"equals to bip44Checkpoint\") {\n//                val actual = BlockSyncer.resolveCheckpoint(syncMode, network, storage)\n//                assertEquals(bip44Checkpoint, actual)\n//            }\n//        }\n//\n//        context(\"when sync mode is Api or New\") {\n//            val syncMode = BitcoinCore.SyncMode.Api()\n//\n//            context(\"when last block in DB earlier than checkpoint block\") {\n//                beforeEach {\n//                    whenever(lastBlockInDB.height).thenReturn(100)\n//                    whenever(lastCheckpointBlock.height).thenReturn(200)\n//                }\n//\n//                it(\"equals to bip44Checkpoint\") {\n//                    val actual = BlockSyncer.resolveCheckpoint(syncMode, network, storage)\n//                    assertEquals(bip44Checkpoint, actual)\n//                }\n//            }\n//\n//            context(\"when last block in DB later than checkpoint block\") {\n//                beforeEach {\n//                    whenever(storage.lastBlock()).thenReturn(lastBlockInDB)\n//                    whenever(lastBlockInDB.height).thenReturn(200)\n//                    whenever(lastCheckpointBlock.height).thenReturn(100)\n//                }\n//\n//                it(\"equals to lastCheckpoint\") {\n//                    val actual = BlockSyncer.resolveCheckpoint(syncMode, network, storage)\n//                    assertEquals(lastCheckpoint, actual)\n//                }\n//            }\n//        }\n//\n//        context(\"when DB has no block\") {\n//            beforeEach {\n//                whenever(storage.lastBlock()).thenReturn(null)\n//            }\n//\n//            it(\"saves checkpoint block to DB\") {\n//                BlockSyncer.resolveCheckpoint(BitcoinCore.SyncMode.Full(), network, storage)\n//\n//                verify(storage).saveBlock(bip44CheckpointBlock)\n//            }\n//        }\n//    }\n\n})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/blocks/BlockchainTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.blocks\n\nimport com.nhaarman.mockitokotlin2.*\nimport io.horizontalsystems.bitcoincore.MockedBlocks\nimport io.horizontalsystems.bitcoincore.blocks.validators.BlockValidatorException\nimport io.horizontalsystems.bitcoincore.blocks.validators.IBlockValidator\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.bitcoincore.models.MerkleBlock\nimport io.horizontalsystems.bitcoincore.storage.BlockHeader\nimport org.junit.Assert.assertEquals\nimport org.junit.Assert.fail\nimport org.mockito.Mockito.mock\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nobject BlockchainTest : Spek({\n\n    lateinit var blockchain: Blockchain\n    lateinit var mockedBlocks: MockedBlocks\n\n    val storage = mock(IStorage::class.java)\n    val blockValidator = mock(IBlockValidator::class.java)\n    val dataListener = mock(IBlockchainDataListener::class.java)\n\n    val prevHash = byteArrayOf(1)\n    val merkleBlock = mock(MerkleBlock::class.java)\n    val blockHeader = mock(BlockHeader::class.java)\n    val block = mock(Block::class.java)\n\n    beforeEachTest {\n        whenever(blockHeader.previousBlockHeaderHash).thenReturn(prevHash)\n        whenever(blockHeader.merkleRoot).thenReturn(byteArrayOf())\n        whenever(blockHeader.hash).thenReturn(byteArrayOf(1, 2))\n        whenever(merkleBlock.header).thenReturn(blockHeader)\n        whenever(merkleBlock.blockHash).thenReturn(byteArrayOf(1, 3))\n\n        blockchain = Blockchain(storage, blockValidator, dataListener)\n    }\n\n    afterEachTest {\n        reset(storage, blockValidator, dataListener)\n        reset(merkleBlock, blockHeader, block)\n    }\n\n    describe(\"#connect\") {\n        beforeEach {\n            whenever(merkleBlock.blockHash).thenReturn(byteArrayOf(1, 2, 3))\n            whenever(merkleBlock.header).thenReturn(blockHeader)\n        }\n\n        context(\"when block exists\") {\n            beforeEach {\n                whenever(storage.getBlock(merkleBlock.blockHash)).thenReturn(block)\n            }\n\n            it(\"returns existing block\") {\n                assertEquals(block, blockchain.connect(merkleBlock))\n            }\n\n            it(\"doesn't add a block to storage\") {\n                blockchain.connect(merkleBlock)\n\n                verify(storage, never()).addBlock(block)\n                verify(dataListener, never()).onBlockInsert(block)\n            }\n        }\n\n        context(\"when block doesn't exist\") {\n            beforeEach {\n                whenever(storage.getBlock(merkleBlock.blockHash)).thenReturn(null)\n            }\n\n            context(\"when block is not in chain\") {\n                beforeEach {\n                    whenever(storage.getBlock(merkleBlock.header.previousBlockHeaderHash)).thenReturn(null)\n                }\n\n                it(\"throws BlockValidatorError.noPreviousBlock error\") {\n                    try {\n                        blockchain.connect(merkleBlock)\n                    } catch (e: Exception) {\n                        if (e !is BlockValidatorException.NoPreviousBlock) {\n                            fail(\"Expected No PreviousBlock exception to be thrown\")\n                        }\n                    }\n                }\n            }\n\n            context(\"when block is in chain\") {\n                beforeEach {\n                    whenever(storage.getBlock(merkleBlock.header.previousBlockHeaderHash)).thenReturn(block)\n                }\n\n                context(\"when block is invalid\") {\n                    it(\"doesn't add a block to storage\") {\n                        whenever(blockValidator.validate(any(), any())).thenThrow(BlockValidatorException.WrongPreviousHeader())\n\n                        try {\n                            blockchain.connect(merkleBlock)\n                        } catch (e: Exception) {\n                        }\n\n                        verify(storage, never()).addBlock(any())\n                    }\n                }\n\n                context(\"when block is valid\") {\n\n                    it(\"adds block to database\") {\n                        try {\n                            blockchain.connect(merkleBlock)\n                        } catch (e: Exception) {\n                            e.printStackTrace()\n                        }\n\n                        verify(storage).addBlock(any())\n                        verify(blockValidator).validate(any(), any())\n                        verify(dataListener).onBlockInsert(any())\n                    }\n                }\n            }\n        }\n    }\n\n    describe(\"#forceAdd\") {\n        lateinit var connectedBlock: Block\n\n        val height = 1\n\n        beforeEach {\n            connectedBlock = blockchain.forceAdd(merkleBlock, height)\n        }\n\n        it(\"doesn't validate block\") {\n            verify(blockValidator, never()).validate(any(), any())\n        }\n\n        it(\"adds block to database\") {\n            verify(storage).addBlock(any())\n            verify(dataListener).onBlockInsert(connectedBlock)\n        }\n    }\n\n    describe(\"#handleFork\") {\n\n        context(\"when no fork found\") {\n            val blocksInChain = sortedMapOf(1 to \"InChain1\", 2 to \"InChain2\", 3 to \"InChain3\")\n            val newBlocks = sortedMapOf(4 to \"NewBlock4\", 5 to \"NewBlock5\", 6 to \"NewBlock6\")\n\n            beforeEach {\n                mockedBlocks = MockedBlocks(storage, blockHeader).create(blocksInChain, newBlocks)\n            }\n\n            it(\"makes new blocks not stale\") {\n                blockchain.handleFork()\n\n                verify(storage).unstaleAllBlocks()\n            }\n        }\n\n        context(\"when fork found and new blocks leaf is longer\") {\n            val blocksInChain = sortedMapOf(1 to \"InChain1\", 2 to \"InChain2\", 3 to \"InChain3\")\n            val newBlocks = sortedMapOf(2 to \"NewBlock2\", 3 to \"NewBlock3\", 4 to \"NewBlock4\")\n\n            beforeEach {\n                mockedBlocks = MockedBlocks(storage, blockHeader).create(blocksInChain, newBlocks)\n            }\n\n            it(\"deletes old blocks in chain after the fork\") {\n                blockchain.handleFork()\n\n                verify(storage).deleteBlocks(mockedBlocks.blocksInChain.takeLast(2))\n                verify(storage, never()).deleteBlocks(mockedBlocks.newBlocks)\n                verify(dataListener).onTransactionsDelete(mockedBlocks.blocksInChainTransactionHashes.takeLast(2))\n            }\n\n            it(\"makes new blocks not stale\") {\n                blockchain.handleFork()\n\n                verify(storage).unstaleAllBlocks()\n            }\n        }\n\n        context(\"when fork found and new blocks leaf is shorter\") {\n            val blocksInChain = sortedMapOf(1 to \"InChain1\", 2 to \"InChain2\", 3 to \"InChain3\", 4 to \"InChain4\")\n            val newBlocks = sortedMapOf(2 to \"NewBlock2\", 3 to \"NewBlock3\")\n\n            beforeEach {\n                mockedBlocks = MockedBlocks(storage, blockHeader).create(blocksInChain, newBlocks)\n            }\n\n            it(\"deletes new blocks\") {\n                blockchain.handleFork()\n\n                verify(storage).deleteBlocks(mockedBlocks.newBlocks)\n                verify(storage, never()).deleteBlocks(mockedBlocks.blocksInChain.takeLast(2))\n                verify(dataListener).onTransactionsDelete(mockedBlocks.newBlocksTransactionHashes)\n            }\n        }\n\n        context(\"when fork exists and two leafs are equal\") {\n            val blocksInChain = sortedMapOf(1 to \"InChain1\", 2 to \"InChain2\", 3 to \"InChain3\")\n            val newBlocks = sortedMapOf(2 to \"NewBlock2\", 3 to \"NewBlock3\")\n\n            beforeEach {\n                mockedBlocks = MockedBlocks(storage, blockHeader).create(blocksInChain, newBlocks)\n            }\n\n            it(\"deletes new blocks\") {\n                blockchain.handleFork()\n\n                verify(storage).deleteBlocks(mockedBlocks.newBlocks)\n                verify(storage, never()).deleteBlocks(mockedBlocks.blocksInChain.takeLast(2))\n                verify(dataListener).onTransactionsDelete(mockedBlocks.newBlocksTransactionHashes)\n            }\n        }\n\n        context(\"when no new(stale) blocks found\") {\n            val blocksInChain = sortedMapOf(1 to \"InChain1\", 2 to \"InChain2\", 3 to \"InChain3\")\n            val newBlocks = sortedMapOf<Int, String>()\n\n            beforeEach {\n                MockedBlocks(storage, blockHeader).create(blocksInChain, newBlocks)\n            }\n\n            it(\"does not do nothing\") {\n                blockchain.handleFork()\n\n                verify(storage, never()).deleteBlocks(any())\n                verify(storage, never()).updateBlock(any())\n                verify(dataListener, never()).onTransactionsDelete(any())\n            }\n        }\n\n        context(\"when no blocks in chain\") {\n            val blocksInChain = sortedMapOf<Int, String>()\n            val newBlocks = sortedMapOf(2 to \"NewBlock2\", 3 to \"NewBlock3\", 4 to \"NewBlock4\")\n\n            beforeEach {\n                mockedBlocks = MockedBlocks(storage, blockHeader).create(blocksInChain, newBlocks)\n            }\n\n            it(\"makes new blocks not stale\") {\n                blockchain.handleFork()\n\n                verify(storage, never()).deleteBlocks(any())\n                verify(storage).unstaleAllBlocks()\n            }\n        }\n\n    }\n})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/blocks/validators/BitsValidatorTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.blocks.validators\n\nimport com.nhaarman.mockitokotlin2.mock\nimport com.nhaarman.mockitokotlin2.whenever\nimport io.horizontalsystems.bitcoincore.models.Block\nimport org.junit.jupiter.api.Assertions.assertDoesNotThrow\nimport org.junit.jupiter.api.assertThrows\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nobject BitsValidatorTest : Spek({\n    lateinit var validator: BitsValidator\n    val block = mock<Block>()\n    val previousBlock = mock<Block>()\n\n    beforeEachTest {\n        validator = BitsValidator()\n    }\n\n    describe(\"#validate\") {\n\n        it(\"passes when block and prev block bits are equal\") {\n            whenever(block.bits).thenReturn(1)\n            whenever(previousBlock.bits).thenReturn(1)\n\n            assertDoesNotThrow {\n                validator.validate(block, previousBlock)\n            }\n        }\n\n        it(\"fails when block and prev block bits are not equal\") {\n            whenever(block.bits).thenReturn(1)\n            whenever(previousBlock.bits).thenReturn(2)\n\n            assertThrows<BlockValidatorException.NotEqualBits> {\n                validator.validate(block, previousBlock)\n            }\n        }\n\n    }\n})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/blocks/validators/LegacyDifficultyAdjustmentValidatorTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.blocks.validators\n\nimport com.nhaarman.mockitokotlin2.mock\nimport com.nhaarman.mockitokotlin2.whenever\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.managers.BlockValidatorHelper\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.bitcoincore.storage.BlockHeader\nimport io.horizontalsystems.bitcoincore.utils.HashUtils\nimport org.junit.Assert\nimport org.junit.jupiter.api.Assertions\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nobject LegacyDifficultyAdjustmentValidatorTest : Spek({\n\n    val storage = mock<IStorage>()\n    val blockHelper = BlockValidatorHelper(storage)\n\n    val validator by memoized {\n        LegacyDifficultyAdjustmentValidator(blockHelper, 2016, 14 * 24 * 60 * 60, 0x1d00ffff)\n    }\n\n    describe(\"#isBlockValidatable\") {\n        val block = mock<Block>()\n        val previousBlock = mock<Block>()\n\n        it(\"is true when block is div by 2016\") {\n            whenever(block.height).thenReturn(4032)\n            Assert.assertTrue(validator.isBlockValidatable(block, previousBlock))\n        }\n\n        it(\"is false when block is not div by 2016\") {\n            whenever(block.height).thenReturn(4033)\n            Assert.assertFalse(validator.isBlockValidatable(block, previousBlock))\n        }\n    }\n\n    describe(\"#validate\") {\n        it(\"passes\") {\n            val check1 = Block(\n                    height = 0, // 536256\n                    header = BlockHeader(\n                            version = 536870912,\n                            previousBlockHeaderHash = HashUtils.toBytesAsLE(\"00000000000000000000943de85f4495f053ff55f27d135edc61c27990c2eec5\"),\n                            merkleRoot = HashUtils.toBytesAsLE(\"167bf70981d49388d07881b1a448ff9b79cf2a32716e45c535345823d8cdd541\"),\n                            timestamp = 1533980459,\n                            bits = 388763047,\n                            nonce = 1545867530,\n                            hash = byteArrayOf(1)\n                    )\n            )\n\n            var prevBlock = check1\n            val blockHead = BlockHeader(\n                    version = 536870912,\n                    previousBlockHeaderHash = HashUtils.toBytesAsLE(\"000000000000000000124a73e879fd66a1b29d1b4b3f1a81de3cbcbe579e21a8\"),\n                    merkleRoot = HashUtils.toBytesAsLE(\"7904930640df999005df3b57f9c6f542088af33c3d773dcec2939f55ced359b8\"),\n                    timestamp = 1535129301,\n                    bits = 388763047,\n                    nonce = 59591417,\n                    hash = byteArrayOf(1)\n            )\n\n            for (i in 1 until 2016) {\n                prevBlock = Block(blockHead, prevBlock)\n            }\n\n            val check2Head = BlockHeader(\n                    version = 536870912,\n                    previousBlockHeaderHash = HashUtils.toBytesAsLE(\"0000000000000000001d9d48d93793aaa85b5f6d17c176d4ef905c7e7112b1cf\"),\n                    merkleRoot = HashUtils.toBytesAsLE(\"3ad0fa0e8c100db5831ebea7cabf6addae2c372e6e1d84f6243555df5bbfa351\"),\n                    timestamp = 1535129431,\n                    bits = 388618029,\n                    nonce = 2367954839,\n                    hash = byteArrayOf(1)\n            )\n\n            val check2 = Block(check2Head, prevBlock)\n\n            whenever(storage.getBlockByHeightStalePrioritized(check2.height - 2016)).thenReturn(check1)\n\n            Assertions.assertDoesNotThrow {\n                validator.validate(check2, prevBlock)\n            }\n        }\n    }\n})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/blocks/validators/LegacyTestNetDifficultyValidatorTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.blocks.validators\n\nimport com.nhaarman.mockitokotlin2.any\nimport com.nhaarman.mockitokotlin2.mock\nimport com.nhaarman.mockitokotlin2.whenever\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.bitcoincore.storage.BlockHeader\nimport io.horizontalsystems.bitcoincore.utils.HashUtils\nimport org.junit.Assert\nimport org.junit.jupiter.api.Assertions\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nobject LegacyTestNetDifficultyValidatorTest : Spek({\n    val storage = mock<IStorage>()\n\n    val validator by memoized {\n        LegacyTestNetDifficultyValidator(storage, 2016, 14 * 24 * 60 * 60, 0x1d00ffff)\n    }\n\n    describe(\"#isBlockValidatable\") {\n        val block = mock<Block>()\n        val previousBlock = mock<Block>()\n\n        it(\"is true when prev block is later then February 16th 2012\") {\n            whenever(previousBlock.timestamp).thenReturn(1329264000 + 1)\n            Assert.assertTrue(validator.isBlockValidatable(block, previousBlock))\n        }\n\n        it(\"is false when prev block is earlier or equal to February 16th 2012\") {\n            whenever(previousBlock.timestamp).thenReturn(1329264000)\n            Assert.assertFalse(validator.isBlockValidatable(block, previousBlock))\n        }\n    }\n\n    describe(\"#validate\") {\n        it(\"passes\") {\n            val check1 = Block(\n                    height = 1411200,\n                    header = BlockHeader(\n                            version = 536870912,\n                            previousBlockHeaderHash = HashUtils.toBytesAsLE(\"00000000000000a5bf9029aebb1956200304ffee31bc09f1323ae412d81fa2b2\"),\n                            merkleRoot = HashUtils.toBytesAsLE(\"dff076f1f3468f86785b42c10e6f23c849ccbc1d40a0fa8909b20b20fb204de2\"),\n                            timestamp = 1535560970,\n                            bits = 424329477,\n                            nonce = 2681700833,\n                            hash = byteArrayOf(1)\n                    )\n            )\n\n            var prevBlock = check1\n            val blockHead = BlockHeader(\n                    version = 536870912,\n                    previousBlockHeaderHash = HashUtils.toBytesAsLE(\"000000000000003e3b50c7edca7bf59075b3d39ee2668076aa1ebe559787ff25\"),\n                    merkleRoot = HashUtils.toBytesAsLE(\"6a05a10911d844e86e7758bf27ce183b2eaa5768108d992efdb6487c8f3f6dae\"),\n                    timestamp = 1536796796,\n                    bits = 424329477,\n                    nonce = 915088888,\n                    hash = byteArrayOf(1)\n            )\n\n            for (i in 1 until 2016) {\n                prevBlock = Block(blockHead, prevBlock)\n            }\n\n            val check2Head = BlockHeader(\n                    version = 536870912,\n                    previousBlockHeaderHash = HashUtils.toBytesAsLE(\"0000000000000046f38ada53de3346d8191f69c8f3c0ba9e1950f5bf291989c4\"),\n                    merkleRoot = HashUtils.toBytesAsLE(\"827bc2d47a164b9144a507eebc40b32f8f3e7e8c784b17e0a1fa245bfe9c100c\"),\n                    timestamp = 1536797113,\n                    nonce = 1267362056,\n                    bits = 424435696,\n                    hash = byteArrayOf(1)\n            )\n\n            val check2 = Block(check2Head, prevBlock)\n\n            whenever(storage.getBlock(hashHash = any())).thenReturn(check1)\n            whenever(storage.getBlock(hashHash = check2.previousBlockHash)).thenReturn(prevBlock)\n\n            Assertions.assertDoesNotThrow {\n                validator.validate(check2, prevBlock)\n            }\n        }\n    }\n})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/blocks/validators/ProofOfWorkValidatorTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.blocks.validators\n\nimport io.horizontalsystems.bitcoincore.Fixtures\nimport io.horizontalsystems.bitcoincore.crypto.CompactBits\nimport org.junit.jupiter.api.Assertions.assertDoesNotThrow\nimport org.junit.jupiter.api.assertThrows\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\nimport java.math.BigInteger\n\nobject ProofOfWorkValidatorTest : Spek({\n    lateinit var validator: ProofOfWorkValidator\n\n    beforeEachTest {\n        validator = ProofOfWorkValidator()\n    }\n\n    describe(\"#validate\") {\n        it(\"passes when proof of work is valid\") {\n            val block = Fixtures.block2\n            val previousBlock = Fixtures.block1\n            assertDoesNotThrow {\n                validator.validate(block, previousBlock)\n            }\n        }\n\n        it(\"fails when proof of work is not valid\") {\n            val block = Fixtures.block2\n            val previousBlock = Fixtures.block1\n            block.bits = CompactBits.encode(BigInteger(block.headerHash).minus(BigInteger.valueOf(1L)))\n\n            assertThrows<BlockValidatorException.InvalidProofOfWork> {\n                validator.validate(block, previousBlock)\n            }\n        }\n    }\n\n})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/core/DataProviderTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.core\n\nimport com.nhaarman.mockitokotlin2.*\nimport io.horizontalsystems.bitcoincore.managers.UnspentOutputProvider\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nobject DataProviderTest : Spek({\n    val storage = mock<IStorage>()\n    val unspentOutputProvider = mock<UnspentOutputProvider>()\n    val transactionInfoConverter = mock<ITransactionInfoConverter>()\n\n    val dataProvider by memoized {\n        DataProvider(storage, unspentOutputProvider, transactionInfoConverter)\n    }\n\n    afterEachTest {\n        reset(storage)\n    }\n\n//    describe(\"with `fromHash`\") {\n//        val fromUid = \"1234\"\n//        val limit = 1\n//\n//        it(\"gets transaction with given hash\") {\n//            dataProvider.transactions(fromUid).test().assertOf {\n//                verify(storage).getValidOrInvalidTransaction(fromUid)\n//            }\n//        }\n//\n//        context(\"when transactions exist with given hash and timestamp\") {\n//            val fromTransaction = mock<Transaction>()\n//\n//            beforeEach {\n//                whenever(storage.getValidOrInvalidTransaction(fromUid)).thenReturn(fromTransaction)\n//            }\n//\n//            it(\"starts loading transactions from that transaction\") {\n//                dataProvider.transactions(fromUid, limit).test().assertOf {\n//                    verify(storage).getValidOrInvalidTransaction(fromUid)\n//\n//                    verify(storage).getFullTransactionInfo(fromTransaction, limit)\n//                }\n//            }\n//        }\n//\n//        context(\"when transactions does not exist with given hash and timestamp\") {\n//            beforeEach {\n//                whenever(storage.getValidOrInvalidTransaction(fromUid)).thenReturn(null)\n//            }\n//\n//            it(\"do not fetch transactions with `fromHash` and `fromTimestamp`\") {\n//                dataProvider.transactions(fromUid, limit).test().assertOf {\n//                    verify(storage).getValidOrInvalidTransaction(fromUid)\n//                    verify(storage, never()).getFullTransactionInfo(null, limit)\n//                }\n//            }\n//        }\n//    }\n\n//    describe(\"without `fromHash`\") {\n//        it(\"loads transactions without starting point\") {\n//            dataProvider.transactions(null, null).test().assertOf {\n//                verify(storage, never()).getTransaction(any())\n//\n//                verify(storage).getFullTransactionInfo(null, null)\n//            }\n//        }\n//    }\n})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/managers/ApiManagerTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.managers\n\nimport com.eclipsesource.json.JsonObject\nimport com.nhaarman.mockitokotlin2.whenever\nimport io.horizontalsystems.bitcoincore.RxTestRule\nimport org.junit.Assert.assertEquals\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.Mockito.mock\nimport org.powermock.api.mockito.PowerMockito\nimport org.powermock.core.classloader.annotations.PrepareForTest\nimport org.powermock.modules.junit4.PowerMockRunner\nimport java.io.FileNotFoundException\nimport java.net.URL\nimport java.net.URLConnection\n\n@RunWith(PowerMockRunner::class)\n@PrepareForTest(ApiManager::class, URL::class)\n\nclass ApiManagerTest {\n\n    private val url = mock(URL::class.java)\n    private val urlConnection = mock(URLConnection::class.java)\n\n    private lateinit var apiManager: ApiManager\n\n    @Before\n    fun setup() {\n        RxTestRule.setup()\n\n        PowerMockito\n                .whenNew(URL::class.java)\n                .withAnyArguments()\n                .thenReturn(url)\n\n        whenever(url.openConnection()).thenReturn(urlConnection)\n\n        apiManager = ApiManager(\"https://ipfs.blocksdecoded.com\")\n    }\n\n    @Test\n    fun get() {\n        val data = \"data\"\n        val resp = \"{\\\"field\\\":\\\"$data\\\"}\"\n\n        whenever(urlConnection.getInputStream()).thenReturn(resp.byteInputStream())\n\n        val json = apiManager.get(\"/file.json\")\n        assert(json is JsonObject)\n//        assertEquals(data, json.asObject()[\"field\"].asString())\n    }\n\n    @Test(expected = FileNotFoundException::class)\n    fun get_Throws() {\n        whenever(urlConnection.getInputStream()).thenThrow(FileNotFoundException())\n        apiManager.get(\"/file.json\")\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/managers/BlockDiscoveryBatchTest.kt",
    "content": "//package io.horizontalsystems.bitcoincore.managers\n//\n//import com.nhaarman.mockitokotlin2.mock\n//import com.nhaarman.mockitokotlin2.whenever\n//import io.horizontalsystems.bitcoincore.core.Wallet\n//import io.horizontalsystems.bitcoincore.models.BlockHash\n//import io.horizontalsystems.bitcoincore.models.PublicKey\n//import org.mockito.Mockito\n//import org.spekframework.spek2.Spek\n//import org.spekframework.spek2.style.specification.describe\n//\n//object BlockDiscoveryBatchTest : Spek({\n//\n//    val wallet = Mockito.mock(Wallet::class.java)\n//    val blockHashFetcher = Mockito.mock(BlockHashFetcher::class.java)\n//\n//    lateinit var syncerApi: BlockDiscoveryBatch\n//\n//    beforeEachTest {\n//        whenever(wallet.gapLimit).thenReturn(3)\n//\n//        syncerApi = BlockDiscoveryBatch(wallet, blockHashFetcher, 100)\n//    }\n//\n//    describe(\"#discoverBlockHashes\") {\n//\n//        it(\"fetches blocks recursively\") {\n//            val account = 0\n//            val external = true\n//\n//            val publicKey0 = mock<PublicKey>()\n//            val publicKey1 = mock<PublicKey>()\n//            val publicKey2 = mock<PublicKey>()\n//            val publicKey3 = mock<PublicKey>()\n//            val publicKey4 = mock<PublicKey>()\n//            val publicKeysCycle1 = listOf(publicKey0, publicKey1, publicKey2)\n//            val publicKeysCycle2 = listOf(publicKey3, publicKey4)\n//            val lastUsedPublicKeyIndex = 1\n//\n//            whenever(wallet.publicKey(account, 0, external)).thenReturn(publicKey0)\n//            whenever(wallet.publicKey(account, 1, external)).thenReturn(publicKey1)\n//            whenever(wallet.publicKey(account, 2, external)).thenReturn(publicKey2)\n//\n//            val blockHash = mock<BlockHash>()\n//\n//            whenever(blockHashFetcher.getBlockHashes(publicKeysCycle1)).thenReturn(Pair(listOf(blockHash), lastUsedPublicKeyIndex))\n//\n//            whenever(wallet.publicKey(account, 3, external)).thenReturn(publicKey3)\n//            whenever(wallet.publicKey(account, 4, external)).thenReturn(publicKey4)\n//\n//            whenever(blockHashFetcher.getBlockHashes(publicKeysCycle2)).thenReturn(Pair(listOf(), -1))\n//\n//            syncerApi.discoverBlockHashes(account, external)\n//                    .test()\n//                    .assertValue(Pair(publicKeysCycle1 + publicKeysCycle2, listOf(blockHash)))\n//\n//        }\n//    }\n//\n//})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/managers/BlockHashFetcherHelperTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.managers\n\nimport io.horizontalsystems.bitcoincore.apisync.model.AddressItem\nimport io.horizontalsystems.bitcoincore.apisync.legacy.BlockHashScanHelper\nimport org.junit.Assert\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nobject BlockHashFetcherHelperTest : Spek({\n\n    val fetcherHelper by memoized {\n        BlockHashScanHelper()\n    }\n\n    describe(\"#lastUsedIndex\") {\n\n        it(\"lastUsedIndex_notFound\") {\n            val addresses = listOf(\n                    listOf(\"address0_0\", \"address0_1\"),\n                    listOf(\"address1_0\", \"address1_1\")\n            )\n            val outputs = listOf(\n                    AddressItem(\"asdasd\", \"asdasd\"),\n                    AddressItem(\"tyrty\", \"sdfasdf\")\n            )\n\n            val result = fetcherHelper.lastUsedIndex(addresses, outputs)\n\n            Assert.assertEquals(-1, result)\n        }\n\n        it(\"lastUsedIndex_foundFirstInAddress\") {\n            val addresses = listOf(\n                    listOf(\"address0_0\", \"address0_1\"),\n                    listOf(\"address1_0\", \"address1_1\")\n            )\n            val outputs = listOf(\n                    AddressItem(\"asdasd\", \"address0_0\"),\n                    AddressItem(\"tyrty\", \"sdfasdf\")\n            )\n\n            val result = fetcherHelper.lastUsedIndex(addresses, outputs)\n\n            Assert.assertEquals(0, result)\n        }\n\n        it(\"lastUsedIndex_foundSecondInScript\") {\n            val addresses = listOf(\n                    listOf(\"address0_0\", \"address0_1\"),\n                    listOf(\"address1_0\", \"address1_1\")\n            )\n            val outputs = listOf(\n                    AddressItem(\"asdasd\", \"address0_0\"),\n                    AddressItem(\"ssfdaddress1_1aaqqw\", \"sdfasdf\")\n            )\n\n            val result = fetcherHelper.lastUsedIndex(addresses, outputs)\n\n            Assert.assertEquals(1, result)\n        }\n    }\n\n})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/managers/BlockHashFetcherTest.kt",
    "content": "//package io.horizontalsystems.bitcoincore.managers\n//\n//import com.nhaarman.mockitokotlin2.mock\n//import com.nhaarman.mockitokotlin2.whenever\n//import io.horizontalsystems.bitcoincore.core.IInitialSyncApi\n//import io.horizontalsystems.bitcoincore.extensions.toReversedHex\n//import io.horizontalsystems.bitcoincore.models.PublicKey\n//import org.junit.Assert\n//import org.mockito.Mockito\n//import org.spekframework.spek2.Spek\n//import org.spekframework.spek2.style.specification.describe\n//\n//object BlockHashFetcherTest : Spek({\n//\n//    val restoreKeyConverter = Mockito.mock(IRestoreKeyConverter::class.java)\n//    val initialSyncApi = Mockito.mock(IInitialSyncApi::class.java)\n//    val helper = Mockito.mock(BlockHashFetcherHelper::class.java)\n//\n//    val blockHashFetcher = BlockHashFetcher(restoreKeyConverter, initialSyncApi, helper)\n//\n//    describe(\"#getBlockHashes\") {\n//\n//        it(\"gets empty BlockHash's\") {\n//            val publicKey0 = mock<PublicKey>()\n//            val publicKey1 = mock<PublicKey>()\n//            val publicKey2 = mock<PublicKey>()\n//\n//            val addresses0 = listOf(\"0_0\", \"0_1\")\n//            val addresses1 = listOf(\"1_0\", \"1_1\")\n//            val addresses2 = listOf(\"2_0\", \"2_1\")\n//\n//            val addresses = addresses0 + addresses1 + addresses2\n//\n//            whenever(restoreKeyConverter.keysForApiRestore(publicKey0)).thenReturn(addresses0)\n//            whenever(restoreKeyConverter.keysForApiRestore(publicKey1)).thenReturn(addresses1)\n//            whenever(restoreKeyConverter.keysForApiRestore(publicKey2)).thenReturn(addresses2)\n//\n//            whenever(initialSyncApi.getTransactions(addresses)).thenReturn(listOf())\n//\n//            val (blockHashes, lastUsedIndex) = blockHashFetcher.getBlockHashes(listOf(publicKey0, publicKey1, publicKey2))\n//\n//            Assert.assertTrue(blockHashes.isEmpty())\n//            Assert.assertEquals(-1, lastUsedIndex)\n//        }\n//\n//        it(\"gets non empty BlockHash's\") {\n//            val publicKey0 = mock<PublicKey>()\n//            val publicKey1 = mock<PublicKey>()\n//            val publicKey2 = mock<PublicKey>()\n//\n//            val addresses0 = listOf(\"0_0\", \"0_1\")\n//            val addresses1 = listOf(\"1_0\", \"1_1\")\n//            val addresses2 = listOf(\"2_0\", \"2_1\")\n//\n//            val addresses = addresses0 + addresses1 + addresses2\n//\n//            whenever(restoreKeyConverter.keysForApiRestore(publicKey0)).thenReturn(addresses0)\n//            whenever(restoreKeyConverter.keysForApiRestore(publicKey1)).thenReturn(addresses1)\n//            whenever(restoreKeyConverter.keysForApiRestore(publicKey2)).thenReturn(addresses2)\n//\n//            val transactionResponse0 = mock<TransactionItem>()\n//            whenever(transactionResponse0.blockHeight).thenReturn(1234)\n//            whenever(transactionResponse0.blockHash).thenReturn(\"1234\")\n//            whenever(transactionResponse0.txOutputs).thenReturn(listOf())\n//\n//            val transactionResponse1 = mock<TransactionItem>()\n//            whenever(transactionResponse1.blockHeight).thenReturn(5678)\n//            whenever(transactionResponse1.blockHash).thenReturn(\"5678\")\n//            whenever(transactionResponse1.txOutputs).thenReturn(listOf())\n//\n//            whenever(initialSyncApi.getTransactions(addresses)).thenReturn(listOf(transactionResponse0, transactionResponse1))\n//            val lastUsedIndex = 1\n//            whenever(helper.lastUsedIndex(listOf(addresses0, addresses1, addresses2), listOf())).thenReturn(lastUsedIndex)\n//\n//            val (blockHashes, actualLastUsedIndex) = blockHashFetcher.getBlockHashes(listOf(publicKey0, publicKey1, publicKey2))\n//\n//            Assert.assertEquals(lastUsedIndex, actualLastUsedIndex)\n//            Assert.assertEquals(2, blockHashes.size)\n//            Assert.assertEquals(\"1234\", blockHashes.first().headerHash.toReversedHex())\n//            Assert.assertEquals(1234, blockHashes.first().height)\n//            Assert.assertEquals(\"5678\", blockHashes.last().headerHash.toReversedHex())\n//            Assert.assertEquals(5678, blockHashes.last().height)\n//        }\n//    }\n//})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/managers/InitialSyncerTest.kt",
    "content": "//package io.horizontalsystems.bitcoincore.managers\n//\n//import com.nhaarman.mockitokotlin2.*\n//import io.horizontalsystems.bitcoincore.RxTestRule\n//import io.horizontalsystems.bitcoincore.core.ErrorStorage\n//import io.horizontalsystems.bitcoincore.core.IStorage\n//import io.horizontalsystems.bitcoincore.core.ISyncStateListener\n//import io.horizontalsystems.bitcoincore.models.BlockHash\n//import io.horizontalsystems.bitcoincore.models.PublicKey\n//import io.reactivex.Single\n//import org.mockito.Mockito.mock\n//import org.mockito.Mockito.reset\n//import org.spekframework.spek2.Spek\n//import org.spekframework.spek2.style.specification.describe\n//\n//object InitialSyncerTest : Spek({\n//\n//    lateinit var initialSyncer: InitialSyncer\n//\n//    val storage = mock(IStorage::class.java)\n//    val blockDiscovery = mock(IBlockDiscovery::class.java)\n//    val stateManager = mock(StateManager::class.java)\n//    val publicKeyManager = mock(PublicKeyManager::class.java)\n//    val stateListener = mock(ISyncStateListener::class.java)\n//    val listener = mock(SyncManager::class.java)\n//    val errorStorage = mock(ErrorStorage::class.java)\n//\n//    beforeEachTest {\n//        RxTestRule.setup()\n//\n//        initialSyncer = InitialSyncer(storage, blockDiscovery, stateManager, publicKeyManager, stateListener, errorStorage)\n//        initialSyncer.listener = listener\n//    }\n//\n//    afterEachTest {\n//        reset(storage, blockDiscovery, stateManager, publicKeyManager, stateListener, listener)\n//    }\n//\n//    describe(\"#sync\") {\n//\n//        context(\"when already synced\") {\n//            beforeEach {\n//                whenever(stateManager.restored).thenReturn(true)\n//\n//                initialSyncer.sync()\n//            }\n//\n//            it(\"triggers #onSyncingFinished event on :listener\") {\n//                verify(listener).onSyncingFinished()\n//\n//                verifyNoMoreInteractions(listener)\n//            }\n//\n//            it(\"does not triggers #onSyncStart event on :stateListener\") {\n//                verify(stateListener, never()).onSyncStart()\n//            }\n//        }\n//\n//        context(\"when not synced yet\") {\n//            val publicKey1 = mock(PublicKey::class.java)\n//            val publicKey2 = mock(PublicKey::class.java)\n//            val blockHash1 = mock(BlockHash::class.java)\n//            val blockHash2 = mock(BlockHash::class.java)\n//\n//            beforeEach {\n//                whenever(stateManager.restored).thenReturn(false)\n//            }\n//\n//            context(\"when blockDiscovery fails to fetch block hashes\") {\n//                beforeEach {\n//                    whenever(blockDiscovery.discoverBlockHashes(0, true)).thenReturn(null)\n//                    whenever(blockDiscovery.discoverBlockHashes(0, false)).thenReturn(null)\n//\n//                    initialSyncer.sync()\n//                }\n//\n//                it(\"triggers #onSyncStop event on :stateListener\") {\n//                    verify(stateListener).onSyncStart()\n//                    verify(stateListener).onSyncStop()\n//                }\n//\n//                it(\"discovers block hashes only for account 0\") {\n//                    verify(blockDiscovery).discoverBlockHashes(0, true)\n//                    verify(blockDiscovery).discoverBlockHashes(0, false)\n//\n//                    verify(blockDiscovery, never()).discoverBlockHashes(1, true)\n//                    verify(blockDiscovery, never()).discoverBlockHashes(1, false)\n//                }\n//\n//                it(\"adds error to ErrorStorage\") {\n//                    verify(errorStorage, atLeast(1)).addApiError(any())\n//                }\n//            }\n//\n//            context(\"when blockDiscovery succeeds\") {\n//                beforeEach {\n//                    // account 1\n//                    whenever(blockDiscovery.discoverBlockHashes(0, true)).thenReturn(Single.just(Pair(listOf(publicKey1), listOf(blockHash1))))\n//                    whenever(blockDiscovery.discoverBlockHashes(0, false)).thenReturn(Single.just(Pair(listOf(publicKey2), listOf(blockHash2))))\n//\n//                    // account 2\n//                    whenever(blockDiscovery.discoverBlockHashes(1, true)).thenReturn(Single.just(Pair(listOf(), listOf())))\n//                    whenever(blockDiscovery.discoverBlockHashes(1, false)).thenReturn(Single.just(Pair(listOf(), listOf())))\n//\n//                    initialSyncer.sync()\n//                }\n//\n//                it(\"fetches block hashes for account 0 and 1\") {\n//                    verify(blockDiscovery).discoverBlockHashes(0, true)\n//                    verify(blockDiscovery).discoverBlockHashes(0, false)\n//\n//                    verify(blockDiscovery).discoverBlockHashes(1, true)\n//                    verify(blockDiscovery).discoverBlockHashes(1, false)\n//                }\n//\n//                it(\"triggers #onSyncStart event on :stateListener\") {\n//                    verify(stateListener).onSyncStart()\n//                    verifyNoMoreInteractions(stateListener)\n//                }\n//\n//                it(\"adds received addresses to address manager\") {\n//                    verify(publicKeyManager).addKeys(listOf(publicKey1, publicKey2))\n//                    verify(publicKeyManager).addKeys(listOf())\n//                }\n//\n//                it(\"saves fetched block hashes to storage\") {\n//                    verify(storage).addBlockHashes(listOf(blockHash1, blockHash2))\n//                }\n//\n//                it(\"triggers #onSyncingFinished event on :listener\") {\n//                    verify(stateManager).restored = true\n//                    verify(listener).onSyncingFinished()\n//                }\n//            }\n//        }\n//    }\n//\n//    describe(\"#stop\") {\n//        it(\"clears disposables\") {}\n//    }\n//})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/managers/IrregularOutputFinderTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.managers\n\nimport com.nhaarman.mockitokotlin2.doReturn\nimport com.nhaarman.mockitokotlin2.mock\nimport com.nhaarman.mockitokotlin2.whenever\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport io.horizontalsystems.bitcoincore.utils.Utils\nimport org.junit.Assert.assertArrayEquals\nimport org.junit.Assert.assertEquals\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nobject IrregularOutputFinderTest : Spek({\n\n    val block = mock<Block> {\n        on { height } doReturn 100\n    }\n\n    val output = mock<TransactionOutput> {\n        on { index } doReturn 1\n        on { transactionHash } doReturn ByteArray(32) { 1 }\n    }\n\n    val storage = mock<IStorage> {\n        on { lastBlock() } doReturn block\n    }\n\n    val irregularScriptTypes = listOf(ScriptType.P2WPKHSH, ScriptType.P2WPKH, ScriptType.P2PK)\n    val outputFinder by memoized { IrregularOutputFinder(storage, listOf()) }\n\n    describe(\"#getBloomFilterElements\") {\n\n        beforeEach {\n            whenever(storage.getOutputsForBloomFilter(block.height - 100, irregularScriptTypes)).thenReturn(listOf(output))\n        }\n\n        it(\"returns outputs\") {\n            val elements = outputFinder.getBloomFilterElements()\n            val outpointIndex = Utils.intToByteArray(output.index).reversedArray()\n\n            assertEquals(1, elements.size)\n            assertArrayEquals(output.transactionHash + outpointIndex, elements[0])\n        }\n\n    }\n})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/managers/StateManagerTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.managers\n\nimport com.nhaarman.mockitokotlin2.whenever\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.network.Network\nimport org.junit.Assert.assertFalse\nimport org.junit.Assert.assertTrue\nimport org.mockito.Mockito.mock\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nobject StateManagerTest : Spek({\n\n    lateinit var stateManager: ApiSyncStateManager\n\n    val storage = mock(IStorage::class.java)\n    val networkSyncableFromApi = mock(Network::class.java)\n    val networkNotSyncableFromApi = mock(Network::class.java)\n\n    beforeEachTest {\n        whenever(networkSyncableFromApi.syncableFromApi).thenReturn(true)\n        whenever(networkNotSyncableFromApi.syncableFromApi).thenReturn(false)\n    }\n\n    describe(\"#restored\") {\n\n        context(\"when `restoreFromApi` is true\") {\n            it(\"marks as `restored`\") {\n                stateManager = ApiSyncStateManager(storage, false)\n                assertTrue(stateManager.restored)\n            }\n        }\n\n        context(\"when already restored\") {\n            beforeEach {\n                whenever(storage.initialRestored).thenReturn(true)\n            }\n\n            it(\"marks as `restored`\") {\n                stateManager = ApiSyncStateManager(storage, true)\n                assertTrue(stateManager.restored)\n            }\n        }\n\n        context(\"when not restored yet\") {\n            beforeEach {\n                whenever(storage.initialRestored).thenReturn(false)\n            }\n\n            it(\"marks as not `restored`\") {\n                stateManager = ApiSyncStateManager(storage, true)\n                assertFalse(stateManager.restored)\n            }\n        }\n    }\n})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/managers/SyncManagerTest.kt",
    "content": "//package io.horizontalsystems.bitcoincore.managers\n//\n//import com.nhaarman.mockitokotlin2.mock\n//import com.nhaarman.mockitokotlin2.verify\n//import io.horizontalsystems.bitcoincore.network.peer.PeerGroup\n//import org.mockito.Mockito.reset\n//import org.spekframework.spek2.Spek\n//import org.spekframework.spek2.style.specification.describe\n//\n//object SyncManagerTest : Spek({\n//\n//    val peerGroup = mock<PeerGroup>()\n//    val initialSyncer = mock<InitialSyncer>()\n//\n//    val syncManager by memoized {\n//        SyncManager(peerGroup, initialSyncer)\n//    }\n//\n//    afterEachTest {\n//        reset(peerGroup, initialSyncer)\n//    }\n//\n//    describe(\"#start\") {\n//        it(\"starts :initialSyncer\") {\n//            syncManager.start()\n//\n//            verify(initialSyncer).sync()\n//        }\n//    }\n//\n//    describe(\"#stop\") {\n//        it(\"stops :peerGroup\") {\n//            syncManager.stop()\n//\n//            verify(peerGroup).stop()\n//        }\n//\n//        it(\"stops :initialSyncer\") {\n//            syncManager.stop()\n//\n//            verify(peerGroup).stop()\n//        }\n//    }\n//\n//    describe(\"#onSyncingFinished\") {\n//        it(\"starts peer group\") {\n//            syncManager.onSyncingFinished()\n//\n//            verify(peerGroup).start()\n//        }\n//    }\n//\n//})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/managers/UnspentOutputProviderTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.managers\n\nimport com.nhaarman.mockitokotlin2.mock\nimport com.nhaarman.mockitokotlin2.whenever\nimport io.horizontalsystems.bitcoincore.Fixtures\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.core.PluginManager\nimport io.horizontalsystems.bitcoincore.extensions.hexToByteArray\nimport io.horizontalsystems.bitcoincore.extensions.toReversedByteArray\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.storage.BlockHeader\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport org.junit.Assert.assertArrayEquals\nimport org.junit.Assert.assertEquals\nimport org.mockito.Mockito.mock\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nobject UnspentOutputProviderTest : Spek({\n    val storage = mock(IStorage::class.java)\n    val pluginManager = mock<PluginManager>()\n\n    val output =\n        TransactionOutput(value = 1, index = 0, script = byteArrayOf(), type = ScriptType.P2PKH, lockingScriptPayload = \"000010000\".hexToByteArray())\n    val pubKey = Fixtures.publicKey\n    val lastBlockHeight = 550368\n    val blockHeader = BlockHeader(\n        version = 1,\n        previousBlockHeaderHash = \"00000000864b744c5025331036aa4a16e9ed1cbb362908c625272150fa059b29\".toReversedByteArray(),\n        merkleRoot = \"70d6379650ac87eaa4ac1de27c21217b81a034a53abf156c422a538150bd80f4\".toReversedByteArray(),\n        timestamp = 1337966314,\n        bits = 486604799,\n        nonce = 2391008772,\n        hash = byteArrayOf(1)\n    )\n\n    val lastBlock = Block(header = blockHeader, height = lastBlockHeight)\n    val confirmationsThreshold = 6\n\n    lateinit var unspentOutput: UnspentOutput\n\n    val transaction by memoized { Transaction() }\n    val provider by memoized {\n        UnspentOutputProvider(storage = storage, confirmationsThreshold = confirmationsThreshold, pluginManager = pluginManager)\n    }\n\n    beforeEachTest {\n        whenever(storage.lastBlock()).thenReturn(lastBlock)\n    }\n\n    describe(\"#getSpendableUtxo\") {\n        context(\"when transaction is outgoing\") {\n            beforeEach {\n                transaction.isOutgoing = true\n                unspentOutput = UnspentOutput(output, pubKey, transaction, null)\n\n                whenever(storage.getUnspentOutputs()).thenReturn(listOf(unspentOutput))\n                whenever(pluginManager.isSpendable(unspentOutput)).thenReturn(true)\n            }\n\n            it(\"returns unspentOutput\") {\n                assertArrayEquals(arrayOf(unspentOutput), provider.getSpendableUtxo().toTypedArray())\n            }\n        }\n\n        context(\"when transaction is not outgoing\") {\n            beforeEach {\n                transaction.isOutgoing = false\n            }\n\n            context(\"when transaction is not included in block\") {\n                beforeEach {\n                    unspentOutput = UnspentOutput(output, pubKey, transaction, null)\n\n                    whenever(storage.getUnspentOutputs()).thenReturn(listOf(unspentOutput))\n                    whenever(pluginManager.isSpendable(unspentOutput)).thenReturn(true)\n                }\n\n                it(\"doesn't return unspentOutput\") {\n                    assertArrayEquals(arrayOf(), provider.getSpendableUtxo().toTypedArray())\n                }\n            }\n\n            context(\"when transaction is included in block\") {\n                val block by memoized {\n                    Fixtures.block1\n                }\n\n                beforeEach {\n                    block.height = lastBlockHeight + 1\n                    unspentOutput = UnspentOutput(output, pubKey, transaction, block)\n\n                    whenever(storage.getUnspentOutputs()).thenReturn(listOf(unspentOutput))\n                    whenever(pluginManager.isSpendable(unspentOutput)).thenReturn(true)\n                }\n\n                context(\"when block has enough confirmations\") {\n                    it(\"returns unspentOutput\") {\n                        block.height = lastBlock.height - confirmationsThreshold\n\n                        assertArrayEquals(arrayOf(unspentOutput), provider.getSpendableUtxo().toTypedArray())\n                    }\n                }\n\n                context(\"when block has not enough confirmations\") {\n                    it(\"doesn't return unspentOutput\") {\n                        block.height = lastBlock.height - confirmationsThreshold + 2\n\n                        assertArrayEquals(arrayOf(), provider.getSpendableUtxo().toTypedArray())\n                    }\n                }\n            }\n        }\n    }\n\n    describe(\"#balance\") {\n        beforeEach {\n            transaction.isOutgoing = true\n        }\n\n        it(\"returns sum of unspentOutputs\") {\n            val unspentOutputs = listOf(\n                UnspentOutput(output = output, publicKey = pubKey, transaction = transaction, block = null),\n                UnspentOutput(output = output, publicKey = pubKey, transaction = transaction, block = null)\n            )\n\n            whenever(storage.getUnspentOutputs()).thenReturn(unspentOutputs)\n            whenever(pluginManager.isSpendable(unspentOutputs[0])).thenReturn(true)\n            whenever(pluginManager.isSpendable(unspentOutputs[1])).thenReturn(true)\n\n            val balance = provider.getBalance()\n\n            assertEquals(unspentOutputs[0].output.value + unspentOutputs[1].output.value, balance.spendable)\n            assertEquals(0, balance.unspendable)\n        }\n    }\n\n})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/managers/UnspentOutputSelectorSingleNoChangeTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.managers\n\nimport com.nhaarman.mockitokotlin2.any\nimport io.horizontalsystems.bitcoincore.DustCalculator\nimport io.horizontalsystems.bitcoincore.Fixtures\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\nimport io.horizontalsystems.bitcoincore.storage.UtxoFilters\nimport io.horizontalsystems.bitcoincore.transactions.TransactionSizeCalculator\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport org.junit.Assert\nimport org.junit.Assert.assertThrows\nimport org.junit.Test\nimport org.mockito.ArgumentMatchers\nimport org.mockito.Mockito.mock\nimport org.mockito.Mockito.`when`\n\nclass UnspentOutputSelectorSingleNoChangeTest {\n\n    private val calculator: TransactionSizeCalculator = mock(TransactionSizeCalculator::class.java)\n    private val dustCalculator: DustCalculator = mock(DustCalculator::class.java)\n    private val unspentOutputProvider: IUnspentOutputProvider =\n        mock(IUnspentOutputProvider::class.java)\n    private val queueParams: UnspentOutputQueue.Parameters =\n        mock(UnspentOutputQueue.Parameters::class.java)\n\n    private val dust = 100\n\n    @Test\n    fun testSelect_DustValue() {\n        val value = 54L\n        val selector =\n            UnspentOutputSelectorSingleNoChange(calculator, dustCalculator, unspentOutputProvider)\n        `when`(dustCalculator.dust(any(), any())).thenReturn(dust)\n\n        assertThrows(SendValueErrors.Dust::class.java) {\n            selector.select(\n                value,\n                null,\n                100,\n                ScriptType.P2PKH,\n                ScriptType.P2WPKH,\n                false,\n                0,\n                false,\n                UtxoFilters()\n            )\n        }\n    }\n\n    @Test\n    fun testSelect_EmptyOutputs() {\n        val selector =\n            UnspentOutputSelectorSingleNoChange(calculator, dustCalculator, unspentOutputProvider)\n        `when`(unspentOutputProvider.getSpendableUtxo(UtxoFilters())).thenReturn(emptyList())\n\n        assertThrows(SendValueErrors.EmptyOutputs::class.java) {\n            selector.select(\n                10000,\n                null,\n                100,\n                ScriptType.P2PKH,\n                ScriptType.P2WPKH,\n                false,\n                0,\n                false,\n                UtxoFilters()\n            )\n        }\n    }\n\n    @Test\n    fun testSelect_NoSingleOutput() {\n        val selector = UnspentOutputSelectorSingleNoChange(calculator, dustCalculator, unspentOutputProvider)\n        val outputs = listOf(\n            createUnspentOutput(5000),\n            createUnspentOutput(10000)\n        )\n\n        val fee = 150\n        val value = 6000L\n\n        `when`(unspentOutputProvider.getSpendableUtxo(UtxoFilters())).thenReturn(outputs)\n        `when`(dustCalculator.dust(any(), any())).thenReturn(dust)\n        `when`(calculator.inputSize(any())).thenReturn(10)\n//        `when`(calculator.outputSize(any())).thenReturn(2)\n        `when`(calculator.transactionSize(\n            ArgumentMatchers.anyList(),\n            ArgumentMatchers.anyList(), any())).thenReturn(30)\n        `when`(queueParams.value).thenReturn(value)\n        `when`(queueParams.fee).thenReturn(fee)\n\n        assertThrows(SendValueErrors.NoSingleOutput::class.java) {\n            selector.select(\n                value,\n                null,\n                100,\n                ScriptType.P2PKH,\n                ScriptType.P2WPKH,\n                false,\n                0,\n                false,\n                UtxoFilters()\n            )\n        }\n    }\n\n    @Test\n    fun testSelect_SingleOutputSuccess() {\n        val selector = UnspentOutputSelectorSingleNoChange(calculator, dustCalculator, unspentOutputProvider)\n        val outputs = listOf(\n            createUnspentOutput(5000),\n            createUnspentOutput(10000)\n        )\n\n        val feeRate = 5\n        val fee = 150\n        val value = 10000L\n\n        `when`(unspentOutputProvider.getSpendableUtxo(UtxoFilters())).thenReturn(outputs)\n        `when`(dustCalculator.dust(any(), any())).thenReturn(dust)\n        `when`(calculator.inputSize(any())).thenReturn(10)\n//        `when`(calculator.outputSize(any())).thenReturn(2)\n        `when`(calculator.transactionSize(\n            ArgumentMatchers.anyList(),\n            ArgumentMatchers.anyList(), any())).thenReturn(30)\n        `when`(queueParams.value).thenReturn(value)\n        `when`(queueParams.fee).thenReturn(fee)\n\n        val selectedInfo =\n            selector.select(\n                value,\n                null,\n                feeRate,\n                ScriptType.P2PKH,\n                ScriptType.P2WPKH,\n                false,\n                0,\n                false,\n                UtxoFilters()\n            )\n        Assert.assertEquals(null, selectedInfo.changeValue)\n        Assert.assertEquals(1, selectedInfo.outputs.size)\n        Assert.assertArrayEquals(arrayOf(outputs[1]), selectedInfo.outputs.toTypedArray())\n    }\n\n    @Test\n    fun testSelect_HasOutputFailedToSpend() {\n        val selector = UnspentOutputSelectorSingleNoChange(calculator, dustCalculator, unspentOutputProvider)\n        val outputs = listOf(\n            createUnspentOutput(5000),\n            createUnspentOutput(10000, true)\n        )\n\n        val fee = 150\n        val value = 10000L\n\n        `when`(unspentOutputProvider.getSpendableUtxo(UtxoFilters())).thenReturn(outputs)\n        `when`(dustCalculator.dust(any(), any())).thenReturn(dust)\n        `when`(calculator.inputSize(any())).thenReturn(10)\n//        `when`(calculator.outputSize(any())).thenReturn(2)\n        `when`(calculator.transactionSize(\n            ArgumentMatchers.anyList(),\n            ArgumentMatchers.anyList(), any())).thenReturn(30)\n        `when`(queueParams.value).thenReturn(value)\n        `when`(queueParams.fee).thenReturn(fee)\n\n        assertThrows(SendValueErrors.HasOutputFailedToSpend::class.java) {\n            selector.select(\n                value,\n                null,\n                100,\n                ScriptType.P2PKH,\n                ScriptType.P2WPKH,\n                false,\n                0,\n                false,\n                UtxoFilters()\n            )\n        }\n    }\n\n    private fun createUnspentOutput(value: Long, failedToSpend: Boolean = false): UnspentOutput {\n        val output =\n            TransactionOutput(\n                value = value,\n                index = 0,\n                script = byteArrayOf(),\n                type = ScriptType.P2PKH,\n                lockingScriptPayload = null\n            )\n        if (failedToSpend) {\n            output.failedToSpend = true\n        }\n        val pubKey = Fixtures.publicKey\n        val transaction = mock(Transaction::class.java)\n        val block = mock(Block::class.java)\n\n        return UnspentOutput(output, pubKey, transaction, block)\n    }\n\n}\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/managers/UnspentOutputSelectorTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.managers\n\nimport com.nhaarman.mockitokotlin2.any\nimport io.horizontalsystems.bitcoincore.DustCalculator\nimport io.horizontalsystems.bitcoincore.Fixtures\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\nimport io.horizontalsystems.bitcoincore.storage.UtxoFilters\nimport io.horizontalsystems.bitcoincore.transactions.TransactionSizeCalculator\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport org.junit.Assert.assertEquals\nimport org.junit.Assert.assertThrows\nimport org.junit.Test\nimport org.mockito.ArgumentMatchers.anyList\nimport org.mockito.Mockito.mock\nimport org.mockito.Mockito.`when`\n\n\nclass UnspentOutputSelectorTest {\n\n    private val calculator: TransactionSizeCalculator = mock(TransactionSizeCalculator::class.java)\n    private val dustCalculator: DustCalculator = mock(DustCalculator::class.java)\n    private val unspentOutputProvider: IUnspentOutputProvider =\n        mock(IUnspentOutputProvider::class.java)\n    private val queueParams: UnspentOutputQueue.Parameters =\n        mock(UnspentOutputQueue.Parameters::class.java)\n    private val dust = 100\n\n\n    @Test\n    fun testSelect_DustValue() {\n        val value = 54L\n        val selector =\n            UnspentOutputSelector(calculator, dustCalculator, unspentOutputProvider, null)\n        `when`(dustCalculator.dust(any(), any())).thenReturn(dust)\n\n        assertThrows(SendValueErrors.Dust::class.java) {\n            selector.select(value, null, 100, ScriptType.P2PKH, ScriptType.P2WPKH, false, 0, false, UtxoFilters())\n        }\n    }\n\n    @Test\n    fun testSelect_EmptyOutputs() {\n        val selector =\n            UnspentOutputSelector(calculator, dustCalculator, unspentOutputProvider, null)\n        `when`(unspentOutputProvider.getSpendableUtxo(UtxoFilters())).thenReturn(emptyList())\n\n        assertThrows(SendValueErrors.InsufficientUnspentOutputs::class.java) {\n            selector.select(10000, null, 100, ScriptType.P2PKH, ScriptType.P2WPKH, false, 0, false, UtxoFilters())\n        }\n    }\n\n    @Test\n    fun testSelect_SuccessfulSelection() {\n        val selector = UnspentOutputSelector(calculator, dustCalculator, unspentOutputProvider)\n        val outputs = listOf(\n            createUnspentOutput(5000),\n            createUnspentOutput(10000)\n        )\n\n        val feeRate = 5\n        val fee = 150\n        val value = 12000\n\n        `when`(unspentOutputProvider.getSpendableUtxo(UtxoFilters())).thenReturn(outputs)\n        `when`(dustCalculator.dust(any(), any())).thenReturn(dust)\n        `when`(calculator.inputSize(any())).thenReturn(10)\n//        `when`(calculator.outputSize(any())).thenReturn(2)\n        `when`(calculator.transactionSize(anyList(), anyList(), any())).thenReturn(30)\n        `when`(queueParams.value).thenReturn(value.toLong())\n        `when`(queueParams.fee).thenReturn(fee)\n\n        val selectedInfo =\n            selector.select(\n                value.toLong(),\n                null,\n                feeRate,\n                ScriptType.P2PKH,\n                ScriptType.P2WPKH,\n                false,\n                0,\n                false,\n                UtxoFilters()\n            )\n        assertEquals(outputs, selectedInfo.outputs)\n        assertEquals(11850, selectedInfo.recipientValue)\n    }\n\n    @Test\n    fun testSelect_Limit() {\n        val feeRate = 5\n        val fee = 150\n        val value = 11000\n        val limit = 4\n        val selector =\n            UnspentOutputSelector(calculator, dustCalculator, unspentOutputProvider, limit)\n\n        val outputs = listOf(\n            createUnspentOutput(1000),\n            createUnspentOutput(2000),\n            createUnspentOutput(3000),\n            createUnspentOutput(4000),\n            createUnspentOutput(5000),\n        )\n\n        `when`(unspentOutputProvider.getSpendableUtxo(UtxoFilters())).thenReturn(outputs)\n        `when`(dustCalculator.dust(any(), any())).thenReturn(dust)\n        `when`(calculator.inputSize(any())).thenReturn(10)\n//        `when`(calculator.outputSize(any())).thenReturn(2)\n        `when`(calculator.transactionSize(anyList(), anyList(), any())).thenReturn(30)\n        `when`(queueParams.value).thenReturn(value.toLong())\n        `when`(queueParams.fee).thenReturn(fee)\n\n        val selectedInfo =\n            selector.select(\n                value.toLong(),\n                null,\n                feeRate,\n                ScriptType.P2PKH,\n                ScriptType.P2WPKH,\n                false,\n                0,\n                false,\n                UtxoFilters()\n            )\n        assertEquals(4, selectedInfo.outputs.size)\n        assertEquals(10850, selectedInfo.recipientValue)\n    }\n\n    private fun createUnspentOutput(value: Long, failedToSpend: Boolean = false): UnspentOutput {\n        val output =\n            TransactionOutput(\n                value = value,\n                index = 0,\n                script = byteArrayOf(),\n                type = ScriptType.P2PKH,\n                lockingScriptPayload = null\n            )\n        if (failedToSpend) {\n            output.failedToSpend = true\n        }\n        val pubKey = Fixtures.publicKey\n        val transaction = mock(Transaction::class.java)\n        val block = mock(Block::class.java)\n\n        return UnspentOutput(output, pubKey, transaction, block)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/message/MerkleBlockExtractorTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.message\n\nimport io.horizontalsystems.bitcoincore.blocks.MerkleBlockExtractor\nimport io.horizontalsystems.bitcoincore.core.DoubleSha256Hasher\nimport io.horizontalsystems.bitcoincore.extensions.toHexString\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.network.messages.MerkleBlockMessage\nimport io.horizontalsystems.bitcoincore.network.messages.MerkleBlockMessageParser\nimport io.horizontalsystems.bitcoincore.serializers.BlockHeaderParser\nimport org.junit.Assert\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nobject MerkleBlockExtractorTest : Spek({\n    val merkleRootHasher = DoubleSha256Hasher()\n    val blockHeaderParser = BlockHeaderParser(merkleRootHasher)\n    val messageParser = MerkleBlockMessageParser(blockHeaderParser)\n\n    val merkleBlockExtractor by memoized {\n        MerkleBlockExtractor(1_000_000)\n    }\n\n    describe(\"#extract\") {\n\n        it(\"extract associated transactions\") {\n            val data = byteArrayOf(0, 0, 0, 32, 109, -94, 121, -122, 45, -39, -27, -60, 114, -54, -4, 71, 111, -80, 31, 78, 121, 112, 57, 123, 31, 11, 54, 0, 0, 0, 0, 0, 0, 0, 0, 0, 62, 20, 113, -25, -9, -115, -34, 71, 115, 26, 108, -21, -128, 93, 118, 111, 64, -9, 83, 80, -89, 94, 21, 101, -16, -14, -98, 92, -116, -67, -35, -71, 0, 38, 13, 91, 73, 90, 65, 23, -94, -118, -41, 117, -104, 1, 0, 0, 40, -79, 100, -36, 63, -28, 61, -74, -33, 78, 5, 40, -66, -31, 51, -44, 77, -116, -115, -110, -108, 104, 91, 75, 40, -102, -54, -116, 89, 55, 24, 50, -96, 12, 63, 20, 25, -82, 66, 103, -121, 49, -92, -114, -84, 78, 86, 98, 111, 64, 3, -19, 115, 6, 10, 48, -6, 4, -30, 9, 66, -25, -127, 12, 83, 4, -18, -84, 32, 97, -6, -80, -54, 53, -8, -19, -95, 3, 123, -76, 51, -11, 16, 107, -37, 124, -11, 22, 0, -105, -103, 53, -31, 6, 5, 118, 116, -93, 25, 88, -53, 103, -1, -62, 116, 43, 64, -98, 87, 113, 100, -109, -105, -16, 23, -3, -31, 75, -125, 123, -57, 30, -126, -69, 19, 10, 0, 126, -119, -25, -59, 29, 107, -74, -64, -70, 32, 120, -118, -58, -90, 122, 67, 55, 4, 69, -81, -53, 48, 47, -75, 46, -53, 59, -106, -24, -54, 31, -78, 81, 29, 33, -49, -105, 94, -119, -122, -98, -12, 64, -63, 95, -27, -99, -105, 42, -63, -42, -117, 79, -103, 36, -72, -67, 1, -41, 70, 62, -8, 29, 120, 7, 58, -38, 46, -97, -107, 51, 86, -41, 101, -9, 97, -57, -37, 66, 9, 61, 79, -112, -59, 123, 49, 86, -8, -86, -63, 57, -65, 103, -30, 106, -120, -77, 60, -7, 88, 66, 53, -51, 81, 110, 70, 46, 91, 7, 124, -44, -23, 104, -55, 74, 54, 62, 60, -21, 55, -61, 99, -72, 7, 53, -66, 105, -24, 121, -60, 15, 8, -37, -85, 52, -1, -56, -10, 15, -118, -76, -94, 3, 54, -23, 69, -36, -118, 43, -20, -121, 106, 83, 76, -41, -65, -8, -109, -30, -41, -89, 24, 16, -98, 19, -97, 125, 91, -73, -21, 56, -3, 89, -111, -128, -75, 54, -5, -1, 55, 72, -51, -93, -127, -42, -12, -22, -118, -32, 19, 114, -69, 45, 119, -39, 37, 0, -23, -18, -14, -103, -14, 53, 72, -93, -113, -80, 21, -127, -49, -66, -50, 97, 38, -105, -5, -27, 67, -83, 16, 15, 106, 69, -127, -61, -98, 9, 118, -94, -89, 91, -22, 118, 76, -44, -85, 66, -18, -107, 92, -124, 46, -112, -117, 61, -96, 66, -33, 61, -74, -48, 37, 54, 48, 127, 13, 72, -123, -33, 121, 63, 74, -47, 56, 18, -96, -123, 4, -59, 15, 56, 88, 57, 21, -8, -99, 80, 107, 3, -79, -1, 116, 113, -43, 25, -20, -120, -20, -75, 55, -53, 78, 61, -48, 38, -75, 4, 62, 25, -68, 115, -97, 43, 75, 44, -9, -1, -82, 33, -116, -4, 102, -71, 68, -95, 25, 110, -7, 113, 9, 19, 99, 109, -118, 41, 48, 110, 77, 7, -60, -60, 88, -14, -110, -32, 48, 48, -12, 56, -21, -77, 21, 122, 52, 3, -87, -105, -59, -8, 72, 69, 63, 105, -18, 67, -120, 125, -81, 27, -28, -40, 99, 28, -124, -106, -37, 33, -70, -71, -89, -18, -14, -29, 3, -70, -85, 52, -94, 122, -104, -53, 18, 117, 18, 109, -112, 41, 10, 0, -41, -32, 70, 12, 1, 78, -102, -43, -63, -119, 46, -93, -38, -73, 13, 68, 112, 89, 90, 119, 40, -18, -97, -99, -26, 21, -106, 91, -3, 121, 114, -29, -21, -40, -29, 106, -72, -101, -102, -62, -23, 28, 107, 125, -92, -90, 120, 64, -115, -73, -21, 121, -36, -118, 87, 18, 21, 51, -82, 29, 100, 34, 50, -89, 59, -105, -74, -82, -94, -121, 37, 5, -123, -37, -106, 47, -10, -29, 31, 13, -38, 0, 93, 41, -86, -11, -14, 1, -35, 83, 58, 114, 25, 53, -121, 100, -71, -98, -60, 53, -119, 84, -123, 106, -16, 22, 36, 64, 56, 94, -42, 122, -71, -79, -85, -3, -29, 81, -109, -47, -95, 32, 23, 45, -8, 34, 86, 31, 49, 18, -19, 102, 57, 82, 29, -110, 77, -117, 35, -37, 0, 61, 42, 115, -9, -23, 84, -54, -28, 32, -116, -51, 122, 116, -25, 70, 113, -21, -17, -62, -118, -15, -83, -13, -77, -60, -16, -82, -123, 7, -63, 74, -92, 114, 41, -33, 119, 24, 75, 115, -38, -50, -87, 97, 99, 106, 9, -98, 101, -102, -91, -36, 77, 15, -12, -70, -69, 92, -53, 3, -69, 15, 60, -35, 53, 0, 3, -123, 87, -77, 80, -41, -89, 50, -4, 100, -6, -75, 98, -98, 30, 29, 30, 81, -25, 20, 38, 28, 110, 110, 117, 83, -11, -88, -31, 81, 106, 64, -84, -89, 111, -116, -28, 44, 52, 99, 15, 3, 10, -30, 103, -111, 78, 36, -125, 122, -42, 42, 46, -93, 3, -67, -104, -28, 22, -50, 112, -38, -117, 43, -63, 30, 65, 12, 8, -18, -108, 65, 117, -86, 118, 42, 18, -122, 123, -118, -55, 104, 57, 10, -17, -118, 117, 70, 50, 2, -75, 39, 92, -98, -96, 62, 36, -104, 10, 80, 95, -11, 96, -109, 3, -23, 26, 121, -22, 104, -47, -49, 89, -120, -8, 101, 119, -43, -50, -75, -84, -72, 99, 100, -56, 71, 23, -67, 120, 71, -37, 35, 83, -65, -18, -108, 68, -28, -47, 105, -35, 42, 5, -11, -22, -88, -68, -81, 116, -39, -21, 118, 80, 110, 38, 50, -81, -20, -110, 67, -6, -87, -17, -18, -119, -16, -106, -25, -91, -59, -37, 71, -121, -115, -45, -108, -44, -105, 23, 90, 88, -1, 81, 17, -55, -126, -28, -102, 121, 57, 22, 54, 38, 56, 95, 117, 94, 83, -100, -55, 35, -76, 114, -116, -20, -24, 122, -107, -15, -10, 8, 123, -92, -128, -67, 99, -18, 17, -104, -36, -118, 56, 7, 125, 79, 61, -10, 77, 125, 124, -34, 15, 99, -111, 57, 57, -66, -119, 15, -105, 74, 94, 59, -25, -94, -64, 71, -9, -95, 25, 2, -118, -66, -11, 8, 93, 48, 24, -43, 108, 1, -89, -43, -126, -87, -78, 123, -96, -8, 82, 90, -93, 45, 116, 6, -83, 101, -97, 77, -44, -106, 15, -17, 62, -82, 125, 117, 103, -86, 19, 29, 46, -112, 58, 92, 91, -119, -112, -110, 76, -95, -99, 16, -35, 79, -48, 104, 4, -48, -41, 127, 44, -103, -118, 50, 54, -97, -6, 103, -25, -23, 22, 32, -24, -121, 21, 66, 57, 38, -66, 62, 125, -96, 63, -54, -2, -107, 86, 41, -33, -13, -89, -83, -78, -79, 67, 101, 85, 112, -78, -9, 53, 13, -25, -116, 97, -85, -12, -10, 75, -49, 8, -36, -37, 28, 68, -44, -38, -124, -85, -43, -4, -106, -118, 120, 116, 18, -113, -77, -39, 19, -39, 60, -44, -40, -51, 11, -96, 10, -47, 46, 105, 94, 75, -100, -120, -13, 124, -2, 73, 104, -35, -37, 1, -68, -99, -5, -128, 23, 65, 46, 3, 86, -67, 31, -43, 72, 41, -124, -95, 68, 99, 27, -60, 55, 115, 62, 25, -66, -16, -50, -10, -33, -30, -23, -90, -88, 9, 34, -80, 72, 34, -8, -109, -109, 20, 22, -12, 109, 71, -99, 103, 14, 70, 92, 24, -27, 103, -54, 36, -48, 14, 13, -22, -74, -19, 39, 74, 96, -93, 88, 118, -106, -63, 3, -107, -79, -127, -24, 6, -9, -34, 52, 32, -2, 11, 111, -125, -63, 25, -14, 108, 123, 24, 52, 118, -31, 108, 4, 124, -71, 93, -110, 63, 21, 57, -11, 92, -52, 127, -69, -102, -8, 23, -62, 78, 88, 56, -32, -43, 116, 14, 116, 100, 34, -113, -77, -31, -27, 98, -8, 50, -95, -90, -18, -30, 55, 124, 33, -102, -16, 88, -66, 115, -97, 40, 5, -79, 5, 103, -93, -32, 48, -122, -118, -121, -20, 19, -36, 10, -33, 42, -66, -123, 85, -3, -61, -73, 121, 1)\n            val input = BitcoinInputMarkable(data)\n\n            val expectedTransactionHashes = arrayOf(\n                    \"e7c51d6bb6c0ba20788ac6a67a43370445afcb302fb52ecb3b96e8ca1fb2511d\",\n                    \"d92500e9eef299f23548a38fb01581cfbece612697fbe543ad100f6a4581c39e\",\n                    \"22561f3112ed6639521d924d8b23db003d2a73f7e954cae4208ccd7a74e74671\",\n                    \"ebefc28af1adf3b3c4f0ae8507c14aa47229df77184b73dacea961636a099e65\",\n                    \"9aa5dc4d0ff4babb5ccb03bb0f3cdd3500038557b350d7a732fc64fab5629e1e\",\n                    \"95f1f6087ba480bd63ee1198dc8a38077d4f3df64d7d7cde0f63913939be890f\",\n                    \"fe955629dff3a7adb2b143655570b2f7350de78c61abf4f64bcf08dcdb1c44d4\"\n            )\n\n            val expectedBlockHash = \"490e924edc714fe5014d6b7f01a86aced7c37b2117a229000000000000000000\"\n\n            val message = messageParser.parseMessage(input)\n            val merkleBlock = merkleBlockExtractor.extract(message as MerkleBlockMessage)\n\n            Assert.assertEquals(expectedBlockHash, merkleBlock.blockHash.toHexString())\n            Assert.assertArrayEquals(expectedTransactionHashes, merkleBlock.associatedTransactionHashes.keys.map { it.bytes.toHexString() }.toTypedArray())\n        }\n    }\n})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/message/TransactionMessageParserTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.message\n\nimport io.horizontalsystems.bitcoincore.extensions.toHexString\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.network.messages.TransactionMessage\nimport io.horizontalsystems.bitcoincore.network.messages.TransactionMessageParser\nimport org.junit.Assert.assertEquals\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nobject TransactionMessageParserTest : Spek({\n    val messageParser by memoized {\n        TransactionMessageParser()\n    }\n\n    describe(\"#parseMessage\") {\n        it(\"parses transactions correctly\") {\n            val data = byteArrayOf(1, 0, 0, 0, 4, -21, -95, 5, -116, -111, -24, -127, -21, 122, 76, -17, -101, -8, 124, 127, -71, -124, 104, 44, 34, 6, 66, -17, -48, 97, -107, -47, 89, 108, -37, -77, -91, 0, 0, 0, 0, 107, 72, 48, 69, 2, 33, 0, -16, -94, -126, 77, -109, 26, -126, -83, 15, -15, 109, -17, 70, 22, -2, -85, -98, 67, 19, 64, 97, -123, -97, 111, -14, -108, 25, 79, 31, 45, 51, 52, 2, 32, 69, -36, -69, 60, -51, 8, 113, -120, 60, -104, -22, 62, -8, -55, 19, 10, 82, -96, -87, 28, 105, 116, 81, -89, 61, 127, 100, 35, 67, 22, -78, -94, 1, 33, 2, 81, 118, -65, 71, 110, 27, 65, -22, -26, 75, 109, -21, 27, 44, 29, 53, 0, -111, 15, -123, 7, -8, 35, -37, -88, 46, 24, 84, 84, -89, 108, -117, -1, -1, -1, -1, 90, 42, -116, -112, -90, -97, 92, 12, -89, 71, -17, 25, -98, 71, 103, -58, 16, 75, -84, 78, -48, 117, 66, 60, 111, -80, 10, 113, 14, 77, -80, 57, 1, 0, 0, 0, 106, 71, 48, 68, 2, 32, 123, -126, 57, 20, -69, 27, -58, -98, 114, 48, -94, -59, 92, 13, 48, -42, 4, -38, -101, 22, 92, 35, 57, 123, 31, 45, -89, -75, 42, 115, 33, -38, 2, 32, 4, -92, 93, 23, 51, 119, 92, 100, 30, 101, -18, -29, -47, -119, 57, -128, -128, 98, -53, 107, 117, 108, 100, -34, 110, 4, -60, -68, -47, 87, 13, -67, 1, 33, 2, 81, 118, -65, 71, 110, 27, 65, -22, -26, 75, 109, -21, 27, 44, 29, 53, 0, -111, 15, -123, 7, -8, 35, -37, -88, 46, 24, 84, 84, -89, 108, -117, -1, -1, -1, -1, -16, -62, -90, -63, -100, 89, 15, -63, 69, -61, 60, -38, -108, 24, -16, 42, 46, 86, 124, 115, -32, -87, 57, 85, -68, 23, 77, -103, -71, 95, -68, -37, 1, 0, 0, 0, 106, 71, 48, 68, 2, 32, 125, 71, -29, 23, -121, -79, -24, 60, -29, 58, 43, 29, 78, -90, -63, -47, -63, -99, 71, -20, -99, 70, -42, -26, -69, -15, -16, -89, 127, -63, -9, -42, 2, 32, 29, 17, 34, -113, -107, 108, -88, -117, -91, 119, -49, 36, 9, -126, -17, -87, 22, 49, 15, -19, -117, -109, -38, -40, 2, 54, 17, 80, -24, -114, 78, -116, 1, 33, 2, -68, -108, -4, 15, -20, -121, -84, -51, 73, 3, -24, 13, 6, -81, -79, 18, -92, 111, 116, 62, -103, 122, -14, -57, 104, 0, 53, -53, -90, -44, -77, -116, -1, -1, -1, -1, 121, 109, -40, -2, -112, -93, -10, -104, 15, 83, 121, -5, -77, -107, 44, -5, -87, 74, -79, 38, -33, -59, -38, 84, -19, 5, 14, 84, -31, -93, 9, 90, 4, 0, 0, 0, 106, 71, 48, 68, 2, 32, 11, -72, 121, 89, 104, -128, -34, -44, -14, -91, 56, -96, -103, -89, -7, -9, 123, 16, -48, -68, 85, -103, 105, -120, -16, -111, -82, 106, 65, 56, -122, -26, 2, 32, 85, 62, 70, 9, 2, 15, -78, -63, -90, -43, -52, -98, 63, -1, -120, -34, 5, -48, -68, 34, 76, 62, -118, -63, -1, 73, 34, -38, -125, 66, 50, -47, 1, 33, 2, -68, -108, -4, 15, -20, -121, -84, -51, 73, 3, -24, 13, 6, -81, -79, 18, -92, 111, 116, 62, -103, 122, -14, -57, 104, 0, 53, -53, -90, -44, -77, -116, -1, -1, -1, -1, 8, -76, 33, -87, 3, 0, 0, 0, 0, 23, -87, 20, -1, -11, -55, 35, 71, -41, -55, -92, 53, -42, -127, 96, -121, -65, 100, -88, 124, 26, -128, -73, -121, 106, 44, 5, 1, 0, 0, 0, 0, 23, -87, 20, 114, 105, -67, 118, 64, 11, -58, 34, 36, -23, -121, 60, 59, -23, -84, -87, 86, 105, 51, -24, -121, 121, 115, 63, 0, 0, 0, 0, 0, 23, -87, 20, -10, -19, 51, -75, 48, -85, 4, 73, -115, 49, 36, -40, -20, -73, 68, -22, -4, 20, -117, 101, -121, -74, -118, 25, 1, 0, 0, 0, 0, 23, -87, 20, -42, 58, 41, 64, -56, 39, 21, 74, -58, 71, -75, 47, 86, -17, 84, 117, -87, -9, 44, 42, -121, -64, 25, 43, 0, 0, 0, 0, 0, 23, -87, 20, -107, -79, -123, -126, 126, 84, 71, -92, -102, 36, 65, 70, 88, -50, 96, -34, 29, -47, 108, 38, -121, 18, -30, 66, 0, 0, 0, 0, 0, 25, 118, -87, 20, 23, 28, 60, -125, 83, -98, -36, -40, 73, -35, -8, -91, 77, 88, 21, -49, -103, 28, 66, 2, -120, -84, -75, 31, -26, 60, 0, 0, 0, 0, 25, 118, -87, 20, -79, -61, 108, -37, -56, 72, -120, 40, -3, -9, -59, 26, 1, -1, -36, 95, -83, 121, 95, 63, -120, -84, 22, -90, -65, 0, 0, 0, 0, 0, 23, -87, 20, -91, 98, 4, -64, -84, -46, -49, 3, 110, 3, 117, 110, 127, 114, -42, -81, -99, 10, 21, -71, -121, 0, 0, 0, 0)\n            val input = BitcoinInputMarkable(data)\n\n            val expectedTransactionHex = \"6ad4070a0d7a14ac75d47373f5bbddf3efd8db7b14789ec08abe487b1a763889\"\n\n            val message = messageParser.parseMessage(input) as TransactionMessage\n            val transaction = message.transaction\n\n            assertEquals(expectedTransactionHex, transaction.header.hash.toHexString())\n        }\n    }\n\n})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/models/TransactionTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.models\n\nimport io.horizontalsystems.bitcoincore.extensions.hexToByteArray\nimport io.horizontalsystems.bitcoincore.extensions.toHexString\nimport io.horizontalsystems.bitcoincore.extensions.toReversedHex\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.serializers.TransactionSerializer\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.bitcoincore.utils.HashUtils\nimport org.junit.Assert.assertEquals\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nobject TransactionTest : Spek({\n\n    lateinit var transaction: Transaction\n    lateinit var fullTransaction: FullTransaction\n\n    val txHashLE = \"169e1e83e930853391bc6f35f605c6754cfead57cf8387639d3b4096c54f18f4\"\n    var txRaw = \"0100000001c997a5e56e104102fa209c6a852dd90660a20b2d9c352423edce25857fcd3704000000004847304402204e45e16932b8af514961a1d3a1a25fdf3f4f7732e9d624c6c61548ab5fb8cd410220181522ec8eca07de4860a4acdd12909d831cc56cbbac4622082221a8768d1d0901ffffffff0200ca9a3b00000000434104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac00286bee0000000043410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac00000000\"\n\n    describe(\"constructor\") {\n\n        it(\"init\") {\n            fullTransaction = TransactionSerializer.deserialize(BitcoinInputMarkable(txRaw.hexToByteArray()))\n            transaction = fullTransaction.header\n\n            assertEquals(fullTransaction.inputs.size, 1)\n            assertEquals(fullTransaction.outputs.size, 2)\n\n            assertEquals(fullTransaction.outputs[0].index, 0)\n            assertEquals(fullTransaction.outputs[1].index, 1)\n\n            assertEquals(transaction.hash.toHexString(), txHashLE)\n        }\n\n        it(\"withWitness\") {\n            val witness = arrayOf(\n                    \"3045022100df7b7e5cda14ddf91290e02ea10786e03eb11ee36ec02dd862fe9a326bbcb7fd02203f5b4496b667e6e281cc654a2da9e4f08660c620a1051337fa8965f727eb191901\",\n                    \"038262a6c6cec93c2d3ecd6c6072efea86d02ff8e3328bbd0242b20af3425990ac\"\n            )\n\n            txRaw = \"0100000000010115e180dc28a2327e687facc33f10f2a20da717e5548406f7ae8b4c811072f8560100000000ffffffff0100b4f505000000001976a9141d7cd6c75c2e86f4cbf98eaed221b30bd9a0b92888ac0248${witness[0]}21${witness[1]}00000000\"\n            fullTransaction = TransactionSerializer.deserialize(BitcoinInputMarkable(txRaw.hexToByteArray()))\n            transaction = fullTransaction.header\n\n            val inputs = fullTransaction.inputs\n            assertEquals(inputs.size, 1)\n            assertEquals(fullTransaction.outputs.size, 1)\n\n            assertEquals(transaction.segwit, true)\n            assertEquals(inputs[0].witness[0].toHexString(), witness[0])\n            assertEquals(inputs[0].witness[1].toHexString(), witness[1])\n        }\n\n        it(\"toByteArray_witness\") {\n            txRaw = \"0100000000010115e180dc28a2327e687facc33f10f2a20da717e5548406f7ae8b4c811072f8560100000000ffffffff0100b4f505000000001976a9141d7cd6c75c2e86f4cbf98eaed221b30bd9a0b92888ac02483045022100df7b7e5cda14ddf91290e02ea10786e03eb11ee36ec02dd862fe9a326bbcb7fd02203f5b4496b667e6e281cc654a2da9e4f08660c620a1051337fa8965f727eb19190121038262a6c6cec93c2d3ecd6c6072efea86d02ff8e3328bbd0242b20af3425990ac00000000\"\n            fullTransaction = TransactionSerializer.deserialize(BitcoinInputMarkable(txRaw.hexToByteArray()))\n            transaction = fullTransaction.header\n\n            assertEquals(fullTransaction.inputs.size, 1)\n            assertEquals(fullTransaction.outputs.size, 1)\n\n            assertEquals(transaction.segwit, true)\n\n            assertEquals(txRaw, TransactionSerializer.serialize(fullTransaction).toHexString())\n        }\n\n        it(\"withoutWitness\") {\n            val txHash = \"8530de8e771c830c4e76909b1fdf0e055ee8872fa1ebbf7c4279375591061a62\"\n            val txWitnessHash = \"f5b5c6fc1b199ecfa04d192a9f81a9cc6ec78bd848e163a80b2d077b4fe1d097\"\n\n            txRaw = \"01000000000101dbf198515cebea6e248a212c63299e63a2a35a2def0a42e43e0106c2efff12860100000000ffffffff02e6988102000000001976a914d1b4380d709e9ea54943a083b1208d6d991893d988ac58271101000000001600149063d7cc1cf2d55f6c0076e65587c755dbe96ed702483045022100fa18145855d55b221c0df4cd72b12dcb26f451aa4b8ca2148ef535d3e374baff02205b13c14fbd8665be6a6da4fb65b46a737679e988956ec353a7c7e40cbe43f7a40121038d0705f4511adf850b16baf4f689d3d92fe63cc9a5f6d5d00d2e4ed699e511f800000000\"\n\n            fullTransaction = TransactionSerializer.deserialize(BitcoinInputMarkable(txRaw.hexToByteArray()))\n            transaction = fullTransaction.header\n\n            assertEquals(txHash, transaction.hash.toReversedHex())\n\n            val bytes = HashUtils.doubleSha256(TransactionSerializer.serialize(fullTransaction, withWitness = true))\n\n            assertEquals(txWitnessHash, bytes.toReversedHex())\n        }\n\n\n        it(\"withoutWitnes\") {\n            val txHash = \"8530de8e771c830c4e76909b1fdf0e055ee8872fa1ebbf7c4279375591061a62\"\n            val txWitnessHash = \"f5b5c6fc1b199ecfa04d192a9f81a9cc6ec78bd848e163a80b2d077b4fe1d097\"\n\n            txRaw = \"01000000000101dbf198515cebea6e248a212c63299e63a2a35a2def0a42e43e0106c2efff12860100000000ffffffff02e6988102000000001976a914d1b4380d709e9ea54943a083b1208d6d991893d988ac58271101000000001600149063d7cc1cf2d55f6c0076e65587c755dbe96ed702483045022100fa18145855d55b221c0df4cd72b12dcb26f451aa4b8ca2148ef535d3e374baff02205b13c14fbd8665be6a6da4fb65b46a737679e988956ec353a7c7e40cbe43f7a40121038d0705f4511adf850b16baf4f689d3d92fe63cc9a5f6d5d00d2e4ed699e511f800000000\"\n\n            fullTransaction = TransactionSerializer.deserialize(BitcoinInputMarkable(txRaw.hexToByteArray()))\n\n            val bytes = HashUtils.doubleSha256(TransactionSerializer.serialize(fullTransaction, true))\n\n            assertEquals(txWitnessHash, bytes.toReversedHex())\n        }\n\n    }\n})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/network/PeerGroupTest.kt",
    "content": "//package io.horizontalsystems.bitcoincore.network\n//\n//import com.nhaarman.mockitokotlin2.whenever\n//import io.horizontalsystems.bitcoincore.extensions.hexToByteArray\n//import io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\n//import io.horizontalsystems.bitcoincore.managers.ConnectionManager\n//import io.horizontalsystems.bitcoincore.models.NetworkAddress\n//import io.horizontalsystems.bitcoincore.network.messages.AddrMessage\n//import io.horizontalsystems.bitcoincore.network.messages.NetworkMessageParser\n//import io.horizontalsystems.bitcoincore.network.messages.NetworkMessageSerializer\n//import io.horizontalsystems.bitcoincore.network.peer.Peer\n//import io.horizontalsystems.bitcoincore.network.peer.PeerAddressManager\n//import io.horizontalsystems.bitcoincore.network.peer.PeerGroup\n//import io.horizontalsystems.bitcoincore.network.peer.PeerManager\n//import io.horizontalsystems.bitcoincore.network.peer.task.SendTransactionTask\n//import org.junit.Before\n//import org.junit.Test\n//import org.junit.runner.RunWith\n//import org.mockito.Mockito.mock\n//import org.mockito.Mockito.verify\n//import org.powermock.api.mockito.PowerMockito\n//import org.powermock.core.classloader.annotations.PrepareForTest\n//import org.powermock.modules.junit4.PowerMockRunner\n//import java.net.SocketTimeoutException\n//\n//@RunWith(PowerMockRunner::class)\n//@PrepareForTest(PeerGroup::class)\n//\n//class PeerGroupTest {\n//\n//    private var peer1 = mock(Peer::class.java)\n//    private var peer2 = mock(Peer::class.java)\n//    private var hostManager = mock(PeerAddressManager::class.java)\n//    private var peerManager = mock(PeerManager::class.java)\n//    private var connectionManager = mock(ConnectionManager::class.java)\n//    private var relayTransactionTask = mock(SendTransactionTask::class.java)\n//    private val networkMessageParser = mock(NetworkMessageParser::class.java)\n//    private val networkMessageSerializer = mock(NetworkMessageSerializer::class.java)\n//\n//    private val peerIp = \"8.8.8.8\"\n//    private val peerIp2 = \"5.5.5.5\"\n//    private val network = mock(Network::class.java)\n//\n//    private lateinit var peerGroup: PeerGroup\n//\n//    @Before\n//    fun setup() {\n//        whenever(peer1.host).thenReturn(peerIp)\n//        whenever(peer2.host).thenReturn(peerIp2)\n//        whenever(hostManager.getIp()).thenReturn(peerIp, peerIp2)\n//        whenever(connectionManager.isConnected).thenReturn(true)\n//\n//        // Peer\n//        PowerMockito.whenNew(Peer::class.java)\n//                .withAnyArguments()\n//                .thenReturn(peer1, peer2)\n//\n//        // RelayTransactionTask\n//        PowerMockito.whenNew(SendTransactionTask::class.java)\n//                .withAnyArguments()\n//                .thenReturn(relayTransactionTask)\n//\n//        peerGroup = PeerGroup(hostManager, network, peerManager, 2, networkMessageParser, networkMessageSerializer, connectionManager, 100)\n//    }\n//\n//    @Test\n//    fun start() { // creates peer connection with given IP address\n//        peerGroup.start()\n//\n//        verify(peer1).start()\n//    }\n//\n//    @Test\n//    fun disconnected_withError() { // removes peer from connection list\n//        peerGroup.onDisconnect(peer1, SocketTimeoutException(\"Some Error\"))\n//\n//        verify(hostManager).markFailed(peerIp)\n//    }\n//\n//    @Test\n//    fun onReceiveMessage() {\n//        val ip4 = \"0A000001\"\n//        val raw = arrayOf(\"E215104D\", \"0100000000000000\", \"00000000000000000000FFFF$ip4\", \"208D\").joinToString(\"\")\n//        val input = BitcoinInputMarkable(raw.hexToByteArray())\n//\n//        val netAddress = NetworkAddress(input, false)\n//\n//        peerGroup.onReceiveMessage(peer1, AddrMessage(listOf(netAddress)))\n//\n//        verify(hostManager).addIps(listOf(\"10.0.0.1\"))\n//    }\n//\n//}\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/network/PeerManagerTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.network\n\nimport com.nhaarman.mockitokotlin2.doReturn\nimport com.nhaarman.mockitokotlin2.mock\nimport com.nhaarman.mockitokotlin2.whenever\nimport io.horizontalsystems.bitcoincore.network.peer.Peer\nimport io.horizontalsystems.bitcoincore.network.peer.PeerManager\nimport org.junit.Assert.assertEquals\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nobject PeerManagerTest : Spek({\n\n    val peer1 by memoized { mock<Peer> { on { host } doReturn \"8.8.8.8\" } }\n    val peer2 by memoized { mock<Peer> { on { host } doReturn \"9.9.9.9\" } }\n    val peer3 by memoized { mock<Peer> { on { host } doReturn \"0.0.0.0\" } }\n\n    val peerManager by memoized { PeerManager() }\n\n    fun addPeer(synced: Boolean, connected: Boolean = true, host: String = \"0.0.0.0\", ready: Boolean = true): Peer {\n        val peer = mock<Peer> {}\n\n        whenever(peer.connected).thenReturn(connected)\n        whenever(peer.synced).thenReturn(synced)\n        whenever(peer.host).thenReturn(host)\n        whenever(peer.ready).thenReturn(ready)\n\n        peerManager.add(peer)\n\n        return peer\n    }\n\n    describe(\"#add\") {\n        it(\"adds peer\") {\n            peerManager.add(peer1)\n\n            assertEquals(1, peerManager.peersCount)\n        }\n    }\n\n    describe(\"#remove\") {\n        it(\"remove peer\") {\n            peerManager.add(peer1)\n            assertEquals(1, peerManager.peersCount)\n\n            peerManager.add(peer2)\n            assertEquals(2, peerManager.peersCount)\n\n            peerManager.remove(peer1)\n            assertEquals(1, peerManager.peersCount)\n        }\n    }\n\n    describe(\"disconnectAll\") {\n        it(\"disconnects all peers\") {\n            peerManager.add(peer1)\n            peerManager.add(peer2)\n            assertEquals(2, peerManager.peersCount)\n\n            peerManager.disconnectAll()\n            assertEquals(0, peerManager.peersCount)\n        }\n    }\n\n    describe(\"#connected\") {\n        it(\"gets connected peers\") {\n            peerManager.add(peer1)\n            peerManager.add(peer2)\n            peerManager.add(peer3)\n            assertEquals(listOf<Peer>(), peerManager.connected())\n\n            whenever(peer2.connected).thenReturn(true)\n            whenever(peer3.connected).thenReturn(true)\n            assertEquals(listOf(peer2, peer3), peerManager.connected())\n        }\n    }\n\n    describe(\"#sorted\") {\n        it(\"gets connected peers sorted by connection time\") {\n            peerManager.add(peer1)\n            peerManager.add(peer2)\n            peerManager.add(peer3)\n            assertEquals(listOf<Peer>(), peerManager.sorted())\n\n            whenever(peer2.connected).thenReturn(true)\n            whenever(peer2.connectionTime).thenReturn(102)\n            whenever(peer3.connected).thenReturn(true)\n            whenever(peer3.connectionTime).thenReturn(100)\n\n            assertEquals(listOf(peer3, peer2), peerManager.sorted())\n        }\n    }\n\n    describe(\"#readyPears\") {\n        it(\"returns a list of ready peers\") {\n            addPeer(host = \"0.0.0.1\", connected = true, synced = true, ready = false)\n            addPeer(host = \"0.0.0.2\", connected = true, synced = true, ready = false)\n            val p3 = addPeer(host = \"0.0.0.3\", connected = true, synced = true, ready = true)\n            val p4 = addPeer(host = \"0.0.0.4\", connected = true, synced = true, ready = true)\n\n            assertEquals(listOf(p3, p4), peerManager.readyPears())\n        }\n\n        it(\"returns an empty list\") {\n            addPeer(host = \"0.0.0.1\", connected = true, synced = true, ready = false)\n            addPeer(host = \"0.0.0.2\", connected = true, synced = true, ready = false)\n\n            assertEquals(listOf<Peer>(), peerManager.readyPears())\n        }\n    }\n\n    describe(\"hasSyncedPeer\") {\n        it(\"is true when at least one peer is synced\") {\n            addPeer(host = \"0.0.0.1\", connected = true, synced = true)\n            addPeer(host = \"0.0.0.2\", connected = true, synced = true)\n            addPeer(host = \"0.0.0.3\", connected = false, synced = false)\n            addPeer(host = \"0.0.0.4\", connected = false, synced = false)\n\n            assertEquals(true, peerManager.hasSyncedPeer())\n        }\n\n        it(\"is false when no peer is synced\") {\n            addPeer(host = \"0.0.0.1\", connected = true, synced = false)\n            addPeer(host = \"0.0.0.2\", connected = true, synced = false)\n            addPeer(host = \"0.0.0.3\", connected = false, synced = false)\n            addPeer(host = \"0.0.0.4\", connected = false, synced = false)\n\n            assertEquals(false, peerManager.hasSyncedPeer())\n        }\n    }\n})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/network/PeerTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.network\n\nimport com.nhaarman.mockitokotlin2.argThat\nimport com.nhaarman.mockitokotlin2.argumentCaptor\nimport com.nhaarman.mockitokotlin2.verify\nimport com.nhaarman.mockitokotlin2.whenever\nimport io.horizontalsystems.bitcoincore.network.messages.*\nimport io.horizontalsystems.bitcoincore.network.peer.Peer\nimport io.horizontalsystems.bitcoincore.network.peer.PeerConnection\nimport org.junit.Assert\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.Mockito.mock\nimport org.powermock.api.mockito.PowerMockito\nimport org.powermock.core.classloader.annotations.PrepareForTest\nimport org.powermock.modules.junit4.PowerMockRunner\nimport java.util.concurrent.ExecutorService\n\n@RunWith(PowerMockRunner::class)\n@PrepareForTest(Peer::class)\n\nclass PeerTest {\n\n    private val listener = mock(Peer.Listener::class.java)\n    private val peerConnection = mock(PeerConnection::class.java)\n    private val network = mock(Network::class.java)\n    private val versionMessage = mock(VersionMessage::class.java)\n    private val addressMessage = mock(AddrMessage::class.java)\n    private val networkMessageParser = mock(NetworkMessageParser::class.java)\n    private val networkMessageSerializer = mock(NetworkMessageSerializer::class.java)\n    private val executorService = mock(ExecutorService::class.java)\n\n    private lateinit var peer: Peer\n\n    @Before\n    fun setup() {\n        PowerMockito\n                .whenNew(PeerConnection::class.java)\n                .withAnyArguments()\n                .thenReturn(peerConnection)\n\n        peer = Peer(\"host\", network, listener, networkMessageParser, networkMessageSerializer, executorService)\n    }\n\n    @Test\n    fun onMessage_versionMessage_success() {\n        whenever(versionMessage.lastBlock).thenReturn(99)\n        whenever(versionMessage.hasBlockChain(network)).thenReturn(true)\n        whenever(versionMessage.supportsBloomFilter(network)).thenReturn(true)\n\n        peer.onMessage(versionMessage)\n\n        argumentCaptor<IMessage>().apply {\n            verify(peerConnection).sendMessage(capture())\n\n            Assert.assertTrue(firstValue is VerAckMessage)\n        }\n    }\n\n    @Test\n    fun onMessage_versionMessage_error_lastBlockIs0() {\n        whenever(versionMessage.lastBlock).thenReturn(0)\n\n        peer.onMessage(versionMessage)\n\n        verify(peerConnection).close(argThat {\n            this is Peer.Error.UnsuitablePeerVersion\n        })\n    }\n\n    @Test\n    fun onMessage_versionMessage_error_notFullNode() {\n        whenever(versionMessage.lastBlock).thenReturn(99)\n        whenever(versionMessage.hasBlockChain(network)).thenReturn(false)\n\n        peer.onMessage(versionMessage)\n\n        verify(peerConnection).close(argThat {\n            this is Peer.Error.UnsuitablePeerVersion\n        })\n    }\n\n    @Test\n    fun onMessage_versionMessage_error_notSupportingBloomFilter() {\n        whenever(versionMessage.lastBlock).thenReturn(99)\n        whenever(versionMessage.supportsBloomFilter(network)).thenReturn(false)\n\n        peer.onMessage(versionMessage)\n\n        verify(peerConnection).close(argThat {\n            this is Peer.Error.UnsuitablePeerVersion\n        })\n    }\n\n    @Test\n    fun onReceiveMessage() {\n        peer.connected = true\n        peer.onMessage(addressMessage)\n\n        verify(listener).onReceiveMessage(peer, addressMessage)\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/network/peer/PeerHostManagerTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.peer\n\nimport com.nhaarman.mockitokotlin2.mock\nimport com.nhaarman.mockitokotlin2.verify\nimport com.nhaarman.mockitokotlin2.verifyNoMoreInteractions\nimport com.nhaarman.mockitokotlin2.whenever\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.models.PeerAddress\nimport io.horizontalsystems.bitcoincore.network.Network\nimport org.junit.Assert.assertEquals\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.mockito.ArgumentMatchers.anyList\nimport org.powermock.api.mockito.PowerMockito\nimport org.powermock.core.classloader.annotations.PrepareForTest\nimport org.powermock.modules.junit4.PowerMockRunner\n\n@RunWith(PowerMockRunner::class)\n@PrepareForTest(PeerAddressManager::class)\n\nclass PeerHostManagerTest {\n    private val storage = mock<IStorage>()\n    private val peerDiscover = mock<PeerDiscover>()\n    private val network = mock<Network>()\n    private val listener = mock<PeerGroup>()\n\n    private val ipsPeers = listOf(\"0.0.0.0\", \"1.1.1.1\")\n    private val dnsSeeds = listOf(\"abc.com\", \"com.abc\")\n\n    private lateinit var peerAddressManager: PeerAddressManager\n\n    @Before\n    fun setup() {\n        // PeerDiscover\n        PowerMockito.whenNew(PeerDiscover::class.java)\n                .withAnyArguments()\n                .thenReturn(peerDiscover)\n\n        whenever(network.dnsSeeds).thenReturn(dnsSeeds)\n        whenever(storage.getLeastScoreFastestPeerAddressExcludingIps(anyList()))\n                .thenReturn(\n                        PeerAddress(ipsPeers[0]),\n                        PeerAddress(ipsPeers[1]))\n\n        peerAddressManager = PeerAddressManager(network, storage)\n        peerAddressManager.listener = listener\n    }\n\n    @Test\n    fun getPeerIp_withoutPeerAddresses() {\n        whenever(storage.getLeastScoreFastestPeerAddressExcludingIps(anyList()))\n                .thenReturn(null)\n\n        val peerIp = peerAddressManager.getIp()\n\n        verify(peerDiscover).lookup(dnsSeeds)\n        assertEquals(null, peerIp)\n    }\n\n    @Test\n    fun getPeerIp_withPeerAddresses() {\n        var peerIp: String?\n\n        // first call\n        peerIp = peerAddressManager.getIp()\n        assertEquals(ipsPeers[0], peerIp)\n\n        // second call\n        peerIp = peerAddressManager.getIp()\n        assertEquals(ipsPeers[1], peerIp)\n\n        verifyNoMoreInteractions(peerDiscover)\n    }\n\n    @Test\n    fun markFailed() {\n        val peerIp = ipsPeers[0]\n        peerAddressManager.markFailed(peerIp)\n\n        verify(storage).deletePeerAddress(peerIp)\n    }\n\n    @Test\n    fun addPeers() {\n        val newIps = listOf(\"2.2.2.2\", \"3.3.3.3\")\n\n        peerAddressManager.addIps(newIps)\n\n        verify(listener).onAddAddress()\n        verify(storage).setPeerAddresses(listOf(\n                PeerAddress(ip = newIps[0], score = 0),\n                PeerAddress(ip = newIps[1], score = 0)))\n    }\n}\n\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/network/peer/task/SendTransactionTaskTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.network.peer.task\n\nimport com.nhaarman.mockitokotlin2.*\nimport io.horizontalsystems.bitcoincore.models.InventoryItem\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.network.messages.GetDataMessage\nimport io.horizontalsystems.bitcoincore.network.messages.IMessage\nimport io.horizontalsystems.bitcoincore.network.messages.TransactionMessage\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport org.junit.Assert\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nobject SendTransactionTaskTest : Spek({\n\n    val fullTransaction = mock<FullTransaction>()\n    val task by memoized { SendTransactionTask(fullTransaction) }\n    val requester by memoized { mock<PeerTask.Requester>() }\n    val listener by memoized { mock<PeerTask.Listener>() }\n\n    beforeEachTest {\n        task.requester = requester\n        task.listener = listener\n    }\n\n    describe(\"#handleMessage\") {\n\n        describe(\"when message is not GetDataMessage\") {\n            val message = mock<IMessage>()\n\n            it(\"is false\") {\n                Assert.assertFalse(task.handleMessage(message))\n            }\n        }\n\n        describe(\"when message is GetDataMessage\") {\n            val txHash = byteArrayOf(1, 2, 3, 4, 5, 6, 7, 8, 9, 0)\n            val message = mock<GetDataMessage>()\n            val transaction = mock<Transaction>()\n\n            beforeEach {\n                whenever(fullTransaction.header).thenReturn(transaction)\n                whenever(transaction.hash).thenReturn(txHash)\n            }\n\n            describe(\"when requested this transaction\") {\n                beforeEach {\n                    val inventoryItem = mock<InventoryItem>()\n                    whenever(message.inventory).thenReturn(listOf(inventoryItem))\n                    whenever(inventoryItem.type).thenReturn(InventoryItem.MSG_TX)\n                    whenever(inventoryItem.hash).thenReturn(txHash)\n                }\n\n                it(\"is true\") {\n                    Assert.assertTrue(task.handleMessage(message))\n                }\n\n                it(\"sends Transaction message and completes itself\") {\n                    argumentCaptor<IMessage>().apply {\n                        task.handleMessage(message)\n\n                        verify(requester).send(capture())\n                        Assert.assertEquals(fullTransaction, (firstValue as TransactionMessage).transaction)\n\n                        verify(listener).onTaskCompleted(task)\n                    }\n                }\n            }\n\n            describe(\"when requested another transaction\") {\n                beforeEach {\n                    val inventoryItem = mock<InventoryItem>()\n                    whenever(message.inventory).thenReturn(listOf(inventoryItem))\n                    whenever(inventoryItem.type).thenReturn(InventoryItem.MSG_TX)\n                    whenever(inventoryItem.hash).thenReturn(txHash.reversedArray())\n                }\n\n                it(\"is false\") {\n                    Assert.assertFalse(task.handleMessage(message))\n\n                    verifyNoMoreInteractions(requester)\n                    verifyNoMoreInteractions(listener)\n                }\n\n            }\n\n        }\n\n    }\n\n})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/transactions/TransactionExtractorTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions\n\nimport com.nhaarman.mockitokotlin2.any\nimport com.nhaarman.mockitokotlin2.mock\nimport com.nhaarman.mockitokotlin2.whenever\nimport io.horizontalsystems.bitcoincore.Fixtures\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.core.PluginManager\nimport io.horizontalsystems.bitcoincore.extensions.hexToByteArray\nimport io.horizontalsystems.bitcoincore.extensions.toHexString\nimport io.horizontalsystems.bitcoincore.models.*\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.bitcoincore.transactions.extractors.MyOutputsCache\nimport io.horizontalsystems.bitcoincore.transactions.extractors.TransactionExtractor\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport io.horizontalsystems.bitcoincore.utils.IAddressConverter\nimport org.junit.Assert.*\nimport org.mockito.Mockito\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nobject TransactionExtractorTest : Spek({\n    val storage = Mockito.mock(IStorage::class.java)\n    val addressConverter = Mockito.mock(IAddressConverter::class.java)\n    val pluginManager = mock<PluginManager>()\n\n    lateinit var transactionOutput: TransactionOutput\n    lateinit var transactionInput: TransactionInput\n    lateinit var fullTransaction: FullTransaction\n    lateinit var extractor: TransactionExtractor\n    lateinit var transactionOutputsCache: MyOutputsCache\n\n//    beforeEachTest {\n//        transactionOutput = TransactionOutput()\n//        transactionInput = TransactionInput(byteArrayOf(), 0)\n//        fullTransaction = FullTransaction(Transaction(), listOf(transactionInput), listOf(transactionOutput))\n//        transactionOutputsCache = MyOutputsCache()\n//\n//        extractor = TransactionExtractor(addressConverter, storage, pluginManager, transactionOutputsCache)\n//    }\n//\n//    describe(\"#extract\") {\n//\n//        //\n//        // Input\n//        //\n//\n//        it(\"extractInputs_P2SH\") {\n//            val address = LegacyAddress(\"00112233\", byteArrayOf(1), AddressType.P2SH)\n//            val signScript =\n//                \"004830450221008c203a0881f75c731d9a3a2e6d2ffa37da7095b7dde61a9e7a906659219cd0fa02202677097ca7f7e164f73924fe8f84e1e6fc6611450efcda360ce771e98af9f73d0147304402201cba9b641483476f67a4cef08d7280f51de8d7615fcce76642d944dc07132a990220323d13175477bbf67c8c36fb243bec0e4c410bc9173a186d9f8e98ce3445363601475221025b64f7c63e30f315259393f64dcca269d18386997b1cc93da1388c4021e3ea8e210386d42d5d7027ac08ddcbb066e2140575091fe7dc1d202a008eb5e036725e975652ae\"\n//\n//            whenever(addressConverter.convert(any<ByteArray>(), any())).thenReturn(address)\n//\n//            transactionInput.sigScript = signScript.hexToByteArray()\n//            extractor.extractInputs(fullTransaction)\n//\n//            assertEquals(address.hash, fullTransaction.inputs[0].lockingScriptPayload)\n//            assertEquals(address.string, fullTransaction.inputs[0].address)\n//        }\n//\n//        it(\"extractInputs_P2PKH\") {\n//            val address = LegacyAddress(\"00112233\", byteArrayOf(1), AddressType.P2PKH)\n//            val signScript =\n//                \"483045022100907103d70cd2215bc76e27e07cafa39e975cbf4a7f5897402883dbd59b42ed5e022000bbaeb898d2f5c687a420ad51e001080035ee9690b19d6af4bc192f1e0a8b17012103aac540428b6955a53bb01fcae6d4279df45253b2c61684fb993b5545935dac7a\"\n//\n//            whenever(addressConverter.convert(any<ByteArray>(), any())).thenReturn(address)\n//\n//            transactionInput.sigScript = signScript.hexToByteArray()\n//            extractor.extractInputs(fullTransaction)\n//\n//            assertEquals(address.hash, fullTransaction.inputs[0].lockingScriptPayload)\n//            assertEquals(address.string, fullTransaction.inputs[0].address)\n//        }\n//\n//        it(\"extractInputs_P2WPKHSH\") {\n//            val address = LegacyAddress(\"00112233\", byteArrayOf(1), AddressType.P2SH)\n//            val signScript = \"1600148749115073ad59a6f3587f1f9e468adedf01473f\"\n//\n//            whenever(addressConverter.convert(any<ByteArray>(), any())).thenReturn(address)\n//\n//            transactionInput.sigScript = signScript.hexToByteArray()\n//            extractor.extractInputs(fullTransaction)\n//\n//            assertEquals(address.hash, fullTransaction.inputs[0].lockingScriptPayload)\n//            assertEquals(address.string, fullTransaction.inputs[0].address)\n//        }\n//\n//        //\n//        // Output\n//        //\n//\n//        it(\"extractOutputs_P2PKH\") {\n//            assertNull(fullTransaction.outputs[0].lockingScriptPayload)\n//\n//            val keyHash = \"1ec865abcb88cec71c484d4dadec3d7dc0271a7b\"\n//            transactionOutput.lockingScript = \"76a914${keyHash}88AC\".hexToByteArray()\n//            extractor.extractOutputs(fullTransaction)\n//\n//            assertEquals(keyHash, fullTransaction.outputs[0].lockingScriptPayload?.toHexString())\n//            assertEquals(ScriptType.P2PKH, fullTransaction.outputs[0].scriptType)\n//        }\n//\n//        it(\"extractOutputs_P2PK\") {\n//            assertNull(fullTransaction.outputs[0].lockingScriptPayload)\n//\n//            val keyHash = \"037d56797fbe9aa506fc263751abf23bb46c9770181a6059096808923f0a64cb15\"\n//            transactionOutput.lockingScript = \"21${keyHash}AC\".hexToByteArray()\n//            extractor.extractOutputs(fullTransaction)\n//\n//            assertEquals(keyHash, fullTransaction.outputs[0].lockingScriptPayload?.toHexString())\n//            assertEquals(ScriptType.P2PK, fullTransaction.outputs[0].scriptType)\n//        }\n//\n//        it(\"extractOutputs_P2SH\") {\n//            assertNull(fullTransaction.outputs[0].lockingScriptPayload)\n//\n//            val keyHash = \"bd82ef4973ebfcbc8f7cb1d540ef0503a791970b\"\n//            transactionOutput.lockingScript = \"A914${keyHash}87\".hexToByteArray()\n//            extractor.extractOutputs(fullTransaction)\n//\n//            assertEquals(keyHash, fullTransaction.outputs[0].lockingScriptPayload?.toHexString())\n//            assertEquals(ScriptType.P2SH, fullTransaction.outputs[0].scriptType)\n//        }\n//\n//        it(\"extractOutputs_P2WPKH\") {\n//            assertNull(fullTransaction.outputs[0].lockingScriptPayload)\n//\n//            val keyHash = \"00148749115073ad59a6f3587f1f9e468adedf01473f\".hexToByteArray()\n//            transactionOutput.lockingScript = keyHash\n//            extractor.extractOutputs(fullTransaction)\n//\n//            assertArrayEquals(keyHash, fullTransaction.outputs[0].lockingScriptPayload)\n//            assertEquals(ScriptType.P2WPKH, fullTransaction.outputs[0].scriptType)\n//        }\n//\n//        //\n//        // Old e2e tests\n//        //\n//\n//        it(\"extractP2PKH\") {\n//            fullTransaction = Fixtures.transactionP2PKH\n//\n//            assertNull(fullTransaction.inputs[0].lockingScriptPayload)\n//            assertNull(fullTransaction.outputs[0].lockingScriptPayload)\n//            assertNull(fullTransaction.outputs[1].lockingScriptPayload)\n//\n//            extractor.extractOutputs(fullTransaction)\n//\n//            // output\n//            assertEquals(ScriptType.P2PKH, fullTransaction.outputs[0].scriptType)\n//            assertEquals(ScriptType.P2PKH, fullTransaction.outputs[1].scriptType)\n//            assertEquals(\"37a9bfe84d9e4883ace248509bbf14c9d72af017\", fullTransaction.outputs[0].lockingScriptPayload?.toHexString())\n//            assertEquals(\"37a9bfe84d9e4883ace248509bbf14c9d72af017\", fullTransaction.outputs[1].lockingScriptPayload?.toHexString())\n//\n//            // // input\n//            // assertEquals(\"f6889a22593e9156ef80bdcda0e1b355e8949e05\", fullTransaction.inputs[0]?.keyHash?.toHexString())\n//            // // address\n//            // assertEquals(\"n3zWAXKu6LBa8qYGEuTEfg9RXeijRHj5rE\", fullTransaction.inputs[0]?.address)\n//            // assertEquals(\"mkbGp1uE1jRfdNxtWAUTGWKc9r2pRsLiUi\", fullTransaction.outputs[0]?.address)\n//            // assertEquals(\"mkbGp1uE1jRfdNxtWAUTGWKc9r2pRsLiUi\", fullTransaction.outputs[1]?.address)\n//        }\n//\n//        it(\"extractP2SH\") {\n//            fullTransaction = Fixtures.transactionP2SH\n//\n//            assertNull(fullTransaction.inputs[0].lockingScriptPayload)\n//            assertNull(fullTransaction.outputs[0].lockingScriptPayload)\n//            assertNull(fullTransaction.outputs[1].lockingScriptPayload)\n//\n//            extractor.extractOutputs(fullTransaction)\n//\n//            // output\n//            assertEquals(ScriptType.P2SH, fullTransaction.outputs[0].scriptType)\n//            assertEquals(ScriptType.P2SH, fullTransaction.outputs[1].scriptType)\n//            assertEquals(\"cdfb2eb01489e9fe8bd9b878ce4a7084dd887764\", fullTransaction.outputs[0].lockingScriptPayload?.toHexString())\n//            assertEquals(\"aed6f804c63da80800892f8fd4cdbad0d3ad6d12\", fullTransaction.outputs[1].lockingScriptPayload?.toHexString())\n//\n//            // // input\n//            // assertEquals(\"aed6f804c63da80800892f8fd4cdbad0d3ad6d12\", fullTransaction.inputs[0].keyHash?.toHexString())\n//            // // address\n//            // assertEquals(\"2N9Bh5xXL1CdQohpcqPiphdqtQGuAquWuaG\", fullTransaction.inputs[0].address)\n//            // assertEquals(\"2NC2MR4p1VsHCgAAo8C5KPmyKhuY6rb6SGN\", fullTransaction.outputs[0].address)\n//            // assertEquals(\"2N9Bh5xXL1CdQohpcqPiphdqtQGuAquWuaG\", fullTransaction.outputs[1].address)\n//        }\n//\n//        it(\"extractP2PK\") {\n//            fullTransaction = Fixtures.transactionP2PK\n//\n//            assertNull(fullTransaction.inputs[0].lockingScriptPayload)\n//            assertNull(fullTransaction.outputs[0].lockingScriptPayload)\n//            assertNull(fullTransaction.outputs[1].lockingScriptPayload)\n//\n//            extractor.extractOutputs(fullTransaction)\n//\n//            assertEquals(ScriptType.P2PK, fullTransaction.outputs[0].scriptType)\n//            assertEquals(\n//                \"04ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84c\",\n//                fullTransaction.outputs[0].lockingScriptPayload?.toHexString()\n//            )\n//            assertEquals(\n//                \"0411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3\",\n//                fullTransaction.outputs[1].lockingScriptPayload?.toHexString()\n//            )\n//\n//            // // address\n//            // assertEquals(\"\", fullTransaction.inputs[0].address)\n//            // assertEquals(\"n4YQoLK25P4RsJ2wJEpKnT6q2WGxt149rs\", fullTransaction.outputs[0].address)\n//            // assertEquals(\"mh8YhPYEAYs3E7EVyKtB5xrcfMExkkdEMF\", fullTransaction.outputs[1].address)\n//        }\n//    }\n})\n\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/transactions/TransactionProcessorTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions\n\nimport io.horizontalsystems.bitcoincore.Fixtures\nimport io.horizontalsystems.bitcoincore.blocks.IBlockchainDataListener\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.managers.BloomFilterManager\nimport io.horizontalsystems.bitcoincore.managers.IIrregularOutputFinder\nimport io.horizontalsystems.bitcoincore.managers.PublicKeyManager\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.models.TransactionInput\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.bitcoincore.transactions.extractors.MyOutputsCache\nimport io.horizontalsystems.bitcoincore.transactions.extractors.TransactionExtractor\nimport org.junit.Assert\nimport org.mockito.Mockito\nimport org.mockito.Mockito.mock\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nobject TransactionProcessorTest : Spek({\n\n    lateinit var processor: PendingTransactionProcessor\n    lateinit var transaction: Transaction\n    lateinit var fullTransaction: FullTransaction\n\n    val storage = mock(IStorage::class.java)\n    val outputsCache = mock(MyOutputsCache::class.java)\n    val extractor = mock(TransactionExtractor::class.java)\n    val publicKeyManager = mock(PublicKeyManager::class.java)\n    val blockchainDataListener = mock(IBlockchainDataListener::class.java)\n    val irregularOutputFinder = mock(IIrregularOutputFinder::class.java)\n    val conflictsResolver = mock(TransactionConflictsResolver::class.java)\n\n    beforeEachTest {\n        fullTransaction = Fixtures.transactionP2PKH\n        transaction = fullTransaction.header\n        processor = PendingTransactionProcessor(storage, extractor, publicKeyManager, irregularOutputFinder, blockchainDataListener, conflictsResolver)\n    }\n\n    fun transactions(): List<FullTransaction> {\n\n        val tx1 = FullTransaction(\n                header = Transaction(),\n                inputs = listOf(TransactionInput(\n                        previousOutputTxHash = byteArrayOf(),\n                        previousOutputIndex = 1\n                )),\n                outputs = listOf(TransactionOutput())\n        )\n        val tx2 = FullTransaction(\n                header = Transaction(),\n                inputs = listOf(TransactionInput(\n                        previousOutputTxHash = tx1.header.hash,\n                        previousOutputIndex = 0\n                )),\n                outputs = listOf(\n                        TransactionOutput().apply { index = 0 },\n                        TransactionOutput().apply { index = 1 })\n        )\n        val tx3 = FullTransaction(\n                header = Transaction(),\n                inputs = listOf(TransactionInput(\n                        previousOutputTxHash = tx2.header.hash,\n                        previousOutputIndex = 0\n                )),\n                outputs = listOf(TransactionOutput().apply { index = 0 })\n        )\n        val tx4 = FullTransaction(\n                header = Transaction(),\n                inputs = listOf(\n                        TransactionInput(\n                                previousOutputTxHash = tx2.header.hash,\n                                previousOutputIndex = 0\n                        ),\n                        TransactionInput(\n                                previousOutputTxHash = tx3.header.hash,\n                                previousOutputIndex = 0\n                        )),\n                outputs = listOf(TransactionOutput().apply { index = 0 })\n        )\n\n        return listOf(tx1, tx2, tx3, tx4)\n    }\n\n    describe(\"process\") {\n\n        it(\"process\") {\n            processor.processCreated(fullTransaction)\n\n            Mockito.verify(extractor).extractOutputs(fullTransaction)\n//            Mockito.verify(outputsCache).hasOutputs(fullTransaction.inputs)\n            // Mockito.verify(blockchainDataListener).onTransactionsUpdate(check {\n            //     Assert.assertArrayEquals(transaction.hash, it.firstOrNull()?.hash)\n            // }, eq(listOf()), any())\n        }\n\n        it(\"process_isMine\") {\n            transaction.isMine = true\n\n            processor.processCreated(fullTransaction)\n\n            Mockito.verify(extractor).extractOutputs(fullTransaction)\n            Mockito.verify(extractor).extractInputs(fullTransaction)\n            Mockito.verify(extractor).extractAddress(fullTransaction)\n            Mockito.verify(outputsCache).add(fullTransaction.outputs)\n        }\n\n        it(\"testProcessTransactions_SeveralMempoolTransactions\") {\n            val transactions = transactions()\n\n            for (transaction in transactions) {\n                transaction.header.isMine = true\n                transaction.header.timestamp = 0\n                transaction.header.order = 0\n            }\n\n            transactions[1].header.status = Transaction.Status.NEW\n\n            try {\n                processor.processReceived(listOf(transactions[3], transactions[1], transactions[2], transactions[0]), false)\n\n                transactions.forEachIndexed { index, _ ->\n                    Assert.assertEquals(transactions[index].header.status, Transaction.Status.RELAYED)\n                }\n            } catch (e: BloomFilterManager.BloomFilterExpired) {\n            }\n        }\n\n        it(\"testProcessTransactions_SeveralTransactionsInBlock\") {\n            val transactions = transactions()\n\n            val block = Fixtures.block1\n\n            for (transaction in transactions) {\n                transaction.header.isMine = true\n                transaction.header.timestamp = 0\n                transaction.header.order = 0\n            }\n\n            processor.processReceived(listOf(transactions[3], transactions[1], transactions[2], transactions[0]), false)\n\n            transactions.forEachIndexed { index, _ ->\n                Assert.assertEquals(transactions[index].header.status, Transaction.Status.RELAYED)\n                Assert.assertEquals(transactions[index].header.timestamp, block.timestamp)\n            }\n        }\n    }\n\n})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/transactions/TransactionSenderTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions\n\nimport com.nhaarman.mockitokotlin2.doReturn\nimport com.nhaarman.mockitokotlin2.mock\nimport com.nhaarman.mockitokotlin2.whenever\nimport io.horizontalsystems.bitcoincore.blocks.InitialBlockDownload\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.network.peer.Peer\nimport io.horizontalsystems.bitcoincore.network.peer.PeerGroup\nimport io.horizontalsystems.bitcoincore.network.peer.PeerManager\nimport org.junit.jupiter.api.Assertions.assertTrue\nimport org.junit.jupiter.api.Assertions.fail\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\nimport java.util.concurrent.CopyOnWriteArrayList\n\nobject TransactionSenderTest : Spek({\n\n    val transactionSyncer by memoized { mock<TransactionSyncer> {} }\n    val peerManager by memoized { mock<PeerManager> {} }\n    val initialBlockDownload by memoized { mock<InitialBlockDownload> {} }\n    val storage by memoized { mock<IStorage> {} }\n    val timer by memoized { mock<TransactionSendTimer> {} }\n\n    val peer1 by memoized { mock<Peer> { on { ready } doReturn true } }\n    val peer2 by memoized { mock<Peer> { on { ready } doReturn true } }\n\n    val transactionSender by memoized { TransactionSender(transactionSyncer, peerManager, initialBlockDownload, storage, timer) }\n\n    describe(\"#canSendTransaction\") {\n\n        context(\"when 0 synced peers\") {\n            beforeEach {\n                whenever(peerManager.peersCount).thenReturn(2)\n                whenever(initialBlockDownload.syncedPeers).thenReturn(CopyOnWriteArrayList())\n            }\n\n            it(\"throws an exception\") {\n                try {\n                    transactionSender.canSendTransaction()\n                    fail(\"Expected an Exception to be thrown\")\n                } catch (e: PeerGroup.Error) {\n                    assertTrue(e.message == \"peers not synced\")\n                }\n            }\n        }\n\n        context(\"when 2 synced peers and 0 ready peers\") {\n            beforeEach {\n                val syncedPeers = CopyOnWriteArrayList<Peer>().apply {\n                    add(peer1)\n                    add(peer2)\n                }\n\n                whenever(peerManager.peersCount).thenReturn(2)\n                whenever(peerManager.readyPears()).thenReturn(emptyList())\n                whenever(initialBlockDownload.syncedPeers).thenReturn(syncedPeers)\n            }\n\n            it(\"throws an exception\") {\n                try {\n                    transactionSender.canSendTransaction()\n                    fail(\"Expected an Exception to be thrown\")\n                } catch (e: PeerGroup.Error) {\n                    assertTrue(e.message == \"peers not synced\")\n                }\n            }\n        }\n\n        context(\"when 1 ready and 1 synced peers\") {\n            beforeEach {\n                val syncedPeers = CopyOnWriteArrayList<Peer>().apply {\n                    add(peer1)\n                }\n\n                val readyPeer = mock<Peer> {\n                    on { ready } doReturn true\n                    on { synced } doReturn true\n                }\n\n                whenever(peerManager.peersCount).thenReturn(2)\n                whenever(peerManager.readyPears()).thenReturn(listOf(readyPeer))\n                whenever(initialBlockDownload.syncedPeers).thenReturn(syncedPeers)\n            }\n\n            it(\"returns nothing\") {\n                transactionSender.canSendTransaction()\n            }\n        }\n\n        context(\"when 1 ready and 2 synced peers\") {\n            beforeEach {\n                val readyPeer = mock<Peer> {\n                    on { host } doReturn \"0.0.0.1\"\n                    on { ready } doReturn true\n                    on { synced } doReturn true\n                }\n\n                val syncedPeer = mock<Peer> {\n                    on { host } doReturn \"0.0.0.2\"\n                    on { ready } doReturn false\n                    on { synced } doReturn true\n                }\n\n                val syncedPeers = CopyOnWriteArrayList<Peer>().apply {\n                    add(readyPeer)\n                    add(syncedPeer)\n                }\n\n\n                whenever(peerManager.peersCount).thenReturn(2)\n                whenever(peerManager.readyPears()).thenReturn(listOf(readyPeer))\n                whenever(initialBlockDownload.syncedPeers).thenReturn(syncedPeers)\n            }\n\n            it(\"returns nothing\") {\n                transactionSender.canSendTransaction()\n            }\n        }\n    }\n})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/transactions/TransactionSizeCalculatorTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions\n\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType.P2PK\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType.P2PKH\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType.P2SH\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType.P2WPKH\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType.P2WPKHSH\nimport org.junit.Assert.assertEquals\nimport org.junit.Test\n\nclass TransactionSizeCalculatorTest {\n    private val calculator = TransactionSizeCalculator()\n\n    private fun outputs(scriptTypes: List<ScriptType>): List<TransactionOutput> {\n        return scriptTypes.map { TransactionOutput().apply { this.scriptType = it } }\n    }\n\n    @Test\n    fun testTransactionSize() {\n        assertEquals(10, calculator.transactionSize(listOf(), listOf(), 0))\n        assertEquals(192, calculator.transactionSize(outputs(listOf(P2PKH)), listOf(P2PKH), 0))\n        assertEquals(306, calculator.transactionSize(outputs(listOf(P2PKH, P2PK)), listOf(P2PKH), 0))\n        assertEquals(303, calculator.transactionSize(outputs(listOf(P2PKH, P2PK)), listOf(P2WPKH), 0))\n        assertEquals(350, calculator.transactionSize(outputs(listOf(P2PKH, P2PK)), listOf(P2PKH, P2PK), 0))\n\n        assertEquals(113, calculator.transactionSize(outputs(listOf(P2WPKH)), listOf(P2PKH), 0))\n        assertEquals(136, calculator.transactionSize(outputs(listOf(P2WPKHSH)), listOf(P2PKH), 0))\n        assertEquals(261, calculator.transactionSize(outputs(listOf(P2WPKH, P2PKH)), listOf(P2PKH), 0))\n    }\n\n    @Test\n    fun testInputSize() {\n        assertEquals(148, calculator.inputSize(P2PKH))\n        assertEquals(114, calculator.inputSize(P2PK))\n        assertEquals(41, calculator.inputSize(P2WPKH))\n        assertEquals(64, calculator.inputSize(P2WPKHSH))\n    }\n\n    @Test\n    fun testOutputSize() {\n        assertEquals(34, calculator.outputSize(P2PKH))\n        assertEquals(32, calculator.outputSize(P2SH))\n        assertEquals(44, calculator.outputSize(P2PK))\n        assertEquals(31, calculator.outputSize(P2WPKH))\n        assertEquals(32, calculator.outputSize(P2WPKHSH))\n    }\n}\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/transactions/TransactionSyncerTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions\n\nimport com.nhaarman.mockitokotlin2.*\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.managers.BloomFilterManager\nimport io.horizontalsystems.bitcoincore.managers.PublicKeyManager\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport org.mockito.Mockito.mock\nimport org.mockito.Mockito.reset\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nobject TransactionSyncerTest : Spek({\n    lateinit var syncer: TransactionSyncer\n\n    val storage = mock(IStorage::class.java)\n    val transactionProcessor = mock(PendingTransactionProcessor::class.java)\n    val publicKeyManager = mock(PublicKeyManager::class.java)\n    val invalidator = mock(TransactionInvalidator::class.java)\n\n    val fullTransaction = mock(FullTransaction::class.java)\n    val transaction = mock(Transaction::class.java)\n\n    beforeEachTest {\n        whenever(transaction.hash).thenReturn(byteArrayOf(1, 2, 3))\n        whenever(fullTransaction.header).thenReturn(transaction)\n\n        syncer = TransactionSyncer(storage, transactionProcessor, invalidator, publicKeyManager)\n    }\n\n    afterEachTest {\n        reset(storage, transactionProcessor, publicKeyManager)\n    }\n\n    describe(\"handleTransactions\") {\n        context(\"when empty array is given\") {\n            it(\"doesn't do anything\") {\n                syncer.handleRelayed(listOf())\n\n                verify(transactionProcessor, never()).processReceived(any(), any())\n                verify(publicKeyManager, never()).fillGap()\n            }\n        }\n\n        context(\"when not empty array is given\") {\n            val transactions = listOf(fullTransaction)\n\n            context(\"when need to update bloom filter\") {\n                beforeEach {\n                    whenever(transactionProcessor.processReceived(eq(transactions), eq(false)))\n                            .thenThrow(BloomFilterManager.BloomFilterExpired)\n                }\n\n                it(\"fills addresses gap and regenerates bloom filter\") {\n                    syncer.handleRelayed(transactions)\n\n                    verify(transactionProcessor).processReceived(eq(transactions), eq(false))\n                    verify(publicKeyManager).fillGap()\n                }\n            }\n\n            context(\"when don't need to update bloom filter\") {\n                it(\"doesn't run address fillGap and doesn't regenerate bloom filter\") {\n                    syncer.handleRelayed(transactions)\n\n                    verify(transactionProcessor).processReceived(eq(transactions), eq(false))\n                    verify(publicKeyManager, never()).fillGap()\n                }\n            }\n        }\n    }\n\n})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/transactions/builder/InputSignerTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions.builder\n\nimport com.nhaarman.mockitokotlin2.any\nimport com.nhaarman.mockitokotlin2.whenever\nimport io.horizontalsystems.bitcoincore.extensions.hexToByteArray\nimport io.horizontalsystems.bitcoincore.extensions.toHexString\nimport io.horizontalsystems.bitcoincore.models.PublicKey\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.models.TransactionInput\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.network.Network\nimport io.horizontalsystems.bitcoincore.storage.InputToSign\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport io.horizontalsystems.bitcoincore.transactions.scripts.Sighash\nimport io.horizontalsystems.hdwalletkit.HDKey\nimport io.horizontalsystems.hdwalletkit.HDWallet\nimport org.junit.jupiter.api.Assertions.assertEquals\nimport org.junit.jupiter.api.assertThrows\nimport org.mockito.Mockito.anyBoolean\nimport org.mockito.Mockito.mock\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nobject InputSignerTest : Spek({\n\n//    lateinit var inputSigner: InputSigner\n//\n//    val publicKey = mock(PublicKey::class.java)\n//    val inputToSign = mock(InputToSign::class.java)\n//    val transactionOutput = mock(TransactionOutput::class.java)\n//    val transactionInput = mock(TransactionInput::class.java)\n//    val transaction = mock(Transaction::class.java)\n//\n//    val network = mock(Network::class.java)\n//    val hdWallet = mock(HDWallet::class.java)\n//    val privateKey = mock(HDKey::class.java)\n//\n//    val derEncodedSignature = \"abc\".hexToByteArray()\n//\n//    beforeEachTest {\n//        whenever(inputToSign.previousOutputPublicKey).thenReturn(publicKey)\n//\n//        whenever(publicKey.publicKey).thenReturn(byteArrayOf(1, 2, 3))\n//        whenever(privateKey.createSignature(any())).thenReturn(derEncodedSignature)\n//        whenever(hdWallet.privateKey(any(), any(), anyBoolean())).thenReturn(privateKey)\n//        whenever(network.sigHashForked).thenReturn(false)\n//        whenever(network.sigHashValue).thenReturn(Sighash.ALL)\n//\n//        inputSigner = InputSigner(hdWallet, network)\n//    }\n//\n//    describe(\"when no private key\") {\n//        beforeEach {\n//            whenever(hdWallet.privateKey(any(), any(), anyBoolean())).thenReturn(null)\n//        }\n//\n//        it(\"throws an exception NoPrivateKey\") {\n//            assertThrows<InputSigner.Error.NoPrivateKey> {\n//                inputSigner.sigScriptData(transaction, listOf(inputToSign), listOf(transactionOutput), 0)\n//            }\n//        }\n//    }\n//\n//    describe(\"when private key exist\") {\n//        val lockingScript = \"76a914e4de5d630c5cacd7af96418a8f35c411c8ff3c0688ac\".hexToByteArray()\n//        val expectedSignature = derEncodedSignature.toHexString() + \"01\"\n//\n//        beforeEach {\n//            whenever(hdWallet.privateKey(any(), any(), anyBoolean())).thenReturn(privateKey)\n//\n//            whenever(transactionOutput.lockingScript).thenReturn(lockingScript)\n//            whenever(transactionOutput.transactionHash).thenReturn(byteArrayOf(1, 2, 3))\n//            whenever(transactionOutput.scriptType).thenReturn(ScriptType.P2PKH)\n//\n//            whenever(inputToSign.previousOutput).thenReturn(transactionOutput)\n//            whenever(inputToSign.input).thenReturn(transactionInput)\n//        }\n//\n//        it(\"signs data\") {\n//            val resultSignature = inputSigner.sigScriptData(transaction, listOf(inputToSign), listOf(transactionOutput), 0)\n//\n//            assertEquals(2, resultSignature.size)\n//            assertEquals(expectedSignature, resultSignature[0].toHexString())\n//            assertEquals(inputToSign.previousOutputPublicKey.publicKey, resultSignature[1])\n//        }\n//\n//        it(\"signs P2PK\") {\n//            whenever(transactionOutput.scriptType).thenReturn(ScriptType.P2PK)\n//            val resultSignature = inputSigner.sigScriptData(transaction, listOf(inputToSign), listOf(transactionOutput), 0)\n//\n//            assertEquals(1, resultSignature.size)\n//            assertEquals(expectedSignature, resultSignature[0].toHexString())\n//        }\n//\n//        it(\"signs P2WPKH\") {\n//            whenever(transactionOutput.scriptType).thenReturn(ScriptType.P2WPKH)\n//            whenever(transactionOutput.lockingScriptPayload).thenReturn(byteArrayOf(1, 2, 3))\n//\n//            val resultSignature = inputSigner.sigScriptData(transaction, listOf(inputToSign), listOf(transactionOutput), 0)\n//\n//            assertEquals(2, resultSignature.size)\n//            assertEquals(expectedSignature, resultSignature[0].toHexString())\n//            assertEquals(inputToSign.previousOutputPublicKey.publicKey, resultSignature[1])\n//        }\n//    }\n})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/transactions/scripts/ScriptTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.transactions.scripts\n\nimport io.horizontalsystems.bitcoincore.extensions.hexToByteArray\nimport org.junit.Assert.assertEquals\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nobject ScriptTest : Spek({\n    lateinit var script: Script\n\n    describe(\"init\") {\n\n        it(\"scriptSig\") {\n            script = Script(\"47304402202b4da291cc39faf8433911988f9f49fc5c995812ca2f94db61468839c228c3e90220628bff3ff32ec95825092fa051cba28558a981fcf59ce184b14f2e215e69106701410414b38f4be3bb9fa0f4f32b74af07152b2f2f630bc02122a491137b6c523e46f18a0d5034418966f93dfc37cc3739ef7b2007213a302b7fba161557f4ad644a1c\".hexToByteArray())\n\n            assertEquals(\"PUSHDATA(71)[304402202b4da291cc39faf8433911988f9f49fc5c995812ca2f94db61468839c228c3e90220628bff3ff32ec95825092fa051cba28558a981fcf59ce184b14f2e215e69106701] PUSHDATA(65)[0414b38f4be3bb9fa0f4f32b74af07152b2f2f630bc02122a491137b6c523e46f18a0d5034418966f93dfc37cc3739ef7b2007213a302b7fba161557f4ad644a1c]\", script.toString())\n        }\n\n\n        it(\"payToPKH\") {\n            script = Script(\"76a91433e81a941e64cda12c6a299ed322ddbdd03f8d0e88ac\".hexToByteArray())\n\n            assertEquals(\"DUP HASH160 PUSHDATA(20)[33e81a941e64cda12c6a299ed322ddbdd03f8d0e] EQUALVERIFY CHECKSIG\", script.toString())\n        }\n\n\n        it(\"payToPK\") {\n            script = Script(\"210378e9dc79ff921df6c3f94d440fb011c65ed03586b1dd01c317934a4f00f251e6ac\".hexToByteArray())\n\n            assertEquals(\"PUSHDATA(33)[0378e9dc79ff921df6c3f94d440fb011c65ed03586b1dd01c317934a4f00f251e6] CHECKSIG\", script.toString())\n        }\n\n\n        it(\"payToWPKH\") {\n            script = Script(\"0014799d283e7f92af1dd242bf4eea513c6efd117de2\".hexToByteArray())\n\n            assertEquals(\"0[] PUSHDATA(20)[799d283e7f92af1dd242bf4eea513c6efd117de2]\", script.toString())\n        }\n\n\n        it(\"payToWSH\") {\n            script = Script(\"0020a99d08fbec6958f4d4a3776c3728ec448934d25fe1142054b8b68bac866dfc3a\".hexToByteArray())\n\n            assertEquals(\"0[] PUSHDATA(32)[a99d08fbec6958f4d4a3776c3728ec448934d25fe1142054b8b68bac866dfc3a]\", script.toString())\n        }\n\n\n        it(\"payToSH\") {\n            script = Script(\"a9144b60b6bcf50bf637fe66c3da5c11524cb3ab971187\".hexToByteArray())\n\n            assertEquals(\"HASH160 PUSHDATA(20)[4b60b6bcf50bf637fe66c3da5c11524cb3ab9711] EQUAL\", script.toString())\n        }\n\n\n        it(\"payFrom_PKH\") {\n            val pk = \"03b1ae868b76e84f8ae1cb3ad958653d8a23b444e8639c0c0f00e8de27541cb977\"\n            script = Script(\"4830450221009b1fc7f43826c4c61bb0bc7c9f667c7383f728647563f19f75db6ca701f4326f02205bc4dd125ffe4e903a59c87ed4362abb86acbf2b1e6fd8021cfc6d4f8384b0e50121$pk\".hexToByteArray())\n\n            assertEquals(\"PUSHDATA(72)[30450221009b1fc7f43826c4c61bb0bc7c9f667c7383f728647563f19f75db6ca701f4326f02205bc4dd125ffe4e903a59c87ed4362abb86acbf2b1e6fd8021cfc6d4f8384b0e501] PUSHDATA(33)[03b1ae868b76e84f8ae1cb3ad958653d8a23b444e8639c0c0f00e8de27541cb977]\", script.toString())\n        }\n\n        // tx: 761cc7102efe24f4353ae7dc816fbed5e15963d11ca93e36449d521bda21ac4d\n        it(\"payFrom_SH\") {\n            script = Script(\"004830450221008c203a0881f75c731d9a3a2e6d2ffa37da7095b7dde61a9e7a906659219cd0fa02202677097ca7f7e164f73924fe8f84e1e6fc6611450efcda360ce771e98af9f73d0147304402201cba9b641483476f67a4cef08d7280f51de8d7615fcce76642d944dc07132a990220323d13175477bbf67c8c36fb243bec0e4c410bc9173a186d9f8e98ce3445363601475221025b64f7c63e30f315259393f64dcca269d18386997b1cc93da1388c4021e3ea8e210386d42d5d7027ac08ddcbb066e2140575091fe7dc1d202a008eb5e036725e975652ae\".hexToByteArray())\n\n            assertEquals(\"0[] PUSHDATA(72)[30450221008c203a0881f75c731d9a3a2e6d2ffa37da7095b7dde61a9e7a906659219cd0fa02202677097ca7f7e164f73924fe8f84e1e6fc6611450efcda360ce771e98af9f73d01] PUSHDATA(71)[304402201cba9b641483476f67a4cef08d7280f51de8d7615fcce76642d944dc07132a990220323d13175477bbf67c8c36fb243bec0e4c410bc9173a186d9f8e98ce3445363601] PUSHDATA(71)[5221025b64f7c63e30f315259393f64dcca269d18386997b1cc93da1388c4021e3ea8e210386d42d5d7027ac08ddcbb066e2140575091fe7dc1d202a008eb5e036725e975652ae]\", script.toString())\n        }\n\n\n        it(\"payFrom_WPKH\") {\n            script = Script(\"483045022100df7b7e5cda14ddf91290e02ea10786e03eb11ee36ec02dd862fe9a326bbcb7fd02203f5b4496b667e6e281cc654a2da9e4f08660c620a1051337fa8965f727eb19190121038262a6c6cec93c2d3ecd6c6072efea86d02ff8e3328bbd0242b20af3425990ac\".hexToByteArray())\n            assertEquals(\"PUSHDATA(72)[3045022100df7b7e5cda14ddf91290e02ea10786e03eb11ee36ec02dd862fe9a326bbcb7fd02203f5b4496b667e6e281cc654a2da9e4f08660c620a1051337fa8965f727eb191901] PUSHDATA(33)[038262a6c6cec93c2d3ecd6c6072efea86d02ff8e3328bbd0242b20af3425990ac]\", script.toString())\n        }\n    }\n})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/utils/AddressConverterTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.utils\n\nimport io.horizontalsystems.bitcoincore.exceptions.AddressFormatException\nimport io.horizontalsystems.bitcoincore.extensions.hexToByteArray\nimport io.horizontalsystems.bitcoincore.extensions.toHexString\nimport io.horizontalsystems.bitcoincore.models.Address\nimport io.horizontalsystems.bitcoincore.models.AddressType\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport org.junit.Assert.assertArrayEquals\nimport org.junit.Assert.assertEquals\nimport org.junit.jupiter.api.assertThrows\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nobject AddressConverterTest : Spek({\n\n    lateinit var converter: IAddressConverter\n    lateinit var bytes: ByteArray\n    lateinit var addressString: String\n    lateinit var address: Address\n\n//    describe(\"parse\") {\n//\n//        it(\"p2pkh\") {\n//            converter = Base58AddressConverter(0, 5)\n//\n//            bytes = \"e34cce70c86373273efcc54ce7d2a491bb4a0e84\".hexToByteArray()\n//            addressString = \"1MirQ9bwyQcGVJPwKUgapu5ouK2E2Ey4gX\"\n//            address = converter.convert(bytes, ScriptType.P2PKH)\n//\n//            assertEquals(addressString, address.string)\n//            assertEquals(AddressType.P2PKH, address.type)\n//\n//            // TestNet\n//            converter = Base58AddressConverter(111, 196)\n//\n//            bytes = \"78b316a08647d5b77283e512d3603f1f1c8de68f\".hexToByteArray()\n//            addressString = \"mrX9vMRYLfVy1BnZbc5gZjuyaqH3ZW2ZHz\"\n//            address = converter.convert(bytes, ScriptType.P2PKH)\n//\n//            assertEquals(addressString, address.string)\n//            assertEquals(AddressType.P2PKH, address.type)\n//\n//            // Wrong prefix\n//            assertThrows<AddressFormatException> {\n//                val testnetAddress = addressString\n//\n//                converter = Base58AddressConverter(9, 5)\n//                address = converter.convert(testnetAddress)\n//            }\n//        }\n//\n//        it(\"p2pkh_cash\") {\n//            converter = CashAddressConverter(\"bitcoincash\")\n//\n//            // MainNet\n//            bytes = \"F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9\".hexToByteArray()\n//            addressString = \"bitcoincash:qr6m7j9njldwwzlg9v7v53unlr4jkmx6eylep8ekg2\"\n//            address = converter.convert(bytes, ScriptType.P2PKH)\n//\n//            assertEquals(addressString, address.string)\n//\n//            // TestNet\n//            converter = CashAddressConverter(\"bchtest\")\n//\n//            bytes = \"F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9\".hexToByteArray()\n//            addressString = \"bchtest:pr6m7j9njldwwzlg9v7v53unlr4jkmx6eyvwc0uz5t\"\n//            address = converter.convert(bytes, ScriptType.P2SH)\n//\n//            assertEquals(addressString, address.string)\n//        }\n//\n//        it(\"p2pkh_cashString\") {\n//            converter = CashAddressConverter(\"bitcoincash\")\n//\n//            bytes = \"f5bf48b397dae70be82b3cca4793f8eb2b6cdac9\".hexToByteArray()\n//            addressString = \"bitcoincash:qr6m7j9njldwwzlg9v7v53unlr4jkmx6eylep8ekg2\"\n//            address = converter.convert(addressString)\n//\n//            assertArrayEquals(bytes, address.hash)\n//        }\n//\n//        it(\"p2pkh_cashString_withoutPrefix\") {\n//            converter = CashAddressConverter(\"bitcoincash\")\n//\n//            bytes = \"f5bf48b397dae70be82b3cca4793f8eb2b6cdac9\".hexToByteArray()\n//            addressString = \"qr6m7j9njldwwzlg9v7v53unlr4jkmx6eylep8ekg2\"\n//            address = converter.convert(addressString)\n//\n//            assertArrayEquals(bytes, address.hash)\n//        }\n//\n//        it(\"p2pkh_string\") {\n//            converter = Base58AddressConverter(0, 5)\n//\n//            bytes = \"e34cce70c86373273efcc54ce7d2a491bb4a0e84\".hexToByteArray()\n//            addressString = \"1MirQ9bwyQcGVJPwKUgapu5ouK2E2Ey4gX\"\n//            address = converter.convert(addressString)\n//\n//            assertEquals(AddressType.P2PKH, address.type)\n//            assertArrayEquals(bytes, address.hash)\n//        }\n//\n//        it(\"p2sh\") {\n//            converter = Base58AddressConverter(0, 5)\n//\n//            bytes = \"f815b036d9bbbce5e9f2a00abd1bf3dc91e95510\".hexToByteArray()\n//            addressString = \"3QJmV3qfvL9SuYo34YihAf3sRCW3qSinyC\"\n//            address = converter.convert(bytes, ScriptType.P2SH)\n//\n//            assertEquals(AddressType.P2SH, address.type)\n//            assertEquals(addressString, address.string)\n//        }\n//\n//        it(\"p2sh_string\") {\n//            converter = Base58AddressConverter(0, 5)\n//\n//            bytes = \"f815b036d9bbbce5e9f2a00abd1bf3dc91e95510\".hexToByteArray()\n//            addressString = \"3QJmV3qfvL9SuYo34YihAf3sRCW3qSinyC\"\n//            address = converter.convert(addressString)\n//\n//            assertEquals(AddressType.P2SH, address.type)\n//            assertArrayEquals(bytes, address.hash)\n//        }\n//\n//        it(\"p2wpkh\") {\n//            converter = SegwitAddressConverter(\"bc\")\n//\n//            addressString = \"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4\"\n//            bytes = \"0014751e76e8199196d454941c45d1b3a323f1433bd6\".hexToByteArray()\n//            address = converter.convert(bytes, ScriptType.P2WPKH)\n//\n//            assertEquals(AddressType.WITNESS, address.type)\n//            assertEquals(addressString, address.string)\n//            assertEquals(\"751e76e8199196d454941c45d1b3a323f1433bd6\", address.hash.toHexString())\n//        }\n//\n//        it(\"p2wpkh_string\") {\n//            converter = SegwitAddressConverter(\"bc\")\n//\n//            bytes = \"751e76e8199196d454941c45d1b3a323f1433bd6\".hexToByteArray()\n//            addressString = \"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4\"\n//            address = converter.convert(addressString)\n//\n//            assertEquals(AddressType.WITNESS, address.type)\n//            assertEquals(addressString, address.string)\n//            assertArrayEquals(bytes, address.hash)\n//        }\n//\n//    }\n})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/utils/Bip69Test.kt",
    "content": "package io.horizontalsystems.bitcoincore.utils\n\nimport com.nhaarman.mockitokotlin2.doReturn\nimport com.nhaarman.mockitokotlin2.mock\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\nimport org.junit.jupiter.api.Assertions\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\nimport java.util.*\n\nclass Bip69Test : Spek({\n\n    describe(\"sort two outputs\") {\n\n        it(\"sort by amount\") {\n            val outputWithBigAmount = TransactionOutput().apply {\n                lockingScriptPayload = \"76a9144a5fba237213a062f6f57978f796390bdcf8d01588ac\".toByteArray()\n                value = 140\n            }\n            val outputWithSmallAmount = TransactionOutput().apply {\n                lockingScriptPayload = \"76a9145be32612930b8323add2212a4ec03c1562084f8488ac\".toByteArray()\n                value = 12\n            }\n\n            val expected = mutableListOf(outputWithSmallAmount, outputWithBigAmount)\n            val list = mutableListOf(outputWithBigAmount, outputWithSmallAmount)\n            Collections.sort(list, Bip69.outputComparator)\n            Assertions.assertEquals(expected, list)\n        }\n\n        it(\"amount are equal, sort by hashes\") {\n            val outputHashA = TransactionOutput().apply {\n                lockingScriptPayload = \"76a9144a5fba237213a062f6f57978f796390bdcf8d01588ac\".toByteArray()\n                value = 12\n            }\n            val outputHashB = TransactionOutput().apply {\n                lockingScriptPayload = \"76a9145be32612930b8323add2212a4ec03c1562084f8488ac\".toByteArray()\n                value = 12\n            }\n\n            val expected = mutableListOf(outputHashA, outputHashB)\n            val list = mutableListOf(outputHashA, outputHashB)\n            Collections.sort(list, Bip69.outputComparator)\n            Assertions.assertEquals(expected, list)\n        }\n\n        it(\"amount are equal, sort by hashes with different length\") {\n            val outputHashA = TransactionOutput().apply {\n                lockingScriptPayload = \"76a9144a5fba237213a062f6f57978f796390bdcf8d01588ac\".toByteArray()\n                value = 12\n            }\n            val outputHashB = TransactionOutput().apply {\n                lockingScriptPayload = \"76a9144a5fba237213a062f6f57978f7\".toByteArray()\n                value = 12\n            }\n\n            val expected = mutableListOf(outputHashB, outputHashA)\n            val list = mutableListOf(outputHashA, outputHashB)\n            Collections.sort(list, Bip69.outputComparator)\n            Assertions.assertEquals(expected, list)\n        }\n\n        it(\"sort by hashes\") {\n            val outputHashA = TransactionOutput().apply {\n                lockingScriptPayload = \"3d8ed454f4fcc03ba35568aa37528748e56c0142\".toByteArray()\n                value = 12\n            }\n            val outputHashB = TransactionOutput().apply {\n                lockingScriptPayload = \"e191794cbc83dfaabe399af396904dd22b721ce2\".toByteArray()\n                value = 12\n            }\n\n            val expected = mutableListOf(outputHashA, outputHashB)\n            val list = mutableListOf(outputHashB, outputHashA)\n            Collections.sort(list, Bip69.outputComparator)\n            Assertions.assertEquals(expected, list)\n        }\n    }\n\n    describe(\"sort two inputs\") {\n\n        it(\"sort by hash\") {\n            val unspentOutput1 = mock<UnspentOutput> {\n                val transactionOutput = mock<TransactionOutput> {\n                    on { transactionHash } doReturn \"76a9144a5fba237213a062f6f57978f796390bdcf8d01588ac\".toByteArray()\n                }\n\n                on { output } doReturn transactionOutput\n            }\n\n            val unspentOutput2 = mock<UnspentOutput> {\n                val transactionOutput = mock<TransactionOutput> {\n                    on { transactionHash } doReturn \"76a9145be32612930b8323add2212a4ec03c1562084f8488ac\".toByteArray()\n                }\n\n                on { output } doReturn transactionOutput\n            }\n\n            val expected = mutableListOf(unspentOutput1, unspentOutput2)\n            val list = mutableListOf(unspentOutput1, unspentOutput2)\n\n            Collections.sort(list, Bip69.inputComparator)\n\n            Assertions.assertEquals(expected, list)\n        }\n\n        it(\"sort by index\") {\n            val unspentOutput1 = mock<UnspentOutput> {\n                val transactionOutput = mock<TransactionOutput> {\n                    on { index } doReturn 1\n                    on { transactionHash } doReturn \"76a9144a5fba237213a062f6f57978f796390bdcf8d01588ac\".toByteArray()\n                }\n\n                on { output } doReturn transactionOutput\n            }\n\n            val unspentOutput2 = mock<UnspentOutput> {\n                val transactionOutput = mock<TransactionOutput> {\n                    on { index } doReturn 0\n                    on { transactionHash } doReturn \"76a9144a5fba237213a062f6f57978f796390bdcf8d01588ac\".toByteArray()\n                }\n\n                on { output } doReturn transactionOutput\n            }\n\n            val expected = mutableListOf(unspentOutput2, unspentOutput1)\n            val list = mutableListOf(unspentOutput1, unspentOutput2)\n\n            Collections.sort(list, Bip69.inputComparator)\n\n            Assertions.assertEquals(expected, list)\n        }\n\n    }\n\n})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/utils/CashAddressConverterTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.utils\n\nimport io.horizontalsystems.bitcoincore.exceptions.AddressFormatException\nimport io.horizontalsystems.bitcoincore.extensions.hexToByteArray\nimport io.horizontalsystems.bitcoincore.models.Address\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport org.junit.Assert.assertEquals\nimport org.junit.Assert.fail\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nobject CashAddressConverterTest : Spek({\n\n    lateinit var converter: CashAddressConverter\n    lateinit var address: Address\n\n    val hrp = \"bitcoincash\"\n\n    fun hashToAddress(hash: String, hrp: String, string: String, type: ScriptType) {\n        converter = CashAddressConverter(hrp)\n        address = converter.convert(hash.hexToByteArray(), type)\n\n//        assertEquals(string, address.string)\n    }\n\n    fun stringToAddress(addressString: String) {\n        try {\n            converter.convert(addressString)\n            fail(\"Expected an Exception to be thrown\")\n        } catch (e: AddressFormatException) {\n\n        } catch (e: Exception) {\n            fail(\"Expected an AddressFormatException to be thrown\")\n        }\n    }\n\n    describe(\"CashAddressConverter\") {\n\n        it(\"convert_strings\") {\n            converter = CashAddressConverter(hrp)\n\n            // empty string\n            stringToAddress(\"\")\n            // invalid upper and lower case at the same time \"Q\" \"zdvr2hn0xrz99fcp6hkjxzk848rjvvhgytv4fket8\"\n            stringToAddress(\"bitcoincash:Qzdvr2hn0xrz99fcp6hkjxzk848rjvvhgytv4fket8\")\n            // no prefix\n            // stringToAddress(\"qr6m7j9njldwwzlg9v7v53unlr4jkmx6eylep8ekg2\")\n            // invalid prefix \"bitcoincash012345\"\n            stringToAddress(\"bitcoincash012345:qzdvr2hn0xrz99fcp6hkjxzk848rjvvhgytv4fket8\")\n            // invalid character \"1\"\n            stringToAddress(\"bitcoincash:111112hn0xrz99fcp6hkjxzk848rjvvhgytv411111\")\n            // unexpected character \"💦😆\"\n            stringToAddress(\"bitcoincash:qzdvr2hn0xrz99fcp6hkjxzk848rjvvhgytv4fket8💦😆\")\n            // invalid checksum\n            stringToAddress(\"bitcoincash:zzzzz2hn0xrz99fcp6hkjxzk848rjvvhgytv4zzzzz\")\n        }\n\n        it(\"convert_bytes\") {\n\n            // The following test cases are from the spec about cashaddr\n            // https://github.com/bitcoincashorg/bitcoincash.org/blob/master/spec/cashaddr.md\n\n            hashToAddress(\"F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9\", \"bitcoincash\", \"bitcoincash:qr6m7j9njldwwzlg9v7v53unlr4jkmx6eylep8ekg2\", ScriptType.P2PKH)\n\n            hashToAddress(\"F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9\", \"bitcoincash\", \"bitcoincash:qr6m7j9njldwwzlg9v7v53unlr4jkmx6eylep8ekg2\", ScriptType.P2PKH)\n            hashToAddress(\"F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9\", \"bchtest\", \"bchtest:pr6m7j9njldwwzlg9v7v53unlr4jkmx6eyvwc0uz5t\", ScriptType.P2SH)\n            hashToAddress(\"F5BF48B397DAE70BE82B3CCA4793F8EB2B6CDAC9\", \"pref\", \"pref:pr6m7j9njldwwzlg9v7v53unlr4jkmx6ey65nvtks5\", ScriptType.P2SH)\n\n            hashToAddress(\"7ADBF6C17084BC86C1706827B41A56F5CA32865925E946EA\", \"bitcoincash\", \"bitcoincash:q9adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2ws4mr9g0\", ScriptType.P2PKH)\n            hashToAddress(\"7ADBF6C17084BC86C1706827B41A56F5CA32865925E946EA\", \"bchtest\", \"bchtest:p9adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2u94tsynr\", ScriptType.P2SH)\n            hashToAddress(\"7ADBF6C17084BC86C1706827B41A56F5CA32865925E946EA\", \"pref\", \"pref:p9adhakpwzztepkpwp5z0dq62m6u5v5xtyj7j3h2khlwwk5v\", ScriptType.P2SH)\n\n            hashToAddress(\"3A84F9CF51AAE98A3BB3A78BF16A6183790B18719126325BFC0C075B\", \"bitcoincash\", \"bitcoincash:qgagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkcw59jxxuz\", ScriptType.P2PKH)\n            hashToAddress(\"3A84F9CF51AAE98A3BB3A78BF16A6183790B18719126325BFC0C075B\", \"bchtest\", \"bchtest:pgagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkcvs7md7wt\", ScriptType.P2SH)\n            hashToAddress(\"3A84F9CF51AAE98A3BB3A78BF16A6183790B18719126325BFC0C075B\", \"pref\", \"pref:pgagf7w02x4wnz3mkwnchut2vxphjzccwxgjvvjmlsxqwkcrsr6gzkn\", ScriptType.P2SH)\n\n            hashToAddress(\"3173EF6623C6B48FFD1A3DCC0CC6489B0A07BB47A37F47CFEF4FE69DE825C060\", \"bitcoincash\", \"bitcoincash:qvch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxq5nlegake\", ScriptType.P2PKH)\n            hashToAddress(\"3173EF6623C6B48FFD1A3DCC0CC6489B0A07BB47A37F47CFEF4FE69DE825C060\", \"bchtest\", \"bchtest:pvch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxq7fqng6m6\", ScriptType.P2SH)\n            hashToAddress(\"3173EF6623C6B48FFD1A3DCC0CC6489B0A07BB47A37F47CFEF4FE69DE825C060\", \"pref\", \"pref:pvch8mmxy0rtfrlarg7ucrxxfzds5pamg73h7370aa87d80gyhqxq4k9m7qf9\", ScriptType.P2SH)\n\n            hashToAddress(\"C07138323E00FA4FC122D3B85B9628EA810B3F381706385E289B0B25631197D194B5C238BEB136FB\", \"bitcoincash\", \"bitcoincash:qnq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklv39gr3uvz\", ScriptType.P2PKH)\n            hashToAddress(\"C07138323E00FA4FC122D3B85B9628EA810B3F381706385E289B0B25631197D194B5C238BEB136FB\", \"bchtest\", \"bchtest:pnq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklvmgm6ynej\", ScriptType.P2SH)\n            hashToAddress(\"C07138323E00FA4FC122D3B85B9628EA810B3F381706385E289B0B25631197D194B5C238BEB136FB\", \"pref\", \"pref:pnq8zwpj8cq05n7pytfmskuk9r4gzzel8qtsvwz79zdskftrzxtar994cgutavfklv0vx5z0w3\", ScriptType.P2SH)\n\n            hashToAddress(\"E361CA9A7F99107C17A622E047E3745D3E19CF804ED63C5C40C6BA763696B98241223D8CE62AD48D863F4CB18C930E4C\", \"bitcoincash\", \"bitcoincash:qh3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7n933jfsunqex2w82sl\", ScriptType.P2PKH)\n            hashToAddress(\"E361CA9A7F99107C17A622E047E3745D3E19CF804ED63C5C40C6BA763696B98241223D8CE62AD48D863F4CB18C930E4C\", \"bchtest\", \"bchtest:ph3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7n933jfsunqnzf7mt6x\", ScriptType.P2SH)\n            hashToAddress(\"E361CA9A7F99107C17A622E047E3745D3E19CF804ED63C5C40C6BA763696B98241223D8CE62AD48D863F4CB18C930E4C\", \"pref\", \"pref:ph3krj5607v3qlqh5c3wq3lrw3wnuxw0sp8dv0zugrrt5a3kj6ucysfz8kxwv2k53krr7n933jfsunqjntdfcwg\", ScriptType.P2SH)\n\n            hashToAddress(\"D9FA7C4C6EF56DC4FF423BAAE6D495DBFF663D034A72D1DC7D52CBFE7D1E6858F9D523AC0A7A5C34077638E4DD1A701BD017842789982041\", \"bitcoincash\", \"bitcoincash:qmvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhvw8ym5d8qx7sz7zz0zvcypqscw8jd03f\", ScriptType.P2PKH)\n            hashToAddress(\"D9FA7C4C6EF56DC4FF423BAAE6D495DBFF663D034A72D1DC7D52CBFE7D1E6858F9D523AC0A7A5C34077638E4DD1A701BD017842789982041\", \"bchtest\", \"bchtest:pmvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhvw8ym5d8qx7sz7zz0zvcypqs6kgdsg2g\", ScriptType.P2SH)\n            hashToAddress(\"D9FA7C4C6EF56DC4FF423BAAE6D495DBFF663D034A72D1DC7D52CBFE7D1E6858F9D523AC0A7A5C34077638E4DD1A701BD017842789982041\", \"pref\", \"pref:pmvl5lzvdm6km38lgga64ek5jhdl7e3aqd9895wu04fvhlnare5937w4ywkq57juxsrhvw8ym5d8qx7sz7zz0zvcypqsammyqffl\", ScriptType.P2SH)\n\n            hashToAddress(\"D0F346310D5513D9E01E299978624BA883E6BDA8F4C60883C10F28C2967E67EC77ECC7EEEAEAFC6DA89FAD72D11AC961E164678B868AEEEC5F2C1DA08884175B\", \"bitcoincash\", \"bitcoincash:qlg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5flttj6ydvjc0pv3nchp52amk97tqa5zygg96mtky5sv5w\", ScriptType.P2PKH)\n            hashToAddress(\"D0F346310D5513D9E01E299978624BA883E6BDA8F4C60883C10F28C2967E67EC77ECC7EEEAEAFC6DA89FAD72D11AC961E164678B868AEEEC5F2C1DA08884175B\", \"bchtest\", \"bchtest:plg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5flttj6ydvjc0pv3nchp52amk97tqa5zygg96mc773cwez\", ScriptType.P2SH)\n            hashToAddress(\"D0F346310D5513D9E01E299978624BA883E6BDA8F4C60883C10F28C2967E67EC77ECC7EEEAEAFC6DA89FAD72D11AC961E164678B868AEEEC5F2C1DA08884175B\", \"pref\", \"pref:plg0x333p4238k0qrc5ej7rzfw5g8e4a4r6vvzyrcy8j3s5k0en7calvclhw46hudk5flttj6ydvjc0pv3nchp52amk97tqa5zygg96mg7pj3lh8\", ScriptType.P2SH)\n        }\n    }\n})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/utils/NetworkUtilsTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.utils\n\nimport okhttp3.OkHttpClient\nimport okhttp3.Request\nimport org.junit.jupiter.api.Test\n\nimport org.junit.jupiter.api.Assertions.*\nimport java.net.URL\nimport javax.net.ssl.SSLHandshakeException\n\ninternal class NetworkUtilsTest {\n\n    // System is  Unable to find valid certification path to below URL\n    private val CERT_ERROR_TEST_URL = \"https://cashexplorer.bitcoin.com/api/sync/\"\n\n    @Test\n    fun testUnsafeHttpRequest() {\n\n        assertThrows(SSLHandshakeException::class.java) { doSafeHttpRequest() }\n        assertThrows(SSLHandshakeException::class.java) { doUrlConnectionRequest() }\n    }\n\n    private fun doSafeHttpRequest() {\n        doOkHttpRequest(OkHttpClient.Builder().build())\n    }\n\n    private fun doUrlConnectionRequest() {\n        URL(CERT_ERROR_TEST_URL)\n                .openConnection()\n                .apply {\n                    connectTimeout = 5000\n                    readTimeout = 60000\n                    setRequestProperty(\"Accept\", \"application/json\")\n                }.getInputStream()\n                .use {\n                    //Success\n                }\n    }\n\n    private fun doOkHttpRequest(httpClient: OkHttpClient) {\n        val request = Request.Builder().url(CERT_ERROR_TEST_URL).build()\n\n        return httpClient.newCall(request)\n                .execute()\n                .use {\n                    //success\n                }\n    }\n}"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/utils/PaymentAddressParserTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.utils\n\nimport io.horizontalsystems.bitcoincore.models.BitcoinPaymentData\nimport org.junit.Assert.assertEquals\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nobject PaymentAddressParserTest : Spek({\n\n    lateinit var addressParser: PaymentAddressParser\n\n    fun checkPaymentData(addressParser: PaymentAddressParser, paymentAddress: String, paymentData: BitcoinPaymentData) {\n        val bitcoinPaymentData = addressParser.parse(paymentAddress)\n        assertEquals(bitcoinPaymentData, paymentData)\n    }\n\n    describe(\"PaymentAddressParser\") {\n\n        it(\"parse_BitcoinPaymentAddress\") {\n            addressParser = PaymentAddressParser(\"bitcoin\", true)\n\n            var paymentData = BitcoinPaymentData(address = \"address_data\")\n            checkPaymentData(addressParser, \"address_data\", paymentData)\n\n            // Check bitcoin addresses parsing with drop scheme if it's valid\n            paymentData = BitcoinPaymentData(address = \"address_data\")\n            checkPaymentData(addressParser, \"bitcoin:address_data\", paymentData)\n\n            // invalid scheme - need to keep scheme\n            paymentData = BitcoinPaymentData(address = \"bitcoincash:address_data\")\n            checkPaymentData(addressParser, \"bitcoincash:address_data\", paymentData)\n\n            // check parameters\n            paymentData = BitcoinPaymentData(address = \"address_data\", version = \"1.0\")\n            checkPaymentData(addressParser, \"address_data;version=1.0\", paymentData)\n\n            paymentData = BitcoinPaymentData(address = \"address_data\", version = \"1.0\", label = \"test\")\n            checkPaymentData(addressParser, \"bitcoin:address_data;version=1.0?label=test\", paymentData)\n\n            paymentData = BitcoinPaymentData(address = \"address_data\", amount = 0.01)\n            checkPaymentData(addressParser, \"bitcoin:address_data?amount=0.01\", paymentData)\n\n            paymentData = BitcoinPaymentData(address = \"address_data\", amount = 0.01, label = \"test_sender\")\n            checkPaymentData(addressParser, \"bitcoin:address_data?amount=0.01?label=test_sender\", paymentData)\n\n            paymentData = BitcoinPaymentData(address = \"address_data\", parameters = mutableMapOf(\"custom\" to \"any\"))\n            checkPaymentData(addressParser, \"bitcoin:address_data?custom=any\", paymentData)\n        }\n\n        it(\"parse_BitcoinCashPaymentAddress\") {\n            addressParser = PaymentAddressParser(\"bitcoincash\", false)\n\n            var paymentData = BitcoinPaymentData(address = \"address_data\")\n            checkPaymentData(addressParser, \"address_data\", paymentData)\n\n            // Check bitcoincash addresses parsing with keep scheme if it's valid\n            paymentData = BitcoinPaymentData(address = \"bitcoincash:address_data\")\n            checkPaymentData(addressParser, \"bitcoincash:address_data\", paymentData)\n\n            // invalid scheme - need to leave scheme\n            paymentData = BitcoinPaymentData(address = \"bitcoin:address_data\")\n            checkPaymentData(addressParser, \"bitcoin:address_data\", paymentData)\n\n            // check parameters\n            paymentData = BitcoinPaymentData(address = \"address_data\", version = \"1.0\")\n            checkPaymentData(addressParser, \"address_data;version=1.0\", paymentData)\n\n            paymentData = BitcoinPaymentData(address = \"bitcoincash:address_data\", version = \"1.0\", label = \"test\")\n            checkPaymentData(addressParser, \"bitcoincash:address_data;version=1.0?label=test\", paymentData)\n\n            paymentData = BitcoinPaymentData(address = \"bitcoincash:address_data\", amount = 0.01)\n            checkPaymentData(addressParser, \"bitcoincash:address_data?amount=0.01\", paymentData)\n\n            paymentData = BitcoinPaymentData(address = \"bitcoincash:address_data\", amount = 0.01, label = \"test_sender\")\n            checkPaymentData(addressParser, \"bitcoincash:address_data?amount=0.01?label=test_sender\", paymentData)\n\n            paymentData = BitcoinPaymentData(address = \"bitcoincash:address_data\", parameters = mutableMapOf(\"custom\" to \"any\"))\n            checkPaymentData(addressParser, \"bitcoincash:address_data?custom=any\", paymentData)\n        }\n    }\n})\n"
  },
  {
    "path": "bitcoincore/src/test/kotlin/io/horizontalsystems/bitcoincore/utils/SegwitAddressConverterTest.kt",
    "content": "package io.horizontalsystems.bitcoincore.utils\n\nimport io.horizontalsystems.bitcoincore.extensions.hexToByteArray\nimport io.horizontalsystems.bitcoincore.models.Address\nimport io.horizontalsystems.bitcoincore.models.AddressType\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport org.junit.Assert.assertArrayEquals\nimport org.junit.Assert.assertEquals\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nobject SegwitAddressConverterTest : Spek({\n\n    lateinit var converter: SegwitAddressConverter\n    lateinit var bytes: ByteArray\n    lateinit var program: ByteArray\n    lateinit var addressString: String\n    lateinit var address: Address\n\n//    describe(\"#convert\") {\n//        it(\"P2WPKH\") {\n//            addressString = \"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4\"\n//            program = \"751e76e8199196d454941c45d1b3a323f1433bd6\".hexToByteArray()\n//            bytes = \"0014\".hexToByteArray() + program\n//\n//            converter = SegwitAddressConverter(\"bc\")\n//            address = converter.convert(bytes, ScriptType.P2WPKH)\n//\n//            assertEquals(AddressType.WITNESS, address.type)\n//            assertEquals(addressString, address.string)\n//            assertArrayEquals(program, address.hash)\n//        }\n//\n//        it(\"P2WSH\") {\n//            addressString = \"tb1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3q0sl5k7\"\n//            program = \"1863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262\".hexToByteArray()\n//            bytes = \"00201863143c14c5166804bd19203356da136c985678cd4d27a1b8c6329604903262\".hexToByteArray()\n//\n//            converter = SegwitAddressConverter(\"tb\")\n//            address = converter.convert(bytes, ScriptType.P2WSH)\n//\n//            assertEquals(AddressType.WITNESS, address.type)\n//            assertEquals(addressString, address.string)\n//            assertArrayEquals(program, address.hash)\n//        }\n//\n//        it(\"witness1\") {\n//            addressString = \"bc1pw508d6qejxtdg4y5r3zarvary0c5xw7kw508d6qejxtdg4y5r3zarvary0c5xw7k7grplx\"\n//            program = \"751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6\".hexToByteArray()\n//            bytes = \"5128751e76e8199196d454941c45d1b3a323f1433bd6751e76e8199196d454941c45d1b3a323f1433bd6\".hexToByteArray()\n//\n//            converter = SegwitAddressConverter(\"bc\")\n//            address = converter.convert(bytes, ScriptType.P2WPKH)\n//\n//            assertEquals(AddressType.WITNESS, address.type)\n//            assertEquals(addressString, address.string)\n//            assertArrayEquals(program, address.hash)\n//        }\n//    }\n})\n"
  },
  {
    "path": "bitcoincore/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker",
    "content": "mock-maker-inline"
  },
  {
    "path": "bitcoinkit/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "bitcoinkit/build.gradle",
    "content": "plugins {\n    id 'com.android.library'\n    id 'kotlin-android'\n    id 'kotlin-kapt'\n    id 'maven-publish'\n}\n\nafterEvaluate {\n    publishing {\n        publications {\n            release(MavenPublication) {\n                from components.release\n            }\n        }\n    }\n}\n\nandroid {\n    namespace 'io.horizontalsystems.bitcoinkit'\n    compileSdk 34\n\n    defaultConfig {\n        minSdkVersion 23\n        targetSdkVersion 34\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    sourceSets {\n        main.java.srcDirs += 'src/main/kotlin'\n        test.java.srcDirs += 'src/test/kotlin'\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_17\n        targetCompatibility JavaVersion.VERSION_17\n    }\n    kotlinOptions { jvmTarget = '17' }\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version\"\n    implementation 'androidx.annotation:annotation:1.1.0'\n\n    // Room\n    implementation 'androidx.room:room-runtime:2.5.0'\n    implementation 'androidx.room:room-rxjava2:2.5.0'\n    kapt 'androidx.room:room-compiler:2.5.0'\n\n    api project(':bitcoincore')\n    api project(':hodler')\n\n    // Test helpers\n    testImplementation 'junit:junit:4.13.2'\n    testImplementation 'org.junit.jupiter:junit-jupiter:5.6.1'\n    testImplementation 'org.mockito:mockito-core:3.3.3'\n    testImplementation 'com.nhaarman:mockito-kotlin-kt1.1:1.6.0'\n    testImplementation 'org.powermock:powermock-api-mockito2:2.0.7'\n    testImplementation 'org.powermock:powermock-module-junit4:2.0.7'\n\n    // Spek\n    testImplementation \"org.spekframework.spek2:spek-dsl-jvm:2.0.9\"\n    testRuntimeOnly \"org.spekframework.spek2:spek-runner-junit5:2.0.9\"\n    testRuntimeOnly \"org.jetbrains.kotlin:kotlin-reflect:$kotlin_version\"\n\n    // Android Instrumentation Test\n    androidTestImplementation 'androidx.test.ext:junit:1.1.1'\n    androidTestImplementation 'com.linkedin.dexmaker:dexmaker-mockito-inline:2.28.3'\n    androidTestImplementation 'com.nhaarman:mockito-kotlin-kt1.1:1.6.0'\n}\n"
  },
  {
    "path": "bitcoinkit/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\">\n\n    <uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\" />\n</manifest>\n\n"
  },
  {
    "path": "bitcoinkit/src/main/kotlin/io/horizontalsystems/bitcoinkit/BitcoinKit.kt",
    "content": "package io.horizontalsystems.bitcoinkit\n\nimport android.content.Context\nimport android.database.sqlite.SQLiteDatabase\nimport io.horizontalsystems.bitcoincore.AbstractKit\nimport io.horizontalsystems.bitcoincore.BitcoinCore\nimport io.horizontalsystems.bitcoincore.BitcoinCore.SyncMode\nimport io.horizontalsystems.bitcoincore.BitcoinCoreBuilder\nimport io.horizontalsystems.bitcoincore.apisync.BCoinApi\nimport io.horizontalsystems.bitcoincore.apisync.BlockHashFetcher\nimport io.horizontalsystems.bitcoincore.apisync.BlockchainComApi\nimport io.horizontalsystems.bitcoincore.apisync.HsBlockHashFetcher\nimport io.horizontalsystems.bitcoincore.apisync.blockchair.BlockchairApi\nimport io.horizontalsystems.bitcoincore.apisync.blockchair.BlockchairBlockHashFetcher\nimport io.horizontalsystems.bitcoincore.apisync.blockchair.BlockchairTransactionProvider\nimport io.horizontalsystems.bitcoincore.blocks.BlockMedianTimeHelper\nimport io.horizontalsystems.bitcoincore.blocks.validators.*\nimport io.horizontalsystems.bitcoincore.core.purpose\nimport io.horizontalsystems.bitcoincore.managers.*\nimport io.horizontalsystems.bitcoincore.models.Address\nimport io.horizontalsystems.bitcoincore.models.Checkpoint\nimport io.horizontalsystems.bitcoincore.models.WatchAddressPublicKey\nimport io.horizontalsystems.bitcoincore.network.Network\nimport io.horizontalsystems.bitcoincore.storage.CoreDatabase\nimport io.horizontalsystems.bitcoincore.storage.Storage\nimport io.horizontalsystems.bitcoincore.utils.AddressConverterChain\nimport io.horizontalsystems.bitcoincore.utils.Base58AddressConverter\nimport io.horizontalsystems.bitcoincore.utils.IAddressConverter\nimport io.horizontalsystems.bitcoincore.utils.PaymentAddressParser\nimport io.horizontalsystems.bitcoincore.utils.SegwitAddressConverter\nimport io.horizontalsystems.bitcoinkit.BitcoinKit.Listener\nimport io.horizontalsystems.bitcoinkit.BitcoinKit.NetworkType\nimport io.horizontalsystems.hdwalletkit.*\nimport io.horizontalsystems.hdwalletkit.HDWallet.Purpose\nimport io.horizontalsystems.hodler.HodlerPlugin\n\n/**\n *\n *\n * The kit that connects to the Bitcoin Network and creates the Bitcoin wallet.\n * Extends from the AbstractKit class.\n * @property NetworkType The enum class type that determines which bitcoin network the kit is connects to. (MainNet, TestNet, or RegTest)\n * @property Listener Interface of BitcoinCore.Listener\n * @property bitcoinCore Reference to the BitcoinCore class.\n * @property network  The type of network that this kit is connected to. It is determined by the NetWorkType enum class.\n * @property listener Changeable variable of BitcoinCore.Listener.\n *\n */\nclass BitcoinKit : AbstractKit {\n\n    enum class NetworkType {\n        MainNet, TestNet, RegTest\n    }\n\n    interface Listener : BitcoinCore.Listener\n\n    override var bitcoinCore: BitcoinCore\n    override var network: Network\n\n    var listener: Listener? = null\n        set(value) {\n            field = value\n            bitcoinCore.listener = value\n        }\n\n    /**\n     * @constructor Creates and initializes the BitcoinKit\n     * @param context The Android context.\n     * @param words A list of words of type String.\n     * @param passphrase The passphrase to the wallet.\n     * @param walletId an arbitrary ID of type String.\n     * @param networkType The network type. The default is MainNet\n     * @param peerSize The # of peer-nodes required. The default is 10 peers.\n     * @param syncMode How the kit syncs with the blockchain. Default is SyncMode.Api().\n     * @param confirmationsThreshold How many confirmations required to be considered confirmed. Default is 6 confirmations.\n     * @param purpose which BIP algorithm to use for wallet generation. Default is BIP44.\n     */\n    constructor(\n        context: Context,\n        words: List<String>,\n        passphrase: String,\n        walletId: String,\n        networkType: NetworkType = defaultNetworkType,\n        peerSize: Int = defaultPeerSize,\n        syncMode: SyncMode = defaultSyncMode,\n        confirmationsThreshold: Int = defaultConfirmationsThreshold,\n        purpose: Purpose = Purpose.BIP44\n    ) : this(context, Mnemonic().toSeed(words, passphrase), walletId, networkType, peerSize, syncMode, confirmationsThreshold, purpose)\n\n\n    /**\n     * @constructor Creates and initializes the BitcoinKit\n     * @param context The Android context.\n     * @param seed A byte array that contains the seed.\n     * @param walletId an arbitrary ID of type String.\n     * @param networkType The network type. The default is MainNet\n     * @param peerSize The # of peer-nodes required. The default is 10 peers.\n     * @param syncMode How the kit syncs with the blockchain. Default is SyncMode.Api().\n     * @param confirmationsThreshold How many confirmations required to be considered confirmed. Default is 6 confirmations.\n     * @param purpose which BIP algorithm to use for wallet generation. Default is BIP44.\n     */\n    constructor(\n        context: Context,\n        seed: ByteArray,\n        walletId: String,\n        networkType: NetworkType = defaultNetworkType,\n        peerSize: Int = defaultPeerSize,\n        syncMode: SyncMode = defaultSyncMode,\n        confirmationsThreshold: Int = defaultConfirmationsThreshold,\n        purpose: Purpose = Purpose.BIP44\n    ) : this(context, HDExtendedKey(seed, purpose), purpose, walletId, networkType, peerSize, syncMode, confirmationsThreshold)\n\n    /**\n     * @constructor Creates and initializes the BitcoinKit\n     * @param context The Android context\n     * @param extendedKey HDExtendedKey that contains HDKey and version\n     * @param purpose Used for HDKey derivation\n     * @param walletId an arbitrary ID of type String.\n     * @param networkType The network type. The default is MainNet.\n     * @param peerSize The # of peer-nodes required. The default is 10 peers.\n     * @param syncMode How the kit syncs with the blockchain. The default is SyncMode.Api().\n     * @param confirmationsThreshold How many confirmations required to be considered confirmed. The default is 6 confirmations.\n     */\n    constructor(\n        context: Context,\n        extendedKey: HDExtendedKey,\n        purpose: Purpose,\n        walletId: String,\n        networkType: NetworkType = defaultNetworkType,\n        peerSize: Int = defaultPeerSize,\n        syncMode: SyncMode = defaultSyncMode,\n        confirmationsThreshold: Int = defaultConfirmationsThreshold\n    ) {\n        network = network(networkType)\n\n        bitcoinCore = bitcoinCore(\n            context = context,\n            extendedKey = extendedKey,\n            watchAddressPublicKey = null,\n            networkType = networkType,\n            network = network,\n            walletId = walletId,\n            syncMode = syncMode,\n            purpose = purpose,\n            peerSize = peerSize,\n            confirmationsThreshold = confirmationsThreshold\n        )\n    }\n\n    /**\n     * @constructor Creates and initializes the BitcoinKit\n     * @param context The Android context\n     * @param watchAddress address for watching in read-only mode\n     * @param walletId an arbitrary ID of type String.\n     * @param networkType The network type. The default is MainNet.\n     * @param peerSize The # of peer-nodes required. The default is 10 peers.\n     * @param syncMode How the kit syncs with the blockchain. The default is SyncMode.Api().\n     * @param confirmationsThreshold How many confirmations required to be considered confirmed. The default is 6 confirmations.\n     */\n\n    constructor(\n        context: Context,\n        watchAddress: String,\n        walletId: String,\n        networkType: NetworkType = defaultNetworkType,\n        peerSize: Int = defaultPeerSize,\n        syncMode: SyncMode = defaultSyncMode,\n        confirmationsThreshold: Int = defaultConfirmationsThreshold\n    ) {\n        network = network(networkType)\n\n        val address = parseAddress(watchAddress, network)\n        val watchAddressPublicKey = WatchAddressPublicKey(address.lockingScriptPayload, address.scriptType)\n        val purpose = address.scriptType.purpose ?: throw IllegalStateException(\"Not supported scriptType ${address.scriptType}\")\n\n        bitcoinCore = bitcoinCore(\n            context = context,\n            extendedKey = null,\n            watchAddressPublicKey = watchAddressPublicKey,\n            purpose = purpose,\n            networkType = networkType,\n            network = network,\n            walletId = walletId,\n            syncMode = syncMode,\n            peerSize = peerSize,\n            confirmationsThreshold = confirmationsThreshold\n        )\n    }\n\n    private fun bitcoinCore(\n        context: Context,\n        extendedKey: HDExtendedKey?,\n        watchAddressPublicKey: WatchAddressPublicKey?,\n        purpose: Purpose,\n        networkType: NetworkType,\n        network: Network,\n        walletId: String,\n        syncMode: SyncMode,\n        peerSize: Int,\n        confirmationsThreshold: Int\n    ): BitcoinCore {\n        val database = CoreDatabase.getInstance(context, getDatabaseName(networkType, walletId, syncMode, purpose))\n        val storage = Storage(database)\n\n        val checkpoint = Checkpoint.resolveCheckpoint(syncMode, network, storage)\n        val apiSyncStateManager = ApiSyncStateManager(storage, network.syncableFromApi && syncMode !is SyncMode.Full)\n        val apiTransactionProvider = apiTransactionProvider(networkType, syncMode, checkpoint)\n        val paymentAddressParser = PaymentAddressParser(\"bitcoin\", removeScheme = true)\n        val blockValidatorSet = blockValidatorSet(networkType, storage)\n\n        val coreBuilder = BitcoinCoreBuilder()\n        val hodlerPlugin = hodlerPlugin(storage, syncMode, coreBuilder.addressConverter)\n\n        val bitcoinCore = coreBuilder\n            .setContext(context)\n            .setExtendedKey(extendedKey)\n            .setWatchAddressPublicKey(watchAddressPublicKey)\n            .setPurpose(purpose)\n            .setNetwork(network)\n            .setCheckpoint(checkpoint)\n            .setPaymentAddressParser(paymentAddressParser)\n            .setPeerSize(peerSize)\n            .setSyncMode(syncMode)\n            .setConfirmationThreshold(confirmationsThreshold)\n            .setStorage(storage)\n            .setApiTransactionProvider(apiTransactionProvider)\n            .setApiSyncStateManager(apiSyncStateManager)\n            .setBlockValidator(blockValidatorSet)\n            .setHandleAddrMessage(false)\n            .addPlugin(hodlerPlugin)\n            .build()\n\n        //  extending bitcoinCore\n        val bech32AddressConverter = SegwitAddressConverter(network.addressSegwitHrp)\n        val base58AddressConverter = Base58AddressConverter(network.addressVersion, network.addressScriptVersion)\n\n        bitcoinCore.prependAddressConverter(bech32AddressConverter)\n\n        when (purpose) {\n            Purpose.BIP44 -> {\n                bitcoinCore.addRestoreKeyConverter(Bip44RestoreKeyConverter(base58AddressConverter))\n                bitcoinCore.addRestoreKeyConverter(hodlerPlugin)\n\n                if (extendedKey != null) {\n                    bitcoinCore.addRestoreKeyConverter(Bip49RestoreKeyConverter(base58AddressConverter))\n                    bitcoinCore.addRestoreKeyConverter(Bip84RestoreKeyConverter(bech32AddressConverter))\n                }\n            }\n\n            Purpose.BIP49 -> {\n                bitcoinCore.addRestoreKeyConverter(Bip49RestoreKeyConverter(base58AddressConverter))\n            }\n\n            Purpose.BIP84 -> {\n                bitcoinCore.addRestoreKeyConverter(Bip84RestoreKeyConverter(bech32AddressConverter))\n            }\n\n            Purpose.BIP86 -> {\n                bitcoinCore.addRestoreKeyConverter(Bip86RestoreKeyConverter(bech32AddressConverter))\n            }\n        }\n\n        return bitcoinCore\n    }\n\n    private fun parseAddress(address: String, network: Network): Address {\n        val addressConverter = AddressConverterChain().apply {\n            prependConverter(SegwitAddressConverter(network.addressSegwitHrp))\n            prependConverter(Base58AddressConverter(network.addressVersion, network.addressScriptVersion))\n        }\n        return addressConverter.convert(address)\n    }\n\n    private fun hodlerPlugin(\n        storage: Storage,\n        syncMode: SyncMode,\n        addressConverter: IAddressConverter\n    ): HodlerPlugin {\n        val blockMedianTimeHelper = BlockMedianTimeHelper(storage, approximate = syncMode is SyncMode.Blockchair)\n        return HodlerPlugin(addressConverter, storage, blockMedianTimeHelper)\n    }\n\n    private fun blockValidatorSet(\n        networkType: NetworkType,\n        storage: Storage\n    ): BlockValidatorSet {\n        val blockHelper = BlockValidatorHelper(storage)\n        val blockValidatorSet = BlockValidatorSet()\n\n        blockValidatorSet.addBlockValidator(ProofOfWorkValidator())\n\n        val blockValidatorChain = BlockValidatorChain()\n        if (networkType == NetworkType.MainNet) {\n            blockValidatorChain.add(LegacyDifficultyAdjustmentValidator(blockHelper, heightInterval, targetTimespan, maxTargetBits))\n            blockValidatorChain.add(BitsValidator())\n        } else if (networkType == NetworkType.TestNet) {\n            blockValidatorChain.add(LegacyDifficultyAdjustmentValidator(blockHelper, heightInterval, targetTimespan, maxTargetBits))\n            blockValidatorChain.add(LegacyTestNetDifficultyValidator(storage, heightInterval, targetSpacing, maxTargetBits))\n            blockValidatorChain.add(BitsValidator())\n        }\n\n        blockValidatorSet.addBlockValidator(blockValidatorChain)\n\n        return blockValidatorSet\n    }\n\n    private fun apiTransactionProvider(\n        networkType: NetworkType,\n        syncMode: SyncMode,\n        checkpoint: Checkpoint\n    ) = when (networkType) {\n        NetworkType.MainNet -> {\n            val hsBlockHashFetcher = HsBlockHashFetcher(\"https://api.blocksdecoded.com/v1/blockchains/bitcoin\")\n            if (syncMode is SyncMode.Blockchair) {\n                val blockchairApi = BlockchairApi(network.blockchairChainId)\n                val blockchairBlockHashFetcher = BlockchairBlockHashFetcher(blockchairApi)\n                val blockHashFetcher = BlockHashFetcher(hsBlockHashFetcher, blockchairBlockHashFetcher, checkpoint.block.height)\n                val blockchairProvider = BlockchairTransactionProvider(blockchairApi, blockHashFetcher)\n                blockchairProvider\n            } else {\n                BlockchainComApi(\"https://blockchain.info\", hsBlockHashFetcher)\n            }\n        }\n\n        NetworkType.TestNet -> {\n            BCoinApi(\"https://btc-testnet.blocksdecoded.com/api\")\n        }\n\n        NetworkType.RegTest -> {\n            null\n        }\n    }\n\n    companion object {\n        const val maxTargetBits: Long = 0x1d00ffff                // Maximum difficulty\n        const val targetSpacing = 10 * 60                         // 10 minutes per block.\n        const val targetTimespan: Long = 14 * 24 * 60 * 60        // 2 weeks per difficulty cycle, on average.\n        const val heightInterval = targetTimespan / targetSpacing // 2016 blocks\n\n        val defaultNetworkType: NetworkType = NetworkType.MainNet\n        val defaultSyncMode: SyncMode = SyncMode.Api()\n        const val defaultPeerSize: Int = 10\n        const val defaultConfirmationsThreshold: Int = 6\n\n        /**\n         * Gets the name of the BitcoinKit database\n         * @param networkType The network type (MAIN, TEST, or REG)\n         * @param walletId The walletID\n         * @param syncMode The SyncMode\n         * @param bip The BIP\n         * @return database name\n         */\n\n        private fun getDatabaseName(networkType: NetworkType, walletId: String, syncMode: SyncMode, purpose: Purpose): String =\n            \"Bitcoin-${networkType.name}-$walletId-${syncMode.javaClass.simpleName}-${purpose.name}\"\n\n        /**\n         * Clears the database\n         * @param context The context of the BitcoinKit.\n         * @param networkType The networkType of the BitcoinKit.\n         * @param walletId The string wallet ID of the BitcoinKit.\n         */\n        fun clear(context: Context, networkType: NetworkType, walletId: String) {\n            for (syncMode in listOf(SyncMode.Api(), SyncMode.Full(), SyncMode.Blockchair())) {\n                for (purpose in Purpose.values()) try {\n                    SQLiteDatabase.deleteDatabase(context.getDatabasePath(getDatabaseName(networkType, walletId, syncMode, purpose)))\n                } catch (ex: Exception) {\n                    continue\n                }\n            }\n        }\n\n        private fun network(networkType: NetworkType) = when (networkType) {\n            NetworkType.MainNet -> MainNet()\n            NetworkType.TestNet -> TestNet()\n            NetworkType.RegTest -> RegTest()\n        }\n\n        private fun addressConverter(purpose: Purpose, network: Network): AddressConverterChain {\n            val addressConverter = AddressConverterChain()\n            when (purpose) {\n                Purpose.BIP44,\n                Purpose.BIP49,\n                    -> {\n                    addressConverter.prependConverter(Base58AddressConverter(network.addressVersion, network.addressScriptVersion))\n                }\n                Purpose.BIP84,\n                Purpose.BIP86,\n                    -> {\n                    addressConverter.prependConverter(SegwitAddressConverter(network.addressSegwitHrp))\n                }\n            }\n\n            return addressConverter\n        }\n\n        fun firstAddress(\n            seed: ByteArray,\n            purpose: Purpose,\n            networkType: NetworkType,\n        ): Address {\n            return BitcoinCore.firstAddress(\n                seed,\n                purpose,\n                network(networkType),\n                addressConverter(purpose, network(networkType))\n            )\n        }\n\n        fun firstAddress(\n            extendedKey: HDExtendedKey,\n            purpose: Purpose,\n            networkType: NetworkType,\n        ): Address {\n            return BitcoinCore.firstAddress(\n                extendedKey,\n                purpose,\n                network(networkType),\n                addressConverter(purpose, network(networkType))\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "bitcoinkit/src/main/kotlin/io/horizontalsystems/bitcoinkit/MainNet.kt",
    "content": "package io.horizontalsystems.bitcoinkit\n\nimport io.horizontalsystems.bitcoincore.network.Network\n\n/**\n *   Extends from the abstract Network class and overrides all variables. Configures connection to the MainNet.\n */\nclass MainNet : Network() {\n\n    override var port: Int = 8333\n\n    override var magic: Long = 0xd9b4bef9L\n    override var bip32HeaderPub: Int = 0x0488B21E   // The 4 byte header that serializes in base58 to \"xpub\".\n    override var bip32HeaderPriv: Int = 0x0488ADE4  // The 4 byte header that serializes in base58 to \"xprv\"\n    override var addressVersion: Int = 0\n    override var addressSegwitHrp: String = \"bc\"\n    override var addressScriptVersion: Int = 5\n    override var coinType: Int = 0\n    override val blockchairChainId: String = \"bitcoin\"\n\n    override val maxBlockSize = 1_000_000\n    override val dustRelayTxFee = 3000 // https://github.com/bitcoin/bitcoin/blob/c536dfbcb00fb15963bf5d507b7017c241718bf6/src/policy/policy.h#L50\n\n    override var dnsSeeds = listOf(\n        \"x5.seed.bitcoin.sipa.be\",             // Pieter Wuille\n        \"x5.dnsseed.bluematt.me\",              // Matt Corallo\n        \"x5.seed.bitcoinstats.com\",            // Chris Decker\n        \"x5.seed.btc.petertodd.org\",           // Peter Todd\n        \"x5.seed.bitcoin.sprovoost.nl\",        // Sjors Provoost\n        \"x5.seed.bitnodes.io\",                 // Addy Yeow\n        \"x5.dnsseed.emzy.de\",                  // Stephan Oeste\n        \"x5.seed.bitcoin.wiz.biz\"              // Jason Maurice\n    )\n}\n"
  },
  {
    "path": "bitcoinkit/src/main/kotlin/io/horizontalsystems/bitcoinkit/RegTest.kt",
    "content": "package io.horizontalsystems.bitcoinkit\n\nimport io.horizontalsystems.bitcoincore.network.Network\n/**\n *\n *\n *  Extends from the abstract Network class and overrides all variables. Configures connection to the RegTest.\n */\nclass RegTest : Network() {\n    override var port: Int = 18444\n\n    override var magic: Long = 0xdab5bffa\n    override var bip32HeaderPub: Int = 0x043587CF\n    override var bip32HeaderPriv: Int = 0x04358394\n    override var addressVersion: Int = 111\n    override var addressSegwitHrp: String = \"tb\"\n    override var addressScriptVersion: Int = 196\n    override var coinType: Int = 1\n    override val blockchairChainId: String = \"\"\n\n    override val maxBlockSize = 1_000_000\n    override val dustRelayTxFee = 3000 // https://github.com/bitcoin/bitcoin/blob/c536dfbcb00fb15963bf5d507b7017c241718bf6/src/policy/policy.h#L50\n    override val syncableFromApi = false\n\n    override var dnsSeeds = listOf(\n            \"btc-regtest.blocksdecoded.com\",\n            \"btc01-regtest.blocksdecoded.com\",\n            \"btc02-regtest.blocksdecoded.com\",\n            \"btc03-regtest.blocksdecoded.com\"\n    )\n\n}\n"
  },
  {
    "path": "bitcoinkit/src/main/kotlin/io/horizontalsystems/bitcoinkit/TestNet.kt",
    "content": "package io.horizontalsystems.bitcoinkit\n\nimport io.horizontalsystems.bitcoincore.network.Network\n/**\n * Extends from the abstract Network class and overrides all variables. Configures connection to the TestNet.\n */\nclass TestNet : Network() {\n\n    override var port: Int = 18333\n\n    override var magic: Long = 0x0709110B\n    override var bip32HeaderPub: Int = 0x043587CF\n    override var bip32HeaderPriv: Int = 0x04358394\n    override var addressVersion: Int = 111\n    override var addressSegwitHrp: String = \"tb\"\n    override var addressScriptVersion: Int = 196\n    override var coinType: Int = 1\n    override val blockchairChainId: String = \"bitcoin/testnet\"\n\n    override val maxBlockSize = 1_000_000\n    override val dustRelayTxFee = 3000 // https://github.com/bitcoin/bitcoin/blob/c536dfbcb00fb15963bf5d507b7017c241718bf6/src/policy/policy.h#L50\n\n    override var dnsSeeds = listOf(\n        \"x5.testnet-seed.bitcoin.jonasschnelli.ch\",\n        \"x5.seed.tbtc.petertodd.org\",\n        \"x5.seed.testnet.bitcoin.sprovoost.nl\",\n        \"testnet-seed.bluematt.me\"\n    )\n}\n"
  },
  {
    "path": "bitcoinkit/src/main/kotlin/io/horizontalsystems/bitcoinkit/core/DataProvider.kt",
    "content": ""
  },
  {
    "path": "bitcoinkit/src/main/kotlin/io/horizontalsystems/bitcoinkit/utils/AddressConverter.kt",
    "content": ""
  },
  {
    "path": "bitcoinkit/src/main/resources/MainNet-bip44.checkpoint",
    "content": "02000000ba3f2b4208ec0495b2e3743465cae2b44d8f1c778b44cf6b0000000000000000d287e52e8045c060c1cee47d1cc7559c7b8ab8db580539fb55fc579a998ea14efe0e50538c9d001926c0c180a08504003f72e59e0db5b38e5210369dc2fb4831ab1e81f3b5dbec3d0000000000000000\n"
  },
  {
    "path": "bitcoinkit/src/main/resources/MainNet.checkpoint",
    "content": "00a04e38373d0c01c28991ba80faec5d306ae89732ab13800b7201000000000000000000b9ff0d1552f4c89dbb9c1b4825da6c49354ea5037cc16aa837c0f20244a1d94a0bc2bd69911a021700e00e14a05d0e00cb999ae4c17f33836ec64a3f23766a6abe09ca0cdddd00000000000000000000\n"
  },
  {
    "path": "bitcoinkit/src/main/resources/TestNet-bip44.checkpoint",
    "content": "0200000097f2b61897ba2bed756cca30058bcc1c2dfbb4ed0e962f47f749dc03000000006b80079a1eda8071424e294fa56849370e331c8ff7e95034576c9789c8db0fa6da551153ab80011c9bdaca25a00b03009a259d2c34148908a4e71853349f033fd7ac5cbc29bd833adebb000000000000\n"
  },
  {
    "path": "bitcoinkit/src/main/resources/TestNet.checkpoint",
    "content": "00607e2a08eab9b236d7b564a0ca280f0bf34c4ee75aae2a7967d0c5b100000000000000ba65fa9ea31062b73d85e6b7aa743e81329e0bb48b7480acd79367e1b3b9d4664c1d1067c0ff3f1917f4a95620b23000160fd5a8c341763895790d4f270a9fa145d3708c8c74e0823900000000000000\n"
  },
  {
    "path": "bitcoinkit/src/test/kotlin/io/horizontalsystems/bitcoinkit/Fixtures.kt",
    "content": "package io.horizontalsystems.bitcoinkit\n\nimport io.horizontalsystems.bitcoincore.extensions.hexToByteArray\nimport io.horizontalsystems.bitcoincore.extensions.toReversedByteArray\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.models.TransactionInput\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.storage.BlockHeader\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport io.horizontalsystems.bitcoincore.utils.HashUtils\n\nobject Fixtures {\n\n    val checkpointBlock1\n        get() = Block(\n            height = 0, // 536256\n            header = BlockHeader(\n                version = 536870912,\n                previousBlockHeaderHash = HashUtils.toBytesAsLE(\"00000000000000000000943de85f4495f053ff55f27d135edc61c27990c2eec5\"),\n                merkleRoot = HashUtils.toBytesAsLE(\"167bf70981d49388d07881b1a448ff9b79cf2a32716e45c535345823d8cdd541\"),\n                timestamp = 1533980459,\n                bits = 388763047,\n                nonce = 1545867530,\n                hash = \"000000000000000000262e508512ce2e6a018e181fb2e5efe048a4e01d21fa7a\".toReversedByteArray()\n            )\n        )\n\n    val block1\n        get() = Block(\n            height = 2013, // 538269\n            header = BlockHeader(\n                version = 536870912,\n                previousBlockHeaderHash = HashUtils.toBytesAsLE(\"00000000000000000011206e641083b68ffc41b7fe6ee1af4a5d69995d1b2d0e\"),\n                merkleRoot = HashUtils.toBytesAsLE(\"5510c0c3d1fd9d2b56a34aab98c29860015caf248fa62a1907b197ddec17c788\"),\n                timestamp = 1535128609,\n                bits = 388763047,\n                nonce = 2295801359,\n                hash = \"0000000000000000000a876dbca5804f792afa90b6dc7946dedb5866245d0c55\".toReversedByteArray()\n            )\n        )\n\n    val block2\n        get() = Block(\n            height = 2014, // 538270\n            header = BlockHeader(\n                version = 536870912,\n                previousBlockHeaderHash = HashUtils.toBytesAsLE(\"0000000000000000000a876dbca5804f792afa90b6dc7946dedb5866245d0c55\"),\n                merkleRoot = HashUtils.toBytesAsLE(\"ccf2737e44e435e2e11481755b00d161815a24e605d605a17bf20da49320ad7d\"),\n                timestamp = 1535128839,\n                bits = 388763047,\n                nonce = 3401296263,\n                hash = \"000000000000000000124a73e879fd66a1b29d1b4b3f1a81de3cbcbe579e21a8\".toReversedByteArray()\n            )\n        )\n\n    val block3\n        get() = Block(\n            height = 2015, // 538271\n            header = BlockHeader(\n                version = 536870912,\n                previousBlockHeaderHash = HashUtils.toBytesAsLE(\"000000000000000000124a73e879fd66a1b29d1b4b3f1a81de3cbcbe579e21a8\"),\n                merkleRoot = HashUtils.toBytesAsLE(\"7904930640df999005df3b57f9c6f542088af33c3d773dcec2939f55ced359b8\"),\n                timestamp = 1535129301,\n                bits = 388763047,\n                nonce = 59591417,\n                hash = \"0000000000000000001d9d48d93793aaa85b5f6d17c176d4ef905c7e7112b1cf\".toReversedByteArray()\n            )\n        )\n\n    val checkpointBlock2\n        get() = Block(\n            height = 2016, // 538272\n            header = BlockHeader(\n                version = 536870912,\n                previousBlockHeaderHash = HashUtils.toBytesAsLE(\"0000000000000000001d9d48d93793aaa85b5f6d17c176d4ef905c7e7112b1cf\"),\n                merkleRoot = HashUtils.toBytesAsLE(\"3ad0fa0e8c100db5831ebea7cabf6addae2c372e6e1d84f6243555df5bbfa351\"),\n                timestamp = 1535129431,\n                bits = 388618029,\n                nonce = 2367954839,\n                hash = \"00000000000000000004f11858464cc6113248537a01e628324968b499848a60\".toReversedByteArray()\n            )\n        )\n\n    //    P2PKH: TestNet tx => 68f297d8a8c9af30cd5a9d6d1eeec5ed3df7be1e4b62f2ced135af6ffe7814c2\n    val transactionP2PKH\n        get() = FullTransaction(\n            header = Transaction(),\n            inputs = listOf(\n                TransactionInput(\n                    previousOutputTxHash = \"093f5f5c5e57ae2ae9728147547e183e2ef5c9e6e879a78bee6ceb59db2b4797\".toReversedByteArray(),\n                    previousOutputIndex = 1,\n                    sigScript = \"473044022018f03676d057a3cb350d9778697ff61da47b813c82fe9fb0f2ea87b231fb865b02200706f5cbbc5ebae6f7bd77e346767bce11c8476aea607671d7321e86a3186ec1012102ce0ef85579f055e2184c935e75e71458db8c4b759cd455b0aa5d91761794eef0\".hexToByteArray(),\n                    sequence = 4294967295\n                )\n            ),\n            outputs = listOf(\n                TransactionOutput(\n                    value = 94734191,\n                    index = 0,\n                    script = \"76a91437a9bfe84d9e4883ace248509bbf14c9d72af01788ac\".hexToByteArray(),\n                    type = ScriptType.UNKNOWN\n                ),\n                TransactionOutput(\n                    value = 100000,\n                    index = 1,\n                    script = \"76a91437a9bfe84d9e4883ace248509bbf14c9d72af01788ac\".hexToByteArray(),\n                    type = ScriptType.UNKNOWN\n                )\n            )\n        )\n\n    //  P2SH: TestNet tx => 761cc7102efe24f4353ae7dc816fbed5e15963d11ca93e36449d521bda21ac4d\n    val transactionP2SH\n        get() = FullTransaction(\n            header = Transaction(),\n            inputs = listOf(\n                TransactionInput(\n                    previousOutputTxHash = \"b6f0ede9cc38cdbceb91936619f89b648bb912f4c42773567037ea5de164873d\".toReversedByteArray(),\n                    previousOutputIndex = 0,\n                    sigScript = \"004830450221008c203a0881f75c731d9a3a2e6d2ffa37da7095b7dde61a9e7a906659219cd0fa02202677097ca7f7e164f73924fe8f84e1e6fc6611450efcda360ce771e98af9f73d0147304402201cba9b641483476f67a4cef08d7280f51de8d7615fcce76642d944dc07132a990220323d13175477bbf67c8c36fb243bec0e4c410bc9173a186d9f8e98ce3445363601475221025b64f7c63e30f315259393f64dcca269d18386997b1cc93da1388c4021e3ea8e210386d42d5d7027ac08ddcbb066e2140575091fe7dc1d202a008eb5e036725e975652ae\".hexToByteArray(),\n                    sequence = 4294967295\n                )\n            ),\n            outputs = listOf(\n                TransactionOutput(\n                    value = 617795422,\n                    index = 0,\n                    script = \"a914cdfb2eb01489e9fe8bd9b878ce4a7084dd88776487\".hexToByteArray(),\n                    type = ScriptType.UNKNOWN\n                ),\n                TransactionOutput(\n                    value = 1407000,\n                    index = 1,\n                    script = \"a914aed6f804c63da80800892f8fd4cdbad0d3ad6d1287\".hexToByteArray(),\n                    type = ScriptType.UNKNOWN\n                )\n            )\n        )\n\n    //  P2PK: TestNet tx => 75b84cb54351866cb5248158735e801d9b2c56592633157ba10d08affa2ffbab\n    val transactionP2PK\n        get() = FullTransaction(\n            header = Transaction(),\n            inputs = listOf(\n                TransactionInput(\n                    previousOutputTxHash = \"978530798f3979322351c190856d17b9e9e7e470c5be4ce87a60bd7a9f7756ac\".toReversedByteArray(),\n                    previousOutputIndex = 0,\n                    sigScript = \"473044022003f9d150b4e291de2825af19dbe1846cc80caf3535d7e9fa03743b2ad019cc47022073294e520c508f702e3ad7a085ecce4a4b311d43faa1e6eb685ec78c002e795d01\".hexToByteArray(),\n                    sequence = 4294967295\n                )\n            ),\n            outputs = listOf(\n                TransactionOutput(\n                    value = 1000000000,\n                    index = 0,\n                    script = \"4104ae1a62fe09c5f51b13905f07f06b99a2f7159b2225f374cd378d71302fa28414e7aab37397f554a7df5f142c21c1b7303b8a0626f1baded5c72a704f7e6cd84cac\".hexToByteArray(),\n                    type = ScriptType.UNKNOWN\n                ),\n                TransactionOutput(\n                    value = 4000000000,\n                    index = 1,\n                    script = \"410411db93e1dcdb8a016b49840f8c53bc1eb68a382e97b1482ecad7b148a6909a5cb2e0eaddfb84ccf9744464f82e160bfa9b8b64f9d4c03f999b8643f656b412a3ac\".hexToByteArray(),\n                    type = ScriptType.UNKNOWN\n                )\n            )\n        )\n\n    val transactionP2WPKH\n        get() = FullTransaction(\n            header = Transaction(version = 1),\n            inputs = listOf(\n                TransactionInput(\n                    previousOutputTxHash = \"a6d1ce683f38a84cfd88a9d48b0ba2d7a8def00f8517e3da02c86fce6c7863d7\".toReversedByteArray(),\n                    previousOutputIndex = 0,\n                    sigScript = \"4730440220302e597d74aebcb0bf7f372be156252017af190bd586466104b079fba4b7efa7022037ebbf84e096ef3d966123a93a83586012353c1d2c11c967d21acf1c94c45df001210347235e12207d21b6093d9fd93a0df4d589a0d44252b98b2e934a8da5ab1d1654\".hexToByteArray(),\n                    sequence = 4294967295\n                )\n            ),\n            outputs = listOf(\n                TransactionOutput(\n                    value = 10792000,\n                    index = 0,\n                    script = \"00148749115073ad59a6f3587f1f9e468adedf01473f\".hexToByteArray(),\n                    type = ScriptType.P2WPKH,\n                    lockingScriptPayload = byteArrayOf()\n                ),\n                TransactionOutput(\n                    value = 0,\n                    index = 0,\n                    script = \"6a4c500000b919000189658af37cd16dbd16e4186ea13c5d8e1f40c5b5a0958326067dd923b8fc8f0767f62eb9a7fd57df4f3e775a96ca5b5eabf5057dff98997a3bbd011366703f5e45075f397f7f3c8465da\".hexToByteArray(),\n                    type = ScriptType.P2PK,\n                    lockingScriptPayload = byteArrayOf()\n                )\n            )\n        )\n}\n"
  },
  {
    "path": "bitcoinkit/src/test/kotlin/io/horizontalsystems/bitcoinkit/MainNetTest.kt",
    "content": "package io.horizontalsystems.bitcoinkit\n\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport org.junit.Assert.assertEquals\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.runner.RunWith\nimport org.powermock.core.classloader.annotations.PrepareForTest\nimport org.powermock.modules.junit4.PowerMockRunner\n\n@RunWith(PowerMockRunner::class)\n@PrepareForTest(MainNet::class)\n\nclass MainNetTest {\n\n    private lateinit var network: MainNet\n\n    @Before\n    fun setup() {\n        network = MainNet()\n    }\n\n    @Test\n    fun packetMagic() {\n        val stream = BitcoinInputMarkable(byteArrayOf(\n                0xf9.toByte(),\n                0xbe.toByte(),\n                0xb4.toByte(),\n                0xd9.toByte()\n        ))\n\n        val magic = stream.readUnsignedInt()\n        assertEquals(magic, network.magic)\n    }\n\n}\n"
  },
  {
    "path": "bitcoinkit/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker",
    "content": "mock-maker-inline"
  },
  {
    "path": "build.gradle",
    "content": "// Top-level build file where you can add configuration options common to all sub-projects/modules.\n\nbuildscript {\n    ext.kotlin_version = '1.8.0'\n    repositories {\n        google()\n        mavenCentral()\n    }\n    dependencies {\n        classpath 'com.android.tools.build:gradle:8.3.1'\n        classpath \"org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version\"\n\n        // NOTE: Do not place your application dependencies here; they belong\n        // in the individual module build.gradle files\n    }\n}\n\nallprojects {\n    repositories {\n        google()\n        mavenCentral()\n        maven { url \"https://jitpack.io\" }\n    }\n}\n\ntask clean(type: Delete) {\n    delete rootProject.buildDir\n}\n"
  },
  {
    "path": "dashkit/.gitignore",
    "content": "/build\n/.cxx"
  },
  {
    "path": "dashkit/build.gradle",
    "content": "plugins {\n    id 'com.android.library'\n    id 'kotlin-android'\n    id 'kotlin-kapt'\n    id 'maven-publish'\n}\n\nafterEvaluate {\n    publishing {\n        publications {\n            release(MavenPublication) {\n                from components.release\n            }\n        }\n    }\n}\n\nandroid {\n    namespace 'io.horizontalsystems.dashkit'\n    compileSdk 34\n\n    defaultConfig {\n        minSdkVersion 23\n        targetSdkVersion 34\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n\n        externalNativeBuild {\n            cmake {\n                cFlags '-DHAVE_CONFIG_H -DWORD=32'\n                cppFlags \"\"\n            }\n        }\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    sourceSets {\n        main.java.srcDirs += 'src/main/kotlin'\n        test.java.srcDirs += 'src/test/kotlin'\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_17\n        targetCompatibility JavaVersion.VERSION_17\n    }\n    kotlinOptions { jvmTarget = '17' }\n\n    externalNativeBuild {\n        cmake {\n            path 'cpp/CMakeLists.txt'\n        }\n    }\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version\"\n    implementation 'androidx.annotation:annotation:1.1.0'\n\n    // JSON\n    implementation 'com.eclipsesource.minimal-json:minimal-json:0.9.5'\n\n    // Room\n    implementation 'androidx.room:room-runtime:2.5.0'\n    implementation 'androidx.room:room-rxjava2:2.5.0'\n    kapt 'androidx.room:room-compiler:2.5.0'\n\n    api project(':bitcoincore')\n    implementation files('libs/dashj-bls-0.15.3.jar')\n    implementation 'de.sfuhrm:saphir-hash-jca:3.0.6'\n\n    // Test helpers\n    testImplementation 'junit:junit:4.13.2'\n    testImplementation 'org.junit.jupiter:junit-jupiter:5.6.1'\n    testImplementation 'org.mockito:mockito-core:3.3.3'\n    testImplementation 'com.nhaarman:mockito-kotlin-kt1.1:1.6.0'\n    testImplementation 'org.powermock:powermock-api-mockito2:2.0.7'\n    testImplementation 'org.powermock:powermock-module-junit4:2.0.7'\n\n    // Spek\n    testImplementation \"org.spekframework.spek2:spek-dsl-jvm:2.0.9\"\n    testRuntimeOnly \"org.spekframework.spek2:spek-runner-junit5:2.0.9\"\n    testRuntimeOnly \"org.jetbrains.kotlin:kotlin-reflect:$kotlin_version\"\n\n    // Android Instrumentation Test\n    androidTestImplementation 'androidx.test.ext:junit:1.1.1'\n    androidTestImplementation 'com.linkedin.dexmaker:dexmaker-mockito-inline:2.28.3'\n    androidTestImplementation 'com.nhaarman:mockito-kotlin-kt1.1:1.6.0'\n}\n"
  },
  {
    "path": "dashkit/cpp/CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.6.0)\n\nadd_subdirectory(dashj-bls)\n"
  },
  {
    "path": "dashkit/cpp/config.h",
    "content": "#define HAVE_DECL_BE64ENC 0\n#define HAVE_MMAP 1\n\n#ifndef __ANDROID__\n#define HAVE_POSIX_MEMALIGN 1\n#endif\n\n#ifdef __ANDROID__\n#include <sys/limits.h>\n#endif\n"
  },
  {
    "path": "dashkit/cpp/dashj-bls/CMakeLists.txt",
    "content": "#\n# dashj-bls\n#\ncmake_minimum_required(VERSION 3.4.1)\nset(CMAKE_CXX_STANDARD 11)\n\nset(OPSYS \"DROID\")\n\nadd_library( # Sets the name of the library.\n        dashjbls\n        # Sets the library as a shared library.\n       SHARED\n        # Provides a relative path to your source file(s).\n        dashj-bls-signature-wrapper.cpp\n        stdio.cpp)\n\n# Find Java\n\n#find_package(JNI)\n#if (JNI_FOUND)\n#    message (STATUS \"JNI_INCLUDE_DIRS=${JNI_INCLUDE_DIRS}\")\n#    message (STATUS \"JNI_LIBRARIES=${JNI_LIBRARIES}\")\n#endif()\n\ninclude_directories(#${JNI_INCLUDE_DIRS}\n\t\t\t\t\t${CMAKE_SOURCE_DIR}/bls-signatures/src\n\t\t\t\t\t${CMAKE_BINARY_DIR}/dashj-bls/bls-signatures/contrib/relic/include\n\t\t\t\t\t${CMAKE_SOURCE_DIR}/bls-signatures/contrib/relic/include\n)\nmessage(STATUS \"binary dir = ${CMAKE_BINARY_DIR}\")\n\nif (${ANDROID_ABI} STREQUAL \"x86_64\")\n\tinclude_directories(${ANDROID_SYSROOT}/usr/include/x86_64-linux-android)\nelseif (${ANDROID_ABI} STREQUAL \"x86\")\n\tinclude_directories(${ANDROID_SYSROOT}/usr/include/i686-linux-android)\nelseif (${ANDROID_ABI} STREQUAL \"arm64-v8a\")\n\tinclude_directories(${ANDROID_SYSROOT}/usr/include/aarch64-linux-android)\nelseif (${ANDROID_ABI} STREQUAL \"armeabi-v7a\")\n\tinclude_directories(${ANDROID_SYSROOT}/usr/include/arm-linux-androideabi)\nendif()\n\n# Specifies libraries CMake should link to your target library. You\n# can link multiple libraries, such as libraries you define in this\n# build script, prebuilt third-party libraries, or system libraries.\n\ntarget_link_libraries( # Specifies the target library.\n        dashjbls\n\n        # Links the target library to the bls library\n        bls blstmp relic_s)\n\n# 16 KB page size alignment required by Google Play\ntarget_link_options(dashjbls PRIVATE \"-Wl,-z,max-page-size=16384\" \"-Wl,--build-id=none\")\n\n#add_subdirectory(src/main/cpp)\ninclude(bls-signatures.cmake)\n\nadd_library(pthread SHARED pthread.c)\ntarget_link_options(pthread PRIVATE \"-Wl,-z,max-page-size=16384\" \"-Wl,--build-id=none\")\n"
  },
  {
    "path": "dashkit/cpp/dashj-bls/bls-signatures-src.cmake",
    "content": "CMAKE_MINIMUM_REQUIRED(VERSION 3.1.0 FATAL_ERROR)\nset (CMAKE_CXX_STANDARD 11)\n\nfile(GLOB HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/*.hpp)\nsource_group(\"SrcHeaders\" FILES ${HEADERS})\n\ninclude_directories(\n  ${INCLUDE_DIRECTORIES}\n  ${CMAKE_CURRENT_SOURCE_DIR}/bls-signatures/contrib/relic/include\n  ${CMAKE_BINARY_DIR}/bls-signatures/src/contrib/relic/include\n  ${CMAKE_CURRENT_SOURCE_DIR}/bls-signatures/src/contrib/catch\n  )\n\nset(C_LIB ${CMAKE_BINARY_DIR}/libbls.a)\n\nadd_library(bls ${CMAKE_CURRENT_SOURCE_DIR}/bls-signatures/src/chaincode.cpp)\n\nadd_library(blstmp ${HEADERS}\n  ${CMAKE_CURRENT_SOURCE_DIR}/bls-signatures/src/extendedpublickey.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/bls-signatures/src/extendedprivatekey.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/bls-signatures/src/chaincode.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/bls-signatures/src/signature.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/bls-signatures/src/publickey.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/bls-signatures/src/privatekey.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/bls-signatures/src/bls.cpp\n  ${CMAKE_CURRENT_SOURCE_DIR}/bls-signatures/src/aggregationinfo.cpp\n)\n\nset(OPREFIX object_)\nfind_library(GMP_NAME NAMES libgmp.a gmp)\nfind_library(SODIUM_NAME NAMES libsodium.a sodium)\n\n\n\n#set(LIBRARIES_TO_COMBINE\n#      COMMAND mkdir ${OPREFIX}$<TARGET_NAME:blstmp> || true && cd ${OPREFIX}$<TARGET_NAME:blstmp> &&  ${CMAKE_AR} -x $<TARGET_FILE:blstmp>\n#      COMMAND mkdir ${OPREFIX}$<TARGET_NAME:relic_s> || true && cd ${OPREFIX}$<TARGET_NAME:relic_s> &&  ${CMAKE_AR} -x $<TARGET_FILE:relic_s>\n#)\n\nif (GMP_FOUND)\n  list(APPEND LIBRARIES_TO_COMBINE COMMAND mkdir ${OPREFIX}gmp || true && cd ${OPREFIX}gmp &&  ${CMAKE_AR} -x ${GMP_NAME})\nendif()\nif (SODIUM_FOUND)\n  list(APPEND LIBRARIES_TO_COMBINE COMMAND mkdir ${OPREFIX}sodium || true && cd ${OPREFIX}sodium &&  ${CMAKE_AR} -x ${SODIUM_NAME})\nendif()\n\n#add_custom_target(combined_custom\n#        ${LIBRARIES_TO_COMBINE}\n#        COMMAND ${CMAKE_AR} -rs ${C_LIB} ${OPREFIX}*/*${CMAKE_C_OUTPUT_EXTENSION}\n#        WORKING_DIRECTORY ${CMAKE_BINARY_DIR}\n#        DEPENDS blstmp relic_s\n#        )\n\n#add_library(combined STATIC IMPORTED GLOBAL)\n#add_dependencies(combined combined_custom)\n\n#target_link_libraries(bls combined)\n\n#set_target_properties(combined\n#        PROPERTIES\n#        IMPORTED_LOCATION ${C_LIB}\n#        )\n\nfile(GLOB includes \"${CMAKE_CURRENT_SOURCE_DIR}/*.hpp\")\ninstall(FILES ${includes} DESTINATION include/chiabls)\ninstall(FILES ${C_LIB} DESTINATION lib)\n\n#add_executable(runtest test.cpp)\n#add_executable(runbench test-bench.cpp)\n\n#if (SODIUM_FOUND)\n#  target_link_libraries(runtest blstmp relic_s sodium)\n#  target_link_libraries(runbench blstmp relic_s sodium)\n#else()\n#  target_link_libraries(runtest blstmp relic_s)\n#  target_link_libraries(runbench blstmp relic_s)\n#endif()"
  },
  {
    "path": "dashkit/cpp/dashj-bls/bls-signatures.cmake",
    "content": "CMAKE_MINIMUM_REQUIRED(VERSION 3.1.0 FATAL_ERROR)\nset (CMAKE_CXX_STANDARD 11)\n\nset(CMAKE_POSITION_INDEPENDENT_CODE ON)\n\nIF(NOT CMAKE_BUILD_TYPE)\n  SET(CMAKE_BUILD_TYPE \"RELEASE\")\nENDIF()\n\nproject(BLS)\n\n# Add path for custom modules\nset(CMAKE_MODULE_PATH\n\t${CMAKE_MODULE_PATH}\n\t${CMAKE_CURRENT_SOURCE_DIR}/cmake_modules\n)\n\n# For android, we will not use sodium or gmp\n#find_package(sodium)\n#if (SODIUM_FOUND)\n#  message(STATUS \"Found libsodium\")\n#  message(STATUS \"Sodium include dir = ${sodium_INCLUDE_DIR}\")\n#  set(BLSALLOC_SODIUM \"1\" CACHE STRING \"\")\n#  include_directories(${sodium_INCLUDE_DIR})\n#endif()\n\n#find_package(gmp)\n#if (GMP_FOUND)\n#  message(STATUS \"Found libgmp\")\n#  include_directories(${GMP_INCLUDE_DIR})\n#  set(ARITH \"gmp\" CACHE STRING \"\")\n#else()\n  set(ARITH \"easy\" CACHE STRING \"\")\n#endif()\n\n\nset(WSIZE 32 CACHE INTEGER \"\")\nset(TIMER \"CYCLE\" CACHE STRING \"\")\nset(CHECK \"off\" CACHE STRING \"\")\nset(VERBS \"off\" CACHE STRING \"\")\nset(ALLOC \"AUTO\" CACHE STRING \"\")\nset(SHLIB \"OFF\" CACHE STRING \"\")\nset(MULTI \"PTHREAD\" CACHE STRING \"\")\n\nset(FP_PRIME 381 CACHE INTEGER \"\")\n\nIF (${CMAKE_SYSTEM_NAME} MATCHES \"Darwin\")\n  set(DSEED \"UDEV\" CACHE STRING \"\")\n  set(FP_QNRES \"off\" CACHE STRING \"\")\nELSEIF (${CMAKE_SYSTEM_NAME} MATCHES \"Windows\")\n  set(SEED \"WCGR\" CACHE STRING \"\")\n  set(FP_QNRES \"on\" CACHE STRING \"\")\nELSE()\n  set(DSEED \"DEV\" CACHE STRING \"\")\n  set(FP_QNRES \"on\" CACHE STRING \"\")\nENDIF()\nset(STBIN \"OFF\" CACHE STRING \"\")\n\nset(FP_METHD \"INTEG;INTEG;INTEG;MONTY;LOWER;SLIDE\" CACHE STRING \"\")\nset(COMP \"-O3 -funroll-loops -fomit-frame-pointer\" CACHE STRING \"\")\nset(FP_PMERS \"off\" CACHE STRING \"\")\nset(FPX_METHD \"INTEG;INTEG;LAZYR\" CACHE STRING \"\")\nset(EP_PLAIN \"off\" CACHE STRING \"\")\nset(EP_SUPER \"off\" CACHE STRING \"\")\n# Disable relic tests and benchmarks\nset(TESTS 0 CACHE INTEGER \"\")\nset(BENCH 0 CACHE INTEGER \"\")\n\nset(PP_EXT \"LAZYR\" CACHE STRING \"\")\nset(PP_METHD \"LAZYR;OATEP\" CACHE STRING \"\")\n\nadd_subdirectory(bls-signatures/contrib/relic)\n#add_subdirectory(bls-signatures/src)\ninclude(bls-signatures-src.cmake)"
  },
  {
    "path": "dashkit/cpp/dashj-bls/dashj-bls-signature-wrapper.cpp",
    "content": "/*\n * Copyright 2018 Dash Core Group\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n *    http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\n/* ----------------------------------------------------------------------------\n * This file was generated by SWIG (http://www.swig.org).\n * Version 3.0.12\n *\n * This file is not intended to be easily readable and contains a number of\n * coding conventions designed to improve portability and efficiency. Do not make\n * changes to this file unless you know what you are doing--modify the SWIG\n * interface file instead.\n *\n *  Hash Engineering Solutions\n *\n * ----------------------------------------------------------------------------- */\n\n\n#ifndef SWIGJAVA\n#define SWIGJAVA\n#endif\n\n\n\n#ifdef __cplusplus\n/* SwigValueWrapper is described in swig.swg */\ntemplate<typename T> class SwigValueWrapper {\n  struct SwigMovePointer {\n    T *ptr;\n    SwigMovePointer(T *p) : ptr(p) { }\n    ~SwigMovePointer() { delete ptr; }\n    SwigMovePointer& operator=(SwigMovePointer& rhs) { T* oldptr = ptr; ptr = 0; delete oldptr; ptr = rhs.ptr; rhs.ptr = 0; return *this; }\n  } pointer;\n  SwigValueWrapper& operator=(const SwigValueWrapper<T>& rhs);\n  SwigValueWrapper(const SwigValueWrapper<T>& rhs);\npublic:\n  SwigValueWrapper() : pointer(0) { }\n  SwigValueWrapper& operator=(const T& t) { SwigMovePointer tmp(new T(t)); pointer = tmp; return *this; }\n  operator T&() const { return *pointer.ptr; }\n  T *operator&() { return pointer.ptr; }\n};\n\ntemplate <typename T> T SwigValueInit() {\n  return T();\n}\n#endif\n\n/* -----------------------------------------------------------------------------\n *  This section contains generic SWIG labels for method/variable\n *  declarations/attributes, and other compiler dependent labels.\n * ----------------------------------------------------------------------------- */\n\n/* template workaround for compilers that cannot correctly implement the C++ standard */\n#ifndef SWIGTEMPLATEDISAMBIGUATOR\n# if defined(__SUNPRO_CC) && (__SUNPRO_CC <= 0x560)\n#  define SWIGTEMPLATEDISAMBIGUATOR template\n# elif defined(__HP_aCC)\n/* Needed even with `aCC -AA' when `aCC -V' reports HP ANSI C++ B3910B A.03.55 */\n/* If we find a maximum version that requires this, the test would be __HP_aCC <= 35500 for A.03.55 */\n#  define SWIGTEMPLATEDISAMBIGUATOR template\n# else\n#  define SWIGTEMPLATEDISAMBIGUATOR\n# endif\n#endif\n\n/* inline attribute */\n#ifndef SWIGINLINE\n# if defined(__cplusplus) || (defined(__GNUC__) && !defined(__STRICT_ANSI__))\n#   define SWIGINLINE inline\n# else\n#   define SWIGINLINE\n# endif\n#endif\n\n/* attribute recognised by some compilers to avoid 'unused' warnings */\n#ifndef SWIGUNUSED\n# if defined(__GNUC__)\n#   if !(defined(__cplusplus)) || (__GNUC__ > 3 || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4))\n#     define SWIGUNUSED __attribute__ ((__unused__))\n#   else\n#     define SWIGUNUSED\n#   endif\n# elif defined(__ICC)\n#   define SWIGUNUSED __attribute__ ((__unused__))\n# else\n#   define SWIGUNUSED\n# endif\n#endif\n\n#ifndef SWIG_MSC_UNSUPPRESS_4505\n# if defined(_MSC_VER)\n#   pragma warning(disable : 4505) /* unreferenced local function has been removed */\n# endif\n#endif\n\n#ifndef SWIGUNUSEDPARM\n# ifdef __cplusplus\n#   define SWIGUNUSEDPARM(p)\n# else\n#   define SWIGUNUSEDPARM(p) p SWIGUNUSED\n# endif\n#endif\n\n/* internal SWIG method */\n#ifndef SWIGINTERN\n# define SWIGINTERN static SWIGUNUSED\n#endif\n\n/* internal inline SWIG method */\n#ifndef SWIGINTERNINLINE\n# define SWIGINTERNINLINE SWIGINTERN SWIGINLINE\n#endif\n\n/* exporting methods */\n#if defined(__GNUC__)\n#  if (__GNUC__ >= 4) || (__GNUC__ == 3 && __GNUC_MINOR__ >= 4)\n#    ifndef GCC_HASCLASSVISIBILITY\n#      define GCC_HASCLASSVISIBILITY\n#    endif\n#  endif\n#endif\n\n#ifndef SWIGEXPORT\n# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)\n#   if defined(STATIC_LINKED)\n#     define SWIGEXPORT\n#   else\n#     define SWIGEXPORT __declspec(dllexport)\n#   endif\n# else\n#   if defined(__GNUC__) && defined(GCC_HASCLASSVISIBILITY)\n#     define SWIGEXPORT __attribute__ ((visibility(\"default\")))\n#   else\n#     define SWIGEXPORT\n#   endif\n# endif\n#endif\n\n/* calling conventions for Windows */\n#ifndef SWIGSTDCALL\n# if defined(_WIN32) || defined(__WIN32__) || defined(__CYGWIN__)\n#   define SWIGSTDCALL __stdcall\n# else\n#   define SWIGSTDCALL\n# endif\n#endif\n\n/* Deal with Microsoft's attempt at deprecating C standard runtime functions */\n#if !defined(SWIG_NO_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_CRT_SECURE_NO_DEPRECATE)\n# define _CRT_SECURE_NO_DEPRECATE\n#endif\n\n/* Deal with Microsoft's attempt at deprecating methods in the standard C++ library */\n#if !defined(SWIG_NO_SCL_SECURE_NO_DEPRECATE) && defined(_MSC_VER) && !defined(_SCL_SECURE_NO_DEPRECATE)\n# define _SCL_SECURE_NO_DEPRECATE\n#endif\n\n/* Deal with Apple's deprecated 'AssertMacros.h' from Carbon-framework */\n#if defined(__APPLE__) && !defined(__ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES)\n# define __ASSERT_MACROS_DEFINE_VERSIONS_WITHOUT_UNDERSCORES 0\n#endif\n\n/* Intel's compiler complains if a variable which was never initialised is\n * cast to void, which is a common idiom which we use to indicate that we\n * are aware a variable isn't used.  So we just silence that warning.\n * See: https://github.com/swig/swig/issues/192 for more discussion.\n */\n#ifdef __INTEL_COMPILER\n# pragma warning disable 592\n#endif\n\n\n/* Fix for jlong on some versions of gcc on Windows */\n#if defined(__GNUC__) && !defined(__INTEL_COMPILER)\n  typedef long long __int64;\n#endif\n\n/* Fix for jlong on 64-bit x86 Solaris */\n#if defined(__x86_64)\n# ifdef _LP64\n#   undef _LP64\n# endif\n#endif\n\n#include <jni.h>\n#include <stdlib.h>\n#include <string.h>\n\n\n/* Support for throwing Java exceptions */\ntypedef enum {\n  SWIG_JavaOutOfMemoryError = 1, \n  SWIG_JavaIOException, \n  SWIG_JavaRuntimeException, \n  SWIG_JavaIndexOutOfBoundsException,\n  SWIG_JavaArithmeticException,\n  SWIG_JavaIllegalArgumentException,\n  SWIG_JavaNullPointerException,\n  SWIG_JavaDirectorPureVirtual,\n  SWIG_JavaUnknownError\n} SWIG_JavaExceptionCodes;\n\ntypedef struct {\n  SWIG_JavaExceptionCodes code;\n  const char *java_exception;\n} SWIG_JavaExceptions_t;\n\n\nstatic void SWIGUNUSED SWIG_JavaThrowException(JNIEnv *jenv, SWIG_JavaExceptionCodes code, const char *msg) {\n  jclass excep;\n  static const SWIG_JavaExceptions_t java_exceptions[] = {\n    { SWIG_JavaOutOfMemoryError, \"java/lang/OutOfMemoryError\" },\n    { SWIG_JavaIOException, \"java/io/IOException\" },\n    { SWIG_JavaRuntimeException, \"java/lang/RuntimeException\" },\n    { SWIG_JavaIndexOutOfBoundsException, \"java/lang/IndexOutOfBoundsException\" },\n    { SWIG_JavaArithmeticException, \"java/lang/ArithmeticException\" },\n    { SWIG_JavaIllegalArgumentException, \"java/lang/IllegalArgumentException\" },\n    { SWIG_JavaNullPointerException, \"java/lang/NullPointerException\" },\n    { SWIG_JavaDirectorPureVirtual, \"java/lang/RuntimeException\" },\n    { SWIG_JavaUnknownError,  \"java/lang/UnknownError\" },\n    { (SWIG_JavaExceptionCodes)0,  \"java/lang/UnknownError\" }\n  };\n  const SWIG_JavaExceptions_t *except_ptr = java_exceptions;\n\n  while (except_ptr->code != code && except_ptr->code)\n    except_ptr++;\n\n  jenv->ExceptionClear();\n  excep = jenv->FindClass(except_ptr->java_exception);\n  if (excep)\n    jenv->ThrowNew(excep, msg);\n}\n\n\n/* Contract support */\n\n#define SWIG_contract_assert(nullreturn, expr, msg) if (!(expr)) {SWIG_JavaThrowException(jenv, SWIG_JavaIllegalArgumentException, msg); return nullreturn; } else\n\n\n#include \"bls-signatures/src/bls.hpp\"\n#include \"bls-signatures/src/chaincode.hpp\"\n#include \"bls-signatures/src/extendedprivatekey.hpp\"\n#include \"bls-signatures/src/extendedpublickey.hpp\"\n#include \"bls-signatures/src/privatekey.hpp\"\n#include \"bls-signatures/src/publickey.hpp\"\n#include \"bls-signatures/src/signature.hpp\"\n#include \"bls-signatures/src/aggregationinfo.hpp\"\nusing namespace bls; //this fixes many undefined symbols\n\n\n#include <vector>\n#include <stdexcept>\n\n\n#include <stdint.h>\t\t// Use the C99 official header\n\n\n#include <typeinfo>\n#include <stdexcept>\n\nSWIGINTERN std::vector< bls::PublicKey >::const_reference std_vector_Sl_bls_PublicKey_Sg__get(std::vector< bls::PublicKey > const *self,int i){\n                return self->at(i);\n            }\nSWIGINTERN std::vector< bls::PublicKey >::value_type std_vector_Sl_bls_PublicKey_Sg__set(std::vector< bls::PublicKey > *self,int i,std::vector< bls::PublicKey >::value_type &VECTOR_VALUE_IN){\n                const bls::PublicKey old = self->at(i);\n                self->at(i) = VECTOR_VALUE_IN;\n                return old;\n            }\nSWIGINTERN int32_t std_vector_Sl_bls_PublicKey_Sg__size(std::vector< bls::PublicKey > const *self){\n              return self->size();\n            }\nSWIGINTERN void std_vector_Sl_bls_PublicKey_Sg__removeRange(std::vector< bls::PublicKey > *self,int32_t from,int32_t to){\n              self->erase(self->begin()+from, self->begin()+to);\n            }\nSWIGINTERN std::vector< bls::PrivateKey >::const_reference std_vector_Sl_bls_PrivateKey_Sg__get(std::vector< bls::PrivateKey > const *self,int i){\n                return self->at(i);\n            }\nSWIGINTERN std::vector< bls::PrivateKey >::value_type std_vector_Sl_bls_PrivateKey_Sg__set(std::vector< bls::PrivateKey > *self,int i,std::vector< bls::PrivateKey >::value_type &VECTOR_VALUE_IN){\n                const bls::PrivateKey old = self->at(i);\n                self->at(i) = VECTOR_VALUE_IN;\n                return old;\n            }\nSWIGINTERN int32_t std_vector_Sl_bls_PrivateKey_Sg__size(std::vector< bls::PrivateKey > const *self){\n              return self->size();\n            }\nSWIGINTERN void std_vector_Sl_bls_PrivateKey_Sg__removeRange(std::vector< bls::PrivateKey > *self,int32_t from,int32_t to){\n              self->erase(self->begin()+from, self->begin()+to);\n            }\nSWIGINTERN std::vector< unsigned char * >::const_reference std_vector_Sl_uint8_t_Sm__Sg__get(std::vector< uint8_t * > const *self,int i){\n                return self->at(i);\n            }\nSWIGINTERN std::vector< unsigned char * >::value_type std_vector_Sl_uint8_t_Sm__Sg__set(std::vector< uint8_t * > *self,int i,std::vector< unsigned char * >::value_type &VECTOR_VALUE_IN){\n                unsigned char * old = self->at(i);\n                self->at(i) = VECTOR_VALUE_IN;\n                return old;\n            }\nSWIGINTERN int32_t std_vector_Sl_uint8_t_Sm__Sg__size(std::vector< uint8_t * > const *self){\n              return self->size();\n            }\nSWIGINTERN void std_vector_Sl_uint8_t_Sm__Sg__removeRange(std::vector< uint8_t * > *self,int32_t from,int32_t to){\n              self->erase(self->begin()+from, self->begin()+to);\n            }\nSWIGINTERN std::vector< bn_t * >::const_reference std_vector_Sl_bn_t_Sm__Sg__get(std::vector< bn_t * > const *self,int i){\n                return self->at(i);\n            }\nSWIGINTERN std::vector< bn_t * >::value_type std_vector_Sl_bn_t_Sm__Sg__set(std::vector< bn_t * > *self,int i,std::vector< bn_t * >::value_type &VECTOR_VALUE_IN){\n                bn_t * old = self->at(i);\n                self->at(i) = VECTOR_VALUE_IN;\n                return old;\n            }\nSWIGINTERN int32_t std_vector_Sl_bn_t_Sm__Sg__size(std::vector< bn_t * > const *self){\n              return self->size();\n            }\nSWIGINTERN void std_vector_Sl_bn_t_Sm__Sg__removeRange(std::vector< bn_t * > *self,int32_t from,int32_t to){\n              self->erase(self->begin()+from, self->begin()+to);\n            }\nSWIGINTERN std::vector< bls::Signature >::const_reference std_vector_Sl_bls_Signature_Sg__get(std::vector< bls::Signature > const *self,int i){\n                return self->at(i);\n            }\nSWIGINTERN std::vector< bls::Signature >::value_type std_vector_Sl_bls_Signature_Sg__set(std::vector< bls::Signature > *self,int i,std::vector< bls::Signature >::value_type const &VECTOR_VALUE_IN){\n                const bls::Signature old = self->at(i);\n                self->at(i) = VECTOR_VALUE_IN;\n                return old;\n            }\nSWIGINTERN int32_t std_vector_Sl_bls_Signature_Sg__size(std::vector< bls::Signature > const *self){\n              return self->size();\n            }\nSWIGINTERN void std_vector_Sl_bls_Signature_Sg__removeRange(std::vector< bls::Signature > *self,int32_t from,int32_t to){\n              self->erase(self->begin()+from, self->begin()+to);\n            }\n\nSWIGINTERN std::vector< bls::InsecureSignature >::const_reference std_vector_Sl_bls_InsecureSignature_Sg__get(std::vector< bls::InsecureSignature > const *self,int i){\n                return self->at(i);\n            }\nSWIGINTERN std::vector< bls::InsecureSignature >::value_type std_vector_Sl_bls_InsecureSignature_Sg__set(std::vector< bls::InsecureSignature > *self,int i,std::vector< bls::InsecureSignature >::value_type const &VECTOR_VALUE_IN){\n                const bls::InsecureSignature old = self->at(i);\n                self->at(i) = VECTOR_VALUE_IN;\n                return old;\n            }\nSWIGINTERN int32_t std_vector_Sl_bls_InsecureSignature_Sg__size(std::vector< bls::InsecureSignature > const *self){\n              return self->size();\n            }\nSWIGINTERN void std_vector_Sl_bls_InsecureSignature_Sg__removeRange(std::vector< bls::InsecureSignature > *self,int32_t from,int32_t to){\n              self->erase(self->begin()+from, self->begin()+to);\n            }\nSWIGINTERN std::vector< bls::AggregationInfo >::const_reference std_vector_Sl_bls_AggregationInfo_Sg__get(std::vector< bls::AggregationInfo > const *self,int i){\n                return self->at(i);\n            }\nSWIGINTERN std::vector< bls::AggregationInfo >::value_type std_vector_Sl_bls_AggregationInfo_Sg__set(std::vector< bls::AggregationInfo > *self,int i,std::vector< bls::AggregationInfo >::value_type const &VECTOR_VALUE_IN){\n                const bls::AggregationInfo old = self->at(i);\n                self->at(i) = VECTOR_VALUE_IN;\n                return old;\n            }\nSWIGINTERN int32_t std_vector_Sl_bls_AggregationInfo_Sg__size(std::vector< bls::AggregationInfo > const *self){\n              return self->size();\n            }\nSWIGINTERN void std_vector_Sl_bls_AggregationInfo_Sg__removeRange(std::vector< bls::AggregationInfo > *self,int32_t from,int32_t to){\n              self->erase(self->begin()+from, self->begin()+to);\n            }\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nSWIGEXPORT jstring JNICALL Java_org_dashj_bls_JNI_BLS_1GROUP_1ORDER_1get(JNIEnv *jenv, jclass jcls) {\n  jstring jresult = 0 ;\n  char *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  result = (char *)(char *)bls::BLS::GROUP_ORDER;\n  if (result) jresult = jenv->NewStringUTF((const char *)result);\n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_BLS_1MESSAGE_1HASH_1LEN_1get(JNIEnv *jenv, jclass jcls) {\n  jlong jresult = 0 ;\n  size_t result;\n  \n  (void)jenv;\n  (void)jcls;\n  result = bls::BLS::MESSAGE_HASH_LEN;\n  jresult = (jlong)result; \n  return jresult;\n}\n\n\nSWIGEXPORT jboolean JNICALL Java_org_dashj_bls_JNI_BLS_1Init(JNIEnv *jenv, jclass jcls) {\n  jboolean jresult = 0 ;\n  bool result;\n  \n  (void)jenv;\n  (void)jcls;\n  result = (bool)bls::BLS::Init();\n  jresult = (jboolean)result; \n  return jresult;\n}\n#ifdef BUILD_DASH_CORE\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_BLS_1ID_1SIZE_1get(JNIEnv *jenv, jclass jcls) {\n  jlong jresult = 0 ;\n  size_t result;\n  \n  (void)jenv;\n  (void)jcls;\n  result = bls::BLS::ID_SIZE;\n  jresult = (jlong)result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL JJava_org_dashj_bls_JNI_BLS_1AssertInitialized(JNIEnv *jenv, jclass jcls) {\n  (void)jenv;\n  (void)jcls;\n  bls::BLS::AssertInitialized();\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_BLS_1Clean(JNIEnv *jenv, jclass jcls) {\n  (void)jenv;\n  (void)jcls;\n  bls::BLS::Clean();\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_BLS_1SetSecureAllocator(JNIEnv *jenv, jclass jcls, jlong jarg1, jlong jarg2) {\n  Util::SecureAllocCallback arg1 ;\n  Util::SecureFreeCallback arg2 ;\n  Util::SecureAllocCallback *argp1 ;\n  Util::SecureFreeCallback *argp2 ;\n  \n  (void)jenv;\n  (void)jcls;\n  argp1 = *(Util::SecureAllocCallback **)&jarg1; \n  if (!argp1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"Attempt to dereference null Util::SecureAllocCallback\");\n    return ;\n  }\n  arg1 = *argp1; \n  argp2 = *(Util::SecureFreeCallback **)&jarg2; \n  if (!argp2) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"Attempt to dereference null Util::SecureFreeCallback\");\n    return ;\n  }\n  arg2 = *argp2; \n  bls::BLS::SetSecureAllocator(arg1,arg2);\n}\n#endif\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_BLS_1HashPubKeys(JNIEnv *jenv, jclass jcls, jlong jarg1, jlong jarg2, jlong jarg3, jobject jarg3_, jlong jarg4) {\n  bn_t *arg1 = (bn_t *) 0 ;\n  size_t arg2 ;\n  std::vector< uint8_t * > *arg3 = 0 ;\n  std::vector< size_t > *arg4 = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg3_;\n  arg1 = *(bn_t **)&jarg1; \n  arg2 = (size_t)jarg2; \n  arg3 = *(std::vector< uint8_t * > **)&jarg3;\n  if (!arg3) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< uint8_t * > const & reference is null\");\n    return ;\n  } \n  arg4 = *(std::vector< size_t > **)&jarg4;\n  if (!arg4) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< size_t > const & reference is null\");\n    return ;\n  } \n  bls::BLS::HashPubKeys(arg1,arg2,(std::vector< unsigned char * > const &)*arg3,(std::vector< size_t > const &)*arg4);\n}\n\n#ifdef BUILD_DASH_CORE\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_BLS_1PrivateKeyShare(JNIEnv *jenv, jclass jcls, jlong jarg1, jbyteArray jarg2) {\n  jlong jresult = 0 ;\n  std::vector< PrivateKey > *arg1 = 0 ;\n  uint8_t *arg2 = (uint8_t *) 0 ;\n  PrivateKey result;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = *(std::vector< PrivateKey > **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< PrivateKey > const & reference is null\");\n    return 0;\n  } \n  {\n    arg2 = (uint8_t *) jenv->GetByteArrayElements(jarg2, 0);\n  }\n  result = bls::BLS::PrivateKeyShare((std::vector< PrivateKey > const &)*arg1,(unsigned char const *)arg2);\n  *(PrivateKey **)&jresult = new PrivateKey((const PrivateKey &)result); \n  {\n    jenv->ReleaseByteArrayElements(jarg2, (jbyte *) arg2, 0);\n  }\n  \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_BLS_1RecoverPrivateKey(JNIEnv *jenv, jclass jcls, jlong jarg1, jlong jarg2) {\n  jlong jresult = 0 ;\n  std::vector< PrivateKey > *arg1 = 0 ;\n  std::vector< uint8_t const * > *arg2 = 0 ;\n  PrivateKey result;\n\n  (void)jenv;\n  (void)jcls;\n  arg1 = *(std::vector< PrivateKey > **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< PrivateKey > const & reference is null\");\n    return 0;\n  }\n  arg2 = *(std::vector< uint8_t const * > **)&jarg2;\n  if (!arg2) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< uint8_t const * > const & reference is null\");\n    return 0;\n  }\n  result = bls::BLS::RecoverPrivateKey((std::vector< PrivateKey > const &)*arg1,(std::vector< unsigned char const * > const &)*arg2);\n  *(PrivateKey **)&jresult = new PrivateKey((const PrivateKey &)result);\n  return jresult;\n}\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_BLS_1PublicKeyShare(JNIEnv *jenv, jclass jcls, jlong jarg1, jbyteArray jarg2) {\n  jlong jresult = 0 ;\n  std::vector< PrivateKey > *arg1 = 0 ;\n  uint8_t *arg2 = (uint8_t *) 0 ;\n  PrivateKey result;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = *(std::vector< PrivateKey > **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< PrivateKey > const & reference is null\");\n    return 0;\n  } \n  {\n    arg2 = (uint8_t *) jenv->GetByteArrayElements(jarg2, 0);\n  }\n  result = bls::BLS::PrivateKeyShare((std::vector< PrivateKey > const &)*arg1,(unsigned char const *)arg2);\n  *(PrivateKey **)&jresult = new PrivateKey((const PrivateKey &)result); \n  {\n    jenv->ReleaseByteArrayElements(jarg2, (jbyte *) arg2, 0);\n  }\n  \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_BLS_1RecoverPublicKey(JNIEnv *jenv, jclass jcls, jlong jarg1, jlong jarg2) {\n  jlong jresult = 0 ;\n  std::vector< PublicKey > *arg1 = 0 ;\n  std::vector< uint8_t const * > *arg2 = 0 ;\n  PublicKey result;\n\n  (void)jenv;\n  (void)jcls;\n  arg1 = *(std::vector< PublicKey > **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< PublicKey > const & reference is null\");\n    return 0;\n  }\n  arg2 = *(std::vector< uint8_t const * > **)&jarg2;\n  if (!arg2) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< uint8_t const * > const & reference is null\");\n    return 0;\n  }\n  result = bls::BLS::RecoverPublicKey((std::vector< PublicKey > const &)*arg1,(std::vector< unsigned char const * > const &)*arg2);\n  *(PublicKey **)&jresult = new PublicKey((const PublicKey &)result);\n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_BLS_1SignatureShare(JNIEnv *jenv, jclass jcls, jlong jarg1, jbyteArray jarg2) {\n  jlong jresult = 0 ;\n  std::vector< InsecureSignature > *arg1 = 0 ;\n  uint8_t *arg2 = (uint8_t *) 0 ;\n  InsecureSignature result;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = *(std::vector< InsecureSignature > **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< InsecureSignature > const & reference is null\");\n    return 0;\n  } \n  {\n    arg2 = (uint8_t *) jenv->GetByteArrayElements(jarg2, 0);\n  }\n  result = bls::BLS::SignatureShare((std::vector< InsecureSignature > const &)*arg1,(unsigned char const *)arg2);\n  *(InsecureSignature **)&jresult = new InsecureSignature((const InsecureSignature &)result); \n  {\n    jenv->ReleaseByteArrayElements(jarg2, (jbyte *) arg2, 0);\n  }\n  \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_BLS_1RecoverSig(JNIEnv *jenv, jclass jcls, jlong jarg1, jlong jarg2) {\n  jlong jresult = 0 ;\n  std::vector< InsecureSignature > *arg1 = 0 ;\n  std::vector< uint8_t const * > *arg2 = 0 ;\n  InsecureSignature result;\n\n  (void)jenv;\n  (void)jcls;\n  arg1 = *(std::vector< InsecureSignature > **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< InsecureSignature > const & reference is null\");\n    return 0;\n  }\n  arg2 = *(std::vector< uint8_t const * > **)&jarg2;\n  if (!arg2) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< uint8_t const * > const & reference is null\");\n    return 0;\n  }\n  result = bls::BLS::RecoverSig((std::vector< InsecureSignature > const &)*arg1,(std::vector< unsigned char const * > const &)*arg2);\n  *(InsecureSignature **)&jresult = new InsecureSignature((const InsecureSignature &)result);\n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_BLS_1DHKeyExchange(JNIEnv *jenv, jclass jcls, jlong jarg1, jlong jarg2) {\n  jlong jresult = 0 ;\n  PrivateKey *arg1 = 0 ;\n  PublicKey *arg2 = 0 ;\n  PublicKey result;\n\n  (void)jenv;\n  (void)jcls;\n  arg1 = *(PrivateKey **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"PrivateKey const & reference is null\");\n    return 0;\n  }\n  arg2 = *(PublicKey **)&jarg2;\n  if (!arg2) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"PublicKey const & reference is null\");\n    return 0;\n  }\n  result = bls::BLS::DHKeyExchange((PrivateKey const &)*arg1,(PublicKey const &)*arg2);\n  *(PublicKey **)&jresult = new PublicKey((const PublicKey &)result);\n  return jresult;\n}\n#endif\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_BLS_1CheckRelicErrors(JNIEnv *jenv, jclass jcls) {\n  (void)jenv;\n  (void)jcls;\n  bls::BLS::CheckRelicErrors();\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_new_1BLS(JNIEnv *jenv, jclass jcls) {\n  jlong jresult = 0 ;\n  bls::BLS *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  result = (bls::BLS *)new bls::BLS();\n  *(bls::BLS **)&jresult = result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_delete_1BLS(JNIEnv *jenv, jclass jcls, jlong jarg1) {\n  bls::BLS *arg1 = (bls::BLS *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = *(bls::BLS **)&jarg1; \n  delete arg1;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ChainCode_1CHAIN_1CODE_1SIZE_1get(JNIEnv *jenv, jclass jcls) {\n  jlong jresult = 0 ;\n  size_t result;\n  \n  (void)jenv;\n  (void)jcls;\n  result = bls::ChainCode::CHAIN_CODE_SIZE;\n  jresult = (jlong)result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ChainCode_1FromBytes(JNIEnv *jenv, jclass jcls, jbyteArray jarg1) {\n  jlong jresult = 0 ;\n  uint8_t *arg1 = (uint8_t *) 0 ;\n  SwigValueWrapper< bls::ChainCode > result;\n  \n  (void)jenv;\n  (void)jcls;\n  {\n    arg1 = (uint8_t *) jenv->GetByteArrayElements(jarg1, 0);\n  }\n  result = bls::ChainCode::FromBytes((unsigned char const *)arg1);\n  *(bls::ChainCode **)&jresult = new bls::ChainCode((const bls::ChainCode &)result); \n  {\n    jenv->ReleaseByteArrayElements(jarg1, (jbyte *) arg1, 0);\n  }\n  \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_new_1ChainCode(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::ChainCode *arg1 = 0 ;\n  bls::ChainCode *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::ChainCode **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"bls::ChainCode const & reference is null\");\n    return 0;\n  } \n  result = (bls::ChainCode *)new bls::ChainCode((bls::ChainCode const &)*arg1);\n  *(bls::ChainCode **)&jresult = result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_ChainCode_1Serialize_1_1SWIG_10(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jbyteArray jarg2) {\n  bls::ChainCode *arg1 = (bls::ChainCode *) 0 ;\n  uint8_t *arg2 = (uint8_t *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::ChainCode **)&jarg1; \n  {\n    arg2 = (uint8_t *) jenv->GetByteArrayElements(jarg2, 0);\n  }\n  ((bls::ChainCode const *)arg1)->Serialize(arg2);\n  {\n    jenv->ReleaseByteArrayElements(jarg2, (jbyte *) arg2, 0);\n  }\n  \n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ChainCode_1Serialize_1_1SWIG_11(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::ChainCode *arg1 = (bls::ChainCode *) 0 ;\n  SwigValueWrapper< std::vector< unsigned char > > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::ChainCode **)&jarg1; \n  result = ((bls::ChainCode const *)arg1)->Serialize();\n  *(std::vector< uint8_t > **)&jresult = new std::vector< uint8_t >((const std::vector< uint8_t > &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_delete_1ChainCode(JNIEnv *jenv, jclass jcls, jlong jarg1) {\n  bls::ChainCode *arg1 = (bls::ChainCode *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = *(bls::ChainCode **)&jarg1; \n  delete arg1;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ExtendedPrivateKey_1EXTENDED_1PRIVATE_1KEY_1SIZE_1get(JNIEnv *jenv, jclass jcls) {\n  jlong jresult = 0 ;\n  unsigned int result;\n  \n  (void)jenv;\n  (void)jcls;\n  result = (unsigned int)bls::ExtendedPrivateKey::EXTENDED_PRIVATE_KEY_SIZE;\n  jresult = (jlong)result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ExtendedPrivateKey_1FromSeed(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jlong jarg2) {\n  jlong jresult = 0 ;\n  uint8_t *arg1 = (uint8_t *) 0 ;\n  size_t arg2 ;\n  SwigValueWrapper< bls::ExtendedPrivateKey > result;\n  \n  (void)jenv;\n  (void)jcls;\n  {\n    arg1 = (uint8_t *) jenv->GetByteArrayElements(jarg1, 0);\n  }\n  arg2 = (size_t)jarg2; \n  result = bls::ExtendedPrivateKey::FromSeed((unsigned char const *)arg1,arg2);\n  *(bls::ExtendedPrivateKey **)&jresult = new bls::ExtendedPrivateKey((const bls::ExtendedPrivateKey &)result); \n  {\n    jenv->ReleaseByteArrayElements(jarg1, (jbyte *) arg1, 0);\n  }\n  \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ExtendedPrivateKey_1FromBytes(JNIEnv *jenv, jclass jcls, jbyteArray jarg1) {\n  jlong jresult = 0 ;\n  uint8_t *arg1 = (uint8_t *) 0 ;\n  SwigValueWrapper< bls::ExtendedPrivateKey > result;\n  \n  (void)jenv;\n  (void)jcls;\n  {\n    arg1 = (uint8_t *) jenv->GetByteArrayElements(jarg1, 0);\n  }\n  result = bls::ExtendedPrivateKey::FromBytes((unsigned char const *)arg1);\n  *(bls::ExtendedPrivateKey **)&jresult = new bls::ExtendedPrivateKey((const bls::ExtendedPrivateKey &)result); \n  {\n    jenv->ReleaseByteArrayElements(jarg1, (jbyte *) arg1, 0);\n  }\n  \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ExtendedPrivateKey_1PrivateChild(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2) {\n  jlong jresult = 0 ;\n  bls::ExtendedPrivateKey *arg1 = (bls::ExtendedPrivateKey *) 0 ;\n  uint32_t arg2 ;\n  SwigValueWrapper< bls::ExtendedPrivateKey > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::ExtendedPrivateKey **)&jarg1; \n  arg2 = (uint32_t)jarg2; \n  result = ((bls::ExtendedPrivateKey const *)arg1)->PrivateChild(arg2);\n  *(bls::ExtendedPrivateKey **)&jresult = new bls::ExtendedPrivateKey((const bls::ExtendedPrivateKey &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ExtendedPrivateKey_1PublicChild(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2) {\n  jlong jresult = 0 ;\n  bls::ExtendedPrivateKey *arg1 = (bls::ExtendedPrivateKey *) 0 ;\n  uint32_t arg2 ;\n  SwigValueWrapper< bls::ExtendedPublicKey > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::ExtendedPrivateKey **)&jarg1; \n  arg2 = (uint32_t)jarg2; \n  result = ((bls::ExtendedPrivateKey const *)arg1)->PublicChild(arg2);\n  *(ExtendedPublicKey **)&jresult = new ExtendedPublicKey((const ExtendedPublicKey &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ExtendedPrivateKey_1GetVersion(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::ExtendedPrivateKey *arg1 = (bls::ExtendedPrivateKey *) 0 ;\n  uint32_t result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::ExtendedPrivateKey **)&jarg1; \n  result = (uint32_t)((bls::ExtendedPrivateKey const *)arg1)->GetVersion();\n  jresult = (jlong)result; \n  return jresult;\n}\n\n\nSWIGEXPORT jshort JNICALL Java_org_dashj_bls_JNI_ExtendedPrivateKey_1GetDepth(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jshort jresult = 0 ;\n  bls::ExtendedPrivateKey *arg1 = (bls::ExtendedPrivateKey *) 0 ;\n  uint8_t result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::ExtendedPrivateKey **)&jarg1; \n  result = (uint8_t)((bls::ExtendedPrivateKey const *)arg1)->GetDepth();\n  jresult = (jshort)result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ExtendedPrivateKey_1GetParentFingerprint(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::ExtendedPrivateKey *arg1 = (bls::ExtendedPrivateKey *) 0 ;\n  uint32_t result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::ExtendedPrivateKey **)&jarg1; \n  result = (uint32_t)((bls::ExtendedPrivateKey const *)arg1)->GetParentFingerprint();\n  jresult = (jlong)result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ExtendedPrivateKey_1GetChildNumber(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::ExtendedPrivateKey *arg1 = (bls::ExtendedPrivateKey *) 0 ;\n  uint32_t result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::ExtendedPrivateKey **)&jarg1; \n  result = (uint32_t)((bls::ExtendedPrivateKey const *)arg1)->GetChildNumber();\n  jresult = (jlong)result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ExtendedPrivateKey_1GetChainCode(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::ExtendedPrivateKey *arg1 = (bls::ExtendedPrivateKey *) 0 ;\n  SwigValueWrapper< bls::ChainCode > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::ExtendedPrivateKey **)&jarg1; \n  result = ((bls::ExtendedPrivateKey const *)arg1)->GetChainCode();\n  *(bls::ChainCode **)&jresult = new bls::ChainCode((const bls::ChainCode &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ExtendedPrivateKey_1GetPrivateKey(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::ExtendedPrivateKey *arg1 = (bls::ExtendedPrivateKey *) 0 ;\n  SwigValueWrapper< bls::PrivateKey > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::ExtendedPrivateKey **)&jarg1; \n  result = ((bls::ExtendedPrivateKey const *)arg1)->GetPrivateKey();\n  *(PrivateKey **)&jresult = new PrivateKey((const PrivateKey &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ExtendedPrivateKey_1GetPublicKey(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::ExtendedPrivateKey *arg1 = (bls::ExtendedPrivateKey *) 0 ;\n  SwigValueWrapper< bls::PublicKey > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::ExtendedPrivateKey **)&jarg1; \n  result = ((bls::ExtendedPrivateKey const *)arg1)->GetPublicKey();\n  *(PublicKey **)&jresult = new PublicKey((const PublicKey &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ExtendedPrivateKey_1GetExtendedPublicKey(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::ExtendedPrivateKey *arg1 = (bls::ExtendedPrivateKey *) 0 ;\n  SwigValueWrapper< bls::ExtendedPublicKey > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::ExtendedPrivateKey **)&jarg1; \n  result = ((bls::ExtendedPrivateKey const *)arg1)->GetExtendedPublicKey();\n  *(ExtendedPublicKey **)&jresult = new ExtendedPublicKey((const ExtendedPublicKey &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_ExtendedPrivateKey_1Serialize_1_1SWIG_10(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jbyteArray jarg2) {\n  bls::ExtendedPrivateKey *arg1 = (bls::ExtendedPrivateKey *) 0 ;\n  uint8_t *arg2 = (uint8_t *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::ExtendedPrivateKey **)&jarg1; \n  {\n    arg2 = (uint8_t *) jenv->GetByteArrayElements(jarg2, 0);\n  }\n  ((bls::ExtendedPrivateKey const *)arg1)->Serialize(arg2);\n  {\n    jenv->ReleaseByteArrayElements(jarg2, (jbyte *) arg2, 0);\n  }\n  \n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ExtendedPrivateKey_1Serialize_1_1SWIG_11(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::ExtendedPrivateKey *arg1 = (bls::ExtendedPrivateKey *) 0 ;\n  SwigValueWrapper< std::vector< unsigned char > > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::ExtendedPrivateKey **)&jarg1; \n  result = ((bls::ExtendedPrivateKey const *)arg1)->Serialize();\n  *(std::vector< uint8_t > **)&jresult = new std::vector< uint8_t >((const std::vector< uint8_t > &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_delete_1ExtendedPrivateKey(JNIEnv *jenv, jclass jcls, jlong jarg1) {\n  bls::ExtendedPrivateKey *arg1 = (bls::ExtendedPrivateKey *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = *(bls::ExtendedPrivateKey **)&jarg1; \n  delete arg1;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ExtendedPublicKey_1VERSION_1get(JNIEnv *jenv, jclass jcls) {\n  jlong jresult = 0 ;\n  unsigned int result;\n  \n  (void)jenv;\n  (void)jcls;\n  result = (unsigned int)bls::ExtendedPublicKey::VERSION;\n  jresult = (jlong)result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ExtendedPublicKey_1EXTENDED_1PUBLIC_1KEY_1SIZE_1get(JNIEnv *jenv, jclass jcls) {\n  jlong jresult = 0 ;\n  unsigned int result;\n  \n  (void)jenv;\n  (void)jcls;\n  result = (unsigned int)bls::ExtendedPublicKey::EXTENDED_PUBLIC_KEY_SIZE;\n  jresult = (jlong)result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ExtendedPublicKey_1FromBytes(JNIEnv *jenv, jclass jcls, jbyteArray jarg1) {\n  jlong jresult = 0 ;\n  uint8_t *arg1 = (uint8_t *) 0 ;\n  SwigValueWrapper< bls::ExtendedPublicKey > result;\n  \n  (void)jenv;\n  (void)jcls;\n  {\n    arg1 = (uint8_t *) jenv->GetByteArrayElements(jarg1, 0);\n  }\n  result = bls::ExtendedPublicKey::FromBytes((unsigned char const *)arg1);\n  *(bls::ExtendedPublicKey **)&jresult = new bls::ExtendedPublicKey((const bls::ExtendedPublicKey &)result); \n  {\n    jenv->ReleaseByteArrayElements(jarg1, (jbyte *) arg1, 0);\n  }\n  \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ExtendedPublicKey_1PublicChild(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2) {\n  jlong jresult = 0 ;\n  bls::ExtendedPublicKey *arg1 = (bls::ExtendedPublicKey *) 0 ;\n  uint32_t arg2 ;\n  SwigValueWrapper< bls::ExtendedPublicKey > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::ExtendedPublicKey **)&jarg1; \n  arg2 = (uint32_t)jarg2; \n  result = ((bls::ExtendedPublicKey const *)arg1)->PublicChild(arg2);\n  *(bls::ExtendedPublicKey **)&jresult = new bls::ExtendedPublicKey((const bls::ExtendedPublicKey &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ExtendedPublicKey_1GetVersion(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::ExtendedPublicKey *arg1 = (bls::ExtendedPublicKey *) 0 ;\n  uint32_t result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::ExtendedPublicKey **)&jarg1; \n  result = (uint32_t)((bls::ExtendedPublicKey const *)arg1)->GetVersion();\n  jresult = (jlong)result; \n  return jresult;\n}\n\n\nSWIGEXPORT jshort JNICALL Java_org_dashj_bls_JNI_ExtendedPublicKey_1GetDepth(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jshort jresult = 0 ;\n  bls::ExtendedPublicKey *arg1 = (bls::ExtendedPublicKey *) 0 ;\n  uint8_t result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::ExtendedPublicKey **)&jarg1; \n  result = (uint8_t)((bls::ExtendedPublicKey const *)arg1)->GetDepth();\n  jresult = (jshort)result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ExtendedPublicKey_1GetParentFingerprint(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::ExtendedPublicKey *arg1 = (bls::ExtendedPublicKey *) 0 ;\n  uint32_t result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::ExtendedPublicKey **)&jarg1; \n  result = (uint32_t)((bls::ExtendedPublicKey const *)arg1)->GetParentFingerprint();\n  jresult = (jlong)result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ExtendedPublicKey_1GetChildNumber(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::ExtendedPublicKey *arg1 = (bls::ExtendedPublicKey *) 0 ;\n  uint32_t result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::ExtendedPublicKey **)&jarg1; \n  result = (uint32_t)((bls::ExtendedPublicKey const *)arg1)->GetChildNumber();\n  jresult = (jlong)result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ExtendedPublicKey_1GetChainCode(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::ExtendedPublicKey *arg1 = (bls::ExtendedPublicKey *) 0 ;\n  SwigValueWrapper< bls::ChainCode > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::ExtendedPublicKey **)&jarg1; \n  result = ((bls::ExtendedPublicKey const *)arg1)->GetChainCode();\n  *(bls::ChainCode **)&jresult = new bls::ChainCode((const bls::ChainCode &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ExtendedPublicKey_1GetPublicKey(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::ExtendedPublicKey *arg1 = (bls::ExtendedPublicKey *) 0 ;\n  SwigValueWrapper< bls::PublicKey > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::ExtendedPublicKey **)&jarg1; \n  result = ((bls::ExtendedPublicKey const *)arg1)->GetPublicKey();\n  *(PublicKey **)&jresult = new PublicKey((const PublicKey &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_ExtendedPublicKey_1Serialize_1_1SWIG_10(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jbyteArray jarg2) {\n  bls::ExtendedPublicKey *arg1 = (bls::ExtendedPublicKey *) 0 ;\n  uint8_t *arg2 = (uint8_t *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::ExtendedPublicKey **)&jarg1; \n  {\n    arg2 = (uint8_t *) jenv->GetByteArrayElements(jarg2, 0);\n  }\n  ((bls::ExtendedPublicKey const *)arg1)->Serialize(arg2);\n  {\n    jenv->ReleaseByteArrayElements(jarg2, (jbyte *) arg2, 0);\n  }\n  \n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ExtendedPublicKey_1Serialize_1_1SWIG_11(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::ExtendedPublicKey *arg1 = (bls::ExtendedPublicKey *) 0 ;\n  SwigValueWrapper< std::vector< unsigned char > > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::ExtendedPublicKey **)&jarg1; \n  result = ((bls::ExtendedPublicKey const *)arg1)->Serialize();\n  *(std::vector< uint8_t > **)&jresult = new std::vector< uint8_t >((const std::vector< uint8_t > &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_delete_1ExtendedPublicKey(JNIEnv *jenv, jclass jcls, jlong jarg1) {\n  bls::ExtendedPublicKey *arg1 = (bls::ExtendedPublicKey *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = *(bls::ExtendedPublicKey **)&jarg1; \n  delete arg1;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_PrivateKey_1PRIVATE_1KEY_1SIZE_1get(JNIEnv *jenv, jclass jcls) {\n  jlong jresult = 0 ;\n  size_t result;\n  \n  (void)jenv;\n  (void)jcls;\n  result = bls::PrivateKey::PRIVATE_KEY_SIZE;\n  jresult = (jlong)result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_PrivateKey_1FromSeed(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jlong jarg2) {\n  jlong jresult = 0 ;\n  uint8_t *arg1 = (uint8_t *) 0 ;\n  size_t arg2 ;\n  SwigValueWrapper< bls::PrivateKey > result;\n  \n  (void)jenv;\n  (void)jcls;\n  {\n    arg1 = (uint8_t *) jenv->GetByteArrayElements(jarg1, 0);\n  }\n  arg2 = (size_t)jarg2; \n  result = bls::PrivateKey::FromSeed((unsigned char const *)arg1,arg2);\n  *(bls::PrivateKey **)&jresult = new bls::PrivateKey((const bls::PrivateKey &)result); \n  {\n    jenv->ReleaseByteArrayElements(jarg1, (jbyte *) arg1, 0);\n  }\n  \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_PrivateKey_1FromBytes_1_1SWIG_10(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jboolean jarg2) {\n  jlong jresult = 0 ;\n  uint8_t *arg1 = (uint8_t *) 0 ;\n  bool arg2 ;\n  SwigValueWrapper< bls::PrivateKey > result;\n  \n  (void)jenv;\n  (void)jcls;\n  {\n    arg1 = (uint8_t *) jenv->GetByteArrayElements(jarg1, 0);\n  }\n  arg2 = jarg2 ? true : false; \n  result = bls::PrivateKey::FromBytes((unsigned char const *)arg1,arg2);\n  *(bls::PrivateKey **)&jresult = new bls::PrivateKey((const bls::PrivateKey &)result); \n  {\n    jenv->ReleaseByteArrayElements(jarg1, (jbyte *) arg1, 0);\n  }\n  \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_PrivateKey_1FromBytes_1_1SWIG_11(JNIEnv *jenv, jclass jcls, jbyteArray jarg1) {\n  jlong jresult = 0 ;\n  uint8_t *arg1 = (uint8_t *) 0 ;\n  SwigValueWrapper< bls::PrivateKey > result;\n  \n  (void)jenv;\n  (void)jcls;\n  {\n    arg1 = (uint8_t *) jenv->GetByteArrayElements(jarg1, 0);\n  }\n  result = bls::PrivateKey::FromBytes((unsigned char const *)arg1);\n  *(bls::PrivateKey **)&jresult = new bls::PrivateKey((const bls::PrivateKey &)result); \n  {\n    jenv->ReleaseByteArrayElements(jarg1, (jbyte *) arg1, 0);\n  }\n  \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_new_1PrivateKey_1_1SWIG_10(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::PrivateKey *arg1 = 0 ;\n  bls::PrivateKey *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::PrivateKey **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"bls::PrivateKey const & reference is null\");\n    return 0;\n  } \n  result = (bls::PrivateKey *)new bls::PrivateKey((bls::PrivateKey const &)*arg1);\n  *(bls::PrivateKey **)&jresult = result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_delete_1PrivateKey(JNIEnv *jenv, jclass jcls, jlong jarg1) {\n  bls::PrivateKey *arg1 = (bls::PrivateKey *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = *(bls::PrivateKey **)&jarg1; \n  delete arg1;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_PrivateKey_1GetPublicKey(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::PrivateKey *arg1 = (bls::PrivateKey *) 0 ;\n  SwigValueWrapper< bls::PublicKey > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::PrivateKey **)&jarg1; \n  result = ((bls::PrivateKey const *)arg1)->GetPublicKey();\n  *(PublicKey **)&jresult = new PublicKey((const PublicKey &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_PrivateKey_1AggregateInsecure(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  std::vector< bls::PrivateKey > *arg1 = 0 ;\n  SwigValueWrapper< bls::PrivateKey > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::PrivateKey > **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::PrivateKey > const & reference is null\");\n    return 0;\n  } \n  result = bls::PrivateKey::AggregateInsecure((std::vector< bls::PrivateKey > const &)*arg1);\n  *(bls::PrivateKey **)&jresult = new bls::PrivateKey((const bls::PrivateKey &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_PrivateKey_1Aggregate(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2) {\n  jlong jresult = 0 ;\n  std::vector< bls::PrivateKey > *arg1 = 0 ;\n  std::vector< PublicKey > *arg2 = 0 ;\n  SwigValueWrapper< bls::PrivateKey > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::PrivateKey > **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::PrivateKey > const & reference is null\");\n    return 0;\n  } \n  arg2 = *(std::vector< PublicKey > **)&jarg2;\n  if (!arg2) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< PublicKey > const & reference is null\");\n    return 0;\n  } \n  result = bls::PrivateKey::Aggregate((std::vector< bls::PrivateKey > const &)*arg1,(std::vector< PublicKey > const &)*arg2);\n  *(bls::PrivateKey **)&jresult = new bls::PrivateKey((const bls::PrivateKey &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_PrivateKey_1Serialize_1_1SWIG_10(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jbyteArray jarg2) {\n  bls::PrivateKey *arg1 = (bls::PrivateKey *) 0 ;\n  uint8_t *arg2 = (uint8_t *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::PrivateKey **)&jarg1; \n  {\n    arg2 = (uint8_t *) jenv->GetByteArrayElements(jarg2, 0);\n  }\n  ((bls::PrivateKey const *)arg1)->Serialize(arg2);\n  {\n    jenv->ReleaseByteArrayElements(jarg2, (jbyte *) arg2, 0);\n  }\n  \n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_PrivateKey_1Serialize_1_1SWIG_11(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::PrivateKey *arg1 = (bls::PrivateKey *) 0 ;\n  SwigValueWrapper< std::vector< unsigned char > > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::PrivateKey **)&jarg1; \n  result = ((bls::PrivateKey const *)arg1)->Serialize();\n  *(std::vector< uint8_t > **)&jresult = new std::vector< uint8_t >((const std::vector< uint8_t > &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_PrivateKey_1SignInsecure(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jbyteArray jarg2, jlong jarg3) {\n  jlong jresult = 0 ;\n  bls::PrivateKey *arg1 = (bls::PrivateKey *) 0 ;\n  uint8_t *arg2 = (uint8_t *) 0 ;\n  size_t arg3 ;\n  SwigValueWrapper< bls::InsecureSignature > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::PrivateKey **)&jarg1; \n  {\n    arg2 = (uint8_t *) jenv->GetByteArrayElements(jarg2, 0);\n  }\n  arg3 = (size_t)jarg3; \n  result = ((bls::PrivateKey const *)arg1)->SignInsecure((uint8_t const *)arg2,arg3);\n  *(InsecureSignature **)&jresult = new InsecureSignature((const InsecureSignature &)result); \n  {\n    jenv->ReleaseByteArrayElements(jarg2, (jbyte *) arg2, 0);\n  }\n  \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_PrivateKey_1SignInsecurePrehashed(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jbyteArray jarg2) {\n  jlong jresult = 0 ;\n  bls::PrivateKey *arg1 = (bls::PrivateKey *) 0 ;\n  uint8_t *arg2 = (uint8_t *) 0 ;\n  SwigValueWrapper< bls::InsecureSignature > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::PrivateKey **)&jarg1; \n  {\n    arg2 = (uint8_t *) jenv->GetByteArrayElements(jarg2, 0);\n  }\n  result = ((bls::PrivateKey const *)arg1)->SignInsecurePrehashed((uint8_t const *)arg2);\n  *(InsecureSignature **)&jresult = new InsecureSignature((const InsecureSignature &)result); \n  {\n    jenv->ReleaseByteArrayElements(jarg2, (jbyte *) arg2, 0);\n  }\n  \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_PrivateKey_1Sign(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jbyteArray jarg2, jlong jarg3) {\n  jlong jresult = 0 ;\n  bls::PrivateKey *arg1 = (bls::PrivateKey *) 0 ;\n  uint8_t *arg2 = (uint8_t *) 0 ;\n  size_t arg3 ;\n  SwigValueWrapper< bls::Signature > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::PrivateKey **)&jarg1; \n  {\n    arg2 = (uint8_t *) jenv->GetByteArrayElements(jarg2, 0);\n  }\n  arg3 = (size_t)jarg3; \n  result = ((bls::PrivateKey const *)arg1)->Sign((uint8_t const *)arg2,arg3);\n  *(Signature **)&jresult = new Signature((const Signature &)result); \n  {\n    jenv->ReleaseByteArrayElements(jarg2, (jbyte *) arg2, 0);\n  }\n  \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_PrivateKey_1SignPrehashed(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jbyteArray jarg2) {\n  jlong jresult = 0 ;\n  bls::PrivateKey *arg1 = (bls::PrivateKey *) 0 ;\n  uint8_t *arg2 = (uint8_t *) 0 ;\n  SwigValueWrapper< bls::Signature > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::PrivateKey **)&jarg1; \n  {\n    arg2 = (uint8_t *) jenv->GetByteArrayElements(jarg2, 0);\n  }\n  result = ((bls::PrivateKey const *)arg1)->SignPrehashed((uint8_t const *)arg2);\n  *(Signature **)&jresult = new Signature((const Signature &)result); \n  {\n    jenv->ReleaseByteArrayElements(jarg2, (jbyte *) arg2, 0);\n  }\n  \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_PublicKey_1PUBLIC_1KEY_1SIZE_1get(JNIEnv *jenv, jclass jcls) {\n  jlong jresult = 0 ;\n  size_t result;\n  \n  (void)jenv;\n  (void)jcls;\n  result = bls::PublicKey::PUBLIC_KEY_SIZE;\n  jresult = (jlong)result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_PublicKey_1FromBytes(JNIEnv *jenv, jclass jcls, jbyteArray jarg1) {\n  jlong jresult = 0 ;\n  uint8_t *arg1 = (uint8_t *) 0 ;\n  SwigValueWrapper< bls::PublicKey > result;\n  \n  (void)jenv;\n  (void)jcls;\n  {\n    arg1 = (uint8_t *) jenv->GetByteArrayElements(jarg1, 0);\n  }\n  result = bls::PublicKey::FromBytes((unsigned char const *)arg1);\n  *(bls::PublicKey **)&jresult = new bls::PublicKey((const bls::PublicKey &)result); \n  {\n    jenv->ReleaseByteArrayElements(jarg1, (jbyte *) arg1, 0);\n  }\n  \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_PublicKey_1FromG1(JNIEnv *jenv, jclass jcls, jlong jarg1) {\n  jlong jresult = 0 ;\n  g1_t *arg1 = (g1_t *) 0 ;\n  SwigValueWrapper< bls::PublicKey > result;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = *(g1_t **)&jarg1; \n  result = bls::PublicKey::FromG1((g1_t const *)arg1);\n  *(bls::PublicKey **)&jresult = new bls::PublicKey((const bls::PublicKey &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_new_1PublicKey(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::PublicKey *arg1 = 0 ;\n  bls::PublicKey *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::PublicKey **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"bls::PublicKey const & reference is null\");\n    return 0;\n  } \n  result = (bls::PublicKey *)new bls::PublicKey((bls::PublicKey const &)*arg1);\n  *(bls::PublicKey **)&jresult = result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_PublicKey_1AggregateInsecure(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  std::vector< bls::PublicKey > *arg1 = 0 ;\n  SwigValueWrapper< bls::PublicKey > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::PublicKey > **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::PublicKey > const & reference is null\");\n    return 0;\n  } \n  result = bls::PublicKey::AggregateInsecure((std::vector< bls::PublicKey > const &)*arg1);\n  *(bls::PublicKey **)&jresult = new bls::PublicKey((const bls::PublicKey &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_PublicKey_1Aggregate(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  std::vector< bls::PublicKey > *arg1 = 0 ;\n  SwigValueWrapper< bls::PublicKey > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::PublicKey > **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::PublicKey > const & reference is null\");\n    return 0;\n  } \n  result = bls::PublicKey::Aggregate((std::vector< bls::PublicKey > const &)*arg1);\n  *(bls::PublicKey **)&jresult = new bls::PublicKey((const bls::PublicKey &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_PublicKey_1Serialize_1_1SWIG_10(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jbyteArray jarg2) {\n  bls::PublicKey *arg1 = (bls::PublicKey *) 0 ;\n  uint8_t *arg2 = (uint8_t *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::PublicKey **)&jarg1; \n  {\n    arg2 = (uint8_t *) jenv->GetByteArrayElements(jarg2, 0);\n  }\n  ((bls::PublicKey const *)arg1)->Serialize(arg2);\n  {\n    jenv->ReleaseByteArrayElements(jarg2, (jbyte *) arg2, 0);\n  }\n  \n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_PublicKey_1Serialize_1_1SWIG_11(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::PublicKey *arg1 = (bls::PublicKey *) 0 ;\n  SwigValueWrapper< std::vector< unsigned char > > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::PublicKey **)&jarg1; \n  result = ((bls::PublicKey const *)arg1)->Serialize();\n  *(std::vector< uint8_t > **)&jresult = new std::vector< uint8_t >((const std::vector< uint8_t > &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_PublicKey_1GetFingerprint(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::PublicKey *arg1 = (bls::PublicKey *) 0 ;\n  uint32_t result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::PublicKey **)&jarg1; \n  result = (uint32_t)((bls::PublicKey const *)arg1)->GetFingerprint();\n  jresult = (jlong)result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_delete_1PublicKey(JNIEnv *jenv, jclass jcls, jlong jarg1) {\n  bls::PublicKey *arg1 = (bls::PublicKey *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = *(bls::PublicKey **)&jarg1; \n  delete arg1;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_InsecureSignature_1SIGNATURE_1SIZE_1get(JNIEnv *jenv, jclass jcls) {\n  jlong jresult = 0 ;\n  size_t result;\n  \n  (void)jenv;\n  (void)jcls;\n  result = bls::InsecureSignature::SIGNATURE_SIZE;\n  jresult = (jlong)result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_InsecureSignature_1FromBytes(JNIEnv *jenv, jclass jcls, jbyteArray jarg1) {\n  jlong jresult = 0 ;\n  uint8_t *arg1 = (uint8_t *) 0 ;\n  SwigValueWrapper< bls::InsecureSignature > result;\n  \n  (void)jenv;\n  (void)jcls;\n  {\n    arg1 = (uint8_t *) jenv->GetByteArrayElements(jarg1, 0);\n  }\n  result = bls::InsecureSignature::FromBytes((unsigned char const *)arg1);\n  *(bls::InsecureSignature **)&jresult = new bls::InsecureSignature((const bls::InsecureSignature &)result); \n  {\n    jenv->ReleaseByteArrayElements(jarg1, (jbyte *) arg1, 0);\n  }\n  \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_InsecureSignature_1FromG2(JNIEnv *jenv, jclass jcls, jlong jarg1) {\n  jlong jresult = 0 ;\n  g2_t *arg1 = (g2_t *) 0 ;\n  SwigValueWrapper< bls::InsecureSignature > result;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = *(g2_t **)&jarg1; \n  result = bls::InsecureSignature::FromG2((g2_t const *)arg1);\n  *(bls::InsecureSignature **)&jresult = new bls::InsecureSignature((const bls::InsecureSignature &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_new_1InsecureSignature(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::InsecureSignature *arg1 = 0 ;\n  bls::InsecureSignature *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::InsecureSignature **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"bls::InsecureSignature const & reference is null\");\n    return 0;\n  } \n  result = (bls::InsecureSignature *)new bls::InsecureSignature((bls::InsecureSignature const &)*arg1);\n  *(bls::InsecureSignature **)&jresult = result; \n  return jresult;\n}\n\n\nSWIGEXPORT jboolean JNICALL Java_org_dashj_bls_JNI_InsecureSignature_1Verify(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2, jlong jarg3, jobject jarg3_) {\n  jboolean jresult = 0 ;\n  bls::InsecureSignature *arg1 = (bls::InsecureSignature *) 0 ;\n  std::vector< uint8_t const * > *arg2 = 0 ;\n  std::vector< bls::PublicKey > *arg3 = 0 ;\n  bool result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  (void)jarg3_;\n  arg1 = *(bls::InsecureSignature **)&jarg1; \n  arg2 = *(std::vector< uint8_t const * > **)&jarg2;\n  if (!arg2) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< uint8_t const * > const & reference is null\");\n    return 0;\n  } \n  arg3 = *(std::vector< bls::PublicKey > **)&jarg3;\n  if (!arg3) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::PublicKey > const & reference is null\");\n    return 0;\n  } \n  result = (bool)((bls::InsecureSignature const *)arg1)->Verify((std::vector< uint8_t const * > const &)*arg2,(std::vector< bls::PublicKey > const &)*arg3);\n  jresult = (jboolean)result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_InsecureSignature_1Aggregate(JNIEnv *jenv, jclass jcls, jlong jarg1) {\n  jlong jresult = 0 ;\n  std::vector< bls::InsecureSignature > *arg1 = 0 ;\n  SwigValueWrapper< bls::InsecureSignature > result;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = *(std::vector< bls::InsecureSignature > **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::InsecureSignature > const & reference is null\");\n    return 0;\n  } \n  result = bls::InsecureSignature::Aggregate((std::vector< bls::InsecureSignature > const &)*arg1);\n  *(bls::InsecureSignature **)&jresult = new bls::InsecureSignature((const bls::InsecureSignature &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_InsecureSignature_1DivideBy(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2) {\n  jlong jresult = 0 ;\n  bls::InsecureSignature *arg1 = (bls::InsecureSignature *) 0 ;\n  std::vector< bls::InsecureSignature > *arg2 = 0 ;\n  SwigValueWrapper< bls::InsecureSignature > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::InsecureSignature **)&jarg1; \n  arg2 = *(std::vector< bls::InsecureSignature > **)&jarg2;\n  if (!arg2) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::InsecureSignature > const & reference is null\");\n    return 0;\n  } \n  result = ((bls::InsecureSignature const *)arg1)->DivideBy((std::vector< bls::InsecureSignature > const &)*arg2);\n  *(bls::InsecureSignature **)&jresult = new bls::InsecureSignature((const bls::InsecureSignature &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_InsecureSignature_1Serialize_1_1SWIG_10(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jbyteArray jarg2) {\n  bls::InsecureSignature *arg1 = (bls::InsecureSignature *) 0 ;\n  uint8_t *arg2 = (uint8_t *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::InsecureSignature **)&jarg1; \n  {\n    arg2 = (uint8_t *) jenv->GetByteArrayElements(jarg2, 0);\n  }\n  ((bls::InsecureSignature const *)arg1)->Serialize(arg2);\n  {\n    jenv->ReleaseByteArrayElements(jarg2, (jbyte *) arg2, 0);\n  }\n  \n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_InsecureSignature_1Serialize_1_1SWIG_11(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::InsecureSignature *arg1 = (bls::InsecureSignature *) 0 ;\n  SwigValueWrapper< std::vector< unsigned char > > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::InsecureSignature **)&jarg1; \n  result = ((bls::InsecureSignature const *)arg1)->Serialize();\n  *(std::vector< uint8_t > **)&jresult = new std::vector< uint8_t >((const std::vector< uint8_t > &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_delete_1InsecureSignature(JNIEnv *jenv, jclass jcls, jlong jarg1) {\n  bls::InsecureSignature *arg1 = (bls::InsecureSignature *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = *(bls::InsecureSignature **)&jarg1; \n  delete arg1;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_Signature_1SIGNATURE_1SIZE_1get(JNIEnv *jenv, jclass jcls) {\n  jlong jresult = 0 ;\n  size_t result;\n  \n  (void)jenv;\n  (void)jcls;\n  result = bls::Signature::SIGNATURE_SIZE;\n  jresult = (jlong)result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_Signature_1FromBytes_1_1SWIG_10(JNIEnv *jenv, jclass jcls, jbyteArray jarg1) {\n  jlong jresult = 0 ;\n  uint8_t *arg1 = (uint8_t *) 0 ;\n  SwigValueWrapper< bls::Signature > result;\n  \n  (void)jenv;\n  (void)jcls;\n  {\n    arg1 = (uint8_t *) jenv->GetByteArrayElements(jarg1, 0);\n  }\n  result = bls::Signature::FromBytes((unsigned char const *)arg1);\n  *(bls::Signature **)&jresult = new bls::Signature((const bls::Signature &)result); \n  {\n    jenv->ReleaseByteArrayElements(jarg1, (jbyte *) arg1, 0);\n  }\n  \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_Signature_1FromBytes_1_1SWIG_11(JNIEnv *jenv, jclass jcls, jbyteArray jarg1, jlong jarg2) {\n  jlong jresult = 0 ;\n  uint8_t *arg1 = (uint8_t *) 0 ;\n  AggregationInfo *arg2 = 0 ;\n  SwigValueWrapper< bls::Signature > result;\n  \n  (void)jenv;\n  (void)jcls;\n  {\n    arg1 = (uint8_t *) jenv->GetByteArrayElements(jarg1, 0);\n  }\n  arg2 = *(AggregationInfo **)&jarg2;\n  if (!arg2) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"AggregationInfo const & reference is null\");\n    return 0;\n  } \n  result = bls::Signature::FromBytes((unsigned char const *)arg1,(AggregationInfo const &)*arg2);\n  *(bls::Signature **)&jresult = new bls::Signature((const bls::Signature &)result); \n  {\n    jenv->ReleaseByteArrayElements(jarg1, (jbyte *) arg1, 0);\n  }\n  \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_Signature_1FromG2_1_1SWIG_10(JNIEnv *jenv, jclass jcls, jlong jarg1) {\n  jlong jresult = 0 ;\n  g2_t *arg1 = (g2_t *) 0 ;\n  SwigValueWrapper< bls::Signature > result;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = *(g2_t **)&jarg1; \n  result = bls::Signature::FromG2((g2_t const *)arg1);\n  *(bls::Signature **)&jresult = new bls::Signature((const bls::Signature &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_Signature_1FromG2_1_1SWIG_11(JNIEnv *jenv, jclass jcls, jlong jarg1, jlong jarg2) {\n  jlong jresult = 0 ;\n  g2_t *arg1 = (g2_t *) 0 ;\n  AggregationInfo *arg2 = 0 ;\n  SwigValueWrapper< bls::Signature > result;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = *(g2_t **)&jarg1; \n  arg2 = *(AggregationInfo **)&jarg2;\n  if (!arg2) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"AggregationInfo const & reference is null\");\n    return 0;\n  } \n  result = bls::Signature::FromG2((g2_t const *)arg1,(AggregationInfo const &)*arg2);\n  *(bls::Signature **)&jresult = new bls::Signature((const bls::Signature &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_Signature_1FromInsecureSig_1_1SWIG_10(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::InsecureSignature *arg1 = 0 ;\n  SwigValueWrapper< bls::Signature > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::InsecureSignature **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"bls::InsecureSignature const & reference is null\");\n    return 0;\n  } \n  result = bls::Signature::FromInsecureSig((bls::InsecureSignature const &)*arg1);\n  *(bls::Signature **)&jresult = new bls::Signature((const bls::Signature &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_Signature_1FromInsecureSig_1_1SWIG_11(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2) {\n  jlong jresult = 0 ;\n  bls::InsecureSignature *arg1 = 0 ;\n  AggregationInfo *arg2 = 0 ;\n  SwigValueWrapper< bls::Signature > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::InsecureSignature **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"bls::InsecureSignature const & reference is null\");\n    return 0;\n  } \n  arg2 = *(AggregationInfo **)&jarg2;\n  if (!arg2) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"AggregationInfo const & reference is null\");\n    return 0;\n  } \n  result = bls::Signature::FromInsecureSig((bls::InsecureSignature const &)*arg1,(AggregationInfo const &)*arg2);\n  *(bls::Signature **)&jresult = new bls::Signature((const bls::Signature &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_new_1Signature(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::Signature *arg1 = 0 ;\n  bls::Signature *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::Signature **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"bls::Signature const & reference is null\");\n    return 0;\n  } \n  result = (bls::Signature *)new bls::Signature((bls::Signature const &)*arg1);\n  *(bls::Signature **)&jresult = result; \n  return jresult;\n}\n\n\nSWIGEXPORT jboolean JNICALL Java_org_dashj_bls_JNI_Signature_1Verify(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jboolean jresult = 0 ;\n  bls::Signature *arg1 = (bls::Signature *) 0 ;\n  bool result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::Signature **)&jarg1; \n  result = (bool)((bls::Signature const *)arg1)->Verify();\n  jresult = (jboolean)result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_Signature_1AggregateSigs(JNIEnv *jenv, jclass jcls, jlong jarg1) {\n  jlong jresult = 0 ;\n  std::vector< bls::Signature > *arg1 = 0 ;\n  SwigValueWrapper< bls::Signature > result;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = *(std::vector< bls::Signature > **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::Signature > const & reference is null\");\n    return 0;\n  } \n  result = bls::Signature::AggregateSigs((std::vector< bls::Signature > const &)*arg1);\n  *(bls::Signature **)&jresult = new bls::Signature((const bls::Signature &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_Signature_1DivideBy(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2) {\n  jlong jresult = 0 ;\n  bls::Signature *arg1 = (bls::Signature *) 0 ;\n  std::vector< bls::Signature > *arg2 = 0 ;\n  SwigValueWrapper< bls::Signature > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::Signature **)&jarg1; \n  arg2 = *(std::vector< bls::Signature > **)&jarg2;\n  if (!arg2) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::Signature > const & reference is null\");\n    return 0;\n  } \n  result = ((bls::Signature const *)arg1)->DivideBy((std::vector< bls::Signature > const &)*arg2);\n  *(bls::Signature **)&jresult = new bls::Signature((const bls::Signature &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_Signature_1GetAggregationInfo(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::Signature *arg1 = (bls::Signature *) 0 ;\n  AggregationInfo *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::Signature **)&jarg1; \n  result = (AggregationInfo *)((bls::Signature const *)arg1)->GetAggregationInfo();\n  *(AggregationInfo **)&jresult = result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_Signature_1SetAggregationInfo(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2) {\n  bls::Signature *arg1 = (bls::Signature *) 0 ;\n  AggregationInfo *arg2 = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::Signature **)&jarg1; \n  arg2 = *(AggregationInfo **)&jarg2;\n  if (!arg2) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"AggregationInfo const & reference is null\");\n    return ;\n  } \n  (arg1)->SetAggregationInfo((AggregationInfo const &)*arg2);\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_Signature_1Serialize_1_1SWIG_10(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jbyteArray jarg2) {\n  bls::Signature *arg1 = (bls::Signature *) 0 ;\n  uint8_t *arg2 = (uint8_t *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::Signature **)&jarg1; \n  {\n    arg2 = (uint8_t *) jenv->GetByteArrayElements(jarg2, 0);\n  }\n  ((bls::Signature const *)arg1)->Serialize(arg2);\n  {\n    jenv->ReleaseByteArrayElements(jarg2, (jbyte *) arg2, 0);\n  }\n  \n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_Signature_1Serialize_1_1SWIG_11(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::Signature *arg1 = (bls::Signature *) 0 ;\n  SwigValueWrapper< std::vector< unsigned char > > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::Signature **)&jarg1; \n  result = ((bls::Signature const *)arg1)->Serialize();\n  *(std::vector< uint8_t > **)&jresult = new std::vector< uint8_t >((const std::vector< uint8_t > &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_delete_1Signature(JNIEnv *jenv, jclass jcls, jlong jarg1) {\n  bls::Signature *arg1 = (bls::Signature *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = *(bls::Signature **)&jarg1; \n  delete arg1;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_AggregationInfo_1FromMsgHash(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jbyteArray jarg2) {\n  jlong jresult = 0 ;\n  bls::PublicKey *arg1 = 0 ;\n  uint8_t *arg2 = (uint8_t *) 0 ;\n  bls::AggregationInfo result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::PublicKey **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"bls::PublicKey const & reference is null\");\n    return 0;\n  } \n  {\n    arg2 = (uint8_t *) jenv->GetByteArrayElements(jarg2, 0);\n  }\n  result = bls::AggregationInfo::FromMsgHash((bls::PublicKey const &)*arg1,(unsigned char const *)arg2);\n  *(bls::AggregationInfo **)&jresult = new bls::AggregationInfo((const bls::AggregationInfo &)result); \n  {\n    jenv->ReleaseByteArrayElements(jarg2, (jbyte *) arg2, 0);\n  }\n  \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_AggregationInfo_1FromMsg(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jbyteArray jarg2, jlong jarg3) {\n  jlong jresult = 0 ;\n  bls::PublicKey *arg1 = 0 ;\n  uint8_t *arg2 = (uint8_t *) 0 ;\n  size_t arg3 ;\n  bls::AggregationInfo result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::PublicKey **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"bls::PublicKey const & reference is null\");\n    return 0;\n  } \n  {\n    arg2 = (uint8_t *) jenv->GetByteArrayElements(jarg2, 0);\n  }\n  arg3 = (size_t)jarg3; \n  result = bls::AggregationInfo::FromMsg((bls::PublicKey const &)*arg1,(unsigned char const *)arg2,arg3);\n  *(bls::AggregationInfo **)&jresult = new bls::AggregationInfo((const bls::AggregationInfo &)result); \n  {\n    jenv->ReleaseByteArrayElements(jarg2, (jbyte *) arg2, 0);\n  }\n  \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_AggregationInfo_1FromVectors(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2, jobject jarg2_, jlong jarg3, jobject jarg3_) {\n  jlong jresult = 0 ;\n  std::vector< bls::PublicKey > *arg1 = 0 ;\n  std::vector< uint8_t * > *arg2 = 0 ;\n  std::vector< bn_t * > *arg3 = 0 ;\n  bls::AggregationInfo result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  (void)jarg2_;\n  (void)jarg3_;\n  arg1 = *(std::vector< bls::PublicKey > **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::PublicKey > const & reference is null\");\n    return 0;\n  } \n  arg2 = *(std::vector< uint8_t * > **)&jarg2;\n  if (!arg2) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< uint8_t * > const & reference is null\");\n    return 0;\n  } \n  arg3 = *(std::vector< bn_t * > **)&jarg3;\n  if (!arg3) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bn_t * > const & reference is null\");\n    return 0;\n  } \n  result = bls::AggregationInfo::FromVectors((std::vector< bls::PublicKey > const &)*arg1,(std::vector< unsigned char * > const &)*arg2,(std::vector< bn_t * > const &)*arg3);\n  *(bls::AggregationInfo **)&jresult = new bls::AggregationInfo((const bls::AggregationInfo &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_AggregationInfo_1MergeInfos(JNIEnv *jenv, jclass jcls, jlong jarg1) {\n  jlong jresult = 0 ;\n  std::vector< bls::AggregationInfo > *arg1 = 0 ;\n  bls::AggregationInfo result;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = *(std::vector< bls::AggregationInfo > **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::AggregationInfo > const & reference is null\");\n    return 0;\n  } \n  result = bls::AggregationInfo::MergeInfos((std::vector< bls::AggregationInfo > const &)*arg1);\n  *(bls::AggregationInfo **)&jresult = new bls::AggregationInfo((const bls::AggregationInfo &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_new_1AggregationInfo_1_1SWIG_10(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::AggregationInfo *arg1 = 0 ;\n  bls::AggregationInfo *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::AggregationInfo **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"bls::AggregationInfo const & reference is null\");\n    return 0;\n  } \n  result = (bls::AggregationInfo *)new bls::AggregationInfo((bls::AggregationInfo const &)*arg1);\n  *(bls::AggregationInfo **)&jresult = result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_AggregationInfo_1RemoveEntries(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2, jobject jarg2_, jlong jarg3, jobject jarg3_) {\n  bls::AggregationInfo *arg1 = (bls::AggregationInfo *) 0 ;\n  std::vector< uint8_t * > *arg2 = 0 ;\n  std::vector< bls::PublicKey > *arg3 = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  (void)jarg2_;\n  (void)jarg3_;\n  arg1 = *(bls::AggregationInfo **)&jarg1; \n  arg2 = *(std::vector< uint8_t * > **)&jarg2;\n  if (!arg2) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< uint8_t * > const & reference is null\");\n    return ;\n  } \n  arg3 = *(std::vector< bls::PublicKey > **)&jarg3;\n  if (!arg3) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::PublicKey > const & reference is null\");\n    return ;\n  } \n  (arg1)->RemoveEntries((std::vector< uint8_t * > const &)*arg2,(std::vector< bls::PublicKey > const &)*arg3);\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_AggregationInfo_1GetExponent(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2, jbyteArray jarg3, jlong jarg4, jobject jarg4_) {\n  bls::AggregationInfo *arg1 = (bls::AggregationInfo *) 0 ;\n  bn_t *arg2 = (bn_t *) 0 ;\n  uint8_t *arg3 = (uint8_t *) 0 ;\n  bls::PublicKey *arg4 = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  (void)jarg4_;\n  arg1 = *(bls::AggregationInfo **)&jarg1; \n  arg2 = *(bn_t **)&jarg2; \n  {\n    arg3 = (uint8_t *) jenv->GetByteArrayElements(jarg3, 0);\n  }\n  arg4 = *(bls::PublicKey **)&jarg4;\n  if (!arg4) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"bls::PublicKey const & reference is null\");\n    return ;\n  } \n  ((bls::AggregationInfo const *)arg1)->GetExponent(arg2,(uint8_t const *)arg3,(bls::PublicKey const &)*arg4);\n  {\n    jenv->ReleaseByteArrayElements(jarg3, (jbyte *) arg3, 0);\n  }\n  \n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_AggregationInfo_1GetPubKeys(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::AggregationInfo *arg1 = (bls::AggregationInfo *) 0 ;\n  std::vector< bls::PublicKey > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::AggregationInfo **)&jarg1; \n  result = ((bls::AggregationInfo const *)arg1)->GetPubKeys();\n  *(std::vector< bls::PublicKey > **)&jresult = new std::vector< bls::PublicKey >((const std::vector< bls::PublicKey > &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_AggregationInfo_1GetMessageHashes(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  bls::AggregationInfo *arg1 = (bls::AggregationInfo *) 0 ;\n  std::vector< uint8_t * > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::AggregationInfo **)&jarg1; \n  result = ((bls::AggregationInfo const *)arg1)->GetMessageHashes();\n  *(std::vector< uint8_t * > **)&jresult = new std::vector< uint8_t * >((const std::vector< uint8_t * > &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jboolean JNICALL Java_org_dashj_bls_JNI_AggregationInfo_1Empty(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jboolean jresult = 0 ;\n  bls::AggregationInfo *arg1 = (bls::AggregationInfo *) 0 ;\n  bool result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(bls::AggregationInfo **)&jarg1; \n  result = (bool)((bls::AggregationInfo const *)arg1)->Empty();\n  jresult = (jboolean)result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_new_1AggregationInfo_1_1SWIG_11(JNIEnv *jenv, jclass jcls) {\n  jlong jresult = 0 ;\n  bls::AggregationInfo *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  result = (bls::AggregationInfo *)new bls::AggregationInfo();\n  *(bls::AggregationInfo **)&jresult = result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_delete_1AggregationInfo(JNIEnv *jenv, jclass jcls, jlong jarg1) {\n  bls::AggregationInfo *arg1 = (bls::AggregationInfo *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = *(bls::AggregationInfo **)&jarg1; \n  delete arg1;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_new_1PublicKeyVec_1_1SWIG_10(JNIEnv *jenv, jclass jcls) {\n  jlong jresult = 0 ;\n  std::vector< bls::PublicKey > *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  result = (std::vector< bls::PublicKey > *)new std::vector< bls::PublicKey >();\n  *(std::vector< bls::PublicKey > **)&jresult = result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_new_1PublicKeyVec_1_1SWIG_12(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  std::vector< bls::PublicKey > *arg1 = 0 ;\n  std::vector< bls::PublicKey > *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::PublicKey > **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::PublicKey > const & reference is null\");\n    return 0;\n  } \n  result = (std::vector< bls::PublicKey > *)new std::vector< bls::PublicKey >((std::vector< bls::PublicKey > const &)*arg1);\n  *(std::vector< bls::PublicKey > **)&jresult = result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_PublicKeyVec_1capacity(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  std::vector< bls::PublicKey > *arg1 = (std::vector< bls::PublicKey > *) 0 ;\n  std::vector< bls::PublicKey >::size_type result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::PublicKey > **)&jarg1; \n  result = ((std::vector< bls::PublicKey > const *)arg1)->capacity();\n  jresult = (jlong)result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_PublicKeyVec_1reserve(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2) {\n  std::vector< bls::PublicKey > *arg1 = (std::vector< bls::PublicKey > *) 0 ;\n  std::vector< bls::PublicKey >::size_type arg2 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::PublicKey > **)&jarg1; \n  arg2 = (std::vector< bls::PublicKey >::size_type)jarg2; \n  (arg1)->reserve(arg2);\n}\n\n\nSWIGEXPORT jboolean JNICALL Java_org_dashj_bls_JNI_PublicKeyVec_1isEmpty(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jboolean jresult = 0 ;\n  std::vector< bls::PublicKey > *arg1 = (std::vector< bls::PublicKey > *) 0 ;\n  bool result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::PublicKey > **)&jarg1; \n  result = (bool)((std::vector< bls::PublicKey > const *)arg1)->empty();\n  jresult = (jboolean)result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_PublicKeyVec_1clear(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  std::vector< bls::PublicKey > *arg1 = (std::vector< bls::PublicKey > *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::PublicKey > **)&jarg1; \n  (arg1)->clear();\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_PublicKeyVec_1push_1back(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2, jobject jarg2_) {\n  std::vector< bls::PublicKey > *arg1 = (std::vector< bls::PublicKey > *) 0 ;\n  std::vector< bls::PublicKey >::value_type *arg2 = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  (void)jarg2_;\n  arg1 = *(std::vector< bls::PublicKey > **)&jarg1; \n  arg2 = *(std::vector< bls::PublicKey >::value_type **)&jarg2;\n  if (!arg2) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::PublicKey >::value_type const & reference is null\");\n    return ;\n  } \n  (arg1)->push_back((std::vector< bls::PublicKey >::value_type const &)*arg2);\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_PublicKeyVec_1get(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jint jarg2) {\n  jlong jresult = 0 ;\n  std::vector< bls::PublicKey > *arg1 = (std::vector< bls::PublicKey > *) 0 ;\n  int arg2 ;\n  std::vector< bls::PublicKey >::value_type *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::PublicKey > **)&jarg1; \n  arg2 = (int)jarg2; \n  try {\n    result = (std::vector< bls::PublicKey >::value_type *) &std_vector_Sl_bls_PublicKey_Sg__get((std::vector< bls::PublicKey > const *)arg1,arg2);\n  }\n  catch(std::out_of_range &_e) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, (&_e)->what());\n    return 0;\n  }\n  \n  *(std::vector< bls::PublicKey >::value_type **)&jresult = result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_PublicKeyVec_1set(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jint jarg2, jlong jarg3, jobject jarg3_) {\n  jlong jresult = 0 ;\n  std::vector< bls::PublicKey > *arg1 = (std::vector< bls::PublicKey > *) 0 ;\n  int arg2 ;\n  std::vector< bls::PublicKey >::value_type *arg3 = 0 ;\n  SwigValueWrapper< bls::PublicKey > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  (void)jarg3_;\n  arg1 = *(std::vector< bls::PublicKey > **)&jarg1; \n  arg2 = (int)jarg2; \n  arg3 = *(std::vector< bls::PublicKey >::value_type **)&jarg3;\n  if (!arg3) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::PublicKey >::value_type const & reference is null\");\n    return 0;\n  } \n  try {\n    result = std_vector_Sl_bls_PublicKey_Sg__set(arg1,arg2,(bls::PublicKey &)*arg3);\n  }\n  catch(std::out_of_range &_e) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, (&_e)->what());\n    return 0;\n  }\n  \n  *(std::vector< bls::PublicKey >::value_type **)&jresult = new std::vector< bls::PublicKey >::value_type((const std::vector< bls::PublicKey >::value_type &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jint JNICALL Java_org_dashj_bls_JNI_PublicKeyVec_1size(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jint jresult = 0 ;\n  std::vector< bls::PublicKey > *arg1 = (std::vector< bls::PublicKey > *) 0 ;\n  int32_t result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::PublicKey > **)&jarg1; \n  result = (int32_t)std_vector_Sl_bls_PublicKey_Sg__size((std::vector< bls::PublicKey > const *)arg1);\n  jresult = (jint)result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_PublicKeyVec_1removeRange(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jint jarg2, jint jarg3) {\n  std::vector< bls::PublicKey > *arg1 = (std::vector< bls::PublicKey > *) 0 ;\n  int32_t arg2 ;\n  int32_t arg3 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::PublicKey > **)&jarg1; \n  arg2 = (int32_t)jarg2; \n  arg3 = (int32_t)jarg3; \n  std_vector_Sl_bls_PublicKey_Sg__removeRange(arg1,arg2,arg3);\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_delete_1PublicKeyVec(JNIEnv *jenv, jclass jcls, jlong jarg1) {\n  std::vector< bls::PublicKey > *arg1 = (std::vector< bls::PublicKey > *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = *(std::vector< bls::PublicKey > **)&jarg1; \n  delete arg1;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_new_1PrivateKeyVec_1_1SWIG_10(JNIEnv *jenv, jclass jcls) {\n  jlong jresult = 0 ;\n  std::vector< bls::PrivateKey > *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  result = (std::vector< bls::PrivateKey > *)new std::vector< bls::PrivateKey >();\n  *(std::vector< bls::PrivateKey > **)&jresult = result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_new_1PrivateKeyVec_1_1SWIG_12(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  std::vector< bls::PrivateKey > *arg1 = 0 ;\n  std::vector< bls::PrivateKey > *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::PrivateKey > **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::PrivateKey > const & reference is null\");\n    return 0;\n  } \n  result = (std::vector< bls::PrivateKey > *)new std::vector< bls::PrivateKey >((std::vector< bls::PrivateKey > const &)*arg1);\n  *(std::vector< bls::PrivateKey > **)&jresult = result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_PrivateKeyVec_1capacity(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  std::vector< bls::PrivateKey > *arg1 = (std::vector< bls::PrivateKey > *) 0 ;\n  std::vector< bls::PrivateKey >::size_type result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::PrivateKey > **)&jarg1; \n  result = ((std::vector< bls::PrivateKey > const *)arg1)->capacity();\n  jresult = (jlong)result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_PrivateKeyVec_1reserve(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2) {\n  std::vector< bls::PrivateKey > *arg1 = (std::vector< bls::PrivateKey > *) 0 ;\n  std::vector< bls::PrivateKey >::size_type arg2 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::PrivateKey > **)&jarg1; \n  arg2 = (std::vector< bls::PrivateKey >::size_type)jarg2; \n  (arg1)->reserve(arg2);\n}\n\n\nSWIGEXPORT jboolean JNICALL Java_org_dashj_bls_JNI_PrivateKeyVec_1isEmpty(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jboolean jresult = 0 ;\n  std::vector< bls::PrivateKey > *arg1 = (std::vector< bls::PrivateKey > *) 0 ;\n  bool result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::PrivateKey > **)&jarg1; \n  result = (bool)((std::vector< bls::PrivateKey > const *)arg1)->empty();\n  jresult = (jboolean)result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_PrivateKeyVec_1clear(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  std::vector< bls::PrivateKey > *arg1 = (std::vector< bls::PrivateKey > *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::PrivateKey > **)&jarg1; \n  (arg1)->clear();\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_PrivateKeyVec_1push_1back(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2, jobject jarg2_) {\n  std::vector< bls::PrivateKey > *arg1 = (std::vector< bls::PrivateKey > *) 0 ;\n  std::vector< bls::PrivateKey >::value_type *arg2 = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  (void)jarg2_;\n  arg1 = *(std::vector< bls::PrivateKey > **)&jarg1; \n  arg2 = *(std::vector< bls::PrivateKey >::value_type **)&jarg2;\n  if (!arg2) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::PrivateKey >::value_type const & reference is null\");\n    return ;\n  } \n  (arg1)->push_back((std::vector< bls::PrivateKey >::value_type const &)*arg2);\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_PrivateKeyVec_1get(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jint jarg2) {\n  jlong jresult = 0 ;\n  std::vector< bls::PrivateKey > *arg1 = (std::vector< bls::PrivateKey > *) 0 ;\n  int arg2 ;\n  std::vector< bls::PrivateKey >::value_type *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::PrivateKey > **)&jarg1; \n  arg2 = (int)jarg2; \n  try {\n    result = (std::vector< bls::PrivateKey >::value_type *) &std_vector_Sl_bls_PrivateKey_Sg__get((std::vector< bls::PrivateKey > const *)arg1,arg2);\n  }\n  catch(std::out_of_range &_e) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, (&_e)->what());\n    return 0;\n  }\n  \n  *(std::vector< bls::PrivateKey >::value_type **)&jresult = result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_PrivateKeyVec_1set(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jint jarg2, jlong jarg3, jobject jarg3_) {\n  jlong jresult = 0 ;\n  std::vector< bls::PrivateKey > *arg1 = (std::vector< bls::PrivateKey > *) 0 ;\n  int arg2 ;\n  std::vector< bls::PrivateKey >::value_type *arg3 = 0 ;\n  SwigValueWrapper< bls::PrivateKey > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  (void)jarg3_;\n  arg1 = *(std::vector< bls::PrivateKey > **)&jarg1; \n  arg2 = (int)jarg2; \n  arg3 = *(std::vector< bls::PrivateKey >::value_type **)&jarg3;\n  if (!arg3) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::PrivateKey >::value_type const & reference is null\");\n    return 0;\n  } \n  try {\n    result = std_vector_Sl_bls_PrivateKey_Sg__set(arg1,arg2,(bls::PrivateKey &)*arg3);\n  }\n  catch(std::out_of_range &_e) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, (&_e)->what());\n    return 0;\n  }\n  \n  *(std::vector< bls::PrivateKey >::value_type **)&jresult = new std::vector< bls::PrivateKey >::value_type((const std::vector< bls::PrivateKey >::value_type &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jint JNICALL Java_org_dashj_bls_JNI_PrivateKeyVec_1size(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jint jresult = 0 ;\n  std::vector< bls::PrivateKey > *arg1 = (std::vector< bls::PrivateKey > *) 0 ;\n  int32_t result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::PrivateKey > **)&jarg1; \n  result = (int32_t)std_vector_Sl_bls_PrivateKey_Sg__size((std::vector< bls::PrivateKey > const *)arg1);\n  jresult = (jint)result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_PrivateKeyVec_1removeRange(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jint jarg2, jint jarg3) {\n  std::vector< bls::PrivateKey > *arg1 = (std::vector< bls::PrivateKey > *) 0 ;\n  int32_t arg2 ;\n  int32_t arg3 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::PrivateKey > **)&jarg1; \n  arg2 = (int32_t)jarg2; \n  arg3 = (int32_t)jarg3; \n  std_vector_Sl_bls_PrivateKey_Sg__removeRange(arg1,arg2,arg3);\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_delete_1PrivateKeyVec(JNIEnv *jenv, jclass jcls, jlong jarg1) {\n  std::vector< bls::PrivateKey > *arg1 = (std::vector< bls::PrivateKey > *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = *(std::vector< bls::PrivateKey > **)&jarg1; \n  delete arg1;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_new_1ByteArrayVec_1_1SWIG_10(JNIEnv *jenv, jclass jcls) {\n  jlong jresult = 0 ;\n  std::vector< uint8_t * > *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  result = (std::vector< uint8_t * > *)new std::vector< uint8_t * >();\n  *(std::vector< uint8_t * > **)&jresult = result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_new_1ByteArrayVec_1_1SWIG_11(JNIEnv *jenv, jclass jcls, jlong jarg1) {\n  jlong jresult = 0 ;\n  std::vector< unsigned char * >::size_type arg1 ;\n  std::vector< uint8_t * > *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = (std::vector< unsigned char * >::size_type)jarg1; \n  result = (std::vector< uint8_t * > *)new std::vector< uint8_t * >(arg1);\n  *(std::vector< uint8_t * > **)&jresult = result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_new_1ByteArrayVec_1_1SWIG_12(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  std::vector< uint8_t * > *arg1 = 0 ;\n  std::vector< uint8_t * > *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< uint8_t * > **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< uint8_t * > const & reference is null\");\n    return 0;\n  } \n  result = (std::vector< uint8_t * > *)new std::vector< uint8_t * >((std::vector< uint8_t * > const &)*arg1);\n  *(std::vector< uint8_t * > **)&jresult = result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_ByteArrayVec_1capacity(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  std::vector< uint8_t * > *arg1 = (std::vector< uint8_t * > *) 0 ;\n  std::vector< unsigned char * >::size_type result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< uint8_t * > **)&jarg1; \n  result = ((std::vector< uint8_t * > const *)arg1)->capacity();\n  jresult = (jlong)result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_ByteArrayVec_1reserve(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2) {\n  std::vector< uint8_t * > *arg1 = (std::vector< uint8_t * > *) 0 ;\n  std::vector< unsigned char * >::size_type arg2 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< uint8_t * > **)&jarg1; \n  arg2 = (std::vector< unsigned char * >::size_type)jarg2; \n  (arg1)->reserve(arg2);\n}\n\n\nSWIGEXPORT jboolean JNICALL Java_org_dashj_bls_JNI_ByteArrayVec_1isEmpty(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jboolean jresult = 0 ;\n  std::vector< uint8_t * > *arg1 = (std::vector< uint8_t * > *) 0 ;\n  bool result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< uint8_t * > **)&jarg1; \n  result = (bool)((std::vector< uint8_t * > const *)arg1)->empty();\n  jresult = (jboolean)result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_ByteArrayVec_1clear(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  std::vector< uint8_t * > *arg1 = (std::vector< uint8_t * > *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< uint8_t * > **)&jarg1;\n  for(uint8_t * element : *arg1)\n    delete element;\n  (arg1)->clear();\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_ByteArrayVec_1push_1back(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jbyteArray jarg2) {\n  std::vector< uint8_t * > *arg1 = (std::vector< uint8_t * > *) 0 ;\n  std::vector< unsigned char * >::value_type arg2 = 0 ;\n  size_t arg2len = 0;\n\n  std::vector< unsigned char * >::value_type temp2 = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n    {\n      arg2 = (std::vector< unsigned char * >::value_type) jenv->GetByteArrayElements(jarg2, 0);\n      arg2len = jenv->GetArrayLength(jarg2);\n    }\n\n    if(arg2len != BLS::MESSAGE_HASH_LEN) {\n      SWIG_JavaThrowException(jenv, SWIG_JavaIllegalArgumentException, \"push_back: Message Hash byte array size not 32 bytes\");\n      return;\n  }\n\n  arg1 = *(std::vector< uint8_t * > **)&jarg1;\n   std::vector< unsigned char * >::value_type arg2copy = new unsigned char[arg2len];\n   memcpy(arg2copy, arg2, arg2len);\n  //temp2 = *(std::vector< unsigned char * >::value_type *)&jarg2;\n  //arg2 = (std::vector< unsigned char * >::value_type *)&temp2;\n  (arg1)->push_back((std::vector< unsigned char * >::value_type const &)arg2copy);\n      {\n        jenv->ReleaseByteArrayElements(jarg2, (jbyte *) arg2, 0);\n      }\n}\n\n\nSWIGEXPORT jbyteArray JNICALL Java_org_dashj_bls_JNI_ByteArrayVec_1get(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jint jarg2) {\n  jbyteArray jresult = 0 ;\n  std::vector< uint8_t * > *arg1 = (std::vector< uint8_t * > *) 0 ;\n  int arg2 ;\n  std::vector< unsigned char * >::value_type result = 0;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< uint8_t * > **)&jarg1; \n  arg2 = (int)jarg2; \n  try {\n    result = (std::vector< unsigned char * >::value_type) std_vector_Sl_uint8_t_Sm__Sg__get((std::vector< unsigned char * > const *)arg1,arg2);\n  }\n  catch(std::out_of_range &_e) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, (&_e)->what());\n    return 0;\n  }\n  \n  jresult = jenv->NewByteArray(BLS::MESSAGE_HASH_LEN);\n  \n    if (result == NULL) {\n        SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, \"Can't create new byte array\");\n        return NULL; //  out of memory error thrown\n    }\n\n    // create bytes from result\n    /*jbyte *bytes = jenv->GetByteArrayElements(jresult, 0);\n    int i;\n    for (i = 0; i < BLS::MESSAGE_HASH_LEN; i++) {\n        bytes[i] = result[i];\n   }\n*/\n    // move from the temp structure to the java structure\n    jenv->SetByteArrayRegion(jresult, 0, BLS::MESSAGE_HASH_LEN, (jbyte*)result); \n  /**/\n  //*(std::vector< unsigned char * >::value_type *)&jresult = *result; \n  return jresult;\n}\n\n\nSWIGEXPORT jbyteArray JNICALL Java_org_dashj_bls_JNI_ByteArrayVec_1set(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jint jarg2, jbyteArray jarg3) {\n  jbyteArray jresult = 0 ;\n  std::vector< uint8_t * > *arg1 = (std::vector< uint8_t * > *) 0 ;\n  int arg2 ;\n  std::vector< unsigned char * >::value_type arg3 = 0 ;\n  std::vector< unsigned char * >::value_type temp3 = 0 ;\n  std::vector< unsigned char * >::value_type result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< uint8_t * > **)&jarg1; \n  arg2 = (int)jarg2; \n  \n  arg3 = (std::vector< unsigned char * >::value_type) jenv->GetByteArrayElements(jarg3, 0);\n  int arg3len = jenv->GetArrayLength(jarg3);\n  if(arg3len != BLS::MESSAGE_HASH_LEN) {\n      SWIG_JavaThrowException(jenv, SWIG_JavaIllegalArgumentException, \"Message Hash byte array size not 32 bytes\");\n      return 0;\n  }\n              \n  \n  std::vector< unsigned char * >::value_type arg3copy = new unsigned char[arg3len];\n  memcpy(arg3copy, arg3, arg3len);\n\n  //temp3 = *(std::vector< unsigned char * >::value_type *)&jarg3;\n  //arg3 = (std::vector< unsigned char * >::value_type *)&temp3; \n  try {\n    result = (std::vector< unsigned char * >::value_type)std_vector_Sl_uint8_t_Sm__Sg__set(arg1,arg2, arg3copy);\n  }\n  catch(std::out_of_range &_e) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, (&_e)->what());\n    return 0;\n  }\n  \n  jresult = jenv->NewByteArray(BLS::MESSAGE_HASH_LEN);\n  \n    if (result == NULL) {\n        SWIG_JavaThrowException(jenv, SWIG_JavaOutOfMemoryError, \"Can't create new byte array\");\n        return NULL; //  out of memory error thrown\n    }\n\n    // create bytes from result\n    /*jbyte *bytes = jenv->GetByteArrayElements(jresult, 0);\n    int i;\n    for (i = 0; i < BLS::MESSAGE_HASH_LEN; i++) {\n        bytes[i] = result[i];\n    }*/\n\n    // move from the temp structure to the java structure\n    jenv->SetByteArrayRegion(jresult, 0, BLS::MESSAGE_HASH_LEN, (jbyte*)result);        \n    delete result; \n  \n  return jresult;\n}\n\n\nSWIGEXPORT jint JNICALL Java_org_dashj_bls_JNI_ByteArrayVec_1size(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jint jresult = 0 ;\n  std::vector< uint8_t * > *arg1 = (std::vector< uint8_t * > *) 0 ;\n  int32_t result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< uint8_t * > **)&jarg1; \n  result = (int32_t)std_vector_Sl_uint8_t_Sm__Sg__size((std::vector< unsigned char * > const *)arg1);\n  jresult = (jint)result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_ByteArrayVec_1removeRange(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jint jarg2, jint jarg3) {\n  std::vector< uint8_t * > *arg1 = (std::vector< uint8_t * > *) 0 ;\n  int32_t arg2 ;\n  int32_t arg3 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< uint8_t * > **)&jarg1; \n  arg2 = (int32_t)jarg2; \n  arg3 = (int32_t)jarg3;\n  for(int i = arg2; i <= arg3; i++)\n      delete (*arg1)[i];\n  std_vector_Sl_uint8_t_Sm__Sg__removeRange(arg1,arg2,arg3);\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_delete_1ByteArrayVec(JNIEnv *jenv, jclass jcls, jlong jarg1) {\n  std::vector< uint8_t * > *arg1 = (std::vector< uint8_t * > *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = *(std::vector< uint8_t * > **)&jarg1; \n  //We must delete all of the elements first\n  for(uint8_t * element : *arg1)\n      delete element;\n  delete arg1;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_new_1BigIntegerVec_1_1SWIG_10(JNIEnv *jenv, jclass jcls) {\n  jlong jresult = 0 ;\n  std::vector< bn_t * > *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  result = (std::vector< bn_t * > *)new std::vector< bn_t * >();\n  *(std::vector< bn_t * > **)&jresult = result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_new_1BigIntegerVec_1_1SWIG_11(JNIEnv *jenv, jclass jcls, jlong jarg1) {\n  jlong jresult = 0 ;\n  std::vector< bn_t * >::size_type arg1 ;\n  std::vector< bn_t * > *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = (std::vector< bn_t * >::size_type)jarg1; \n  result = (std::vector< bn_t * > *)new std::vector< bn_t * >(arg1);\n  *(std::vector< bn_t * > **)&jresult = result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_new_1BigIntegerVec_1_1SWIG_12(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  std::vector< bn_t * > *arg1 = 0 ;\n  std::vector< bn_t * > *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bn_t * > **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bn_t * > const & reference is null\");\n    return 0;\n  } \n  result = (std::vector< bn_t * > *)new std::vector< bn_t * >((std::vector< bn_t * > const &)*arg1);\n  *(std::vector< bn_t * > **)&jresult = result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_BigIntegerVec_1capacity(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  std::vector< bn_t * > *arg1 = (std::vector< bn_t * > *) 0 ;\n  std::vector< bn_t * >::size_type result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bn_t * > **)&jarg1; \n  result = ((std::vector< bn_t * > const *)arg1)->capacity();\n  jresult = (jlong)result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_BigIntegerVec_1reserve(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2) {\n  std::vector< bn_t * > *arg1 = (std::vector< bn_t * > *) 0 ;\n  std::vector< bn_t * >::size_type arg2 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bn_t * > **)&jarg1; \n  arg2 = (std::vector< bn_t * >::size_type)jarg2; \n  (arg1)->reserve(arg2);\n}\n\n\nSWIGEXPORT jboolean JNICALL Java_org_dashj_bls_JNI_BigIntegerVec_1isEmpty(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jboolean jresult = 0 ;\n  std::vector< bn_t * > *arg1 = (std::vector< bn_t * > *) 0 ;\n  bool result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bn_t * > **)&jarg1; \n  result = (bool)((std::vector< bn_t * > const *)arg1)->empty();\n  jresult = (jboolean)result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_BigIntegerVec_1clear(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  std::vector< bn_t * > *arg1 = (std::vector< bn_t * > *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bn_t * > **)&jarg1; \n  (arg1)->clear();\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_BigIntegerVec_1push_1back(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2) {\n  std::vector< bn_t * > *arg1 = (std::vector< bn_t * > *) 0 ;\n  std::vector< bn_t * >::value_type *arg2 = 0 ;\n  std::vector< bn_t * >::value_type temp2 = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bn_t * > **)&jarg1; \n  temp2 = *(std::vector< bn_t * >::value_type *)&jarg2;\n  arg2 = (std::vector< bn_t * >::value_type *)&temp2; \n  (arg1)->push_back((std::vector< bn_t * >::value_type const &)*arg2);\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_BigIntegerVec_1get(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jint jarg2) {\n  jlong jresult = 0 ;\n  std::vector< bn_t * > *arg1 = (std::vector< bn_t * > *) 0 ;\n  int arg2 ;\n  std::vector< bn_t * >::value_type *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bn_t * > **)&jarg1; \n  arg2 = (int)jarg2; \n  try {\n    result = (std::vector< bn_t * >::value_type *) &std_vector_Sl_bn_t_Sm__Sg__get((std::vector< bn_t * > const *)arg1,arg2);\n  }\n  catch(std::out_of_range &_e) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, (&_e)->what());\n    return 0;\n  }\n  \n  *(std::vector< bn_t * >::value_type *)&jresult = *result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_BigIntegerVec_1set(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jint jarg2, jlong jarg3) {\n  jlong jresult = 0 ;\n  std::vector< bn_t * > *arg1 = (std::vector< bn_t * > *) 0 ;\n  int arg2 ;\n  std::vector< bn_t * >::value_type *arg3 = 0 ;\n  std::vector< bn_t * >::value_type temp3 = 0 ;\n  std::vector< bn_t * >::value_type result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bn_t * > **)&jarg1; \n  arg2 = (int)jarg2; \n  temp3 = *(std::vector< bn_t * >::value_type *)&jarg3;\n  arg3 = (std::vector< bn_t * >::value_type *)&temp3; \n  try {\n    result = (std::vector< bn_t * >::value_type)std_vector_Sl_bn_t_Sm__Sg__set(arg1,arg2,(bn_t * &)*arg3);\n  }\n  catch(std::out_of_range &_e) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, (&_e)->what());\n    return 0;\n  }\n  \n  *(std::vector< bn_t * >::value_type *)&jresult = result; \n  return jresult;\n}\n\n\nSWIGEXPORT jint JNICALL Java_org_dashj_bls_JNI_BigIntegerVec_1size(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jint jresult = 0 ;\n  std::vector< bn_t * > *arg1 = (std::vector< bn_t * > *) 0 ;\n  int32_t result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bn_t * > **)&jarg1; \n  result = (int32_t)std_vector_Sl_bn_t_Sm__Sg__size((std::vector< bn_t * > const *)arg1);\n  jresult = (jint)result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_BigIntegerVec_1removeRange(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jint jarg2, jint jarg3) {\n  std::vector< bn_t * > *arg1 = (std::vector< bn_t * > *) 0 ;\n  int32_t arg2 ;\n  int32_t arg3 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bn_t * > **)&jarg1; \n  arg2 = (int32_t)jarg2; \n  arg3 = (int32_t)jarg3; \n  std_vector_Sl_bn_t_Sm__Sg__removeRange(arg1,arg2,arg3);\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_delete_1BigIntegerVec(JNIEnv *jenv, jclass jcls, jlong jarg1) {\n  std::vector< bn_t * > *arg1 = (std::vector< bn_t * > *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = *(std::vector< bn_t * > **)&jarg1; \n  delete arg1;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_new_1SignatureVector_1_1SWIG_10(JNIEnv *jenv, jclass jcls) {\n  jlong jresult = 0 ;\n  std::vector< bls::Signature > *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  result = (std::vector< bls::Signature > *)new std::vector< bls::Signature >();\n  *(std::vector< bls::Signature > **)&jresult = result; \n  return jresult;\n}\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_new_1SignatureVector_1_1SWIG_12(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  std::vector< bls::Signature > *arg1 = 0 ;\n  std::vector< bls::Signature > *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::Signature > **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::Signature > const & reference is null\");\n    return 0;\n  } \n  result = (std::vector< bls::Signature > *)new std::vector< bls::Signature >((std::vector< bls::Signature > const &)*arg1);\n  *(std::vector< bls::Signature > **)&jresult = result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_SignatureVector_1capacity(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  std::vector< bls::Signature > *arg1 = (std::vector< bls::Signature > *) 0 ;\n  std::vector< bls::Signature >::size_type result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::Signature > **)&jarg1; \n  result = ((std::vector< bls::Signature > const *)arg1)->capacity();\n  jresult = (jlong)result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_SignatureVector_1reserve(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2) {\n  std::vector< bls::Signature > *arg1 = (std::vector< bls::Signature > *) 0 ;\n  std::vector< bls::Signature >::size_type arg2 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::Signature > **)&jarg1; \n  arg2 = (std::vector< bls::Signature >::size_type)jarg2; \n  (arg1)->reserve(arg2);\n}\n\n\nSWIGEXPORT jboolean JNICALL Java_org_dashj_bls_JNI_SignatureVector_1isEmpty(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jboolean jresult = 0 ;\n  std::vector< bls::Signature > *arg1 = (std::vector< bls::Signature > *) 0 ;\n  bool result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::Signature > **)&jarg1; \n  result = (bool)((std::vector< bls::Signature > const *)arg1)->empty();\n  jresult = (jboolean)result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_SignatureVector_1clear(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  std::vector< bls::Signature > *arg1 = (std::vector< bls::Signature > *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::Signature > **)&jarg1; \n  (arg1)->clear();\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_SignatureVector_1push_1back(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2, jobject jarg2_) {\n  std::vector< bls::Signature > *arg1 = (std::vector< bls::Signature > *) 0 ;\n  std::vector< bls::Signature >::value_type *arg2 = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  (void)jarg2_;\n  arg1 = *(std::vector< bls::Signature > **)&jarg1; \n  arg2 = *(std::vector< bls::Signature >::value_type **)&jarg2;\n  if (!arg2) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::Signature >::value_type const & reference is null\");\n    return ;\n  } \n  (arg1)->push_back((std::vector< bls::Signature >::value_type const &)*arg2);\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_SignatureVector_1get(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jint jarg2) {\n  jlong jresult = 0 ;\n  std::vector< bls::Signature > *arg1 = (std::vector< bls::Signature > *) 0 ;\n  int arg2 ;\n  std::vector< bls::Signature >::value_type *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::Signature > **)&jarg1; \n  arg2 = (int)jarg2; \n  try {\n    result = (std::vector< bls::Signature >::value_type *) &std_vector_Sl_bls_Signature_Sg__get((std::vector< bls::Signature > const *)arg1,arg2);\n  }\n  catch(std::out_of_range &_e) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, (&_e)->what());\n    return 0;\n  }\n  \n  *(std::vector< bls::Signature >::value_type **)&jresult = result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_SignatureVector_1set(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jint jarg2, jlong jarg3, jobject jarg3_) {\n  jlong jresult = 0 ;\n  std::vector< bls::Signature > *arg1 = (std::vector< bls::Signature > *) 0 ;\n  int arg2 ;\n  std::vector< bls::Signature >::value_type *arg3 = 0 ;\n  SwigValueWrapper< bls::Signature > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  (void)jarg3_;\n  arg1 = *(std::vector< bls::Signature > **)&jarg1; \n  arg2 = (int)jarg2; \n  arg3 = *(std::vector< bls::Signature >::value_type **)&jarg3;\n  if (!arg3) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::Signature >::value_type const & reference is null\");\n    return 0;\n  } \n  try {\n    result = std_vector_Sl_bls_Signature_Sg__set(arg1,arg2,(bls::Signature const &)*arg3);\n  }\n  catch(std::out_of_range &_e) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, (&_e)->what());\n    return 0;\n  }\n  \n  *(std::vector< bls::Signature >::value_type **)&jresult = new std::vector< bls::Signature >::value_type((const std::vector< bls::Signature >::value_type &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jint JNICALL Java_org_dashj_bls_JNI_SignatureVector_1size(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jint jresult = 0 ;\n  std::vector< bls::Signature > *arg1 = (std::vector< bls::Signature > *) 0 ;\n  int32_t result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::Signature > **)&jarg1; \n  result = (int32_t)std_vector_Sl_bls_Signature_Sg__size((std::vector< bls::Signature > const *)arg1);\n  jresult = (jint)result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_SignatureVector_1removeRange(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jint jarg2, jint jarg3) {\n  std::vector< bls::Signature > *arg1 = (std::vector< bls::Signature > *) 0 ;\n  int32_t arg2 ;\n  int32_t arg3 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::Signature > **)&jarg1; \n  arg2 = (int32_t)jarg2; \n  arg3 = (int32_t)jarg3; \n  std_vector_Sl_bls_Signature_Sg__removeRange(arg1,arg2,arg3);\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_delete_1SignatureVector(JNIEnv *jenv, jclass jcls, jlong jarg1) {\n  std::vector< bls::Signature > *arg1 = (std::vector< bls::Signature > *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  arg1 = *(std::vector< bls::Signature > **)&jarg1; \n  delete arg1;\n}\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_new_1InsecureSignatureVector_1_1SWIG_10(JNIEnv *jenv, jclass jcls) {\n  jlong jresult = 0 ;\n  std::vector< bls::InsecureSignature > *result = 0 ;\n\n  (void)jenv;\n  (void)jcls;\n  result = (std::vector< bls::InsecureSignature > *)new std::vector< bls::InsecureSignature >();\n  *(std::vector< bls::InsecureSignature > **)&jresult = result;\n  return jresult;\n}\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_new_1InsecureSignatureVector_1_1SWIG_12(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  std::vector< bls::InsecureSignature > *arg1 = 0 ;\n  std::vector< bls::InsecureSignature > *result = 0 ;\n\n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::InsecureSignature > **)&jarg1;\n  if (!arg1) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::InsecureSignature > const & reference is null\");\n    return 0;\n  }\n  result = (std::vector< bls::InsecureSignature > *)new std::vector< bls::InsecureSignature >((std::vector< bls::InsecureSignature > const &)*arg1);\n  *(std::vector< bls::InsecureSignature > **)&jresult = result;\n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_InsecureSignatureVector_1capacity(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  std::vector< bls::InsecureSignature > *arg1 = (std::vector< bls::InsecureSignature > *) 0 ;\n  std::vector< bls::InsecureSignature >::size_type result;\n\n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::InsecureSignature > **)&jarg1;\n  result = ((std::vector< bls::InsecureSignature > const *)arg1)->capacity();\n  jresult = (jlong)result;\n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_InsecureSignatureVector_1reserve(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2) {\n  std::vector< bls::InsecureSignature > *arg1 = (std::vector< bls::InsecureSignature > *) 0 ;\n  std::vector< bls::InsecureSignature >::size_type arg2 ;\n\n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::InsecureSignature > **)&jarg1;\n  arg2 = (std::vector< bls::InsecureSignature >::size_type)jarg2;\n  (arg1)->reserve(arg2);\n}\n\n\nSWIGEXPORT jboolean JNICALL Java_org_dashj_bls_JNI_InsecureSignatureVector_1isEmpty(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jboolean jresult = 0 ;\n  std::vector< bls::InsecureSignature > *arg1 = (std::vector< bls::InsecureSignature > *) 0 ;\n  bool result;\n\n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::InsecureSignature > **)&jarg1;\n  result = (bool)((std::vector< bls::InsecureSignature > const *)arg1)->empty();\n  jresult = (jboolean)result;\n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_InsecureSignatureVector_1clear(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  std::vector< bls::InsecureSignature > *arg1 = (std::vector< bls::InsecureSignature > *) 0 ;\n\n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::InsecureSignature > **)&jarg1;\n  (arg1)->clear();\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_InsecureSignatureVector_1push_1back(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2, jobject jarg2_) {\n  std::vector< bls::InsecureSignature > *arg1 = (std::vector< bls::InsecureSignature > *) 0 ;\n  std::vector< bls::InsecureSignature >::value_type *arg2 = 0 ;\n\n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  (void)jarg2_;\n  arg1 = *(std::vector< bls::InsecureSignature > **)&jarg1;\n  arg2 = *(std::vector< bls::InsecureSignature >::value_type **)&jarg2;\n  if (!arg2) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::InsecureSignature >::value_type const & reference is null\");\n    return ;\n  }\n  (arg1)->push_back((std::vector< bls::InsecureSignature >::value_type const &)*arg2);\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_InsecureSignatureVector_1get(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jint jarg2) {\n  jlong jresult = 0 ;\n  std::vector< bls::InsecureSignature > *arg1 = (std::vector< bls::InsecureSignature > *) 0 ;\n  int arg2 ;\n  std::vector< bls::InsecureSignature >::value_type *result = 0 ;\n\n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::InsecureSignature > **)&jarg1;\n  arg2 = (int)jarg2;\n  try {\n    result = (std::vector< bls::InsecureSignature >::value_type *) &std_vector_Sl_bls_InsecureSignature_Sg__get((std::vector< bls::InsecureSignature > const *)arg1,arg2);\n  }\n  catch(std::out_of_range &_e) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, (&_e)->what());\n    return 0;\n  }\n\n  *(std::vector< bls::InsecureSignature >::value_type **)&jresult = result;\n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_InsecureSignatureVector_1set(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jint jarg2, jlong jarg3, jobject jarg3_) {\n  jlong jresult = 0 ;\n  std::vector< bls::InsecureSignature > *arg1 = (std::vector< bls::InsecureSignature > *) 0 ;\n  int arg2 ;\n  std::vector< bls::InsecureSignature >::value_type *arg3 = 0 ;\n  SwigValueWrapper< bls::InsecureSignature > result;\n\n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  (void)jarg3_;\n  arg1 = *(std::vector< bls::InsecureSignature > **)&jarg1;\n  arg2 = (int)jarg2;\n  arg3 = *(std::vector< bls::InsecureSignature >::value_type **)&jarg3;\n  if (!arg3) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::InsecureSignature >::value_type const & reference is null\");\n    return 0;\n  }\n  try {\n    result = std_vector_Sl_bls_InsecureSignature_Sg__set(arg1,arg2,(bls::InsecureSignature const &)*arg3);\n  }\n  catch(std::out_of_range &_e) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, (&_e)->what());\n    return 0;\n  }\n\n  *(std::vector< bls::InsecureSignature >::value_type **)&jresult = new std::vector< bls::InsecureSignature >::value_type((const std::vector< bls::InsecureSignature >::value_type &)result);\n  return jresult;\n}\n\n\nSWIGEXPORT jint JNICALL Java_org_dashj_bls_JNI_InsecureSignatureVector_1size(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jint jresult = 0 ;\n  std::vector< bls::InsecureSignature > *arg1 = (std::vector< bls::InsecureSignature > *) 0 ;\n  int32_t result;\n\n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::InsecureSignature > **)&jarg1;\n  result = (int32_t)std_vector_Sl_bls_InsecureSignature_Sg__size((std::vector< bls::InsecureSignature > const *)arg1);\n  jresult = (jint)result;\n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_InsecureSignatureVector_1removeRange(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jint jarg2, jint jarg3) {\n  std::vector< bls::InsecureSignature > *arg1 = (std::vector< bls::InsecureSignature > *) 0 ;\n  int32_t arg2 ;\n  int32_t arg3 ;\n\n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::InsecureSignature > **)&jarg1;\n  arg2 = (int32_t)jarg2;\n  arg3 = (int32_t)jarg3;\n  std_vector_Sl_bls_InsecureSignature_Sg__removeRange(arg1,arg2,arg3);\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_delete_1InsecureSignatureVector(JNIEnv *jenv, jclass jcls, jlong jarg1) {\n  std::vector< bls::InsecureSignature > *arg1 = (std::vector< bls::InsecureSignature > *) 0 ;\n\n  (void)jenv;\n  (void)jcls;\n  arg1 = *(std::vector< bls::InsecureSignature > **)&jarg1;\n  delete arg1;\n}\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_new_1AggregationInfoVector_1_1SWIG_10(JNIEnv *jenv, jclass jcls) {\n  jlong jresult = 0 ;\n  std::vector< bls::AggregationInfo > *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  result = (std::vector< bls::AggregationInfo > *)new std::vector< bls::AggregationInfo >();\n  *(std::vector< bls::AggregationInfo > **)&jresult = result; \n  return jresult;\n}\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_AggregationInfoVector_1capacity(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jlong jresult = 0 ;\n  std::vector< bls::AggregationInfo > *arg1 = (std::vector< bls::AggregationInfo > *) 0 ;\n  std::vector< bls::AggregationInfo >::size_type result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::AggregationInfo > **)&jarg1; \n  result = ((std::vector< bls::AggregationInfo > const *)arg1)->capacity();\n  jresult = (jlong)result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_AggregationInfoVector_1reserve(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2) {\n  std::vector< bls::AggregationInfo > *arg1 = (std::vector< bls::AggregationInfo > *) 0 ;\n  std::vector< bls::AggregationInfo >::size_type arg2 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::AggregationInfo > **)&jarg1; \n  arg2 = (std::vector< bls::AggregationInfo >::size_type)jarg2; \n  (arg1)->reserve(arg2);\n}\n\n\nSWIGEXPORT jboolean JNICALL Java_org_dashj_bls_JNI_AggregationInfoVector_1isEmpty(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jboolean jresult = 0 ;\n  std::vector< bls::AggregationInfo > *arg1 = (std::vector< bls::AggregationInfo > *) 0 ;\n  bool result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::AggregationInfo > **)&jarg1; \n  result = (bool)((std::vector< bls::AggregationInfo > const *)arg1)->empty();\n  jresult = (jboolean)result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_AggregationInfoVector_1clear(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  std::vector< bls::AggregationInfo > *arg1 = (std::vector< bls::AggregationInfo > *) 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::AggregationInfo > **)&jarg1; \n  (arg1)->clear();\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_AggregationInfoVector_1push_1back(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jlong jarg2, jobject jarg2_) {\n  std::vector< bls::AggregationInfo > *arg1 = (std::vector< bls::AggregationInfo > *) 0 ;\n  std::vector< bls::AggregationInfo >::value_type *arg2 = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  (void)jarg2_;\n  arg1 = *(std::vector< bls::AggregationInfo > **)&jarg1; \n  arg2 = *(std::vector< bls::AggregationInfo >::value_type **)&jarg2;\n  if (!arg2) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::AggregationInfo >::value_type const & reference is null\");\n    return ;\n  } \n  (arg1)->push_back((std::vector< bls::AggregationInfo >::value_type const &)*arg2);\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_AggregationInfoVector_1get(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jint jarg2) {\n  jlong jresult = 0 ;\n  std::vector< bls::AggregationInfo > *arg1 = (std::vector< bls::AggregationInfo > *) 0 ;\n  int arg2 ;\n  std::vector< bls::AggregationInfo >::value_type *result = 0 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::AggregationInfo > **)&jarg1; \n  arg2 = (int)jarg2; \n  try {\n    result = (std::vector< bls::AggregationInfo >::value_type *) &std_vector_Sl_bls_AggregationInfo_Sg__get((std::vector< bls::AggregationInfo > const *)arg1,arg2);\n  }\n  catch(std::out_of_range &_e) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, (&_e)->what());\n    return 0;\n  }\n  \n  *(std::vector< bls::AggregationInfo >::value_type **)&jresult = result; \n  return jresult;\n}\n\n\nSWIGEXPORT jlong JNICALL Java_org_dashj_bls_JNI_AggregationInfoVector_1set(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jint jarg2, jlong jarg3, jobject jarg3_) {\n  jlong jresult = 0 ;\n  std::vector< bls::AggregationInfo > *arg1 = (std::vector< bls::AggregationInfo > *) 0 ;\n  int arg2 ;\n  std::vector< bls::AggregationInfo >::value_type *arg3 = 0 ;\n  SwigValueWrapper< bls::AggregationInfo > result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  (void)jarg3_;\n  arg1 = *(std::vector< bls::AggregationInfo > **)&jarg1; \n  arg2 = (int)jarg2; \n  arg3 = *(std::vector< bls::AggregationInfo >::value_type **)&jarg3;\n  if (!arg3) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaNullPointerException, \"std::vector< bls::AggregationInfo >::value_type const & reference is null\");\n    return 0;\n  } \n  try {\n    result = std_vector_Sl_bls_AggregationInfo_Sg__set(arg1,arg2,(bls::AggregationInfo const &)*arg3);\n  }\n  catch(std::out_of_range &_e) {\n    SWIG_JavaThrowException(jenv, SWIG_JavaIndexOutOfBoundsException, (&_e)->what());\n    return 0;\n  }\n  \n  *(std::vector< bls::AggregationInfo >::value_type **)&jresult = new std::vector< bls::AggregationInfo >::value_type((const std::vector< bls::AggregationInfo >::value_type &)result); \n  return jresult;\n}\n\n\nSWIGEXPORT jint JNICALL Java_org_dashj_bls_JNI_AggregationInfoVector_1size(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_) {\n  jint jresult = 0 ;\n  std::vector< bls::AggregationInfo > *arg1 = (std::vector< bls::AggregationInfo > *) 0 ;\n  int32_t result;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::AggregationInfo > **)&jarg1; \n  result = (int32_t)std_vector_Sl_bls_AggregationInfo_Sg__size((std::vector< bls::AggregationInfo > const *)arg1);\n  jresult = (jint)result; \n  return jresult;\n}\n\n\nSWIGEXPORT void JNICALL Java_org_dashj_bls_JNI_AggregationInfoVector_1removeRange(JNIEnv *jenv, jclass jcls, jlong jarg1, jobject jarg1_, jint jarg2, jint jarg3) {\n  std::vector< bls::AggregationInfo > *arg1 = (std::vector< bls::AggregationInfo > *) 0 ;\n  int32_t arg2 ;\n  int32_t arg3 ;\n  \n  (void)jenv;\n  (void)jcls;\n  (void)jarg1_;\n  arg1 = *(std::vector< bls::AggregationInfo > **)&jarg1; \n  arg2 = (int32_t)jarg2; \n  arg3 = (int32_t)jarg3; \n  std_vector_Sl_bls_AggregationInfo_Sg__removeRange(arg1,arg2,arg3);\n}\n\n\n#ifdef __cplusplus\n}\n#endif\n\n"
  },
  {
    "path": "dashkit/cpp/dashj-bls/pthread.c",
    "content": "//\n// Created by hashengineering on 12/1/18.\n//\nint dummy = 0;\n"
  },
  {
    "path": "dashkit/cpp/dashj-bls/stdio.cpp",
    "content": "//\n// Created by hashengineering on 12/1/18.\n//\n\n#include <stdio.h>\n\n//\n// This file adds stdin, stdout and stderr for API < 23\n//\n\n#if __ANDROID_API__ < __ANDROID_API_M__\n#undef stdin\n#undef stdout\n#undef stderr\nextern \"C\" {\n\nFILE * stdin (&__sF[0]);\nFILE * stdout (&__sF[1]);\nFILE * stderr (&__sF[2]);\n\n}\n#endif"
  },
  {
    "path": "dashkit/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "dashkit/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"/>\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/DashKit.kt",
    "content": "package io.horizontalsystems.dashkit\n\nimport android.content.Context\nimport android.database.sqlite.SQLiteDatabase\nimport io.horizontalsystems.bitcoincore.AbstractKit\nimport io.horizontalsystems.bitcoincore.BitcoinCore\nimport io.horizontalsystems.bitcoincore.BitcoinCore.SyncMode\nimport io.horizontalsystems.bitcoincore.BitcoinCoreBuilder\nimport io.horizontalsystems.bitcoincore.DustCalculator\nimport io.horizontalsystems.bitcoincore.apisync.BiApiTransactionProvider\nimport io.horizontalsystems.bitcoincore.apisync.InsightApi\nimport io.horizontalsystems.bitcoincore.apisync.blockchair.BlockchairApi\nimport io.horizontalsystems.bitcoincore.apisync.blockchair.BlockchairBlockHashFetcher\nimport io.horizontalsystems.bitcoincore.apisync.blockchair.BlockchairTransactionProvider\nimport io.horizontalsystems.bitcoincore.blocks.validators.BlockValidatorChain\nimport io.horizontalsystems.bitcoincore.blocks.validators.BlockValidatorSet\nimport io.horizontalsystems.bitcoincore.blocks.validators.ProofOfWorkValidator\nimport io.horizontalsystems.bitcoincore.extensions.hexToByteArray\nimport io.horizontalsystems.bitcoincore.managers.ApiSyncStateManager\nimport io.horizontalsystems.bitcoincore.managers.Bip44RestoreKeyConverter\nimport io.horizontalsystems.bitcoincore.managers.BlockValidatorHelper\nimport io.horizontalsystems.bitcoincore.managers.UnspentOutputSelector\nimport io.horizontalsystems.bitcoincore.managers.UnspentOutputSelectorSingleNoChange\nimport io.horizontalsystems.bitcoincore.models.Address\nimport io.horizontalsystems.bitcoincore.models.BalanceInfo\nimport io.horizontalsystems.bitcoincore.models.BlockInfo\nimport io.horizontalsystems.bitcoincore.models.Checkpoint\nimport io.horizontalsystems.bitcoincore.models.TransactionFilterType\nimport io.horizontalsystems.bitcoincore.models.TransactionInfo\nimport io.horizontalsystems.bitcoincore.models.WatchAddressPublicKey\nimport io.horizontalsystems.bitcoincore.network.Network\nimport io.horizontalsystems.bitcoincore.storage.CoreDatabase\nimport io.horizontalsystems.bitcoincore.storage.Storage\nimport io.horizontalsystems.bitcoincore.transactions.TransactionSizeCalculator\nimport io.horizontalsystems.bitcoincore.utils.AddressConverterChain\nimport io.horizontalsystems.bitcoincore.utils.Base58AddressConverter\nimport io.horizontalsystems.bitcoincore.utils.MerkleBranch\nimport io.horizontalsystems.bitcoincore.utils.PaymentAddressParser\nimport io.horizontalsystems.dashkit.core.DashTransactionInfoConverter\nimport io.horizontalsystems.dashkit.core.SingleSha256Hasher\nimport io.horizontalsystems.dashkit.instantsend.BLS\nimport io.horizontalsystems.dashkit.instantsend.InstantSendFactory\nimport io.horizontalsystems.dashkit.instantsend.InstantSendLockValidator\nimport io.horizontalsystems.dashkit.instantsend.InstantTransactionManager\nimport io.horizontalsystems.dashkit.instantsend.TransactionLockVoteValidator\nimport io.horizontalsystems.dashkit.instantsend.instantsendlock.InstantSendLockHandler\nimport io.horizontalsystems.dashkit.instantsend.instantsendlock.InstantSendLockManager\nimport io.horizontalsystems.dashkit.instantsend.transactionlockvote.TransactionLockVoteHandler\nimport io.horizontalsystems.dashkit.instantsend.transactionlockvote.TransactionLockVoteManager\nimport io.horizontalsystems.dashkit.managers.ConfirmedUnspentOutputProvider\nimport io.horizontalsystems.dashkit.managers.MasternodeListManager\nimport io.horizontalsystems.dashkit.managers.MasternodeListSyncer\nimport io.horizontalsystems.dashkit.managers.MasternodeSortedList\nimport io.horizontalsystems.dashkit.managers.QuorumListManager\nimport io.horizontalsystems.dashkit.managers.QuorumSortedList\nimport io.horizontalsystems.dashkit.masternodelist.MasternodeCbTxHasher\nimport io.horizontalsystems.dashkit.masternodelist.MasternodeListMerkleRootCalculator\nimport io.horizontalsystems.dashkit.masternodelist.MerkleRootCreator\nimport io.horizontalsystems.dashkit.masternodelist.MerkleRootHasher\nimport io.horizontalsystems.dashkit.masternodelist.QuorumListMerkleRootCalculator\nimport io.horizontalsystems.dashkit.messages.GetMasternodeListDiffMessageSerializer\nimport io.horizontalsystems.dashkit.messages.ISLockMessageParser\nimport io.horizontalsystems.dashkit.messages.MasternodeListDiffMessageParser\nimport io.horizontalsystems.dashkit.messages.TransactionLockMessageParser\nimport io.horizontalsystems.dashkit.messages.TransactionLockVoteMessageParser\nimport io.horizontalsystems.dashkit.messages.TransactionMessageParser\nimport io.horizontalsystems.dashkit.models.CoinbaseTransactionSerializer\nimport io.horizontalsystems.dashkit.models.DashTransactionInfo\nimport io.horizontalsystems.dashkit.models.InstantTransactionState\nimport io.horizontalsystems.dashkit.storage.DashKitDatabase\nimport io.horizontalsystems.dashkit.storage.DashStorage\nimport io.horizontalsystems.dashkit.tasks.PeerTaskFactory\nimport io.horizontalsystems.dashkit.validators.DarkGravityWaveTestnetValidator\nimport io.horizontalsystems.dashkit.validators.DarkGravityWaveValidator\nimport io.horizontalsystems.hdwalletkit.HDExtendedKey\nimport io.horizontalsystems.hdwalletkit.HDWallet.Purpose\nimport io.horizontalsystems.hdwalletkit.Mnemonic\nimport io.reactivex.Single\n\nclass DashKit : AbstractKit, IInstantTransactionDelegate, BitcoinCore.Listener {\n    enum class NetworkType {\n        MainNet,\n        TestNet\n    }\n\n    interface Listener {\n        fun onTransactionsUpdate(inserted: List<DashTransactionInfo>, updated: List<DashTransactionInfo>)\n        fun onTransactionsDelete(hashes: List<String>)\n        fun onBalanceUpdate(balance: BalanceInfo)\n        fun onLastBlockInfoUpdate(blockInfo: BlockInfo)\n        fun onKitStateUpdate(state: BitcoinCore.KitState)\n    }\n\n    var listener: Listener? = null\n\n    override var bitcoinCore: BitcoinCore\n    override var network: Network\n\n    private val dashStorage: DashStorage\n    private val instantSend: InstantSend\n    private val dashTransactionInfoConverter: DashTransactionInfoConverter\n\n    constructor(\n        context: Context,\n        words: List<String>,\n        passphrase: String,\n        walletId: String,\n        networkType: NetworkType = defaultNetworkType,\n        peerSize: Int = defaultPeerSize,\n        syncMode: SyncMode = defaultSyncMode,\n        confirmationsThreshold: Int = defaultConfirmationsThreshold\n    ) : this(context, Mnemonic().toSeed(words, passphrase), walletId, networkType, peerSize, syncMode, confirmationsThreshold)\n\n    constructor(\n        context: Context,\n        seed: ByteArray,\n        walletId: String,\n        networkType: NetworkType = defaultNetworkType,\n        peerSize: Int = defaultPeerSize,\n        syncMode: SyncMode = defaultSyncMode,\n        confirmationsThreshold: Int = defaultConfirmationsThreshold\n    ) : this(\n        context = context,\n        extendedKey = HDExtendedKey(seed, Purpose.BIP44),\n        walletId = walletId,\n        networkType = networkType,\n        peerSize = peerSize,\n        syncMode = syncMode,\n        confirmationsThreshold = confirmationsThreshold\n    )\n\n    constructor(\n        context: Context,\n        watchAddress: String,\n        walletId: String,\n        networkType: NetworkType = defaultNetworkType,\n        peerSize: Int = defaultPeerSize,\n        syncMode: SyncMode = defaultSyncMode,\n        confirmationsThreshold: Int = defaultConfirmationsThreshold\n    ) : this(\n        context = context,\n        extendedKey = null,\n        watchAddress = parseAddress(watchAddress, network(networkType)),\n        walletId = walletId,\n        networkType = networkType,\n        peerSize = peerSize,\n        syncMode = syncMode,\n        confirmationsThreshold = confirmationsThreshold\n    )\n\n    constructor(\n        context: Context,\n        extendedKey: HDExtendedKey,\n        walletId: String,\n        networkType: NetworkType = defaultNetworkType,\n        peerSize: Int = defaultPeerSize,\n        syncMode: SyncMode = defaultSyncMode,\n        confirmationsThreshold: Int = defaultConfirmationsThreshold\n    ) : this(\n        context = context,\n        extendedKey = extendedKey,\n        watchAddress = null,\n        walletId = walletId,\n        networkType = networkType,\n        peerSize = peerSize,\n        syncMode = syncMode,\n        confirmationsThreshold = confirmationsThreshold\n    )\n    /**\n     * @constructor Creates and initializes the BitcoinKit\n     * @param context The Android context\n     * @param extendedKey HDExtendedKey that contains HDKey and version\n     * @param watchAddress address for watching in read-only mode\n     * @param walletId an arbitrary ID of type String.\n     * @param networkType The network type. The default is MainNet.\n     * @param peerSize The # of peer-nodes required. The default is 10 peers.\n     * @param syncMode How the kit syncs with the blockchain. The default is SyncMode.Api().\n     * @param confirmationsThreshold How many confirmations required to be considered confirmed. The default is 6 confirmations.\n     */\n    private constructor(\n        context: Context,\n        extendedKey: HDExtendedKey?,\n        watchAddress: Address?,\n        walletId: String,\n        networkType: NetworkType,\n        peerSize: Int,\n        syncMode: SyncMode,\n        confirmationsThreshold: Int\n    ) {\n        val coreDatabase = CoreDatabase.getInstance(context, getDatabaseNameCore(networkType, walletId, syncMode))\n        val dashDatabase = DashKitDatabase.getInstance(context, getDatabaseName(networkType, walletId, syncMode))\n\n        val coreStorage = Storage(coreDatabase)\n        dashStorage = DashStorage(dashDatabase, coreStorage)\n\n        network = network(networkType)\n\n        val checkpoint = Checkpoint.resolveCheckpoint(syncMode, network, coreStorage)\n        val apiSyncStateManager = ApiSyncStateManager(coreStorage, network.syncableFromApi && syncMode !is SyncMode.Full)\n\n        val apiTransactionProvider = apiTransactionProvider(networkType, syncMode, apiSyncStateManager)\n\n        val paymentAddressParser = PaymentAddressParser(\"dash\", removeScheme = true)\n        val instantTransactionManager = InstantTransactionManager(dashStorage, InstantSendFactory(), InstantTransactionState())\n\n        dashTransactionInfoConverter = DashTransactionInfoConverter(instantTransactionManager)\n\n        val blockHelper = BlockValidatorHelper(coreStorage)\n\n        val blockValidatorSet = BlockValidatorSet()\n        blockValidatorSet.addBlockValidator(ProofOfWorkValidator())\n\n        val blockValidatorChain = BlockValidatorChain()\n\n        if (network is MainNetDash) {\n            blockValidatorChain.add(DarkGravityWaveValidator(blockHelper, heightInterval, targetTimespan, maxTargetBits, 68589))\n        } else {\n            blockValidatorChain.add(DarkGravityWaveTestnetValidator(targetSpacing, targetTimespan, maxTargetBits, 4002))\n            blockValidatorChain.add(DarkGravityWaveValidator(blockHelper, heightInterval, targetTimespan, maxTargetBits, 4002))\n        }\n\n        blockValidatorSet.addBlockValidator(blockValidatorChain)\n\n        val watchAddressPublicKey = watchAddress?.let {\n            WatchAddressPublicKey(watchAddress.lockingScriptPayload, watchAddress.scriptType)\n        }\n\n        bitcoinCore = BitcoinCoreBuilder()\n            .setContext(context)\n            .setExtendedKey(extendedKey)\n            .setWatchAddressPublicKey(watchAddressPublicKey)\n            .setPurpose(Purpose.BIP44)\n            .setNetwork(network)\n            .setCheckpoint(checkpoint)\n            .setPaymentAddressParser(paymentAddressParser)\n            .setPeerSize(peerSize)\n            .setSyncMode(syncMode)\n            .setConfirmationThreshold(confirmationsThreshold)\n            .setStorage(coreStorage)\n            .setBlockHeaderHasher(X11Hasher())\n            .setApiTransactionProvider(apiTransactionProvider)\n            .setApiSyncStateManager(apiSyncStateManager)\n            .setTransactionInfoConverter(dashTransactionInfoConverter)\n            .setBlockValidator(blockValidatorSet)\n            .build()\n\n        bitcoinCore.listener = this\n\n        //  extending bitcoinCore\n\n        bitcoinCore.addMessageParser(MasternodeListDiffMessageParser())\n            .addMessageParser(TransactionLockMessageParser())\n            .addMessageParser(TransactionLockVoteMessageParser())\n            .addMessageParser(ISLockMessageParser())\n            .addMessageParser(TransactionMessageParser())\n\n        bitcoinCore.addMessageSerializer(GetMasternodeListDiffMessageSerializer())\n\n        val merkleRootHasher = MerkleRootHasher()\n        val merkleRootCreator = MerkleRootCreator(merkleRootHasher)\n        val masternodeListMerkleRootCalculator = MasternodeListMerkleRootCalculator(merkleRootCreator)\n        val masternodeCbTxHasher = MasternodeCbTxHasher(CoinbaseTransactionSerializer(), merkleRootHasher)\n\n        val quorumListManager = QuorumListManager(dashStorage, QuorumListMerkleRootCalculator(merkleRootCreator), QuorumSortedList())\n        val masternodeListManager = MasternodeListManager(\n            dashStorage,\n            masternodeListMerkleRootCalculator,\n            masternodeCbTxHasher,\n            MerkleBranch(),\n            MasternodeSortedList(),\n            quorumListManager\n        )\n        val masternodeSyncer = MasternodeListSyncer(bitcoinCore, PeerTaskFactory(), masternodeListManager, bitcoinCore.initialDownload)\n\n        bitcoinCore.addPeerTaskHandler(masternodeSyncer)\n        bitcoinCore.addPeerSyncListener(masternodeSyncer)\n        bitcoinCore.addPeerGroupListener(masternodeSyncer)\n\n        val base58AddressConverter = Base58AddressConverter(network.addressVersion, network.addressScriptVersion)\n        bitcoinCore.addRestoreKeyConverter(Bip44RestoreKeyConverter(base58AddressConverter))\n\n        val singleHasher = SingleSha256Hasher()\n        val bls = BLS()\n        val transactionLockVoteValidator = TransactionLockVoteValidator(dashStorage, singleHasher, bls)\n        val instantSendLockValidator = InstantSendLockValidator(quorumListManager, bls)\n\n        val transactionLockVoteManager = TransactionLockVoteManager(transactionLockVoteValidator)\n        val instantSendLockManager = InstantSendLockManager(instantSendLockValidator)\n\n        val instantSendLockHandler = InstantSendLockHandler(instantTransactionManager, instantSendLockManager)\n        instantSendLockHandler.delegate = this\n        val transactionLockVoteHandler = TransactionLockVoteHandler(instantTransactionManager, transactionLockVoteManager)\n        transactionLockVoteHandler.delegate = this\n\n        val instantSend = InstantSend(bitcoinCore.transactionSyncer, transactionLockVoteHandler, instantSendLockHandler)\n        this.instantSend = instantSend\n\n        bitcoinCore.addInventoryItemsHandler(instantSend)\n        bitcoinCore.addPeerTaskHandler(instantSend)\n\n        val calculator = TransactionSizeCalculator()\n        val dustCalculator = DustCalculator(network.dustRelayTxFee, calculator)\n        val confirmedUnspentOutputProvider = ConfirmedUnspentOutputProvider(coreStorage, confirmationsThreshold)\n        bitcoinCore.prependUnspentOutputSelector(UnspentOutputSelector(calculator, dustCalculator, confirmedUnspentOutputProvider))\n        bitcoinCore.prependUnspentOutputSelector(UnspentOutputSelectorSingleNoChange(calculator, dustCalculator, confirmedUnspentOutputProvider))\n    }\n\n    private fun apiTransactionProvider(\n        networkType: NetworkType,\n        syncMode: SyncMode,\n        apiSyncStateManager: ApiSyncStateManager\n    ) = when (networkType) {\n        NetworkType.MainNet -> {\n            val insightApiProvider = InsightApi(\"https://insight.dash.org/insight-api\")\n\n            if (syncMode is SyncMode.Blockchair) {\n                val blockchairApi = BlockchairApi(network.blockchairChainId)\n                val blockchairBlockHashFetcher = BlockchairBlockHashFetcher(blockchairApi)\n                val blockchairProvider = BlockchairTransactionProvider(blockchairApi, blockchairBlockHashFetcher)\n\n                BiApiTransactionProvider(\n                    restoreProvider = insightApiProvider,\n                    syncProvider = blockchairProvider,\n                    syncStateManager = apiSyncStateManager\n                )\n            } else {\n                insightApiProvider\n            }\n        }\n\n        NetworkType.TestNet -> {\n            InsightApi(\"https://testnet-insight.dash.org/insight-api\")\n        }\n    }\n\n    fun dashTransactions(fromUid: String? = null, type: TransactionFilterType? = null, limit: Int? = null): Single<List<DashTransactionInfo>> {\n        return transactions(fromUid, type, limit).map {\n            it.mapNotNull { it as? DashTransactionInfo }\n        }\n    }\n\n    fun getDashTransaction(hash: String): DashTransactionInfo? {\n        return getTransaction(hash) as? DashTransactionInfo\n    }\n\n    // BitcoinCore.Listener\n    override fun onTransactionsUpdate(inserted: List<TransactionInfo>, updated: List<TransactionInfo>) {\n        // check for all new transactions if it's has instant lock\n        inserted.map { it.transactionHash.hexToByteArray().reversedArray() }.forEach {\n            instantSend.handle(it)\n        }\n\n        listener?.onTransactionsUpdate(inserted.mapNotNull { it as? DashTransactionInfo }, updated.mapNotNull { it as? DashTransactionInfo })\n    }\n\n    override fun onTransactionsDelete(hashes: List<String>) {\n        listener?.onTransactionsDelete(hashes)\n    }\n\n    override fun onBalanceUpdate(balance: BalanceInfo) {\n        listener?.onBalanceUpdate(balance)\n    }\n\n    override fun onLastBlockInfoUpdate(blockInfo: BlockInfo) {\n        listener?.onLastBlockInfoUpdate(blockInfo)\n    }\n\n    override fun onKitStateUpdate(state: BitcoinCore.KitState) {\n        listener?.onKitStateUpdate(state)\n    }\n\n    // IInstantTransactionDelegate\n    override fun onUpdateInstant(transactionHash: ByteArray) {\n        val transaction = dashStorage.getFullTransactionInfo(transactionHash) ?: return\n        val transactionInfo = dashTransactionInfoConverter.transactionInfo(transaction)\n\n        bitcoinCore.listenerExecutor.execute {\n            listener?.onTransactionsUpdate(listOf(), listOf(transactionInfo))\n        }\n    }\n\n    companion object {\n        const val maxTargetBits: Long = 0x1e0fffff\n\n        const val targetSpacing = 150             // 2.5 min. for mining 1 Block\n        const val targetTimespan = 3600L          // 1 hour for 24 blocks\n        const val heightInterval = targetTimespan / targetSpacing\n\n        val defaultNetworkType: NetworkType = NetworkType.MainNet\n        val defaultSyncMode: SyncMode = SyncMode.Api()\n        const val defaultPeerSize: Int = 10\n        const val defaultConfirmationsThreshold: Int = 6\n\n        private fun getDatabaseNameCore(networkType: NetworkType, walletId: String, syncMode: SyncMode) =\n            \"${getDatabaseName(networkType, walletId, syncMode)}-core\"\n\n        private fun getDatabaseName(networkType: NetworkType, walletId: String, syncMode: SyncMode) =\n            \"Dash-${networkType.name}-$walletId-${syncMode.javaClass.simpleName}\"\n\n        private fun parseAddress(address: String, network: Network): Address {\n            return Base58AddressConverter(network.addressVersion, network.addressScriptVersion).convert(address)\n        }\n\n        private fun network(networkType: NetworkType) = when (networkType) {\n            NetworkType.MainNet -> MainNetDash()\n            NetworkType.TestNet -> TestNetDash()\n        }\n\n        fun clear(context: Context, networkType: NetworkType, walletId: String) {\n            for (syncMode in listOf(SyncMode.Api(), SyncMode.Full(), SyncMode.Blockchair())) {\n                try {\n                    SQLiteDatabase.deleteDatabase(context.getDatabasePath(getDatabaseNameCore(networkType, walletId, syncMode)))\n                    SQLiteDatabase.deleteDatabase(context.getDatabasePath(getDatabaseName(networkType, walletId, syncMode)))\n                } catch (ex: Exception) {\n                    continue\n                }\n            }\n        }\n\n        private fun addressConverter(network: Network): AddressConverterChain {\n            val addressConverter = AddressConverterChain()\n            addressConverter.prependConverter(Base58AddressConverter(network.addressVersion, network.addressScriptVersion))\n            return addressConverter\n        }\n\n        fun firstAddress(\n            seed: ByteArray,\n            networkType: NetworkType,\n        ): Address {\n            return BitcoinCore.firstAddress(\n                seed,\n                Purpose.BIP44,\n                network(networkType),\n                addressConverter(network(networkType))\n            )\n        }\n\n        fun firstAddress(\n            extendedKey: HDExtendedKey,\n            networkType: NetworkType,\n        ): Address {\n            return BitcoinCore.firstAddress(\n                extendedKey,\n                Purpose.BIP44,\n                network(networkType),\n                addressConverter(network(networkType))\n            )\n        }\n    }\n\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/DashKitErrors.kt",
    "content": "package io.horizontalsystems.dashkit\n\nobject DashKitErrors {\n    sealed class LockVoteValidation : Exception() {\n        class MasternodeNotFound : LockVoteValidation()\n        class MasternodeNotInTop : LockVoteValidation()\n        class TxInputNotFound : LockVoteValidation()\n        class SignatureNotValid : LockVoteValidation()\n    }\n\n    sealed class ISLockValidation : Exception() {\n        class SignatureNotValid : ISLockValidation()\n        class QuorumNotFound : ISLockValidation()\n    }\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/IDashStorage.kt",
    "content": "package io.horizontalsystems.dashkit\n\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.bitcoincore.models.TransactionInput\nimport io.horizontalsystems.dashkit.models.*\n\ninterface IDashStorage {\n    fun getBlock(blockHash: ByteArray): Block?\n    fun instantTransactionHashes(): List<ByteArray>\n    fun instantTransactionInputs(txHash: ByteArray): List<InstantTransactionInput>\n    fun getTransactionInputs(txHash: ByteArray): List<TransactionInput>\n    fun addInstantTransactionInput(instantTransactionInput: InstantTransactionInput)\n    fun addInstantTransactionHash(txHash: ByteArray)\n    fun removeInstantTransactionInputs(txHash: ByteArray)\n    fun isTransactionExists(txHash: ByteArray): Boolean\n    fun getQuorumsByType(quorumType: QuorumType): List<Quorum>\n\n    var masternodes: List<Masternode>\n    var masternodeListState: MasternodeListState?\n    var quorums: List<Quorum>\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/IInstantTransactionDelegate.kt",
    "content": "package io.horizontalsystems.dashkit\n\n// TODO Rename to listener\ninterface IInstantTransactionDelegate {\n    fun onUpdateInstant(transactionHash: ByteArray)\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/IMerkleHasher.kt",
    "content": "package io.horizontalsystems.dashkit\n\ninterface IMerkleHasher {\n    fun hash(first: ByteArray, second: ByteArray) : ByteArray\n}"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/InstantSend.kt",
    "content": "package io.horizontalsystems.dashkit\n\nimport io.horizontalsystems.bitcoincore.models.InventoryItem\nimport io.horizontalsystems.bitcoincore.network.peer.IInventoryItemsHandler\nimport io.horizontalsystems.bitcoincore.network.peer.IPeerTaskHandler\nimport io.horizontalsystems.bitcoincore.network.peer.Peer\nimport io.horizontalsystems.bitcoincore.network.peer.task.PeerTask\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.bitcoincore.transactions.TransactionSyncer\nimport io.horizontalsystems.dashkit.instantsend.instantsendlock.InstantSendLockHandler\nimport io.horizontalsystems.dashkit.instantsend.transactionlockvote.TransactionLockVoteHandler\nimport io.horizontalsystems.dashkit.messages.ISLockMessage\nimport io.horizontalsystems.dashkit.messages.TransactionLockVoteMessage\nimport io.horizontalsystems.dashkit.tasks.RequestInstantSendLocksTask\nimport io.horizontalsystems.dashkit.tasks.RequestTransactionLockRequestsTask\nimport io.horizontalsystems.dashkit.tasks.RequestTransactionLockVotesTask\nimport java.util.concurrent.Executors\n\nclass InstantSend(\n        private val transactionSyncer: TransactionSyncer,\n        private val transactionLockVoteHandler: TransactionLockVoteHandler,\n        private val instantSendLockHandler: InstantSendLockHandler\n) : IInventoryItemsHandler, IPeerTaskHandler {\n\n    private val dispatchQueue = Executors.newSingleThreadExecutor()\n\n    fun handle(insertedTxHash: ByteArray) {\n        instantSendLockHandler.handle(insertedTxHash)\n    }\n\n    override fun handleInventoryItems(peer: Peer, inventoryItems: List<InventoryItem>) {\n        val transactionLockRequests = mutableListOf<ByteArray>()\n        val transactionLockVotes = mutableListOf<ByteArray>()\n        val isLocks = mutableListOf<ByteArray>()\n\n        inventoryItems.forEach { item ->\n            when (item.type) {\n                InventoryType.MSG_TXLOCK_REQUEST -> {\n                    transactionLockRequests.add(item.hash)\n                }\n                InventoryType.MSG_TXLOCK_VOTE -> {\n                    transactionLockVotes.add(item.hash)\n                }\n                InventoryType.MSG_ISLOCK -> {\n                    isLocks.add(item.hash)\n                }\n            }\n        }\n\n        if (transactionLockRequests.isNotEmpty()) {\n            peer.addTask(RequestTransactionLockRequestsTask(transactionLockRequests))\n        }\n\n        if (transactionLockVotes.isNotEmpty()) {\n            peer.addTask(RequestTransactionLockVotesTask(transactionLockVotes))\n        }\n\n        if (isLocks.isNotEmpty()) {\n            peer.addTask(RequestInstantSendLocksTask(isLocks))\n        }\n\n    }\n\n    override fun handleCompletedTask(peer: Peer, task: PeerTask): Boolean {\n        return when (task) {\n            is RequestTransactionLockRequestsTask -> {\n                dispatchQueue.execute {\n                    handleTransactions(task.transactions)\n                }\n                true\n            }\n            is RequestTransactionLockVotesTask -> {\n                dispatchQueue.execute {\n                    handleTransactionLockVotes(task.transactionLockVotes)\n                }\n                true\n            }\n            is RequestInstantSendLocksTask -> {\n                dispatchQueue.execute {\n                    handleInstantSendLocks(task.isLocks)\n                }\n                true\n            }\n            else -> false\n        }\n    }\n\n    private fun handleTransactions(transactions: List<FullTransaction>) {\n        transactionSyncer.handleRelayed(transactions)\n\n        for (transaction in transactions) {\n            transactionLockVoteHandler.handle(transaction)\n        }\n    }\n\n    private fun handleTransactionLockVotes(transactionLockVotes: List<TransactionLockVoteMessage>) {\n        for (vote in transactionLockVotes) {\n            transactionLockVoteHandler.handle(vote)\n        }\n    }\n\n    private fun handleInstantSendLocks(isLocks: List<ISLockMessage>) {\n        for (isLock in isLocks) {\n            instantSendLockHandler.handle(isLock)\n        }\n    }\n\n    companion object {\n        const val requiredVoteCount = 6\n    }\n\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/InventoryType.kt",
    "content": "package io.horizontalsystems.dashkit\n\nobject InventoryType {\n    const val MSG_TXLOCK_REQUEST = 4\n    const val MSG_TXLOCK_VOTE = 5\n    const val MSG_ISLOCK = 30\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/MainNetDash.kt",
    "content": "package io.horizontalsystems.dashkit\n\nimport io.horizontalsystems.bitcoincore.network.Network\n\nclass MainNetDash : Network() {\n\n    override val protocolVersion = 70228\n    override val noBloomVersion = 70201\n\n    override var port: Int = 9999\n\n    override var magic: Long = 0xbd6b0cbf\n    override var bip32HeaderPub: Int = 0x0488B21E   // The 4 byte header that serializes in base58 to \"xpub\".\n    override var bip32HeaderPriv: Int = 0x0488ADE4  // The 4 byte header that serializes in base58 to \"xprv\"\n    override var addressVersion: Int = 76\n    override var addressSegwitHrp: String = \"bc\"\n    override var addressScriptVersion: Int = 16\n    override var coinType: Int = 5\n    override val blockchairChainId: String = \"dash\"\n\n    override val maxBlockSize = 1_000_000\n    override val dustRelayTxFee = 1000 // https://github.com/dashpay/dash/blob/master/src/policy/policy.h#L36\n\n    override var dnsSeeds = listOf(\n            \"dnsseed.dash.org\",\n            \"dnsseed.dashdot.io\",\n            \"dnsseed.masternode.io\"\n    )\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/TestNetDash.kt",
    "content": "package io.horizontalsystems.dashkit\n\nimport io.horizontalsystems.bitcoincore.network.Network\n\nclass TestNetDash : Network() {\n\n    override val protocolVersion = 70214\n    override val noBloomVersion = 70201\n\n    override var port: Int = 19999\n\n    override var magic: Long = 0xffcae2ce\n    override var bip32HeaderPub: Int = 0x0488B21E   // The 4 byte header that serializes in base58 to \"xpub\".\n    override var bip32HeaderPriv: Int = 0x0488ADE4  // The 4 byte header that serializes in base58 to \"xprv\"\n    override var addressVersion: Int = 140\n    override var addressSegwitHrp: String = \"bc\"\n    override var addressScriptVersion: Int = 19\n    override var coinType: Int = 1\n    override val blockchairChainId: String = \"\"\n\n    override val maxBlockSize = 1_000_000\n    override val dustRelayTxFee = 1000 // https://github.com/dashpay/dash/blob/master/src/policy/policy.h#L36\n\n    override var dnsSeeds = listOf(\n            \"testnet-seed.dashdot.io\",\n            \"test.dnsseed.masternode.io\"\n    )\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/X11Hasher.kt",
    "content": "package io.horizontalsystems.dashkit\n\nimport fr.cryptohash.*\nimport io.horizontalsystems.bitcoincore.core.IHasher\nimport java.util.*\n\nclass X11Hasher : IHasher {\n    private val algorithms = listOf(\n            BLAKE512(),\n            BMW512(),\n            Groestl512(),\n            Skein512(),\n            JH512(),\n            Keccak512(),\n            Luffa512(),\n            CubeHash512(),\n            SHAvite512(),\n            SIMD512(),\n            ECHO512()\n    )\n\n    override fun hash(data: ByteArray): ByteArray {\n        var hash = data\n\n        algorithms.forEach {\n            hash = it.digest(hash)\n        }\n\n        return hash.copyOfRange(0, 32)\n    }\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/core/DashTransactionInfoConverter.kt",
    "content": "package io.horizontalsystems.dashkit.core\n\nimport io.horizontalsystems.bitcoincore.core.BaseTransactionInfoConverter\nimport io.horizontalsystems.bitcoincore.core.ITransactionInfoConverter\nimport io.horizontalsystems.bitcoincore.extensions.toHexString\nimport io.horizontalsystems.bitcoincore.models.InvalidTransaction\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.models.TransactionMetadata\nimport io.horizontalsystems.bitcoincore.models.TransactionStatus\nimport io.horizontalsystems.bitcoincore.storage.FullTransactionInfo\nimport io.horizontalsystems.dashkit.instantsend.InstantTransactionManager\nimport io.horizontalsystems.dashkit.models.DashTransactionInfo\n\nclass DashTransactionInfoConverter(private val instantTransactionManager: InstantTransactionManager) : ITransactionInfoConverter {\n    override lateinit var baseConverter: BaseTransactionInfoConverter\n\n    override fun transactionInfo(fullTransactionInfo: FullTransactionInfo): DashTransactionInfo {\n        val transaction = fullTransactionInfo.header\n\n        if (transaction.status == Transaction.Status.INVALID) {\n            (transaction as? InvalidTransaction)?.let {\n                return getInvalidTransactionInfo(it, fullTransactionInfo.metadata)\n            }\n        }\n\n        val txInfo = baseConverter.transactionInfo(fullTransactionInfo)\n\n        return DashTransactionInfo(\n                txInfo.uid,\n                txInfo.transactionHash,\n                txInfo.transactionIndex,\n                txInfo.inputs,\n                txInfo.outputs,\n                txInfo.amount,\n                txInfo.type,\n                txInfo.fee,\n                txInfo.blockHeight,\n                txInfo.timestamp,\n                txInfo.status,\n                txInfo.conflictingTxHash,\n                instantTransactionManager.isTransactionInstant(fullTransactionInfo.header.hash)\n        )\n    }\n\n    private fun getInvalidTransactionInfo(\n        transaction: InvalidTransaction,\n        metadata: TransactionMetadata\n    ): DashTransactionInfo {\n        return try {\n            DashTransactionInfo(transaction.serializedTxInfo)\n        } catch (ex: Exception) {\n            DashTransactionInfo(\n                uid = transaction.uid,\n                transactionHash = transaction.hash.toHexString(),\n                transactionIndex = transaction.order,\n                inputs = listOf(),\n                outputs = listOf(),\n                amount = metadata.amount,\n                type = metadata.type,\n                fee = null,\n                blockHeight = null,\n                timestamp = transaction.timestamp,\n                status = TransactionStatus.INVALID,\n                conflictingTxHash = null,\n                instantTx = false\n            )\n        }\n    }\n\n}"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/core/DoubleSha256Hasher.kt",
    "content": "package io.horizontalsystems.dashkit.core\n\nimport io.horizontalsystems.bitcoincore.core.IHasher\nimport io.horizontalsystems.bitcoincore.utils.HashUtils\n\nclass SingleSha256Hasher : IHasher {\n    override fun hash(data: ByteArray): ByteArray {\n        return HashUtils.sha256(data)\n    }\n}"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/instantsend/BLS.kt",
    "content": "package io.horizontalsystems.dashkit.instantsend\n\nimport org.dashj.bls.InsecureSignature\nimport org.dashj.bls.JNI\nimport org.dashj.bls.PublicKey\nimport java.util.logging.Logger\n\nclass BLS {\n    private val logger = Logger.getLogger(\"BLS\")\n\n    init {\n        System.loadLibrary(JNI.LIBRARY_NAME)\n    }\n\n    fun verifySignature(pubKeyOperator: ByteArray, vchMasternodeSignature: ByteArray, hash: ByteArray): Boolean {\n        return try {\n            val pk = PublicKey.FromBytes(pubKeyOperator)\n            val insecureSignature = InsecureSignature.FromBytes(vchMasternodeSignature)\n\n            insecureSignature.Verify(hash, pk)\n        } catch (e: Exception) {\n            logger.severe(\"Verifying BLS signature failed with exception: $e\")\n\n            false\n        }\n    }\n\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/instantsend/InstantSendFactory.kt",
    "content": "package io.horizontalsystems.dashkit.instantsend\n\nimport io.horizontalsystems.dashkit.models.InstantTransactionInput\nimport java.util.*\n\nclass InstantSendFactory {\n\n    fun instantTransactionInput(txHash: ByteArray, inputTxHash: ByteArray, voteCount: Int, blockHeight: Int?) : InstantTransactionInput {\n        val timeCreated = Date().time / 1000\n\n        return InstantTransactionInput(txHash, inputTxHash, timeCreated, voteCount, blockHeight)\n    }\n\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/instantsend/InstantSendLockValidator.kt",
    "content": "package io.horizontalsystems.dashkit.instantsend\n\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\nimport io.horizontalsystems.bitcoincore.utils.HashUtils\nimport io.horizontalsystems.dashkit.DashKitErrors\nimport io.horizontalsystems.dashkit.managers.QuorumListManager\nimport io.horizontalsystems.dashkit.messages.ISLockMessage\nimport io.horizontalsystems.dashkit.models.QuorumType\n\nclass InstantSendLockValidator(\n        private val quorumListManager: QuorumListManager,\n        private val bls: BLS\n) {\n\n    @Throws\n    fun validate(islock: ISLockMessage) {\n        // 01. Select quorum\n        val quorum = quorumListManager.getQuorum(QuorumType.LLMQ_50_60, islock.requestId)\n\n        // 02. Make signId data to verify signature\n        val signIdPayload = BitcoinOutput()\n                .writeByte(quorum.type)\n                .write(quorum.quorumHash)\n                .write(islock.requestId)\n                .write(islock.txHash)\n                .toByteArray()\n\n        val signId = HashUtils.doubleSha256(signIdPayload)\n\n        // 03. Verify signature by BLS\n        if (!bls.verifySignature(quorum.quorumPublicKey, islock.sign, signId)) {\n            throw DashKitErrors.ISLockValidation.SignatureNotValid()\n        }\n    }\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/instantsend/InstantTransactionManager.kt",
    "content": "package io.horizontalsystems.dashkit.instantsend\n\nimport io.horizontalsystems.bitcoincore.models.TransactionInput\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.dashkit.DashKitErrors\nimport io.horizontalsystems.dashkit.IDashStorage\nimport io.horizontalsystems.dashkit.InstantSend\nimport io.horizontalsystems.dashkit.models.InstantTransactionInput\nimport io.horizontalsystems.dashkit.models.InstantTransactionState\n\nclass InstantTransactionManager(\n        private val storage: IDashStorage,\n        private val instantSendFactory: InstantSendFactory,\n        private val state: InstantTransactionState\n) {\n    init {\n        state.instantTransactionHashes = storage.instantTransactionHashes().toMutableList()\n    }\n\n    fun instantTransactionInputs(txHash: ByteArray, instantTransaction: FullTransaction?): List<InstantTransactionInput> {\n        // check if inputs already created\n        val inputs = storage.instantTransactionInputs(txHash)\n        if (inputs.isNotEmpty()) {\n            return inputs\n        }\n\n        // if not check coming ix\n        instantTransaction?.let { transaction ->\n            return makeInputs(txHash, transaction.inputs)\n        }\n\n        // if we can't get inputs and ix is null, try get tx inputs from db\n        return makeInputs(txHash, storage.getTransactionInputs(txHash))\n    }\n\n    @Throws\n    fun updateInput(inputTxHash: ByteArray, transactionInputs: List<InstantTransactionInput>) {\n        val updatedInputs = transactionInputs.toMutableList()\n\n        val inputIndex = transactionInputs.indexOfFirst { it.inputTxHash.contentEquals(inputTxHash) }\n        if (inputIndex == -1) {\n            throw DashKitErrors.LockVoteValidation.TxInputNotFound()\n        }\n\n        val input = transactionInputs[inputIndex]\n        val increasedInput = instantSendFactory.instantTransactionInput(input.txHash, input.inputTxHash, input.voteCount + 1, input.blockHeight)\n        storage.addInstantTransactionInput(increasedInput)\n\n        updatedInputs[inputIndex] = increasedInput\n        if (updatedInputs.none { it.voteCount < InstantSend.requiredVoteCount }) {\n            state.append(input.txHash)\n            storage.addInstantTransactionHash(input.txHash)\n            storage.removeInstantTransactionInputs(input.txHash)\n        }\n    }\n\n    fun isTransactionInstant(txHash: ByteArray): Boolean {\n        return state.instantTransactionHashes.any { it.contentEquals(txHash) }\n    }\n\n    fun isTransactionExists(txHash: ByteArray): Boolean {\n        return storage.isTransactionExists(txHash)\n    }\n\n    fun makeInstant(txHash: ByteArray) {\n        state.append(txHash)\n        storage.addInstantTransactionHash(txHash)\n    }\n\n    private fun makeInputs(txHash: ByteArray, inputs: List<TransactionInput>): List<InstantTransactionInput> {\n        val instantInputs = mutableListOf<InstantTransactionInput>()\n        for (input in inputs) {\n            val instantInput = instantSendFactory.instantTransactionInput(txHash, input.previousOutputTxHash, 0, null)\n\n            storage.addInstantTransactionInput(instantInput)\n            instantInputs.add(instantInput)\n        }\n        return instantInputs\n    }\n\n}"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/instantsend/QuorumMasternode.kt",
    "content": "package io.horizontalsystems.dashkit.instantsend\n\nimport io.horizontalsystems.dashkit.models.Masternode\n\nclass QuorumMasternode(val quorumHash: ByteArray, val masternode: Masternode) : Comparable<QuorumMasternode> {\n\n    override fun compareTo(other: QuorumMasternode): Int {\n        for (i in 0 until quorumHash.size) {\n            val b1: Int = quorumHash[i].toInt() and 0xff\n            val b2: Int = other.quorumHash[i].toInt() and 0xff\n\n            val res = b1.compareTo(b2)\n            if (res != 0) return res\n        }\n\n        return 0\n    }\n}"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/instantsend/TransactionLockVoteValidator.kt",
    "content": "package io.horizontalsystems.dashkit.instantsend\n\nimport io.horizontalsystems.bitcoincore.core.IHasher\nimport io.horizontalsystems.dashkit.DashKitErrors\nimport io.horizontalsystems.dashkit.IDashStorage\n\nclass TransactionLockVoteValidator(private val storage: IDashStorage, private val hasher: IHasher, private val bls: BLS) {\n\n    companion object {\n        private const val totalSignatures = 10\n    }\n\n    @Throws\n    fun validate(quorumModifierHash: ByteArray, masternodeProTxHash: ByteArray, vchMasternodeSignature: ByteArray, hash: ByteArray) {\n        val masternodes = storage.masternodes.filter { it.isValid }\n\n        val quorumMasternodes = mutableListOf<QuorumMasternode>()\n\n        // 1. Make list of masternodes with quorumHashes\n        masternodes.forEach { masternode ->\n            val quorumHash = hasher.hash(hasher.hash(masternode.proRegTxHash + masternode.confirmedHash) + quorumModifierHash).reversedArray() //Score calculated for littleEndiad (check last bytes, then previous and ...)\n            quorumMasternodes.add(QuorumMasternode(quorumHash, masternode))\n        }\n\n        // 2. Sort descending\n        quorumMasternodes.sortDescending()\n\n        // 3. Find index for masternode\n        val index = quorumMasternodes.indexOfFirst { it.masternode.proRegTxHash.contentEquals(masternodeProTxHash) }\n        if (index == -1) {\n            throw DashKitErrors.LockVoteValidation.MasternodeNotFound()\n        }\n\n        // 4. Check masternode in first 10 scores\n        if (index > totalSignatures) {\n            throw DashKitErrors.LockVoteValidation.MasternodeNotInTop()\n        }\n\n        // 5. Check signature\n        val masternode = quorumMasternodes[index].masternode\n        if (!bls.verifySignature(masternode.pubKeyOperator, vchMasternodeSignature, hash)) {\n            throw DashKitErrors.LockVoteValidation.SignatureNotValid()\n        }\n    }\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/instantsend/instantsendlock/InstantSendLockHandler.kt",
    "content": "package io.horizontalsystems.dashkit.instantsend.instantsendlock\n\nimport io.horizontalsystems.dashkit.IInstantTransactionDelegate\nimport io.horizontalsystems.dashkit.instantsend.InstantTransactionManager\nimport io.horizontalsystems.dashkit.messages.ISLockMessage\nimport java.util.logging.Logger\n\nclass InstantSendLockHandler(\n        private val instantTransactionManager: InstantTransactionManager,\n        private val instantLockManager: InstantSendLockManager\n) {\n\n    var delegate: IInstantTransactionDelegate? = null\n    private val logger = Logger.getLogger(\"InstantSendLockHandler\")\n\n    fun handle(transactionHash: ByteArray) {\n        // get relayed lock for inserted transaction and check it\n        instantLockManager.takeRelayedLock(transactionHash)?.let { lock ->\n            validateSendLock(lock)\n        }\n    }\n\n    fun handle(isLock: ISLockMessage) {\n        // check transaction already not in instant\n        if (instantTransactionManager.isTransactionInstant(isLock.txHash)) {\n            return\n        }\n\n        // do nothing if tx doesn't exist\n        if (!instantTransactionManager.isTransactionExists(isLock.txHash)) {\n            instantLockManager.add(isLock)\n            return\n        }\n\n        // validation\n        validateSendLock(isLock)\n    }\n\n    private fun validateSendLock(isLock: ISLockMessage) {\n        try {\n            instantLockManager.validate(isLock)\n\n            instantTransactionManager.makeInstant(isLock.txHash)\n            delegate?.onUpdateInstant(isLock.txHash)\n        } catch (e: Exception) {\n            logger.severe(e.message)\n        }\n    }\n\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/instantsend/instantsendlock/InstantSendLockManager.kt",
    "content": "package io.horizontalsystems.dashkit.instantsend.instantsendlock\n\nimport io.horizontalsystems.bitcoincore.core.HashBytes\nimport io.horizontalsystems.dashkit.instantsend.InstantSendLockValidator\nimport io.horizontalsystems.dashkit.messages.ISLockMessage\n\nclass InstantSendLockManager(private val instantSendLockValidator: InstantSendLockValidator) {\n    private val relayedLocks = mutableMapOf<HashBytes, ISLockMessage>()\n\n    fun add(relayed: ISLockMessage) {\n        relayedLocks[HashBytes(relayed.txHash)] = relayed\n    }\n\n    fun takeRelayedLock(txHash: ByteArray): ISLockMessage? {\n        relayedLocks[HashBytes(txHash)]?.let {\n            relayedLocks.remove(HashBytes(txHash))\n            return it\n        }\n        return null\n    }\n\n    @Throws\n    fun validate(isLock: ISLockMessage) {\n        instantSendLockValidator.validate(isLock)\n    }\n\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/instantsend/transactionlockvote/TransactionLockVoteHandler.kt",
    "content": "package io.horizontalsystems.dashkit.instantsend.transactionlockvote\n\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.dashkit.IInstantTransactionDelegate\nimport io.horizontalsystems.dashkit.instantsend.InstantTransactionManager\nimport io.horizontalsystems.dashkit.messages.TransactionLockVoteMessage\nimport io.horizontalsystems.dashkit.models.InstantTransactionInput\nimport java.util.logging.Logger\n\nclass TransactionLockVoteHandler(\n        private val instantTransactionManager: InstantTransactionManager,\n        private val lockVoteManager: TransactionLockVoteManager,\n        private val requiredVoteCount: Int = 6\n) {\n    var delegate: IInstantTransactionDelegate? = null\n\n    private val logger = Logger.getLogger(\"TransactionLockVoteHandler\")\n\n    fun handle(transaction: FullTransaction) {\n        // check transaction already not in instant\n        if (instantTransactionManager.isTransactionInstant(transaction.header.hash)) {\n            return\n        }\n\n        // prepare instant inputs for ix\n        val inputs = instantTransactionManager.instantTransactionInputs(transaction.header.hash, transaction)\n\n        // poll relayed lock votes to update inputs\n        val relayedVotes = lockVoteManager.takeRelayedLockVotes(transaction.header.hash)\n        relayedVotes.forEach { vote ->\n            handle(vote, inputs)\n        }\n    }\n\n    fun handle(lockVote: TransactionLockVoteMessage) {\n        // check transaction already not in instant\n        if (instantTransactionManager.isTransactionInstant(lockVote.txHash)) {\n            return\n        }\n        if (lockVoteManager.processed(lockVote.hash)) {\n            return\n        }\n\n        val inputs = instantTransactionManager.instantTransactionInputs(lockVote.txHash, null)\n        if (inputs.isEmpty()) {\n            lockVoteManager.addRelayed(lockVote)\n            return\n        }\n\n        handle(lockVote, inputs)\n\n    }\n\n    private fun handle(lockVote: TransactionLockVoteMessage, instantInputs: List<InstantTransactionInput>) {\n        lockVoteManager.addChecked(lockVote)\n        // ignore votes for inputs which already has 6 votes\n        val input = instantInputs.firstOrNull { it.inputTxHash.contentEquals(lockVote.outpoint.txHash) }\n\n        if (input == null || input.voteCount >= requiredVoteCount) {\n            return\n        }\n\n        try {\n            lockVoteManager.validate(lockVote)\n            instantTransactionManager.updateInput(lockVote.outpoint.txHash, instantInputs)\n\n            if (instantTransactionManager.isTransactionInstant(lockVote.txHash)) {\n                delegate?.onUpdateInstant(lockVote.txHash)\n            }\n        } catch(e: Exception) {\n            logger.severe(e.message)\n        }\n    }\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/instantsend/transactionlockvote/TransactionLockVoteManager.kt",
    "content": "package io.horizontalsystems.dashkit.instantsend.transactionlockvote\n\nimport io.horizontalsystems.dashkit.instantsend.TransactionLockVoteValidator\nimport io.horizontalsystems.dashkit.messages.TransactionLockVoteMessage\n\nclass TransactionLockVoteManager(private val transactionLockVoteValidator: TransactionLockVoteValidator) {\n    val relayedLockVotes = mutableListOf<TransactionLockVoteMessage>()\n    val checkedLockVotes = mutableListOf<TransactionLockVoteMessage>()\n\n    fun takeRelayedLockVotes(txHash: ByteArray): List<TransactionLockVoteMessage> {\n        val votes = relayedLockVotes.filter {\n            it.txHash.contentEquals(txHash)\n        }\n        relayedLockVotes.removeAll(votes)\n        return votes\n    }\n\n    fun addRelayed(vote: TransactionLockVoteMessage) {\n        relayedLockVotes.add(vote)\n    }\n\n    fun addChecked(vote: TransactionLockVoteMessage) {\n        checkedLockVotes.add(vote)\n    }\n\n    fun processed(lvHash: ByteArray): Boolean {\n        return relayedLockVotes.any { it.hash.contentEquals(lvHash) } || checkedLockVotes.any { it.hash.contentEquals(lvHash) }\n    }\n\n    @Throws\n    fun validate(lockVote: TransactionLockVoteMessage) {\n        // validate masternode in top 10 masternodes for quorumModifier\n        transactionLockVoteValidator.validate(lockVote.quorumModifierHash, lockVote.masternodeProTxHash, lockVote.vchMasternodeSignature, lockVote.hash)\n    }\n\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/managers/ConfirmedUnspentOutputProvider.kt",
    "content": "package io.horizontalsystems.dashkit.managers\n\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.managers.IUnspentOutputProvider\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\nimport io.horizontalsystems.bitcoincore.storage.UtxoFilters\n\nclass ConfirmedUnspentOutputProvider(private val storage: IStorage, private val confirmationsThreshold: Int) : IUnspentOutputProvider {\n    override fun getSpendableUtxo(filters: UtxoFilters): List<UnspentOutput> {\n        val lastBlockHeight = storage.lastBlock()?.height ?: 0\n\n        return storage.getUnspentOutputs().filter {\n            isOutputConfirmed(it, lastBlockHeight) && filters.filterUtxo(it, storage)\n        }\n    }\n\n    private fun isOutputConfirmed(unspentOutput: UnspentOutput, lastBlockHeight: Int): Boolean {\n        val block = unspentOutput.block ?: return false\n\n        return block.height <= lastBlockHeight - confirmationsThreshold + 1\n    }\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/managers/MasternodeListManager.kt",
    "content": "package io.horizontalsystems.dashkit.managers\n\nimport io.horizontalsystems.bitcoincore.core.HashBytes\nimport io.horizontalsystems.bitcoincore.utils.HashUtils\nimport io.horizontalsystems.bitcoincore.utils.MerkleBranch\nimport io.horizontalsystems.dashkit.IDashStorage\nimport io.horizontalsystems.dashkit.masternodelist.MasternodeCbTxHasher\nimport io.horizontalsystems.dashkit.masternodelist.MasternodeListMerkleRootCalculator\nimport io.horizontalsystems.dashkit.messages.MasternodeListDiffMessage\nimport io.horizontalsystems.dashkit.models.MasternodeListState\n\nclass MasternodeListManager(\n        private val storage: IDashStorage,\n        private val masternodeListMerkleRootCalculator: MasternodeListMerkleRootCalculator,\n        private val masternodeCbTxHasher: MasternodeCbTxHasher,\n        private val merkleBranch: MerkleBranch,\n        private val masternodeSortedList: MasternodeSortedList,\n        private val quorumListManager: QuorumListManager\n) {\n\n    open class ValidationError : Exception() {\n        object WrongMerkleRootList : ValidationError()\n        object WrongCoinbaseHash : ValidationError()\n        object NoMerkleBlockHeader : ValidationError()\n        object WrongMerkleRoot : ValidationError()\n    }\n\n    val baseBlockHash: ByteArray\n        get() {\n            return storage.masternodeListState?.baseBlockHash\n                    ?: HashUtils.toBytesAsLE(\"0000000000000000000000000000000000000000000000000000000000000000\")\n        }\n\n    //01. Create a copy of the masternode list which was valid at “baseBlockHash”. If “baseBlockHash” is all-zero, an empty list must be used.\n    //02. Delete all entries found in “deletedMNs” from this list. Please note that “deletedMNs” contains the ProRegTx hashes of the masternodes and NOT the hashes of the SML entries.\n    //03. Add or replace all entries found in “mnList” in the list\n    //04. Calculate the merkle root of the list by following the “Calculating the merkle root of the Masternode list” section\n    //05. Compare the calculated merkle root with what is found in “cbTx”. If it does not match, abort the process and ask for diffs from another node.\n    //06. Calculate the hash of “cbTx” and verify existence of this transaction in the block specified by “blockHash”. To do this, use the already received block header and the fields “totalTransactions”, “merkleHashes” and “merkleFlags” from the MNLISTDIFF message and perform a merkle verification the same way as done when a “MERKLEBLOCK” message is received. If the verification fails, abort the process and ask for diffs from another node.\n    //07. Store the resulting validated masternode list identified by “blockHash”\n    @Throws(ValidationError::class)\n    fun updateList(masternodeListDiffMessage: MasternodeListDiffMessage) {\n        masternodeSortedList.removeAll()\n\n        //01.\n        masternodeSortedList.add(storage.masternodes)\n        //02.\n        masternodeSortedList.remove(masternodeListDiffMessage.deletedMNs)\n        //03.\n        masternodeSortedList.add(masternodeListDiffMessage.mnList)\n        //04.\n        val hash = masternodeListMerkleRootCalculator.calculateMerkleRoot(masternodeSortedList.masternodes)\n\n        //05.\n        if (hash != null && !masternodeListDiffMessage.cbTx.merkleRootMNList.contentEquals(hash)) {\n            throw ValidationError.WrongMerkleRootList\n        }\n        //06.\n        val cbTxHash = masternodeCbTxHasher.hash(masternodeListDiffMessage.cbTx)\n\n        val matchedHashes = mutableMapOf<HashBytes, Boolean>()\n\n        val calculatedMerkleRoot = merkleBranch.calculateMerkleRoot(masternodeListDiffMessage.totalTransactions.toInt(), masternodeListDiffMessage.merkleHashes, masternodeListDiffMessage.merkleFlags, matchedHashes)\n\n        if (matchedHashes[cbTxHash] != true) {\n            throw ValidationError.WrongCoinbaseHash\n        }\n\n        val block = storage.getBlock(masternodeListDiffMessage.blockHash)\n        val merkleRoot = block?.merkleRoot\n\n        if (block == null || merkleRoot == null) {\n            throw ValidationError.NoMerkleBlockHeader\n        }\n\n        if (!merkleRoot.contentEquals(calculatedMerkleRoot)) {\n            throw ValidationError.WrongMerkleRoot\n        }\n\n        quorumListManager.updateList(masternodeListDiffMessage)\n\n        //07.\n        storage.masternodes = masternodeSortedList.masternodes\n        storage.masternodeListState = MasternodeListState(masternodeListDiffMessage.blockHash)\n        // todo: Can optimize. Update only difference of masternode list\n    }\n\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/managers/MasternodeListSyncer.kt",
    "content": "package io.horizontalsystems.dashkit.managers\n\nimport io.horizontalsystems.bitcoincore.BitcoinCore\nimport io.horizontalsystems.bitcoincore.blocks.IPeerSyncListener\nimport io.horizontalsystems.bitcoincore.core.IInitialDownload\nimport io.horizontalsystems.bitcoincore.extensions.toReversedByteArray\nimport io.horizontalsystems.bitcoincore.network.peer.IPeerTaskHandler\nimport io.horizontalsystems.bitcoincore.network.peer.Peer\nimport io.horizontalsystems.bitcoincore.network.peer.PeerGroup\nimport io.horizontalsystems.bitcoincore.network.peer.task.PeerTask\nimport io.horizontalsystems.dashkit.tasks.PeerTaskFactory\nimport io.horizontalsystems.dashkit.tasks.RequestMasternodeListDiffTask\nimport java.util.concurrent.Executors\n\nclass MasternodeListSyncer(\n    private val bitcoinCore: BitcoinCore,\n    private val peerTaskFactory: PeerTaskFactory,\n    private val masternodeListManager: MasternodeListManager,\n    private val initialBlockDownload: IInitialDownload\n) : IPeerTaskHandler, IPeerSyncListener, PeerGroup.Listener {\n\n    @Volatile\n    private var workingPeer: Peer? = null\n    private val peersQueue = Executors.newSingleThreadExecutor()\n\n    override fun onPeerSynced(peer: Peer) {\n        assignNextSyncPeer()\n    }\n\n    override fun onPeerDisconnect(peer: Peer, e: Exception?) {\n        if (peer == workingPeer) {\n            workingPeer = null\n\n            assignNextSyncPeer()\n        }\n    }\n\n    private fun assignNextSyncPeer() {\n        peersQueue.execute {\n            if (workingPeer == null) {\n                bitcoinCore.lastBlockInfo?.let { lastBlockInfo ->\n                    initialBlockDownload.syncedPeers.firstOrNull()?.let { syncedPeer ->\n                        val blockHash = lastBlockInfo.headerHash.toReversedByteArray()\n                        val baseBlockHash = masternodeListManager.baseBlockHash\n\n                        if (!blockHash.contentEquals(baseBlockHash)) {\n                            val task = peerTaskFactory.createRequestMasternodeListDiffTask(baseBlockHash, blockHash)\n                            syncedPeer.addTask(task)\n\n                            workingPeer = syncedPeer\n                        }\n                    }\n                }\n            }\n        }\n    }\n\n\n    override fun handleCompletedTask(peer: Peer, task: PeerTask): Boolean {\n        return when (task) {\n            is RequestMasternodeListDiffTask -> {\n                task.masternodeListDiffMessage?.let { masternodeListDiffMessage ->\n                    masternodeListManager.updateList(masternodeListDiffMessage)\n                    workingPeer = null\n                }\n                true\n            }\n\n            else -> false\n        }\n    }\n\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/managers/MasternodeSortedList.kt",
    "content": "package io.horizontalsystems.dashkit.managers\n\nimport io.horizontalsystems.dashkit.models.Masternode\n\nclass MasternodeSortedList {\n    private val masternodeList = mutableListOf<Masternode>()\n\n    val masternodes: List<Masternode>\n        get() = masternodeList.sorted()\n\n    fun add(masternodes: List<Masternode>) {\n        masternodeList.removeAll(masternodes)\n        masternodeList.addAll(masternodes)\n    }\n\n    fun remove(proRegTxHashes: List<ByteArray>) {\n        proRegTxHashes.forEach { hash ->\n            val index = masternodeList.indexOfFirst { masternode ->\n                masternode.proRegTxHash.contentEquals(hash)\n            }\n\n            if (index != -1) {\n                masternodeList.removeAt(index)\n            }\n        }\n    }\n\n    fun removeAll() {\n        masternodeList.clear()\n    }\n\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/managers/QuorumListManager.kt",
    "content": "package io.horizontalsystems.dashkit.managers\n\nimport io.horizontalsystems.bitcoincore.core.HashBytes\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\nimport io.horizontalsystems.bitcoincore.utils.HashUtils\nimport io.horizontalsystems.dashkit.DashKitErrors\nimport io.horizontalsystems.dashkit.IDashStorage\nimport io.horizontalsystems.dashkit.masternodelist.QuorumListMerkleRootCalculator\nimport io.horizontalsystems.dashkit.messages.MasternodeListDiffMessage\nimport io.horizontalsystems.dashkit.models.Quorum\nimport io.horizontalsystems.dashkit.models.QuorumType\n\nclass QuorumListManager(\n        private val storage: IDashStorage,\n        private val quorumListMerkleRootCalculator: QuorumListMerkleRootCalculator,\n        private val quorumSortedList: QuorumSortedList\n) {\n    open class ValidationError : Exception() {\n        object WrongMerkleRootList : ValidationError()\n    }\n\n    @Throws(ValidationError::class)\n    fun updateList(masternodeListDiffMessage: MasternodeListDiffMessage) {\n        quorumSortedList.removeAll()\n\n        //01. Create a copy of the active LLMQ sets which were given at \"baseBlockHash\". If “baseBlockHash” is all-zero, empty sets must be used.\n        quorumSortedList.add(storage.quorums)\n        //02. Delete all entries found in \"deletedQuorums\" from the corresponding active LLMQ sets.\n        quorumSortedList.remove(masternodeListDiffMessage.deletedQuorums)\n        //03. Verify each final commitment found in \"newQuorums\", by the same rules found in DIP6 - Long-Living Masternode Quorums. If any final commitment is invalid, abort the process and ask for diffs from another node.\n        masternodeListDiffMessage.quorumList.forEach {\n            // TODO()\n        }\n        //04. Add the LLMQ defined by the final commitments found in \"newQuorums\" to the corresponding active LLMQ sets.\n        quorumSortedList.add(masternodeListDiffMessage.quorumList)\n\n        masternodeListDiffMessage.cbTx.merkleRootQuorums?.let { merkleRootQuorums ->\n            //05. Calculate the merkle root of the active LLMQ sets by following the “Calculating the merkle root of the active LLMQs” section\n            val hash = quorumListMerkleRootCalculator.calculateMerkleRoot(quorumSortedList.quorums)\n\n            //06. Compare the calculated merkle root with what is found in “cbTx”. If it does not match, abort the process and ask for diffs from another node.\n            if (hash != null && !merkleRootQuorums.contentEquals(hash)) {\n                throw ValidationError.WrongMerkleRootList\n            }\n        }\n\n        //07. Store the new active LLMQ sets the same way the masternode list is stored.\n        storage.quorums = quorumSortedList.quorums\n    }\n\n    fun getQuorum(quorumType: QuorumType, requestId: ByteArray): Quorum {\n        val typedQuorums = storage.getQuorumsByType(quorumType)\n\n        return typedQuorums.minWith(Comparator { quorum1, quorum2 ->\n            val orderingHash1 = orderingHash(quorum1, requestId)\n            val orderingHash2 = orderingHash(quorum2, requestId)\n\n            orderingHash1.compareTo(orderingHash2)\n        }) ?: throw DashKitErrors.ISLockValidation.QuorumNotFound()\n    }\n\n    private fun orderingHash(quorum: Quorum, requestId: ByteArray): HashBytes {\n        val orderingPayload = BitcoinOutput()\n                .writeByte(quorum.type)\n                .write(quorum.quorumHash)\n                .write(requestId)\n                .toByteArray()\n\n        return HashBytes(HashUtils.doubleSha256(orderingPayload))\n    }\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/managers/QuorumSortedList.kt",
    "content": "package io.horizontalsystems.dashkit.managers\n\nimport io.horizontalsystems.dashkit.models.Quorum\n\nclass QuorumSortedList {\n    private val quorumList = mutableListOf<Quorum>()\n\n    val quorums: List<Quorum>\n        get() = quorumList.sorted()\n\n\n    fun add(quorums: List<Quorum>) {\n        quorumList.removeAll(quorums)\n        quorumList.addAll(quorums)\n    }\n\n    fun remove(quorums: List<Pair<Int, ByteArray>>) {\n        quorums.forEach { (type, quorumHash) ->\n            val index = quorumList.indexOfFirst { quorum ->\n                quorum.type == type && quorum.quorumHash.contentEquals(quorumHash)\n            }\n\n            if (index != -1) {\n                quorumList.removeAt(index)\n            }\n        }\n    }\n\n    fun removeAll() {\n        quorumList.clear()\n    }\n\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/masternodelist/MasternodeCbTxHasher.kt",
    "content": "package io.horizontalsystems.dashkit.masternodelist\n\nimport io.horizontalsystems.bitcoincore.core.HashBytes\nimport io.horizontalsystems.bitcoincore.core.IHasher\nimport io.horizontalsystems.dashkit.models.CoinbaseTransaction\nimport io.horizontalsystems.dashkit.models.CoinbaseTransactionSerializer\n\nclass MasternodeCbTxHasher(private val coinbaseTransactionSerializer: CoinbaseTransactionSerializer, private val hasher: IHasher) {\n\n    fun hash(coinbaseTransaction: CoinbaseTransaction): HashBytes {\n        val serialized = coinbaseTransactionSerializer.serialize(coinbaseTransaction)\n\n        return HashBytes(hasher.hash(serialized))\n    }\n\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/masternodelist/MasternodeListMerkleRootCalculator.kt",
    "content": "package io.horizontalsystems.dashkit.masternodelist\n\nimport io.horizontalsystems.dashkit.models.Masternode\n\nclass MasternodeListMerkleRootCalculator(val masternodeMerkleRootCreator: MerkleRootCreator) {\n\n    fun calculateMerkleRoot(sortedMasternodes: List<Masternode>): ByteArray? {\n        return masternodeMerkleRootCreator.create(sortedMasternodes.map { it.hash })\n    }\n\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/masternodelist/MerkleRootCreator.kt",
    "content": "package io.horizontalsystems.dashkit.masternodelist\n\nimport io.horizontalsystems.dashkit.IMerkleHasher\n\nclass MerkleRootCreator(val hasher: IMerkleHasher) {\n\n    fun create(hashes: List<ByteArray>): ByteArray? {\n        if (hashes.isEmpty()) return null\n\n        var tmpHashes = hashes\n\n        do {\n            tmpHashes = joinHashes(tmpHashes)\n        } while (tmpHashes.size > 1)\n\n        return tmpHashes.first()\n    }\n\n    private fun joinHashes(hashes: List<ByteArray>): List<ByteArray> {\n        val chunks = hashes.chunked(2)\n\n        return chunks.map {\n            hasher.hash(it.first(), it.last())\n        }\n    }\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/masternodelist/MerkleRootHasher.kt",
    "content": "package io.horizontalsystems.dashkit.masternodelist\n\nimport io.horizontalsystems.bitcoincore.core.IHasher\nimport io.horizontalsystems.bitcoincore.utils.HashUtils\nimport io.horizontalsystems.dashkit.IMerkleHasher\n\nclass MerkleRootHasher: IHasher, IMerkleHasher {\n\n    override fun hash(data: ByteArray): ByteArray {\n        return HashUtils.doubleSha256(data)\n    }\n\n    override fun hash(first: ByteArray, second: ByteArray): ByteArray {\n        return HashUtils.doubleSha256(first + second)\n    }\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/masternodelist/QuorumListMerkleRootCalculator.kt",
    "content": "package io.horizontalsystems.dashkit.masternodelist\n\nimport io.horizontalsystems.dashkit.models.Quorum\n\nclass QuorumListMerkleRootCalculator(private val merkleRootCreator: MerkleRootCreator) {\n\n    fun calculateMerkleRoot(sortedQuorums: List<Quorum>): ByteArray? {\n        return merkleRootCreator.create(sortedQuorums.map { it.hash })\n    }\n\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/messages/GetMasternodeListDiffMessage.kt",
    "content": "package io.horizontalsystems.dashkit.messages\n\nimport io.horizontalsystems.bitcoincore.extensions.toReversedHex\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\nimport io.horizontalsystems.bitcoincore.network.messages.IMessage\nimport io.horizontalsystems.bitcoincore.network.messages.IMessageSerializer\n\nclass GetMasternodeListDiffMessage(val baseBlockHash: ByteArray, val blockHash: ByteArray) : IMessage {\n    override fun toString(): String {\n        return \"GetMasternodeListDiffMessage(baseBlockHash=${baseBlockHash.toReversedHex()}, blockHash=${blockHash.toReversedHex()})\"\n    }\n}\n\nclass GetMasternodeListDiffMessageSerializer : IMessageSerializer {\n    override val command: String = \"getmnlistd\"\n\n    override fun serialize(message: IMessage): ByteArray? {\n        if (message !is GetMasternodeListDiffMessage) {\n            return null\n        }\n\n        return BitcoinOutput()\n                .write(message.baseBlockHash)\n                .write(message.blockHash)\n                .toByteArray()\n    }\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/messages/ISLockMessage.kt",
    "content": "package io.horizontalsystems.dashkit.messages\n\nimport io.horizontalsystems.bitcoincore.extensions.toReversedHex\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\nimport io.horizontalsystems.bitcoincore.network.messages.IMessage\nimport io.horizontalsystems.bitcoincore.network.messages.IMessageParser\nimport io.horizontalsystems.bitcoincore.utils.HashUtils\n\nclass ISLockMessage(\n        val inputs: List<Outpoint>,\n        val txHash: ByteArray,\n        val sign: ByteArray,\n        val hash: ByteArray,\n        val requestId: ByteArray\n) : IMessage {\n\n    override fun toString(): String {\n        return \"ISLockMessage(hash=${hash.toReversedHex()}, txHash=${txHash.toReversedHex()})\"\n    }\n}\n\nclass ISLockMessageParser : IMessageParser {\n    override val command: String = \"islock\"\n\n    override fun parseMessage(input: BitcoinInputMarkable): IMessage {\n        input.mark()\n        val payload = input.readBytes(input.count)\n        input.reset()\n\n        val inputsSize = input.readVarInt()\n        val inputs = List(inputsSize.toInt()) {\n            Outpoint(input)\n        }\n        val txHash = input.readBytes(32)\n        val sign = input.readBytes(96)\n\n        val requestPayload = BitcoinOutput()\n        requestPayload.writeString(\"islock\")\n        requestPayload.writeVarInt(inputsSize)\n        inputs.forEach {\n            requestPayload.write(it.txHash)\n            requestPayload.writeUnsignedInt(it.vout)\n        }\n\n        val requestId = HashUtils.doubleSha256(requestPayload.toByteArray())\n\n        val hash = HashUtils.doubleSha256(payload)\n\n        return ISLockMessage(inputs, txHash, sign, hash, requestId)\n    }\n}\n\n\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/messages/MasternodeListDiffMessage.kt",
    "content": "package io.horizontalsystems.dashkit.messages\n\nimport io.horizontalsystems.bitcoincore.extensions.toReversedHex\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.network.messages.IMessage\nimport io.horizontalsystems.bitcoincore.network.messages.IMessageParser\nimport io.horizontalsystems.dashkit.models.CoinbaseTransaction\nimport io.horizontalsystems.dashkit.models.Masternode\nimport io.horizontalsystems.dashkit.models.Quorum\n\nclass MasternodeListDiffMessage(\n    val baseBlockHash: ByteArray,\n    val blockHash: ByteArray,\n    val totalTransactions: Long,\n    val merkleHashes: List<ByteArray>,\n    val merkleFlags: ByteArray,\n    val cbTx: CoinbaseTransaction,\n    val version: Int,\n    val deletedMNs: List<ByteArray>,\n    val mnList: List<Masternode>,\n    val deletedQuorums: List<Pair<Int, ByteArray>>,\n    val quorumList: List<Quorum>\n) : IMessage {\n\n    override fun toString(): String {\n        return \"MnListDiffMessage(baseBlockHash=${baseBlockHash.toReversedHex()}, blockHash=${blockHash.toReversedHex()})\"\n    }\n\n}\n\nclass MasternodeListDiffMessageParser : IMessageParser {\n    override val command: String = \"mnlistdiff\"\n\n    override fun parseMessage(input: BitcoinInputMarkable): IMessage {\n        val baseBlockHash = input.readBytes(32)\n        val blockHash = input.readBytes(32)\n        val totalTransactions = input.readUnsignedInt()\n        val merkleHashesCount = input.readVarInt()\n        val merkleHashes = mutableListOf<ByteArray>()\n        repeat(merkleHashesCount.toInt()) {\n            merkleHashes.add(input.readBytes(32))\n        }\n        val merkleFlagsCount = input.readVarInt()\n        val merkleFlags = input.readBytes(merkleFlagsCount.toInt())\n        val cbTx = CoinbaseTransaction(input)\n        val version = input.readUnsignedShort()\n        val deletedMNsCount = input.readVarInt()\n        val deletedMNs = mutableListOf<ByteArray>()\n        repeat(deletedMNsCount.toInt()) {\n            deletedMNs.add(input.readBytes(32))\n        }\n        val mnListCount = input.readVarInt()\n        val mnList = mutableListOf<Masternode>()\n        repeat(mnListCount.toInt()) {\n            mnList.add(Masternode(input))\n        }\n\n        val deletedQuorumsCount = input.readVarInt()\n        val deletedQuorums = mutableListOf<Pair<Int, ByteArray>>()\n        repeat(deletedQuorumsCount.toInt()) {\n            deletedQuorums.add(Pair(input.read(), input.readBytes(32)))\n        }\n        val newQuorumsCount = input.readVarInt()\n        val quorumList = mutableListOf<Quorum>()\n        repeat(newQuorumsCount.toInt()) {\n            quorumList.add(Quorum(input))\n        }\n\n        return MasternodeListDiffMessage(baseBlockHash, blockHash, totalTransactions, merkleHashes, merkleFlags, cbTx, version, deletedMNs, mnList, deletedQuorums, quorumList)\n    }\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/messages/TransactionLockMessage.kt",
    "content": "package io.horizontalsystems.dashkit.messages\n\nimport io.horizontalsystems.bitcoincore.extensions.toReversedHex\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.network.messages.IMessage\nimport io.horizontalsystems.bitcoincore.network.messages.IMessageParser\nimport io.horizontalsystems.bitcoincore.serializers.TransactionSerializer\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\n\nclass TransactionLockMessage(var transaction: FullTransaction) : IMessage {\n    override fun toString(): String {\n        return \"TransactionLockMessage(${transaction.header.hash.toReversedHex()})\"\n    }\n}\n\nclass TransactionLockMessageParser : IMessageParser {\n    override val command: String = \"ix\"\n\n    override fun parseMessage(input: BitcoinInputMarkable): IMessage {\n        val transaction = TransactionSerializer.deserialize(input)\n        return TransactionLockMessage(transaction)\n    }\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/messages/TransactionLockVoteMessage.kt",
    "content": "package io.horizontalsystems.dashkit.messages\n\nimport io.horizontalsystems.bitcoincore.extensions.toReversedHex\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\nimport io.horizontalsystems.bitcoincore.network.messages.IMessage\nimport io.horizontalsystems.bitcoincore.network.messages.IMessageParser\nimport io.horizontalsystems.bitcoincore.utils.HashUtils\n\nclass TransactionLockVoteMessage(\n        var txHash: ByteArray,\n        var outpoint: Outpoint,\n        var outpointMasternode: Outpoint,\n        var quorumModifierHash: ByteArray,\n        var masternodeProTxHash: ByteArray,\n        var vchMasternodeSignature: ByteArray,\n        var hash: ByteArray) : IMessage {\n\n    override fun toString(): String {\n        return \"TransactionLockVoteMessage(hash=${hash.toReversedHex()}, txHash=${txHash.toReversedHex()})\"\n    }\n\n}\n\nclass Outpoint(val txHash: ByteArray, val vout: Long) {\n    constructor(input: BitcoinInputMarkable) : this(input.readBytes(32), input.readUnsignedInt())\n}\n\nclass TransactionLockVoteMessageParser : IMessageParser {\n    override val command: String = \"txlvote\"\n\n    override fun parseMessage(input: BitcoinInputMarkable): IMessage {\n        val txHash = input.readBytes(32)\n        val outpoint = Outpoint(input)\n        val outpointMasternode = Outpoint(input)\n        val quorumModifierHash = input.readBytes(32)\n        val masternodeProTxHash = input.readBytes(32)\n        val signatureLength = input.readVarInt()\n        val vchMasternodeSignature = ByteArray(signatureLength.toInt())\n        input.readFully(vchMasternodeSignature)\n\n        val hashPayload = BitcoinOutput()\n                .write(txHash)\n                .write(outpoint.txHash)\n                .writeUnsignedInt(outpoint.vout)\n                .write(outpointMasternode.txHash)\n                .writeUnsignedInt(outpointMasternode.vout)\n                .write(quorumModifierHash)\n                .write(masternodeProTxHash)\n                .toByteArray()\n\n        val hash = HashUtils.doubleSha256(hashPayload)\n\n        return TransactionLockVoteMessage(txHash, outpoint, outpointMasternode, quorumModifierHash, masternodeProTxHash, vchMasternodeSignature, hash)\n    }\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/messages/TransactionMessage.kt",
    "content": "package io.horizontalsystems.dashkit.messages\n\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\nimport io.horizontalsystems.bitcoincore.network.messages.IMessage\nimport io.horizontalsystems.bitcoincore.network.messages.IMessageParser\nimport io.horizontalsystems.bitcoincore.network.messages.TransactionMessage\nimport io.horizontalsystems.bitcoincore.serializers.TransactionSerializer\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.bitcoincore.utils.HashUtils\nimport io.horizontalsystems.dashkit.models.SpecialTransaction\nimport java.nio.ByteBuffer\n\nclass TransactionMessageParser : IMessageParser {\n    override val command: String = \"tx\"\n\n    private fun parseSpecialTxData(input: BitcoinInputMarkable, transaction: FullTransaction): SpecialTransaction {\n        val payloadSize = input.readVarInt()\n        val payload = input.readBytes(payloadSize.toInt())\n\n        val output = BitcoinOutput()\n        output.write(TransactionSerializer.serialize(transaction))\n        output.writeVarInt(payloadSize)\n        output.write(payload)\n\n        val hash = HashUtils.doubleSha256(output.toByteArray())\n        transaction.setHash(hash)\n\n        return SpecialTransaction(transaction, payload, false)\n    }\n\n    override fun parseMessage(input: BitcoinInputMarkable): IMessage {\n        var transaction = TransactionSerializer.deserialize(input)\n\n        val bytes = ByteBuffer.allocate(4).putInt(transaction.header.version).array()\n        val txType = bytes[0] + bytes[1]\n\n        if (txType > 0) {\n            transaction = parseSpecialTxData(input, transaction)\n        }\n\n        return TransactionMessage(transaction, input.count)\n    }\n\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/models/CoinbaseTransaction.kt",
    "content": "package io.horizontalsystems.dashkit.models\n\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.serializers.TransactionSerializer\n\nclass CoinbaseTransaction(input: BitcoinInputMarkable) {\n    val transaction = TransactionSerializer.deserialize(input)\n    val coinbaseTransactionSize: Long\n    val version: Int\n    val height: Long\n    val merkleRootMNList: ByteArray\n    val merkleRootQuorums: ByteArray?\n    var bestCLHeightDiff: Long? = null\n    var bestCLSignature: ByteArray? = null\n    var creditPoolBalance: Long? = null\n\n    init {\n        coinbaseTransactionSize = input.readVarInt()\n\n        version = input.readUnsignedShort()\n        height = input.readUnsignedInt()\n        merkleRootMNList = input.readBytes(32)\n        merkleRootQuorums = when {\n            version >= 2 -> input.readBytes(32)\n            else -> null\n        }\n\n        if (version >= 3) {\n            bestCLHeightDiff = input.readVarInt()\n            bestCLSignature = input.readBytes(96)\n            creditPoolBalance = input.readLong()\n        }\n    }\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/models/CoinbaseTransactionSerializer.kt",
    "content": "package io.horizontalsystems.dashkit.models\n\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\nimport io.horizontalsystems.bitcoincore.serializers.TransactionSerializer\n\nclass CoinbaseTransactionSerializer {\n\n    fun serialize(coinbaseTransaction: CoinbaseTransaction): ByteArray {\n        val output = BitcoinOutput()\n\n        output.write(TransactionSerializer.serialize(coinbaseTransaction.transaction))\n        output.writeVarInt(coinbaseTransaction.coinbaseTransactionSize)\n        output.writeUnsignedShort(coinbaseTransaction.version)\n        output.writeUnsignedInt(coinbaseTransaction.height)\n        output.write(coinbaseTransaction.merkleRootMNList)\n\n        if (coinbaseTransaction.version >= 2) {\n            output.write(coinbaseTransaction.merkleRootQuorums)\n        }\n\n        if (coinbaseTransaction.version >= 3) {\n            coinbaseTransaction.bestCLHeightDiff?.let { output.writeVarInt(it) }\n            coinbaseTransaction.bestCLSignature?.let { output.write(it) }\n            coinbaseTransaction.creditPoolBalance?.let { output.writeLong(it) }\n        }\n\n        return output.toByteArray()\n    }\n\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/models/DashTransactionInfo.kt",
    "content": "package io.horizontalsystems.dashkit.models\n\nimport com.eclipsesource.json.Json\nimport com.eclipsesource.json.JsonObject\nimport io.horizontalsystems.bitcoincore.models.*\n\nclass DashTransactionInfo : TransactionInfo {\n\n    var instantTx: Boolean = false\n\n    constructor(uid: String,\n                transactionHash: String,\n                transactionIndex: Int,\n                inputs: List<TransactionInputInfo>,\n                outputs: List<TransactionOutputInfo>,\n                amount: Long,\n                type: TransactionType,\n                fee: Long?,\n                blockHeight: Int?,\n                timestamp: Long,\n                status: TransactionStatus,\n                conflictingTxHash: String?,\n                instantTx: Boolean\n    ) : super(uid, transactionHash, transactionIndex, inputs, outputs, amount, type, fee, blockHeight, timestamp, status, conflictingTxHash) {\n        this.instantTx = instantTx\n    }\n\n    @Throws\n    constructor(serialized: String) : super(serialized) {\n        val jsonObject = Json.parse(serialized).asObject()\n        this.instantTx = jsonObject[\"instantTx\"].asBoolean()\n    }\n\n    override fun asJsonObject(): JsonObject {\n        val jsonObject = super.asJsonObject()\n        jsonObject[\"instantTx\"] = instantTx\n        return jsonObject\n    }\n\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/models/InstantTransactionHash.kt",
    "content": "package io.horizontalsystems.dashkit.models\n\nimport androidx.room.Entity\nimport androidx.room.PrimaryKey\n\n@Entity\ndata class InstantTransactionHash(@PrimaryKey val txHash: ByteArray) {\n\n    override fun equals(other: Any?): Boolean {\n        return other === this || other is InstantTransactionHash && txHash.contentEquals(other.txHash)\n    }\n\n    override fun hashCode(): Int {\n        return txHash.contentHashCode()\n    }\n\n}\n\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/models/InstantTransactionInput.kt",
    "content": "package io.horizontalsystems.dashkit.models\n\nimport androidx.room.Entity\n\n@Entity(primaryKeys = [\"txHash\", \"inputTxHash\"])\nclass InstantTransactionInput(\n        val txHash: ByteArray,\n        val inputTxHash: ByteArray,\n        val timeCreated: Long,\n        val voteCount: Int,\n        val blockHeight: Int?)"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/models/InstantTransactionState.kt",
    "content": "package io.horizontalsystems.dashkit.models\n\nclass InstantTransactionState {\n    var instantTransactionHashes = mutableListOf<ByteArray>()\n\n    fun append(hash: ByteArray) {\n        instantTransactionHashes.add(hash)\n    }\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/models/Masternode.kt",
    "content": "package io.horizontalsystems.dashkit.models\n\nimport androidx.room.Entity\nimport androidx.room.PrimaryKey\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\nimport io.horizontalsystems.bitcoincore.utils.HashUtils\n\n@Entity\nclass Masternode() : Comparable<Masternode> {\n    var nVersion = 0\n    @PrimaryKey\n    var proRegTxHash = byteArrayOf()\n    var confirmedHash = byteArrayOf()\n    var ipAddress = byteArrayOf()\n    var port = 0\n    var pubKeyOperator = byteArrayOf()\n    var keyIDVoting = byteArrayOf()\n    var isValid = false\n    var type: Int? = null\n    var platformHTTPPort: Int? = null\n    var platformNodeID: ByteArray? = null\n\n    var hash = byteArrayOf()\n\n    constructor(input: BitcoinInputMarkable) : this() {\n        nVersion = input.readUnsignedShort()\n        proRegTxHash = input.readBytes(32)\n        confirmedHash = input.readBytes(32)\n        ipAddress = input.readBytes(16)\n        port = input.readUnsignedShort()\n        pubKeyOperator = input.readBytes(48)\n        keyIDVoting = input.readBytes(20)\n        isValid = input.read() != 0\n\n        if (nVersion >= 2) {\n            type = input.readUnsignedShort()\n            if (type == 1) {\n                platformHTTPPort = input.readUnsignedShort()\n                platformNodeID = input.readBytes(20)\n            }\n        }\n\n        val payload = BitcoinOutput()\n            .write(proRegTxHash)\n            .write(confirmedHash)\n            .write(ipAddress)\n            .writeUnsignedShort(port)\n            .write(pubKeyOperator)\n            .write(keyIDVoting)\n            .writeByte(if (isValid) 1 else 0)\n\n        type?.let {\n            payload.writeUnsignedShort(it)\n        }\n        platformHTTPPort?.let {\n            payload.writeUnsignedShort(it)\n        }\n        platformNodeID?.let {\n            payload.write(it)\n        }\n\n        hash = HashUtils.doubleSha256(payload.toByteArray())\n    }\n\n    override fun compareTo(other: Masternode): Int {\n\n        for (i in 0 until 32) {\n            val b1: Int = proRegTxHash[i].toInt() and 0xff\n            val b2: Int = other.proRegTxHash[i].toInt() and 0xff\n\n            val res = b1.compareTo(b2)\n            if (res != 0) return res\n        }\n\n        return 0\n    }\n\n    override fun equals(other: Any?) = when (other) {\n        is Masternode -> proRegTxHash.contentEquals(other.proRegTxHash)\n        else -> false\n    }\n\n    override fun hashCode(): Int {\n        return proRegTxHash.contentHashCode()\n    }\n\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/models/MasternodeListState.kt",
    "content": "package io.horizontalsystems.dashkit.models\n\nimport androidx.room.Entity\nimport androidx.room.PrimaryKey\n\n@Entity\nclass MasternodeListState(var baseBlockHash: ByteArray) {\n\n    @PrimaryKey\n    var primaryKey: String = \"primary-key\"\n\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/models/Quorum.kt",
    "content": "package io.horizontalsystems.dashkit.models\n\nimport androidx.room.Entity\nimport androidx.room.PrimaryKey\nimport io.horizontalsystems.bitcoincore.io.BitcoinInputMarkable\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\nimport io.horizontalsystems.bitcoincore.utils.HashUtils\n\n@Entity\nclass Quorum() : Comparable<Quorum> {\n\n    @PrimaryKey\n    var hash = byteArrayOf()\n    var version = 0\n    var type = 0\n    var quorumHash = byteArrayOf()\n    var quorumIndex: Int? = null\n    var signers = byteArrayOf()\n    var validMembers = byteArrayOf()\n    var quorumPublicKey = byteArrayOf()\n    var quorumVvecHash = byteArrayOf()\n    var quorumSig = byteArrayOf()\n    var sig = byteArrayOf()\n\n    constructor(input: BitcoinInputMarkable) : this() {\n        version = input.readUnsignedShort()\n        type = input.read()\n        quorumHash = input.readBytes(32)\n\n        if (version == 2 || version == 4) {\n            quorumIndex = input.readUnsignedShort()\n        }\n\n        val signersSize = input.readVarInt().toInt()\n\n        signers = input.readBytes((signersSize + 7) / 8)\n\n        val validMembersSize = input.readVarInt().toInt()\n        validMembers = input.readBytes((validMembersSize + 7) / 8)\n\n        quorumPublicKey = input.readBytes(48)\n        quorumVvecHash = input.readBytes(32)\n        quorumSig = input.readBytes(96)\n        sig = input.readBytes(96)\n\n        val hashPayload = BitcoinOutput()\n                .writeUnsignedShort(version)\n                .writeByte(type)\n                .write(quorumHash)\n\n        quorumIndex?.let {\n            hashPayload.writeUnsignedShort(it)\n        }\n\n        hashPayload\n                .writeVarInt(signersSize.toLong())\n                .write(signers)\n                .writeVarInt(validMembersSize.toLong())\n                .write(validMembers)\n                .write(quorumPublicKey)\n                .write(quorumVvecHash)\n                .write(quorumSig)\n                .write(sig)\n\n        hash = HashUtils.doubleSha256(hashPayload.toByteArray())\n    }\n\n    override fun compareTo(other: Quorum): Int {\n        // todo refactor duplicated code\n        for (i in 0 until 32) {\n            val b1: Int = hash[i].toInt() and 0xff\n            val b2: Int = other.hash[i].toInt() and 0xff\n\n            val res = b1.compareTo(b2)\n            if (res != 0) return res\n        }\n\n        return 0\n\n    }\n\n    override fun equals(other: Any?) = when (other) {\n        is Quorum -> hash.contentEquals(other.hash)\n        else -> false\n    }\n\n    override fun hashCode(): Int {\n        return hash.contentHashCode()\n    }\n}\n\nenum class QuorumType(val value: Int) {\n    LLMQ_50_60(1)\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/models/SpecialTransaction.kt",
    "content": "package io.horizontalsystems.dashkit.models\n\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\n\nclass SpecialTransaction(\n        val transaction: FullTransaction,\n        extraPayload: ByteArray,\n        forceHashUpdate: Boolean = true\n): FullTransaction(transaction.header, transaction.inputs, transaction.outputs, forceHashUpdate)\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/storage/DashKitDatabase.kt",
    "content": "package io.horizontalsystems.dashkit.storage\n\nimport android.content.Context\nimport androidx.room.*\nimport io.horizontalsystems.dashkit.models.*\n\n@Database(version = 4, exportSchema = false, entities = [\n    Masternode::class,\n    Quorum::class,\n    MasternodeListState::class,\n    InstantTransactionInput::class,\n    InstantTransactionHash::class\n])\n\nabstract class DashKitDatabase : RoomDatabase() {\n    abstract val instantTransactionHashDao: InstantTransactionHashDao\n    abstract val masternodeDao: MasternodeDao\n    abstract val quorumDao: QuorumDao\n    abstract val masternodeListStateDao: MasternodeListStateDao\n    abstract val instantTransactionInputDao: InstantTransactionInputDao\n\n    companion object {\n\n        @Volatile\n        private var instance: DashKitDatabase? = null\n\n        @Synchronized\n        fun getInstance(context: Context, dbName: String): DashKitDatabase {\n            return instance ?: buildDatabase(context, dbName).also { instance = it }\n        }\n\n        private fun buildDatabase(context: Context, dbName: String): DashKitDatabase {\n            return Room.databaseBuilder(context, DashKitDatabase::class.java, dbName)\n                    .fallbackToDestructiveMigration()\n                    .allowMainThreadQueries()\n                    .build()\n        }\n    }\n}\n\n@Dao\ninterface InstantTransactionHashDao {\n    @Query(\"SELECT * FROM InstantTransactionHash\")\n    fun getAll(): List<InstantTransactionHash>\n\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    fun insert(instantTransactionHash: InstantTransactionHash)\n\n}\n\n@Dao\ninterface InstantTransactionInputDao {\n    @Query(\"SELECT * FROM InstantTransactionInput WHERE txHash = :txHash\")\n    fun getByTx(txHash: ByteArray): List<InstantTransactionInput>\n\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    fun insert(transaction: InstantTransactionInput)\n\n    @Query(\"DELETE FROM InstantTransactionInput WHERE txHash = :txHash\")\n    fun deleteByTx(txHash: ByteArray)\n}\n\n@Dao\ninterface MasternodeDao {\n    @Insert\n    fun insertAll(masternodes: List<Masternode>)\n\n    @Query(\"SELECT * FROM Masternode\")\n    fun getAll(): List<Masternode>\n\n    @Query(\"DELETE FROM Masternode\")\n    fun clearAll()\n}\n\n@Dao\ninterface QuorumDao {\n    @Insert\n    fun insertAll(masternodes: List<Quorum>)\n\n    @Query(\"SELECT * FROM Quorum\")\n    fun getAll(): List<Quorum>\n\n    @Query(\"SELECT * FROM Quorum WHERE type = :type\")\n    fun getByType(type: Int): List<Quorum>\n\n    @Query(\"DELETE FROM Quorum\")\n    fun clearAll()\n}\n\n@Dao\ninterface MasternodeListStateDao {\n    @Query(\"SELECT * FROM MasternodeListState LIMIT 1\")\n    fun getState(): MasternodeListState?\n\n    @Insert(onConflict = OnConflictStrategy.REPLACE)\n    fun setState(state: MasternodeListState)\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/storage/DashStorage.kt",
    "content": "package io.horizontalsystems.dashkit.storage\n\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.bitcoincore.models.TransactionInput\nimport io.horizontalsystems.bitcoincore.storage.FullTransactionInfo\nimport io.horizontalsystems.bitcoincore.storage.Storage\nimport io.horizontalsystems.dashkit.IDashStorage\nimport io.horizontalsystems.dashkit.models.*\n\nclass DashStorage(private val dashStore: DashKitDatabase, private val coreStorage: Storage) : IDashStorage {\n\n    override fun getBlock(blockHash: ByteArray): Block? {\n        return coreStorage.getBlock(blockHash)\n    }\n\n    override fun instantTransactionHashes(): List<ByteArray> {\n        return dashStore.instantTransactionHashDao.getAll().map { it.txHash }\n    }\n\n    override fun instantTransactionInputs(txHash: ByteArray): List<InstantTransactionInput> {\n        return dashStore.instantTransactionInputDao.getByTx(txHash)\n    }\n\n    override fun getTransactionInputs(txHash: ByteArray): List<TransactionInput> {\n        return coreStorage.getTransactionInputs(txHash)\n    }\n\n    override fun addInstantTransactionInput(instantTransactionInput: InstantTransactionInput) {\n        dashStore.instantTransactionInputDao.insert(instantTransactionInput)\n    }\n\n    override fun addInstantTransactionHash(txHash: ByteArray) {\n        dashStore.instantTransactionHashDao.insert(InstantTransactionHash(txHash))\n    }\n\n    override fun removeInstantTransactionInputs(txHash: ByteArray) {\n        dashStore.instantTransactionInputDao.deleteByTx(txHash)\n    }\n\n    override fun isTransactionExists(txHash: ByteArray): Boolean {\n        return coreStorage.isTransactionExists(txHash)\n    }\n\n    override fun getQuorumsByType(quorumType: QuorumType): List<Quorum> {\n        return dashStore.quorumDao.getByType(quorumType.value)\n    }\n\n    fun getFullTransactionInfo(txHash: ByteArray): FullTransactionInfo? {\n        return coreStorage.getFullTransactionInfo(txHash)\n    }\n\n    override var masternodes: List<Masternode>\n        get() = dashStore.masternodeDao.getAll()\n        set(value) {\n            dashStore.masternodeDao.clearAll()\n            dashStore.masternodeDao.insertAll(value)\n        }\n\n    override var masternodeListState: MasternodeListState?\n        get() = dashStore.masternodeListStateDao.getState()\n        set(value) {\n            value?.let {\n                dashStore.masternodeListStateDao.setState(value)\n            }\n        }\n\n    override var quorums: List<Quorum>\n        get() = dashStore.quorumDao.getAll()\n        set(value) {\n            dashStore.quorumDao.clearAll()\n            dashStore.quorumDao.insertAll(value)\n        }\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/tasks/PeerTaskFactory.kt",
    "content": "package io.horizontalsystems.dashkit.tasks\n\nclass PeerTaskFactory {\n\n    fun createRequestMasternodeListDiffTask(baseBlockHash: ByteArray, blockHash: ByteArray): RequestMasternodeListDiffTask {\n        return RequestMasternodeListDiffTask(baseBlockHash, blockHash)\n    }\n\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/tasks/RequestInstantSendLocksTask.kt",
    "content": "package io.horizontalsystems.dashkit.tasks\n\nimport io.horizontalsystems.bitcoincore.models.InventoryItem\nimport io.horizontalsystems.bitcoincore.network.messages.GetDataMessage\nimport io.horizontalsystems.bitcoincore.network.messages.IMessage\nimport io.horizontalsystems.bitcoincore.network.peer.task.PeerTask\nimport io.horizontalsystems.dashkit.InventoryType\nimport io.horizontalsystems.dashkit.messages.ISLockMessage\n\nclass RequestInstantSendLocksTask(hashes: List<ByteArray>) : PeerTask() {\n\n    val hashes = hashes.toMutableList()\n    var isLocks = mutableListOf<ISLockMessage>()\n\n    override fun start() {\n        requester?.send(GetDataMessage(hashes.map { InventoryItem(InventoryType.MSG_ISLOCK, it) }))\n    }\n\n    override fun handleMessage(message: IMessage) = when (message) {\n        is ISLockMessage -> handleISLockVote(message)\n        else -> false\n    }\n\n    private fun handleISLockVote(isLockMessage: ISLockMessage): Boolean {\n        val hash = hashes.firstOrNull { it.contentEquals(isLockMessage.hash) } ?: return false\n\n        hashes.remove(hash)\n        isLocks.add(isLockMessage)\n\n        if (hashes.isEmpty()) {\n            listener?.onTaskCompleted(this)\n        }\n\n        return true\n    }\n\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/tasks/RequestMasternodeListDiffTask.kt",
    "content": "package io.horizontalsystems.dashkit.tasks\n\nimport io.horizontalsystems.bitcoincore.network.messages.IMessage\nimport io.horizontalsystems.bitcoincore.network.peer.task.PeerTask\nimport io.horizontalsystems.dashkit.messages.GetMasternodeListDiffMessage\nimport io.horizontalsystems.dashkit.messages.MasternodeListDiffMessage\nimport java.util.concurrent.TimeUnit\n\nclass RequestMasternodeListDiffTask(private val baseBlockHash: ByteArray, private val blockHash: ByteArray) : PeerTask() {\n\n    var masternodeListDiffMessage: MasternodeListDiffMessage? = null\n\n    init {\n        allowedIdleTime = TimeUnit.SECONDS.toMillis(5)\n    }\n\n    override fun handleTimeout() {\n        listener?.onTaskFailed(this, Exception(\"RequestMasternodeListDiffTask Timeout\"))\n    }\n\n\n    override fun start() {\n        requester?.send(GetMasternodeListDiffMessage(baseBlockHash, blockHash))\n        resetTimer()\n    }\n\n    override fun handleMessage(message: IMessage): Boolean {\n        if (message is MasternodeListDiffMessage\n                && message.baseBlockHash.contentEquals(baseBlockHash)\n                && message.blockHash.contentEquals(blockHash)) {\n\n            masternodeListDiffMessage = message\n\n            listener?.onTaskCompleted(this)\n\n            return true\n        }\n\n        return false\n    }\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/tasks/RequestTransactionLockRequestsTask.kt",
    "content": "package io.horizontalsystems.dashkit.tasks\n\nimport io.horizontalsystems.bitcoincore.models.InventoryItem\nimport io.horizontalsystems.bitcoincore.network.messages.GetDataMessage\nimport io.horizontalsystems.bitcoincore.network.messages.IMessage\nimport io.horizontalsystems.bitcoincore.network.peer.task.PeerTask\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.dashkit.InventoryType\nimport io.horizontalsystems.dashkit.messages.TransactionLockMessage\n\nclass RequestTransactionLockRequestsTask(hashes: List<ByteArray>) : PeerTask() {\n\n    val hashes = hashes.toMutableList()\n    var transactions = mutableListOf<FullTransaction>()\n\n    override fun start() {\n        val items = hashes.map { hash ->\n            InventoryItem(InventoryType.MSG_TXLOCK_REQUEST, hash)\n        }\n\n        requester?.send(GetDataMessage(items))\n    }\n\n    override fun handleMessage(message: IMessage) = when (message) {\n        is TransactionLockMessage -> handleTransactionLockRequest(message.transaction)\n        else -> false\n    }\n\n    private fun handleTransactionLockRequest(transaction: FullTransaction): Boolean {\n        val hash = hashes.firstOrNull { it.contentEquals(transaction.header.hash) } ?: return false\n\n        hashes.remove(hash)\n        transactions.add(transaction)\n\n        if (hashes.isEmpty()) {\n            listener?.onTaskCompleted(this)\n        }\n\n        return true\n    }\n\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/tasks/RequestTransactionLockVotesTask.kt",
    "content": "package io.horizontalsystems.dashkit.tasks\n\nimport io.horizontalsystems.bitcoincore.models.InventoryItem\nimport io.horizontalsystems.bitcoincore.network.messages.GetDataMessage\nimport io.horizontalsystems.bitcoincore.network.messages.IMessage\nimport io.horizontalsystems.bitcoincore.network.peer.task.PeerTask\nimport io.horizontalsystems.dashkit.InventoryType\nimport io.horizontalsystems.dashkit.messages.TransactionLockVoteMessage\n\nclass RequestTransactionLockVotesTask(hashes: List<ByteArray>) : PeerTask() {\n\n    val hashes = hashes.toMutableList()\n    var transactionLockVotes = mutableListOf<TransactionLockVoteMessage>()\n\n    override fun start() {\n        val items = hashes.map { hash ->\n            InventoryItem(InventoryType.MSG_TXLOCK_VOTE, hash)\n        }\n\n        requester?.send(GetDataMessage(items))\n    }\n\n    override fun handleMessage(message: IMessage) = when (message) {\n        is TransactionLockVoteMessage -> handleTransactionLockVote(message)\n        else -> false\n    }\n\n    private fun handleTransactionLockVote(transactionLockVote: TransactionLockVoteMessage): Boolean {\n        val hash = hashes.firstOrNull { it.contentEquals(transactionLockVote.hash) } ?: return false\n\n        hashes.remove(hash)\n        transactionLockVotes.add(transactionLockVote)\n\n        if (hashes.isEmpty()) {\n            listener?.onTaskCompleted(this)\n        }\n\n        return true\n    }\n\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/validators/DarkGravityWaveTestnetValidator.kt",
    "content": "package io.horizontalsystems.dashkit.validators\n\nimport io.horizontalsystems.bitcoincore.blocks.validators.BlockValidatorException\nimport io.horizontalsystems.bitcoincore.blocks.validators.IBlockChainedValidator\nimport io.horizontalsystems.bitcoincore.crypto.CompactBits\nimport io.horizontalsystems.bitcoincore.models.Block\n\nclass DarkGravityWaveTestnetValidator(\n        private val targetSpacing: Int,\n        private val targetTimespan: Long,\n        private val maxTargetBits: Long,\n        private val powDGWHeight: Int\n) : IBlockChainedValidator {\n\n    override fun validate(block: Block, previousBlock: Block) {\n        if (block.timestamp > previousBlock.timestamp + 2 * targetTimespan) { // more than 2 cycles\n            if (block.bits != maxTargetBits) {\n                throw BlockValidatorException.NotEqualBits()\n            }\n\n            return\n        }\n\n        val blockTarget = CompactBits.decode(previousBlock.bits)\n\n        var expectedBits = CompactBits.encode(blockTarget.multiply(10.toBigInteger()))\n        if (expectedBits > maxTargetBits) {\n            expectedBits = maxTargetBits\n        }\n\n        if (expectedBits != block.bits) {\n            throw BlockValidatorException.NotEqualBits()\n        }\n    }\n\n    override fun isBlockValidatable(block: Block, previousBlock: Block): Boolean {\n        return block.height >= powDGWHeight && block.timestamp > previousBlock.timestamp + 4 * targetSpacing\n    }\n}\n"
  },
  {
    "path": "dashkit/src/main/kotlin/io/horizontalsystems/dashkit/validators/DarkGravityWaveValidator.kt",
    "content": "package io.horizontalsystems.dashkit.validators\n\nimport io.horizontalsystems.bitcoincore.blocks.validators.BlockValidatorException\nimport io.horizontalsystems.bitcoincore.blocks.validators.IBlockChainedValidator\nimport io.horizontalsystems.bitcoincore.crypto.CompactBits\nimport io.horizontalsystems.bitcoincore.managers.BlockValidatorHelper\nimport io.horizontalsystems.bitcoincore.models.Block\nimport java.math.BigInteger\nimport kotlin.math.min\n\nclass DarkGravityWaveValidator(\n        private val blockHelper: BlockValidatorHelper,\n        private val heightInterval: Long,\n        private val targetTimespan: Long,\n        private val maxTargetBits: Long,\n        private val powDGWHeight: Int\n) : IBlockChainedValidator {\n\n    override fun validate(block: Block, previousBlock: Block) {\n        var actualTimeSpan = 0L\n        var avgTargets = CompactBits.decode(previousBlock.bits)\n        var prevBlock = blockHelper.getPrevious(previousBlock, 1)\n\n        for (blockCount in 2..heightInterval) {\n            val currentBlock = checkNotNull(prevBlock) {\n                throw BlockValidatorException.NoPreviousBlock()\n            }\n\n            avgTargets *= BigInteger.valueOf(blockCount)\n            avgTargets += CompactBits.decode(currentBlock.bits)\n            avgTargets /= BigInteger.valueOf(blockCount + 1)\n\n            if (blockCount < heightInterval) {\n                prevBlock = blockHelper.getPrevious(currentBlock, 1)\n            } else {\n                actualTimeSpan = previousBlock.timestamp - currentBlock.timestamp\n            }\n        }\n\n        var darkTarget = avgTargets\n\n        if (actualTimeSpan < targetTimespan / 3)\n            actualTimeSpan = targetTimespan / 3\n        if (actualTimeSpan > targetTimespan * 3)\n            actualTimeSpan = targetTimespan * 3\n\n        //  Retarget\n        darkTarget = darkTarget * BigInteger.valueOf(actualTimeSpan) / BigInteger.valueOf(targetTimespan)\n\n        val compact = min(CompactBits.encode(darkTarget), maxTargetBits)\n        if (compact != block.bits) {\n            throw BlockValidatorException.NotEqualBits()\n        }\n    }\n\n    override fun isBlockValidatable(block: Block, previousBlock: Block): Boolean {\n        return block.height >= powDGWHeight\n    }\n}\n"
  },
  {
    "path": "dashkit/src/main/resources/MainNetDash-bip44.checkpoint",
    "content": "02000000fa029e39ca627f2b2d4140b5825e48f254ba3f3fa411d3e2c2641b00000000004360ee43af75123dbf202153f78c5a5a0f60367324bd85b0605d5d85d8973e0f46fa11532849011c970a400088650000dc10be5ef167075d45b69ae388668f262522452131423457bc89d00000000000\n02000000adcce9364a714874a1afb2a24d423a2e0a3655c0c0f28250d10f8e0000000000034c43a7dbfc6e8bed9dceb8ed88cec72478741e6184b96bbf628b6e732178bb84f911533449011cab2a1c0087650000fa029e39ca627f2b2d4140b5825e48f254ba3f3fa411d3e2c2641b0000000000\n0200000022dfb410c8d52af4b4daf60b9e6b9333c4a3fed79156083744080300000000008d105952fd0b550e9522f3fe55a304a7c0cf386298f11e1441d98965a32a69cbf2f81153de64011cca876f0086650000adcce9364a714874a1afb2a24d423a2e0a3655c0c0f28250d10f8e0000000000\n020000008e4a92eabd305bb1c400ef724a464042a9bfa604e734e55a3565360100000000a851146c1458f4512766671995ce28d9fa5a5439d8543442f664b4754d22629390f811533949011cc78fe7008565000022dfb410c8d52af4b4daf60b9e6b9333c4a3fed7915608374408030000000000\n020000000f395f8c0da314d4bf98bc93cfa0194506ffff77a653b1a3bf8d79000000000032c0c29a80e6b0060d45cbedd95c7d040835c3cfcbc7f1949ab91c150d6eacb1d4f71153cd64011c0e193900846500008e4a92eabd305bb1c400ef724a464042a9bfa604e734e55a3565360100000000\n02000000181325530e7bae746c9c728e6ff471cffa3b25ada956e9345bdb0f0100000000d86510bae9e6ed590d36df2a57e9aa2d70d385ebcbf109534fcef8b712c669cd86f71153eb64011c4c771400836500000f395f8c0da314d4bf98bc93cfa0194506ffff77a653b1a3bf8d790000000000\n020000003e748632e0e2a81e683c831d444d88a414ee21f22a8fa43656777e0000000000cd6a92ba5a57746a8566ab4d5eaf1d59367ce8551a7f613a72420898472945d1fbf61153b064011c4336150082650000181325530e7bae746c9c728e6ff471cffa3b25ada956e9345bdb0f0100000000\n0200000039fc860aee959bfc90f3e72427518ca666aba9bc03d27a40699e02010000000030e2dacb2d40c6151f0a38d7ccd819d5eeaf692f228d908707c631e5cf3a31eb51f611531365011ce6d73600816500003e748632e0e2a81e683c831d444d88a414ee21f22a8fa43656777e0000000000\n0200000085e38ff12d09af3675c5fb7c9d3011b8e7b32784f3f7104f0bc0fe0000000000d313bee2654f93703f25b268df396927d859895fa274845587a59b9863ff6a6fdef511530c48011c980ed3768065000039fc860aee959bfc90f3e72427518ca666aba9bc03d27a40699e020100000000\n02000000da416992da83a6043c91e2588d71786f1da42b9022066af8c722160000000000e2ef21b3a5a7532583b6eb01fe46eed995e11986c852fe80ae33256b24f77acfcdf411537147011c2b3380007f65000085e38ff12d09af3675c5fb7c9d3011b8e7b32784f3f7104f0bc0fe0000000000\n020000005242774ca1ef80a0ebb6a7f5d98549b04ddcfe5f46bcda035fcf060100000000827c436d0816585576c03e986ea71c458e4da7d3d25ab7d7fbcdbb0c029164d313f411536448011c3e868a007e650000da416992da83a6043c91e2588d71786f1da42b9022066af8c722160000000000\n02000000cb9a992b05c60b523e23b57ce7a39f5c95f1aed5ea3a9cf89af6bd0000000000004e6211a08ee36c0e7f8769a7b58584e5b66e3eaaa69ee873a788ca5f4bd795b8f311535c45011c7eab26007d6500005242774ca1ef80a0ebb6a7f5d98549b04ddcfe5f46bcda035fcf060100000000\n020000003a5b9d1c66488db710dd0112ea09b4ce0a11a289fde448bb4b15970000000000e9399ef53bcf614785dc39ebbf782d1aedfc3695f1fe809ee6bd432db9fa829486f211531748011c95780e007c650000cb9a992b05c60b523e23b57ce7a39f5c95f1aed5ea3a9cf89af6bd0000000000\n02000000d3c8f6275432186fccbed47a116c22205dbd6aaab3f989c83d55210000000000e20c78502f83ea3b915e0505959ec64f3a4803f18114f74aad4cee4448c1aa457bf211539465011c8cb42a007b6500003a5b9d1c66488db710dd0112ea09b4ce0a11a289fde448bb4b15970000000000\n020000000487c065b7eb9b9cf1ad03f52a5e4c66ae51dc3a9ee90347a86798000000000070bbbe23a780d538b66afbb24c48e0bf7c4d37611028642df50702241b2022ff51f21153c644011c5ee313007a650000d3c8f6275432186fccbed47a116c22205dbd6aaab3f989c83d55210000000000\n02000000996c0204ec8d1780549d65b6a950e5b4574100d56a021e137935a40000000000d5bc62113ede383825a722a2d0c9ab1f83cd1ca54d078eeca0f5b35e55e69030b3f01153f545011c6027baff796500000487c065b7eb9b9cf1ad03f52a5e4c66ae51dc3a9ee90347a867980000000000\n02000000931bc6eb4792f9f1061f8a72e1ecd7b047bb7c735c296b785c2a510100000000cbe2d3a31a5f0ccd516f549e3471fef0353acf9309481466bb5ed9b248478a4d63f01153b147011c7620250178650000996c0204ec8d1780549d65b6a950e5b4574100d56a021e137935a40000000000\n020000003e3a9b7c9a6c7e9dc2396a2669c621731a7c5abcd811379d11085b000000000080c1b033583290356474859dcfb026033965ba645fb3cdf3e4da22b3f0ae69571af01153c465011c6aae870577650000931bc6eb4792f9f1061f8a72e1ecd7b047bb7c735c296b785c2a510100000000\n020000006d7826f0a9c1d79200e1160157335fbc1201022f98b6de003b158e000000000089148da4cf9f98001b5f53c9b128fa4efe805c021acf3d6bcaf4b1701ed30df2b5ef11534866011c01ba5600766500003e3a9b7c9a6c7e9dc2396a2669c621731a7c5abcd811379d11085b0000000000\n02000000bfd7f8a88965d66f168712b3baab617e935ddefdde76af8e13b95a0000000000af662ca09cdae2354b383e323bd2d0ba7a1bbe85252f8cd734a103ba7004c8a9a7ef1153fd66011cd804a505756500006d7826f0a9c1d79200e1160157335fbc1201022f98b6de003b158e0000000000\n02000000671fbb8ae24afac35f5a319dc8a4a41b98f97c831bf64ab970740701000000000e8bad5aa06b556d0711641d51f71624654f528e7b5c44a84aef4fc4f186138c4def11536c46011ce2f8260074650000bfd7f8a88965d66f168712b3baab617e935ddefdde76af8e13b95a0000000000\n020000007de6343400672358903cfbbd4b64fd4d64b934c5bb909aa76d0542000000000061d188bb4e1e679039dc329d6419f2a6d95372b28ae217c861546a7b775d92b9a2ed11537e40011c7128cb0073650000671fbb8ae24afac35f5a319dc8a4a41b98f97c831bf64ab97074070100000000\n0200000093ff0fa01b2f254f54bdc9ecb8737733f9b271be8ba7f4b014e2fb00000000007c6d109ea8a7761faa997e524fb3776993aa58206f39e015082c3beb88b2e82b14ec1153dd42011cbf0e0500726500007de6343400672358903cfbbd4b64fd4d64b934c5bb909aa76d05420000000000\n020000001469c0cbe42d3c7a8e52bbc75e94916ac35603e4003b4c617e21b90000000000fbe74995dc243e4e5592f2fef61c6761aa1739f87716e327ba4233d5018d0fcbcceb11533644011c99e404007165000093ff0fa01b2f254f54bdc9ecb8737733f9b271be8ba7f4b014e2fb0000000000\n"
  },
  {
    "path": "dashkit/src/main/resources/MainNetDash.checkpoint",
    "content": "000000209d8efd3bbb4816e99f70d0eb02e38cc9db4096d703550c4a190000000000000080b6e38de5a8d50be02034d91f8adeabefe71311f36566623fd9d49fa6cfcd79afa6cb69d70f361927690605085725002af4b1a8b0127c76e70193a5a16bcca6fb049c3a8d44aadb1c00000000000000\n0000002075234034bb3a852ba5e3bace512883800f1ab2bd67b09b7201000000000000005e3a0828453636a7542d5ad3ba8242f096882f17cf803eaaa5bd0044d4586e3dc6a5cb69f8de36190c2e4e06075725009d8efd3bbb4816e99f70d0eb02e38cc9db4096d703550c4a1900000000000000\n0000002043c2186569446416e51ec173a7bf1e0aea4a251f18d3e9630b00000000000000679500d5a73f134c8a17bb4f9b1bc39ad9314cdad083ece8d3c838adabb1bfb284a5cb69de563619452ec92f0657250075234034bb3a852ba5e3bace512883800f1ab2bd67b09b720100000000000000\n00000020eeb3bea2f2e4141b49915d0ee342c0c788ada33cc2acfbca0b000000000000009bfe895f55cf0dcb3a13bd18936c0b66350573b177526cf51290bee2828f2f9af9a4cb6973063519d8311ef10557250043c2186569446416e51ec173a7bf1e0aea4a251f18d3e9630b00000000000000\n00000020afe46d7c91399326de1780d2cbe91fa090cfe7e7d4c9c3451900000000000000373772be3509a45a88cc39c3e75744af0a41d02ee3b05aaa0833b969fc4c4ab460a4cb69522635193e53b45404572500eeb3bea2f2e4141b49915d0ee342c0c788ada33cc2acfbca0b00000000000000\n00000020925c894bc42b59f8237c5a4d3e227ffb6a63ba647fcb99941e00000000000000479226fbbb340ad4484df7e5c5dc773af41263bc1b02e3f8ba9438739b5c60602da4cb69e78e32193e403eba03572500afe46d7c91399326de1780d2cbe91fa090cfe7e7d4c9c3451900000000000000\n0000002098ff30948b32dfb226440492798e41b0b11480debcb9ec890c00000000000000ec24bd0bf83c3d88a8188bdc1dc3ece7647c1168b46f3703113d8a3ad30e179ebba2cb69fadb3419ec082c2402572500925c894bc42b59f8237c5a4d3e227ffb6a63ba647fcb99941e00000000000000\n000000206e41a50c90bc60b3ebeb8789e24f1fbd6d29c0dce2120b3e2700000000000000728b32f28161a4c01f7c67500e633a8abefc254fe20730109b46198e73b4aaef52a1cb697b9235198d163aa00157250098ff30948b32dfb226440492798e41b0b11480debcb9ec890c00000000000000\n000000207f7ab2206f6054be9174ff33c492f7a314137d53ac65612e2e00000000000000bf4a8dc10b41fa227a421f18f2adc05fc55fa3a7262518693ef1df5c28dc58632ca1cb69f9a63519a84c6ae9005725006e41a50c90bc60b3ebeb8789e24f1fbd6d29c0dce2120b3e2700000000000000\n00000020f6dfb2b6ec050bcdfcda33fbfa87cf3db6451ebda0467e2d1d00000000000000f482fa7509bfc56bf0e84f386cf3ca7fa672081004512f46c1e958762d6345000aa1cb6973d03d19b2000c30ff5625007f7ab2206f6054be9174ff33c492f7a314137d53ac65612e2e00000000000000\n0000002081876f09c7cef09d1f86db45583db7f0034630510266b6ee1900000000000000981a922b691ed4de8bf064cc18ee91692e1bc5dbaf83517c685b31fcdec5d795759fcb6966d43b19aaa5f939fe562500f6dfb2b6ec050bcdfcda33fbfa87cf3db6451ebda0467e2d1d00000000000000\n0000002086f9f7981473418ec605d1dac93681b81afabc0363dc42862600000000000000738ee9b9814769b52471a11e4b41d2b39ed618cf02ce75e34d1de568f541aa77169fcb69a3af3b197ea0a570fd56250081876f09c7cef09d1f86db45583db7f0034630510266b6ee1900000000000000\n00000020d62ddd31e31b91595cfe23a7617efcd05fde9e176d10008f1e00000000000000470c826338122a3729649487d54bedb9aacd526b351ed042ca027aebffa5cbfebb9ecb69a2473719be4cd1cafc56250086f9f7981473418ec605d1dac93681b81afabc0363dc42862600000000000000\n0000002046f6b4c8080ee862b2d0b12fe36554350fb25255d6b1008a13000000000000008bab96711040dfeb46f51e2f47b7331c257050a486f9dfe956b901bbf212ce71c99ccb6950d93519f8a59f5cfb562500d62ddd31e31b91595cfe23a7617efcd05fde9e176d10008f1e00000000000000\n00000020a6860daaed5f41f1fb222070393496c3d7d4b57f6ed710e83300000000000000e3ef7163de9e93f6f2199ad72a6d8adb322e70349c2c4e2df6f745a1170a2677fe9bcb6925ae35195674c97efa56250046f6b4c8080ee862b2d0b12fe36554350fb25255d6b1008a1300000000000000\n00000020c392eeca0bc2e5962f4257b9b9aebb299fe46e87bd0738ed1f00000000000000f7d12f107c4a885acfb45f7d8c7d384ac8e3afa6949f368ec62f0abd7ec31c90fc9bcb6904013619a42696c3f9562500a6860daaed5f41f1fb222070393496c3d7d4b57f6ed710e83300000000000000\n000000206b76b85f676b460d0d5aed65232835607ce91af296924070030000000000000018259efec2d643235fbd48947fe9e12783323517177ce4a41730bd65a262212b399bcb69c9b73619ea10f915f8562500c392eeca0bc2e5962f4257b9b9aebb299fe46e87bd0738ed1f00000000000000\n000000206a8c1c0db79877174ac255f21d3fa237afc403e15302c81b0b000000000000007fa839aeef825f3af529158909051bb80df0ee56f210cb6a7c4d89292aeb4527379bcb693f8933193a61c6c9f75625006b76b85f676b460d0d5aed65232835607ce91af2969240700300000000000000\n0000002065d4bffdb69a3fd00fdc801b1fad344e09bb5e1cb522e0b40c000000000000008a5bcf7221fafeed17e1ea743225429a6b0ac885650310e9e911815ffbfd7ae3519acb695e48321930a174fcf65625006a8c1c0db79877174ac255f21d3fa237afc403e15302c81b0b00000000000000\n000000206fdcfcb5662bc8a1799a1850fc0a2259a5fcc0efc9f25a8629000000000000007a843916e4c41c71b78fcee60fedd40fe78600439a405d15e8894b4110b89fcfb999cb69639231199c511ebff556250065d4bffdb69a3fd00fdc801b1fad344e09bb5e1cb522e0b40c00000000000000\n00000020878b64adeaa7c26a331a37a247e0bf7ae66afd694a6f733b2d00000000000000dda85526299e10021b7ba6400797c154a7a243c395bb19d337a4d5f836d539d22499cb696a223119d4a7525ff45625006fdcfcb5662bc8a1799a1850fc0a2259a5fcc0efc9f25a862900000000000000\n0000002061081253a8fd5258e1c22867d3b993287a0a509c643058d00100000000000000e57aa7ca21d75cc92bcaeb98c0387bbad1252dae7c0c30e458505615710f8ee6c998cb69f7bb30196c6067c2f3562500878b64adeaa7c26a331a37a247e0bf7ae66afd694a6f733b2d00000000000000\n0000002000b63c2fc0b3738eb97055949b5cadbf0c78cb0d866325c70d00000000000000117915f7410b84a34f8cecf3c1ce0ac6f0e63e0f9c07d05a98f8a9bd1bef12ce3598cb6936823119ca6d79bbf256250061081253a8fd5258e1c22867d3b993287a0a509c643058d00100000000000000\n0000002003f5de0f65f06306fba70aa913a6515859dee7bb72b68a942f000000000000008f2f76f5615dd504d2e1417a2e765f36d9c88a419c09e338359d10aeffa4c1c1d197cb69e8913419e2432cf1f156250000b63c2fc0b3738eb97055949b5cadbf0c78cb0d866325c70d00000000000000\n"
  },
  {
    "path": "dashkit/src/main/resources/TestNetDash-bip44.checkpoint",
    "content": "020000009d8aba465508848988491bb13a72093a84a47533cc50807010bd5e2ef7010000b86ce8d74370ecb66b12c776e822f6b3c39d17c85ca5cfb308702e2a06b442d1583fc953f0ff0f1ec4eb0000300000003c1d1daf5d63ca78a757de2e606a371cdc50e352bf99e8333730c2832c050000\n02000000e06abb1efb3553e7de64517363008e38a6eb16373b9e00f97ddbf3b64107000097d03465f7804dfbaecf3a5c9d8fe812068ebba34c8dae5553cba03dd97fc059563fc953f0ff0f1e23f500002f0000009d8aba465508848988491bb13a72093a84a47533cc50807010bd5e2ef7010000\n02000000baa600ea9bd552ea56686a6cb0362e2f4766a49d6b67cf91847414ff570d0000d15140ac63af921c21d1001b5018127ccdc8c8473dd8dbf76a9ed2aebc38c39d533fc953f0ff0f1e473301002e000000e06abb1efb3553e7de64517363008e38a6eb16373b9e00f97ddbf3b641070000\n0200000028358ee185479ae0488211d394be0a44ee929a3369be889c3043e2317c060000ff2540ab637a9327e3defab5eb68a005a206a9f3a543081e062b0768af2bce6d503fc953f0ff0f1ec24601002d000000baa600ea9bd552ea56686a6cb0362e2f4766a49d6b67cf91847414ff570d0000\n02000000fd50b6bc05839a891ae063afe23486c3c3e0f9986cf973ffb58231d2620b00009f482443cf1339621cbf9d94d0a5e8dd69312812b160d27fcd46a0d71573b4d04d3fc953f0ff0f1eb38c08002c00000028358ee185479ae0488211d394be0a44ee929a3369be889c3043e2317c060000\n02000000183c92e0c21e6eab0da773f6d5d9371019aed51bc9d520882474a047eb0f0000c756fd8aba9e4000320636d4a66618ada15ecc00af5716195398632770a410eb373fc953f0ff0f1e4bf101002b000000fd50b6bc05839a891ae063afe23486c3c3e0f9986cf973ffb58231d2620b0000\n02000000bade9bbe1d805ea00fed36a051ba24e748065ff745e009b5cbde5316fd040000fe2087d0accc4a2f5282e34e0d3695dae3d440ec4fb87954b8cb988050790ac9323fc953f0ff0f1eb89c00002a000000183c92e0c21e6eab0da773f6d5d9371019aed51bc9d520882474a047eb0f0000\n02000000b9b76c2b82dd415c4434efc93e4081c070f4711566f6ce5fef45beeab10f00003d1cd18a0042ae85d1dea63ca5c1550e49d55b5ef77a639db20060fa82f4e007313fc953f0ff0f1e5d43040029000000bade9bbe1d805ea00fed36a051ba24e748065ff745e009b5cbde5316fd040000\n02000000a08556b1aef5758ec1bb3ccc690b0d2e3bad913c1941e9c221413c4afc0300004b9956f8e9d38b48082f325a9222bfa97784ca5a248d46de7b7c40e1fde21111263fc953f0ff0f1e6bc8000028000000b9b76c2b82dd415c4434efc93e4081c070f4711566f6ce5fef45beeab10f0000\n02000000979da9e4ea4fcfb6967502b66f9d89410c8d3b2178be8f3c09aa4b23f803000082d641514dacfce1bac3c9bbbc3358a8faaa89dc1d7a5b205b8022cc78f030f3243fc953f0ff0f1e5ea5010027000000a08556b1aef5758ec1bb3ccc690b0d2e3bad913c1941e9c221413c4afc030000\n0200000067ac3acd7d1c0d00948238c14d4a497e6353fe19c37e32a72398d1f3170d0000882e59d72fea00f0563f3c80411138d8633a80d5bcc67041c8c2dba02a41c769203fc953f0ff0f1e2daa000026000000979da9e4ea4fcfb6967502b66f9d89410c8d3b2178be8f3c09aa4b23f8030000\n020000008f48755f2778402a7857f5ed970a14a731e1471a150a256cb97e4dfe9607000024c930e500c78cefa0b37d2506f120e68e5a496678c9220c115a2e03dc66e3aa0c3fc953f0ff0f1e74ea20002500000067ac3acd7d1c0d00948238c14d4a497e6353fe19c37e32a72398d1f3170d0000\n020000008c168dc008c76dd242b662d751a8b43de32ec41c54a11b8984bd7f1550090000de4233697696679f76afd49a20af6bf74d9f2a950be5281108d91528c13e249ae53ec953f0ff0f1e98a40000240000008f48755f2778402a7857f5ed970a14a731e1471a150a256cb97e4dfe96070000\n02000000751192ac5d41d7414f2b024204f484b6494d07e6c4148c8901488f78160f0000478c0b03bfe1e2685a255902fa6207331ea74ce6efdcda4bf58a615405ebbb9ae43ec953f0ff0f1eac6c0300230000008c168dc008c76dd242b662d751a8b43de32ec41c54a11b8984bd7f1550090000\n0200000039a1a433827b67e40c1b8aa780ab094d148128dbfbb5bff80dc6c15a6d0d00004835e3d567a5312ecb6cfcfb853a581bfab61a4369a82b2dd39fa85329671b91d53ec953f0ff0f1ec058050022000000751192ac5d41d7414f2b024204f484b6494d07e6c4148c8901488f78160f0000\n02000000a422296ed4d35d429c09f675998c8a2c20812db130faf113b340aeba1f0d00005326fbc769c7067bf52430e8ca0089c0e6fa437c1229c567f98b1a78e444d1c3cf3ec953f0ff0f1e052e07002100000039a1a433827b67e40c1b8aa780ab094d148128dbfbb5bff80dc6c15a6d0d0000\n02000000ab1f3ff7709decd075d1f37d4e95c8d50dc4a151e0e551d675483726630100003fc82b967a8a9bfbc2d2ec8c4a49f45005759a23267298929cba005b065805b0c73ec953f0ff0f1ecd57050020000000a422296ed4d35d429c09f675998c8a2c20812db130faf113b340aeba1f0d0000\n020000008fc2a4ea645b8f0bf49281f171aa4f5bf2c596fd8edb648671355e4f55080000a287eeebb4624e5a19a22b5a11cfaa24a4c4cd089a9555625d08cd585c513e92c03ec953f0ff0f1e7e9003001f000000ab1f3ff7709decd075d1f37d4e95c8d50dc4a151e0e551d67548372663010000\n02000000886dca1e1d85999f028299d84faf557aa5e4d033c8a0de508e337b96020d00003b1576afdfae95f791af01ef8c44f96ba6afe9b4cff8dc6acc795666720214ccb13ec953f0ff0f1ed5d700001e0000008fc2a4ea645b8f0bf49281f171aa4f5bf2c596fd8edb648671355e4f55080000\n02000000aaf6cda39eeda63fb9f47d03a55257d1b46e253530bb12852f249ab52d0a000014fc57176256888ae97a1ae5325a46544f745819a1f785beeb0a59fe5b9ff6d6ad3ec953f0ff0f1e542f02001d000000886dca1e1d85999f028299d84faf557aa5e4d033c8a0de508e337b96020d0000\n02000000999b7ed6bdf5aa964d461c26c21a51a62a7b2954ddf62de03982a9fc2201000060113ce6c394fb155b3c7ef653a9329c2b1d4b5ab16181a96fe554c32318a1adaa3ec953f0ff0f1e0fd809001c000000aaf6cda39eeda63fb9f47d03a55257d1b46e253530bb12852f249ab52d0a0000\n02000000d160799a385be1c968328aa2f8c488c8db0b5a908ddd37171102a3a85300000013440ec308e8b69db21a9960e6d3d14b56d7c8660c0be7f6d49908d7004ee5409e3ec953f0ff0f1e094b03001b000000999b7ed6bdf5aa964d461c26c21a51a62a7b2954ddf62de03982a9fc22010000\n020000007ad835393c079dba2298672267d0a1ea57134c57fbf75086b81de242710600002caf899d0b99dd054faa66e93501dd3e6d1e44c969f7c8994a58b312ff027dd6913ec953f0ff0f1e1d4402001a000000d160799a385be1c968328aa2f8c488c8db0b5a908ddd37171102a3a853000000\n0200000061f6e5d25232bb4ffc9794a65ac67009b8f88c7cc38aa2ef30a566e6f10d0000dca66a6906e2c09dc91aa5ff29fed65e140eb381e4154da42d881d3b89c850408e3ec953f0ff0f1e55610500190000007ad835393c079dba2298672267d0a1ea57134c57fbf75086b81de24271060000\n"
  },
  {
    "path": "dashkit/src/main/resources/TestNetDash.checkpoint",
    "content": "0000002046a013abd643c64bab5b1d4650ef4e27b8de36d8bbe53296cbaddf23ef0000007f1a79eb8f8ec688a0b179cd201c9e78948fc1b7ba4e23fabe91fd0ce4eccef7d5fce16220f4001e7151000038bf0b00728ad4fd0f0ed4cb9d960be1399697a21736cdacb8a8e0df529fef1ede000000\n0000002053790e52a7ae8c7d5867d6fc799ab9041cd2cad2eb90068e0267b33be7000000fe5500c2a649cd0e4a57ced6ab4394afe1f447713095c23a419a4e700afc13f1c5fce16281f8001e48db020037bf0b0046a013abd643c64bab5b1d4650ef4e27b8de36d8bbe53296cbaddf23ef000000\n00000020ca6b9b650e5aceb89376d867a41a1a3d624f200fca9a158da91c85d1d4000000798f0739b380e89c4d850b9ba067301f8f071b4a464c5dfd46f7da81ea6ef9d8a6fce16252fb001e3e69010036bf0b0053790e52a7ae8c7d5867d6fc799ab9041cd2cad2eb90068e0267b33be7000000\n0000002069699b9e8042ff5976f22b3219d865e7b007e5f6f39fcfc498d7d769190000008ce28bf3539b49b11b810eff3cca8e75f03b3a94c997995e8a862ce3dcb0fe7836fce162c918011e238d010035bf0b00ca6b9b650e5aceb89376d867a41a1a3d624f200fca9a158da91c85d1d4000000\n00000020ee67579bf44ee81daee153c148f7da93e69e8acce718122071546cdb1c01000086b6958cfa2a74ea1e26aee5a821c853e83492d52c2ef148398644641e677f9016fce1629f2c011e41b80a0034bf0b0069699b9e8042ff5976f22b3219d865e7b007e5f6f39fcfc498d7d76919000000\n000000203c223305833d80a49b62081b8614cb75ab8c8b86b7ad626a7b78b1de55000000a8f187d93e197155add2ffcb8939af484a0ac13487d79215748e2e6dc739188702fce162e135011ee99e0d0033bf0b00ee67579bf44ee81daee153c148f7da93e69e8acce718122071546cdb1c010000\n000000209ce44d3b24ea7104fd92c0c7a22a32f406959d380090dad86dde339b380000004198cc765f1265f79159a4f6eff2c734133eea0418ce6395700f602597beeb7ef9fbe162f732011e7f36020032bf0b003c223305833d80a49b62081b8614cb75ab8c8b86b7ad626a7b78b1de55000000\n00000020b66739125ecaa2607f37bc32512a55c5ecb11de6ca55848ec572f0ed810000003842c4cd0864d6819fc876c20aabfcbaa59aff17e655084c9c694802a28f6eba91fbe162991e011e409e010031bf0b009ce44d3b24ea7104fd92c0c7a22a32f406959d380090dad86dde339b38000000\n000000207cb3deb82debf0fdd9dba197b4934cfa6b2447106cb6ea92eb036dbe14010000ef0fd75965597f867c92a41d6077b606470b1d08fa1cb4a61f999169afc79bcf62fae162b132011ebd2b060030bf0b00b66739125ecaa2607f37bc32512a55c5ecb11de6ca55848ec572f0ed81000000\n000000206ade203e449ae165f5f0612291b9eaf7d0ed43ab1c58c8af2b311be550000000c94d4785d7248485ed7ac1f1af47c6761131705e2e9b6353e46d4044868920f20dfae162c738011e905c09002fbf0b007cb3deb82debf0fdd9dba197b4934cfa6b2447106cb6ea92eb036dbe14010000\n000000206fce0e5ec7d1a35aac7620fcaf399a0dea484ee7d3e87e2a3febeca84a0000000b4b1c3bd6b8e38269e889d8d1eb98cbc2d233696e5e0487d0256e1262d9f695a4f9e1628143011e54530b002ebf0b006ade203e449ae165f5f0612291b9eaf7d0ed43ab1c58c8af2b311be550000000\n0000002089e46b95d23b0051f630d4339e102c110e0ac5a53f0ee158c2b712be010000001257ce6b07f28f4fc69ffe8da3140743914c66263158ae68f65f3d721f96779e6cf9e1629951011e868600002dbf0b006fce0e5ec7d1a35aac7620fcaf399a0dea484ee7d3e87e2a3febeca84a000000\n0000002019e6a8b308cccc79ab2d3b690f9b754428b8f68d5f30e3c1654b880fa3000000b3e52d2503a55d892dc1daca020df4815e5844f347318d8aa4a653d2c562ad8641f9e1622d53011e818b03002cbf0b0089e46b95d23b0051f630d4339e102c110e0ac5a53f0ee158c2b712be01000000\n00000020854085a16417f3aa37599fa5df3c8735b4a6f2bf2c13cad7cae5e451e0000000c0f15ef7a0f64b3be90e80bdb1c15ad6124cc1486aab8d6975639d87ab89f261d4f8e162dc5c011e1d160e002bbf0b0019e6a8b308cccc79ab2d3b690f9b754428b8f68d5f30e3c1654b880fa3000000\n000000207fd5e0b5914c69572470ea559edf552bdcd4d8c7e4924815cd012646420000009857ad25594bd2535e13864c73cef2b158eefd8b85a21b33eab1b52b601785701cf8e1629273011e1a3f0d002abf0b00854085a16417f3aa37599fa5df3c8735b4a6f2bf2c13cad7cae5e451e0000000\n00000020340400d20f587509ddb6874d7277ad57bdd618c32e57ecb5856df2eda8000000b332ca9ba931021d48d8961fb82ee455f4b59750307f89cf59ba90354b6e67f205f8e162ee74011e232c040029bf0b007fd5e0b5914c69572470ea559edf552bdcd4d8c7e4924815cd01264642000000\n000000202d8e586acc2f1eb9bc2bf535b8eb4a93b436da97b7bcf23b39a92df881000000ac03a1abb3621a724bee348138097f23f68cf5998bc0dad30b7d9fdc6698986b73f7e162ee79011eae780e0028bf0b00340400d20f587509ddb6874d7277ad57bdd618c32e57ecb5856df2eda8000000\n00000020e71c501bc04838d7a27a2bb598fd1956275e1ce3ab06323d18a0b3de64010000201ad7c93649725146da536ad153da70acd6f34337b0414fbcf399918f63e9fbedf6e162c894011e9580030027bf0b002d8e586acc2f1eb9bc2bf535b8eb4a93b436da97b7bcf23b39a92df881000000\n00000020d5b748f7bbf310eacc0d24e486e61be248203fd6a69ceb17c91e8b5821010000cdcfa16c08a8c3a18b9e89f525b4150563018ca535ba7f6abb244c939ca6b578ddf6e1624b82011ea78c0d0026bf0b00e71c501bc04838d7a27a2bb598fd1956275e1ce3ab06323d18a0b3de64010000\n00000020f62482c16fd990ff62fa8c8eb4d481ecf7ec6f0bd5f735041209602af900000053a445e14a6ae7ecaddcbd82b6b659af4276c95d3a39e1321a8eb852291819a5c7f5e162c078011e8c39000025bf0b00d5b748f7bbf310eacc0d24e486e61be248203fd6a69ceb17c91e8b5821010000\n00000020342da3dfb4035e5915cad5a2db37c28a20a293c85e19cd541f4e93ccf20000002aa7dab3778a0d9dc6cc3f6b5112e29d1530e9a871656abade6499c039af7d5808f5e1629383011e928f050024bf0b00f62482c16fd990ff62fa8c8eb4d481ecf7ec6f0bd5f735041209602af9000000\n00000020af5fc0041a82d8c4cd07134097dd75d42ebdfaf83d1ff10f5aaa3d835c000000aa96d4e2216608c47898657f776875f7aece7274dca039edf5c7b419343daadf90f4e1621768011e0d2e050023bf0b00342da3dfb4035e5915cad5a2db37c28a20a293c85e19cd541f4e93ccf2000000\n00000020861b7be6aca651f9d86a2987ac12e1a7cb5ed57c6070d820d6054ba1f3000000bee074397e404a4de0f6c98a84f5c42ae8f948a2ea6bdfc31b8a7a15d18672a257f3e162d653011e283c010022bf0b00af5fc0041a82d8c4cd07134097dd75d42ebdfaf83d1ff10f5aaa3d835c000000\n0000002090577746cc3fdf9fca77ea39a7b8cfde4f6597d1101fc4dd676a0a83fc000000f6cecac358f6d57e4947ab09b2c2c1507e3f249c22a5ad7ff3977a9a5c6c8be580f2e1625761011ef145090021bf0b00861b7be6aca651f9d86a2987ac12e1a7cb5ed57c6070d820d6054ba1f3000000\n"
  },
  {
    "path": "dashkit/src/test/kotlin/io/horizontalsystems/dashkit/messages/MasternodeListDiffMessageParserTest.kt",
    "content": "package io.horizontalsystems.dashkit.messages\n\nimport org.junit.jupiter.api.Assertions\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\nimport java.io.File\n\nclass MasternodeListDiffMessageParserTest : Spek({\n    val messageParser = MasternodeListDiffMessageParser()\n\n    describe(\"#parseMessage\") {\n        it(\"parses successfully\") {\n            val resource = javaClass.classLoader.getResource(\"messages/mnlistdiff.bin\")\n            val payload = File(resource.path).readBytes()\n\n            Assertions.assertDoesNotThrow {\n                messageParser.parseMessage(payload)\n            }\n        }\n    }\n})\n"
  },
  {
    "path": "dashkit/src/test/kotlin/io/horizontalsystems/dashkit/validators/DarkGravityWaveValidatorTest.kt",
    "content": "package io.horizontalsystems.dashkit.validators\n\nimport com.nhaarman.mockito_kotlin.doReturn\nimport com.nhaarman.mockito_kotlin.mock\nimport com.nhaarman.mockito_kotlin.whenever\nimport io.horizontalsystems.bitcoincore.blocks.validators.BlockValidatorException\nimport io.horizontalsystems.bitcoincore.managers.BlockValidatorHelper\nimport io.horizontalsystems.bitcoincore.models.Block\nimport org.junit.jupiter.api.Assertions.assertDoesNotThrow\nimport org.junit.jupiter.api.assertThrows\nimport org.spekframework.spek2.Spek\nimport org.spekframework.spek2.style.specification.describe\n\nclass DarkGravityWaveValidatorTest : Spek({\n    val bitsList = arrayOf(\n            0x1b104be1, 0x1b10e09e, 0x1b11a33c, 0x1b121cf3, 0x1b11951e, 0x1b11abac, 0x1b118d9c,\n            0x1b1123f9, 0x1b1141bf, 0x1b110764, 0x1b107556, 0x1b104297, 0x1b1063d0, 0x1b10e878,\n            0x1b0dfaff, 0x1b0c9ab8, 0x1b0c03d6, 0x1b0dd168, 0x1b10b864, 0x1b0fed89, 0x1b113ff1,\n            0x1b10460b, 0x1b13b83f, 0x1b1418d4)\n\n    val timestampArray = arrayOf(\n            1408728124, 1408728332, 1408728479, 1408728495, 1408728608, 1408728744, 1408728756,\n            1408728950, 1408729116, 1408729179, 1408729305, 1408729474, 1408729576, 1408729587,\n            1408729647, 1408729678, 1408730179, 1408730862, 1408730914, 1408731242, 1408731256,\n            1408732229, 1408732257, 1408732489) // 123433 - 123456\n\n    val blockHelper = mock<BlockValidatorHelper>()\n    val candidateHeight = 25\n    val heightInterval = 24\n\n    val validator by memoized {\n        DarkGravityWaveValidator(blockHelper, 24, 3600, 0x1e0fffff, 0)\n    }\n\n    describe(\"#validate\") {\n        context(\"when candidate bits are valid\") {\n            val candidate = mock<Block> {\n                on { version } doReturn 1\n                on { timestamp } doReturn 1408732505\n                on { bits } doReturn 0x1b1441de\n                on { nonce } doReturn 1\n                on { height } doReturn candidateHeight\n            }\n\n            beforeEach {\n                var lastBlock = candidate\n\n                repeat(heightInterval) { i ->\n                    val block = mock<Block> {\n                        on { version } doReturn 1\n                        on { timestamp } doReturn timestampArray[timestampArray.size - i - 1].toLong()\n                        on { bits } doReturn bitsList[bitsList.size - i - 1].toLong()\n                        on { nonce } doReturn 0\n                        on { height } doReturn candidateHeight - i - 1\n                    }\n\n                    whenever(blockHelper.getPrevious(lastBlock, 1)).thenReturn(block)\n\n                    lastBlock = block\n                }\n            }\n\n            it(\"checks bits\") {\n                assertDoesNotThrow {\n                    val prevBlock = blockHelper.getPrevious(candidate, 1)!!\n                    validator.validate(candidate, prevBlock)\n                }\n            }\n        }\n\n        context(\"when there is no NoPreviousBlock\") {\n            val candidateBlock = mock<Block> {\n                on { bits } doReturn 0x1000000f\n            }\n\n            val previousBlock = mock<Block> {\n                on { height } doReturn 25\n            }\n\n            beforeEach {\n                whenever(blockHelper.getPrevious(previousBlock, 1)).thenReturn(null)\n            }\n\n            it(\"throws an exception NoPreviousBlock\") {\n                assertThrows<BlockValidatorException.NoPreviousBlock> {\n                    validator.validate(candidateBlock, previousBlock)\n                }\n            }\n        }\n\n    }\n})\n"
  },
  {
    "path": "dashkit/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker",
    "content": "mock-maker-inline"
  },
  {
    "path": "ecashkit/.gitignore",
    "content": "/build"
  },
  {
    "path": "ecashkit/build.gradle",
    "content": "plugins {\n    id 'com.android.library'\n    id 'kotlin-android'\n    id 'kotlin-kapt'\n    id 'maven-publish'\n}\n\nafterEvaluate {\n    publishing {\n        publications {\n            release(MavenPublication) {\n                from components.release\n            }\n        }\n    }\n}\n\nandroid {\n    namespace 'io.horizontalsystems.ecashkit'\n    compileSdk 34\n\n    defaultConfig {\n        minSdkVersion 23\n        targetSdkVersion 34\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    sourceSets {\n        main.java.srcDirs += 'src/main/kotlin'\n        test.java.srcDirs += 'src/test/kotlin'\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_17\n        targetCompatibility JavaVersion.VERSION_17\n    }\n    kotlinOptions { jvmTarget = '17' }\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version\"\n    implementation 'androidx.annotation:annotation:1.1.0'\n\n    implementation 'com.google.protobuf:protobuf-javalite:3.22.2'\n\n    // Room\n    implementation 'androidx.room:room-runtime:2.5.0'\n    implementation 'androidx.room:room-rxjava2:2.5.0'\n    kapt 'androidx.room:room-compiler:2.5.0'\n\n    api project(':bitcoincore')\n    api project(':bitcoincashkit')\n\n    // Test helpers\n    testImplementation 'junit:junit:4.13.2'\n    testImplementation 'org.junit.jupiter:junit-jupiter:5.6.1'\n    testImplementation 'org.mockito:mockito-core:3.3.3'\n    testImplementation 'com.nhaarman:mockito-kotlin-kt1.1:1.6.0'\n    testImplementation 'org.powermock:powermock-api-mockito2:2.0.7'\n    testImplementation 'org.powermock:powermock-module-junit4:2.0.7'\n\n    // Spek\n    testImplementation \"org.spekframework.spek2:spek-dsl-jvm:2.0.9\"\n    testRuntimeOnly \"org.spekframework.spek2:spek-runner-junit5:2.0.9\"\n    testRuntimeOnly \"org.jetbrains.kotlin:kotlin-reflect:$kotlin_version\"\n\n    // Android Instrumentation Test\n    androidTestImplementation 'androidx.test.ext:junit:1.1.1'\n    androidTestImplementation 'com.linkedin.dexmaker:dexmaker-mockito-inline:2.28.3'\n    androidTestImplementation 'com.nhaarman:mockito-kotlin-kt1.1:1.6.0'\n}\n"
  },
  {
    "path": "ecashkit/src/main/AndroidManifest.xml",
    "content": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest>\n\n</manifest>"
  },
  {
    "path": "ecashkit/src/main/kotlin/chronik/Chronik.java",
    "content": "// Generated by the protocol buffer compiler.  DO NOT EDIT!\n// source: chronik.proto\n\npackage chronik;\n\npublic final class Chronik {\n  private Chronik() {}\n  public static void registerAllExtensions(\n      com.google.protobuf.ExtensionRegistryLite registry) {\n  }\n  /**\n   * Protobuf enum {@code chronik.SlpTokenType}\n   */\n  public enum SlpTokenType\n      implements com.google.protobuf.Internal.EnumLite {\n    /**\n     * <code>FUNGIBLE = 0;</code>\n     */\n    FUNGIBLE(0),\n    /**\n     * <code>NFT1_GROUP = 1;</code>\n     */\n    NFT1_GROUP(1),\n    /**\n     * <code>NFT1_CHILD = 2;</code>\n     */\n    NFT1_CHILD(2),\n    /**\n     * <code>UNKNOWN_TOKEN_TYPE = 3;</code>\n     */\n    UNKNOWN_TOKEN_TYPE(3),\n    UNRECOGNIZED(-1),\n    ;\n\n    /**\n     * <code>FUNGIBLE = 0;</code>\n     */\n    public static final int FUNGIBLE_VALUE = 0;\n    /**\n     * <code>NFT1_GROUP = 1;</code>\n     */\n    public static final int NFT1_GROUP_VALUE = 1;\n    /**\n     * <code>NFT1_CHILD = 2;</code>\n     */\n    public static final int NFT1_CHILD_VALUE = 2;\n    /**\n     * <code>UNKNOWN_TOKEN_TYPE = 3;</code>\n     */\n    public static final int UNKNOWN_TOKEN_TYPE_VALUE = 3;\n\n\n    @java.lang.Override\n    public final int getNumber() {\n      if (this == UNRECOGNIZED) {\n        throw new java.lang.IllegalArgumentException(\n            \"Can't get the number of an unknown enum value.\");\n      }\n      return value;\n    }\n\n    /**\n     * @param value The number of the enum to look for.\n     * @return The enum associated with the given number.\n     * @deprecated Use {@link #forNumber(int)} instead.\n     */\n    @java.lang.Deprecated\n    public static SlpTokenType valueOf(int value) {\n      return forNumber(value);\n    }\n\n    public static SlpTokenType forNumber(int value) {\n      switch (value) {\n        case 0: return FUNGIBLE;\n        case 1: return NFT1_GROUP;\n        case 2: return NFT1_CHILD;\n        case 3: return UNKNOWN_TOKEN_TYPE;\n        default: return null;\n      }\n    }\n\n    public static com.google.protobuf.Internal.EnumLiteMap<SlpTokenType>\n        internalGetValueMap() {\n      return internalValueMap;\n    }\n    private static final com.google.protobuf.Internal.EnumLiteMap<\n        SlpTokenType> internalValueMap =\n          new com.google.protobuf.Internal.EnumLiteMap<SlpTokenType>() {\n            @java.lang.Override\n            public SlpTokenType findValueByNumber(int number) {\n              return SlpTokenType.forNumber(number);\n            }\n          };\n\n    public static com.google.protobuf.Internal.EnumVerifier \n        internalGetVerifier() {\n      return SlpTokenTypeVerifier.INSTANCE;\n    }\n\n    private static final class SlpTokenTypeVerifier implements \n         com.google.protobuf.Internal.EnumVerifier { \n            static final com.google.protobuf.Internal.EnumVerifier           INSTANCE = new SlpTokenTypeVerifier();\n            @java.lang.Override\n            public boolean isInRange(int number) {\n              return SlpTokenType.forNumber(number) != null;\n            }\n          };\n\n    private final int value;\n\n    private SlpTokenType(int value) {\n      this.value = value;\n    }\n\n    // @@protoc_insertion_point(enum_scope:chronik.SlpTokenType)\n  }\n\n  /**\n   * Protobuf enum {@code chronik.SlpTxType}\n   */\n  public enum SlpTxType\n      implements com.google.protobuf.Internal.EnumLite {\n    /**\n     * <code>GENESIS = 0;</code>\n     */\n    GENESIS(0),\n    /**\n     * <code>SEND = 1;</code>\n     */\n    SEND(1),\n    /**\n     * <code>MINT = 2;</code>\n     */\n    MINT(2),\n    /**\n     * <code>BURN = 4;</code>\n     */\n    BURN(4),\n    /**\n     * <code>UNKNOWN_TX_TYPE = 3;</code>\n     */\n    UNKNOWN_TX_TYPE(3),\n    UNRECOGNIZED(-1),\n    ;\n\n    /**\n     * <code>GENESIS = 0;</code>\n     */\n    public static final int GENESIS_VALUE = 0;\n    /**\n     * <code>SEND = 1;</code>\n     */\n    public static final int SEND_VALUE = 1;\n    /**\n     * <code>MINT = 2;</code>\n     */\n    public static final int MINT_VALUE = 2;\n    /**\n     * <code>BURN = 4;</code>\n     */\n    public static final int BURN_VALUE = 4;\n    /**\n     * <code>UNKNOWN_TX_TYPE = 3;</code>\n     */\n    public static final int UNKNOWN_TX_TYPE_VALUE = 3;\n\n\n    @java.lang.Override\n    public final int getNumber() {\n      if (this == UNRECOGNIZED) {\n        throw new java.lang.IllegalArgumentException(\n            \"Can't get the number of an unknown enum value.\");\n      }\n      return value;\n    }\n\n    /**\n     * @param value The number of the enum to look for.\n     * @return The enum associated with the given number.\n     * @deprecated Use {@link #forNumber(int)} instead.\n     */\n    @java.lang.Deprecated\n    public static SlpTxType valueOf(int value) {\n      return forNumber(value);\n    }\n\n    public static SlpTxType forNumber(int value) {\n      switch (value) {\n        case 0: return GENESIS;\n        case 1: return SEND;\n        case 2: return MINT;\n        case 4: return BURN;\n        case 3: return UNKNOWN_TX_TYPE;\n        default: return null;\n      }\n    }\n\n    public static com.google.protobuf.Internal.EnumLiteMap<SlpTxType>\n        internalGetValueMap() {\n      return internalValueMap;\n    }\n    private static final com.google.protobuf.Internal.EnumLiteMap<\n        SlpTxType> internalValueMap =\n          new com.google.protobuf.Internal.EnumLiteMap<SlpTxType>() {\n            @java.lang.Override\n            public SlpTxType findValueByNumber(int number) {\n              return SlpTxType.forNumber(number);\n            }\n          };\n\n    public static com.google.protobuf.Internal.EnumVerifier \n        internalGetVerifier() {\n      return SlpTxTypeVerifier.INSTANCE;\n    }\n\n    private static final class SlpTxTypeVerifier implements \n         com.google.protobuf.Internal.EnumVerifier { \n            static final com.google.protobuf.Internal.EnumVerifier           INSTANCE = new SlpTxTypeVerifier();\n            @java.lang.Override\n            public boolean isInRange(int number) {\n              return SlpTxType.forNumber(number) != null;\n            }\n          };\n\n    private final int value;\n\n    private SlpTxType(int value) {\n      this.value = value;\n    }\n\n    // @@protoc_insertion_point(enum_scope:chronik.SlpTxType)\n  }\n\n  /**\n   * Protobuf enum {@code chronik.Network}\n   */\n  public enum Network\n      implements com.google.protobuf.Internal.EnumLite {\n    /**\n     * <code>BCH = 0;</code>\n     */\n    BCH(0),\n    /**\n     * <code>XEC = 1;</code>\n     */\n    XEC(1),\n    /**\n     * <code>XPI = 2;</code>\n     */\n    XPI(2),\n    /**\n     * <code>XRG = 3;</code>\n     */\n    XRG(3),\n    UNRECOGNIZED(-1),\n    ;\n\n    /**\n     * <code>BCH = 0;</code>\n     */\n    public static final int BCH_VALUE = 0;\n    /**\n     * <code>XEC = 1;</code>\n     */\n    public static final int XEC_VALUE = 1;\n    /**\n     * <code>XPI = 2;</code>\n     */\n    public static final int XPI_VALUE = 2;\n    /**\n     * <code>XRG = 3;</code>\n     */\n    public static final int XRG_VALUE = 3;\n\n\n    @java.lang.Override\n    public final int getNumber() {\n      if (this == UNRECOGNIZED) {\n        throw new java.lang.IllegalArgumentException(\n            \"Can't get the number of an unknown enum value.\");\n      }\n      return value;\n    }\n\n    /**\n     * @param value The number of the enum to look for.\n     * @return The enum associated with the given number.\n     * @deprecated Use {@link #forNumber(int)} instead.\n     */\n    @java.lang.Deprecated\n    public static Network valueOf(int value) {\n      return forNumber(value);\n    }\n\n    public static Network forNumber(int value) {\n      switch (value) {\n        case 0: return BCH;\n        case 1: return XEC;\n        case 2: return XPI;\n        case 3: return XRG;\n        default: return null;\n      }\n    }\n\n    public static com.google.protobuf.Internal.EnumLiteMap<Network>\n        internalGetValueMap() {\n      return internalValueMap;\n    }\n    private static final com.google.protobuf.Internal.EnumLiteMap<\n        Network> internalValueMap =\n          new com.google.protobuf.Internal.EnumLiteMap<Network>() {\n            @java.lang.Override\n            public Network findValueByNumber(int number) {\n              return Network.forNumber(number);\n            }\n          };\n\n    public static com.google.protobuf.Internal.EnumVerifier \n        internalGetVerifier() {\n      return NetworkVerifier.INSTANCE;\n    }\n\n    private static final class NetworkVerifier implements \n         com.google.protobuf.Internal.EnumVerifier { \n            static final com.google.protobuf.Internal.EnumVerifier           INSTANCE = new NetworkVerifier();\n            @java.lang.Override\n            public boolean isInRange(int number) {\n              return Network.forNumber(number) != null;\n            }\n          };\n\n    private final int value;\n\n    private Network(int value) {\n      this.value = value;\n    }\n\n    // @@protoc_insertion_point(enum_scope:chronik.Network)\n  }\n\n  /**\n   * Protobuf enum {@code chronik.UtxoStateVariant}\n   */\n  public enum UtxoStateVariant\n      implements com.google.protobuf.Internal.EnumLite {\n    /**\n     * <code>UNSPENT = 0;</code>\n     */\n    UNSPENT(0),\n    /**\n     * <code>SPENT = 1;</code>\n     */\n    SPENT(1),\n    /**\n     * <code>NO_SUCH_TX = 2;</code>\n     */\n    NO_SUCH_TX(2),\n    /**\n     * <code>NO_SUCH_OUTPUT = 3;</code>\n     */\n    NO_SUCH_OUTPUT(3),\n    UNRECOGNIZED(-1),\n    ;\n\n    /**\n     * <code>UNSPENT = 0;</code>\n     */\n    public static final int UNSPENT_VALUE = 0;\n    /**\n     * <code>SPENT = 1;</code>\n     */\n    public static final int SPENT_VALUE = 1;\n    /**\n     * <code>NO_SUCH_TX = 2;</code>\n     */\n    public static final int NO_SUCH_TX_VALUE = 2;\n    /**\n     * <code>NO_SUCH_OUTPUT = 3;</code>\n     */\n    public static final int NO_SUCH_OUTPUT_VALUE = 3;\n\n\n    @java.lang.Override\n    public final int getNumber() {\n      if (this == UNRECOGNIZED) {\n        throw new java.lang.IllegalArgumentException(\n            \"Can't get the number of an unknown enum value.\");\n      }\n      return value;\n    }\n\n    /**\n     * @param value The number of the enum to look for.\n     * @return The enum associated with the given number.\n     * @deprecated Use {@link #forNumber(int)} instead.\n     */\n    @java.lang.Deprecated\n    public static UtxoStateVariant valueOf(int value) {\n      return forNumber(value);\n    }\n\n    public static UtxoStateVariant forNumber(int value) {\n      switch (value) {\n        case 0: return UNSPENT;\n        case 1: return SPENT;\n        case 2: return NO_SUCH_TX;\n        case 3: return NO_SUCH_OUTPUT;\n        default: return null;\n      }\n    }\n\n    public static com.google.protobuf.Internal.EnumLiteMap<UtxoStateVariant>\n        internalGetValueMap() {\n      return internalValueMap;\n    }\n    private static final com.google.protobuf.Internal.EnumLiteMap<\n        UtxoStateVariant> internalValueMap =\n          new com.google.protobuf.Internal.EnumLiteMap<UtxoStateVariant>() {\n            @java.lang.Override\n            public UtxoStateVariant findValueByNumber(int number) {\n              return UtxoStateVariant.forNumber(number);\n            }\n          };\n\n    public static com.google.protobuf.Internal.EnumVerifier \n        internalGetVerifier() {\n      return UtxoStateVariantVerifier.INSTANCE;\n    }\n\n    private static final class UtxoStateVariantVerifier implements \n         com.google.protobuf.Internal.EnumVerifier { \n            static final com.google.protobuf.Internal.EnumVerifier           INSTANCE = new UtxoStateVariantVerifier();\n            @java.lang.Override\n            public boolean isInRange(int number) {\n              return UtxoStateVariant.forNumber(number) != null;\n            }\n          };\n\n    private final int value;\n\n    private UtxoStateVariant(int value) {\n      this.value = value;\n    }\n\n    // @@protoc_insertion_point(enum_scope:chronik.UtxoStateVariant)\n  }\n\n  public interface ValidateUtxoRequestOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.ValidateUtxoRequest)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n     */\n    java.util.List<chronik.Chronik.OutPoint> \n        getOutpointsList();\n    /**\n     * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n     */\n    chronik.Chronik.OutPoint getOutpoints(int index);\n    /**\n     * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n     */\n    int getOutpointsCount();\n  }\n  /**\n   * Protobuf type {@code chronik.ValidateUtxoRequest}\n   */\n  public  static final class ValidateUtxoRequest extends\n      com.google.protobuf.GeneratedMessageLite<\n          ValidateUtxoRequest, ValidateUtxoRequest.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.ValidateUtxoRequest)\n      ValidateUtxoRequestOrBuilder {\n    private ValidateUtxoRequest() {\n      outpoints_ = emptyProtobufList();\n    }\n    public static final int OUTPOINTS_FIELD_NUMBER = 1;\n    private com.google.protobuf.Internal.ProtobufList<chronik.Chronik.OutPoint> outpoints_;\n    /**\n     * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n     */\n    @java.lang.Override\n    public java.util.List<chronik.Chronik.OutPoint> getOutpointsList() {\n      return outpoints_;\n    }\n    /**\n     * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n     */\n    public java.util.List<? extends chronik.Chronik.OutPointOrBuilder> \n        getOutpointsOrBuilderList() {\n      return outpoints_;\n    }\n    /**\n     * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n     */\n    @java.lang.Override\n    public int getOutpointsCount() {\n      return outpoints_.size();\n    }\n    /**\n     * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.OutPoint getOutpoints(int index) {\n      return outpoints_.get(index);\n    }\n    /**\n     * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n     */\n    public chronik.Chronik.OutPointOrBuilder getOutpointsOrBuilder(\n        int index) {\n      return outpoints_.get(index);\n    }\n    private void ensureOutpointsIsMutable() {\n      com.google.protobuf.Internal.ProtobufList<chronik.Chronik.OutPoint> tmp = outpoints_;\n      if (!tmp.isModifiable()) {\n        outpoints_ =\n            com.google.protobuf.GeneratedMessageLite.mutableCopy(tmp);\n       }\n    }\n\n    /**\n     * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n     */\n    private void setOutpoints(\n        int index, chronik.Chronik.OutPoint value) {\n      value.getClass();\n  ensureOutpointsIsMutable();\n      outpoints_.set(index, value);\n    }\n    /**\n     * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n     */\n    private void addOutpoints(chronik.Chronik.OutPoint value) {\n      value.getClass();\n  ensureOutpointsIsMutable();\n      outpoints_.add(value);\n    }\n    /**\n     * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n     */\n    private void addOutpoints(\n        int index, chronik.Chronik.OutPoint value) {\n      value.getClass();\n  ensureOutpointsIsMutable();\n      outpoints_.add(index, value);\n    }\n    /**\n     * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n     */\n    private void addAllOutpoints(\n        java.lang.Iterable<? extends chronik.Chronik.OutPoint> values) {\n      ensureOutpointsIsMutable();\n      com.google.protobuf.AbstractMessageLite.addAll(\n          values, outpoints_);\n    }\n    /**\n     * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n     */\n    private void clearOutpoints() {\n      outpoints_ = emptyProtobufList();\n    }\n    /**\n     * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n     */\n    private void removeOutpoints(int index) {\n      ensureOutpointsIsMutable();\n      outpoints_.remove(index);\n    }\n\n    public static chronik.Chronik.ValidateUtxoRequest parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.ValidateUtxoRequest parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.ValidateUtxoRequest parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.ValidateUtxoRequest parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.ValidateUtxoRequest parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.ValidateUtxoRequest parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.ValidateUtxoRequest parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.ValidateUtxoRequest parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.ValidateUtxoRequest parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.ValidateUtxoRequest parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.ValidateUtxoRequest parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.ValidateUtxoRequest parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.ValidateUtxoRequest prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.ValidateUtxoRequest}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.ValidateUtxoRequest, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.ValidateUtxoRequest)\n        chronik.Chronik.ValidateUtxoRequestOrBuilder {\n      // Construct using chronik.Chronik.ValidateUtxoRequest.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n       */\n      @java.lang.Override\n      public java.util.List<chronik.Chronik.OutPoint> getOutpointsList() {\n        return java.util.Collections.unmodifiableList(\n            instance.getOutpointsList());\n      }\n      /**\n       * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n       */\n      @java.lang.Override\n      public int getOutpointsCount() {\n        return instance.getOutpointsCount();\n      }/**\n       * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.OutPoint getOutpoints(int index) {\n        return instance.getOutpoints(index);\n      }\n      /**\n       * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n       */\n      public Builder setOutpoints(\n          int index, chronik.Chronik.OutPoint value) {\n        copyOnWrite();\n        instance.setOutpoints(index, value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n       */\n      public Builder setOutpoints(\n          int index, chronik.Chronik.OutPoint.Builder builderForValue) {\n        copyOnWrite();\n        instance.setOutpoints(index,\n            builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n       */\n      public Builder addOutpoints(chronik.Chronik.OutPoint value) {\n        copyOnWrite();\n        instance.addOutpoints(value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n       */\n      public Builder addOutpoints(\n          int index, chronik.Chronik.OutPoint value) {\n        copyOnWrite();\n        instance.addOutpoints(index, value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n       */\n      public Builder addOutpoints(\n          chronik.Chronik.OutPoint.Builder builderForValue) {\n        copyOnWrite();\n        instance.addOutpoints(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n       */\n      public Builder addOutpoints(\n          int index, chronik.Chronik.OutPoint.Builder builderForValue) {\n        copyOnWrite();\n        instance.addOutpoints(index,\n            builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n       */\n      public Builder addAllOutpoints(\n          java.lang.Iterable<? extends chronik.Chronik.OutPoint> values) {\n        copyOnWrite();\n        instance.addAllOutpoints(values);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n       */\n      public Builder clearOutpoints() {\n        copyOnWrite();\n        instance.clearOutpoints();\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.OutPoint outpoints = 1;</code>\n       */\n      public Builder removeOutpoints(int index) {\n        copyOnWrite();\n        instance.removeOutpoints(index);\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.ValidateUtxoRequest)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.ValidateUtxoRequest();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"outpoints_\",\n              chronik.Chronik.OutPoint.class,\n            };\n            java.lang.String info =\n                \"\\u0000\\u0001\\u0000\\u0000\\u0001\\u0001\\u0001\\u0000\\u0001\\u0000\\u0001\\u001b\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.ValidateUtxoRequest> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.ValidateUtxoRequest.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.ValidateUtxoRequest>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.ValidateUtxoRequest)\n    private static final chronik.Chronik.ValidateUtxoRequest DEFAULT_INSTANCE;\n    static {\n      ValidateUtxoRequest defaultInstance = new ValidateUtxoRequest();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        ValidateUtxoRequest.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.ValidateUtxoRequest getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<ValidateUtxoRequest> PARSER;\n\n    public static com.google.protobuf.Parser<ValidateUtxoRequest> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface ValidateUtxoResponseOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.ValidateUtxoResponse)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n     */\n    java.util.List<chronik.Chronik.UtxoState> \n        getUtxoStatesList();\n    /**\n     * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n     */\n    chronik.Chronik.UtxoState getUtxoStates(int index);\n    /**\n     * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n     */\n    int getUtxoStatesCount();\n  }\n  /**\n   * Protobuf type {@code chronik.ValidateUtxoResponse}\n   */\n  public  static final class ValidateUtxoResponse extends\n      com.google.protobuf.GeneratedMessageLite<\n          ValidateUtxoResponse, ValidateUtxoResponse.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.ValidateUtxoResponse)\n      ValidateUtxoResponseOrBuilder {\n    private ValidateUtxoResponse() {\n      utxoStates_ = emptyProtobufList();\n    }\n    public static final int UTXO_STATES_FIELD_NUMBER = 1;\n    private com.google.protobuf.Internal.ProtobufList<chronik.Chronik.UtxoState> utxoStates_;\n    /**\n     * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n     */\n    @java.lang.Override\n    public java.util.List<chronik.Chronik.UtxoState> getUtxoStatesList() {\n      return utxoStates_;\n    }\n    /**\n     * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n     */\n    public java.util.List<? extends chronik.Chronik.UtxoStateOrBuilder> \n        getUtxoStatesOrBuilderList() {\n      return utxoStates_;\n    }\n    /**\n     * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n     */\n    @java.lang.Override\n    public int getUtxoStatesCount() {\n      return utxoStates_.size();\n    }\n    /**\n     * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.UtxoState getUtxoStates(int index) {\n      return utxoStates_.get(index);\n    }\n    /**\n     * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n     */\n    public chronik.Chronik.UtxoStateOrBuilder getUtxoStatesOrBuilder(\n        int index) {\n      return utxoStates_.get(index);\n    }\n    private void ensureUtxoStatesIsMutable() {\n      com.google.protobuf.Internal.ProtobufList<chronik.Chronik.UtxoState> tmp = utxoStates_;\n      if (!tmp.isModifiable()) {\n        utxoStates_ =\n            com.google.protobuf.GeneratedMessageLite.mutableCopy(tmp);\n       }\n    }\n\n    /**\n     * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n     */\n    private void setUtxoStates(\n        int index, chronik.Chronik.UtxoState value) {\n      value.getClass();\n  ensureUtxoStatesIsMutable();\n      utxoStates_.set(index, value);\n    }\n    /**\n     * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n     */\n    private void addUtxoStates(chronik.Chronik.UtxoState value) {\n      value.getClass();\n  ensureUtxoStatesIsMutable();\n      utxoStates_.add(value);\n    }\n    /**\n     * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n     */\n    private void addUtxoStates(\n        int index, chronik.Chronik.UtxoState value) {\n      value.getClass();\n  ensureUtxoStatesIsMutable();\n      utxoStates_.add(index, value);\n    }\n    /**\n     * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n     */\n    private void addAllUtxoStates(\n        java.lang.Iterable<? extends chronik.Chronik.UtxoState> values) {\n      ensureUtxoStatesIsMutable();\n      com.google.protobuf.AbstractMessageLite.addAll(\n          values, utxoStates_);\n    }\n    /**\n     * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n     */\n    private void clearUtxoStates() {\n      utxoStates_ = emptyProtobufList();\n    }\n    /**\n     * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n     */\n    private void removeUtxoStates(int index) {\n      ensureUtxoStatesIsMutable();\n      utxoStates_.remove(index);\n    }\n\n    public static chronik.Chronik.ValidateUtxoResponse parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.ValidateUtxoResponse parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.ValidateUtxoResponse parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.ValidateUtxoResponse parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.ValidateUtxoResponse parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.ValidateUtxoResponse parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.ValidateUtxoResponse parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.ValidateUtxoResponse parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.ValidateUtxoResponse parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.ValidateUtxoResponse parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.ValidateUtxoResponse parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.ValidateUtxoResponse parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.ValidateUtxoResponse prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.ValidateUtxoResponse}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.ValidateUtxoResponse, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.ValidateUtxoResponse)\n        chronik.Chronik.ValidateUtxoResponseOrBuilder {\n      // Construct using chronik.Chronik.ValidateUtxoResponse.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n       */\n      @java.lang.Override\n      public java.util.List<chronik.Chronik.UtxoState> getUtxoStatesList() {\n        return java.util.Collections.unmodifiableList(\n            instance.getUtxoStatesList());\n      }\n      /**\n       * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n       */\n      @java.lang.Override\n      public int getUtxoStatesCount() {\n        return instance.getUtxoStatesCount();\n      }/**\n       * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.UtxoState getUtxoStates(int index) {\n        return instance.getUtxoStates(index);\n      }\n      /**\n       * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n       */\n      public Builder setUtxoStates(\n          int index, chronik.Chronik.UtxoState value) {\n        copyOnWrite();\n        instance.setUtxoStates(index, value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n       */\n      public Builder setUtxoStates(\n          int index, chronik.Chronik.UtxoState.Builder builderForValue) {\n        copyOnWrite();\n        instance.setUtxoStates(index,\n            builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n       */\n      public Builder addUtxoStates(chronik.Chronik.UtxoState value) {\n        copyOnWrite();\n        instance.addUtxoStates(value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n       */\n      public Builder addUtxoStates(\n          int index, chronik.Chronik.UtxoState value) {\n        copyOnWrite();\n        instance.addUtxoStates(index, value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n       */\n      public Builder addUtxoStates(\n          chronik.Chronik.UtxoState.Builder builderForValue) {\n        copyOnWrite();\n        instance.addUtxoStates(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n       */\n      public Builder addUtxoStates(\n          int index, chronik.Chronik.UtxoState.Builder builderForValue) {\n        copyOnWrite();\n        instance.addUtxoStates(index,\n            builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n       */\n      public Builder addAllUtxoStates(\n          java.lang.Iterable<? extends chronik.Chronik.UtxoState> values) {\n        copyOnWrite();\n        instance.addAllUtxoStates(values);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n       */\n      public Builder clearUtxoStates() {\n        copyOnWrite();\n        instance.clearUtxoStates();\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.UtxoState utxo_states = 1;</code>\n       */\n      public Builder removeUtxoStates(int index) {\n        copyOnWrite();\n        instance.removeUtxoStates(index);\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.ValidateUtxoResponse)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.ValidateUtxoResponse();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"utxoStates_\",\n              chronik.Chronik.UtxoState.class,\n            };\n            java.lang.String info =\n                \"\\u0000\\u0001\\u0000\\u0000\\u0001\\u0001\\u0001\\u0000\\u0001\\u0000\\u0001\\u001b\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.ValidateUtxoResponse> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.ValidateUtxoResponse.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.ValidateUtxoResponse>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.ValidateUtxoResponse)\n    private static final chronik.Chronik.ValidateUtxoResponse DEFAULT_INSTANCE;\n    static {\n      ValidateUtxoResponse defaultInstance = new ValidateUtxoResponse();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        ValidateUtxoResponse.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.ValidateUtxoResponse getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<ValidateUtxoResponse> PARSER;\n\n    public static com.google.protobuf.Parser<ValidateUtxoResponse> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface BroadcastTxRequestOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.BroadcastTxRequest)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>bytes raw_tx = 1;</code>\n     * @return The rawTx.\n     */\n    com.google.protobuf.ByteString getRawTx();\n\n    /**\n     * <code>bool skip_slp_check = 2;</code>\n     * @return The skipSlpCheck.\n     */\n    boolean getSkipSlpCheck();\n  }\n  /**\n   * Protobuf type {@code chronik.BroadcastTxRequest}\n   */\n  public  static final class BroadcastTxRequest extends\n      com.google.protobuf.GeneratedMessageLite<\n          BroadcastTxRequest, BroadcastTxRequest.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.BroadcastTxRequest)\n      BroadcastTxRequestOrBuilder {\n    private BroadcastTxRequest() {\n      rawTx_ = com.google.protobuf.ByteString.EMPTY;\n    }\n    public static final int RAW_TX_FIELD_NUMBER = 1;\n    private com.google.protobuf.ByteString rawTx_;\n    /**\n     * <code>bytes raw_tx = 1;</code>\n     * @return The rawTx.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getRawTx() {\n      return rawTx_;\n    }\n    /**\n     * <code>bytes raw_tx = 1;</code>\n     * @param value The rawTx to set.\n     */\n    private void setRawTx(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      rawTx_ = value;\n    }\n    /**\n     * <code>bytes raw_tx = 1;</code>\n     */\n    private void clearRawTx() {\n\n      rawTx_ = getDefaultInstance().getRawTx();\n    }\n\n    public static final int SKIP_SLP_CHECK_FIELD_NUMBER = 2;\n    private boolean skipSlpCheck_;\n    /**\n     * <code>bool skip_slp_check = 2;</code>\n     * @return The skipSlpCheck.\n     */\n    @java.lang.Override\n    public boolean getSkipSlpCheck() {\n      return skipSlpCheck_;\n    }\n    /**\n     * <code>bool skip_slp_check = 2;</code>\n     * @param value The skipSlpCheck to set.\n     */\n    private void setSkipSlpCheck(boolean value) {\n      \n      skipSlpCheck_ = value;\n    }\n    /**\n     * <code>bool skip_slp_check = 2;</code>\n     */\n    private void clearSkipSlpCheck() {\n\n      skipSlpCheck_ = false;\n    }\n\n    public static chronik.Chronik.BroadcastTxRequest parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.BroadcastTxRequest parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.BroadcastTxRequest parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.BroadcastTxRequest parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.BroadcastTxRequest parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.BroadcastTxRequest parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.BroadcastTxRequest parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.BroadcastTxRequest parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.BroadcastTxRequest parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.BroadcastTxRequest parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.BroadcastTxRequest parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.BroadcastTxRequest parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.BroadcastTxRequest prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.BroadcastTxRequest}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.BroadcastTxRequest, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.BroadcastTxRequest)\n        chronik.Chronik.BroadcastTxRequestOrBuilder {\n      // Construct using chronik.Chronik.BroadcastTxRequest.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>bytes raw_tx = 1;</code>\n       * @return The rawTx.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getRawTx() {\n        return instance.getRawTx();\n      }\n      /**\n       * <code>bytes raw_tx = 1;</code>\n       * @param value The rawTx to set.\n       * @return This builder for chaining.\n       */\n      public Builder setRawTx(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setRawTx(value);\n        return this;\n      }\n      /**\n       * <code>bytes raw_tx = 1;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearRawTx() {\n        copyOnWrite();\n        instance.clearRawTx();\n        return this;\n      }\n\n      /**\n       * <code>bool skip_slp_check = 2;</code>\n       * @return The skipSlpCheck.\n       */\n      @java.lang.Override\n      public boolean getSkipSlpCheck() {\n        return instance.getSkipSlpCheck();\n      }\n      /**\n       * <code>bool skip_slp_check = 2;</code>\n       * @param value The skipSlpCheck to set.\n       * @return This builder for chaining.\n       */\n      public Builder setSkipSlpCheck(boolean value) {\n        copyOnWrite();\n        instance.setSkipSlpCheck(value);\n        return this;\n      }\n      /**\n       * <code>bool skip_slp_check = 2;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearSkipSlpCheck() {\n        copyOnWrite();\n        instance.clearSkipSlpCheck();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.BroadcastTxRequest)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.BroadcastTxRequest();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"rawTx_\",\n              \"skipSlpCheck_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0002\\u0000\\u0000\\u0001\\u0002\\u0002\\u0000\\u0000\\u0000\\u0001\\n\\u0002\\u0007\" +\n                \"\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.BroadcastTxRequest> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.BroadcastTxRequest.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.BroadcastTxRequest>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.BroadcastTxRequest)\n    private static final chronik.Chronik.BroadcastTxRequest DEFAULT_INSTANCE;\n    static {\n      BroadcastTxRequest defaultInstance = new BroadcastTxRequest();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        BroadcastTxRequest.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.BroadcastTxRequest getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<BroadcastTxRequest> PARSER;\n\n    public static com.google.protobuf.Parser<BroadcastTxRequest> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface BroadcastTxResponseOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.BroadcastTxResponse)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>bytes txid = 1;</code>\n     * @return The txid.\n     */\n    com.google.protobuf.ByteString getTxid();\n  }\n  /**\n   * Protobuf type {@code chronik.BroadcastTxResponse}\n   */\n  public  static final class BroadcastTxResponse extends\n      com.google.protobuf.GeneratedMessageLite<\n          BroadcastTxResponse, BroadcastTxResponse.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.BroadcastTxResponse)\n      BroadcastTxResponseOrBuilder {\n    private BroadcastTxResponse() {\n      txid_ = com.google.protobuf.ByteString.EMPTY;\n    }\n    public static final int TXID_FIELD_NUMBER = 1;\n    private com.google.protobuf.ByteString txid_;\n    /**\n     * <code>bytes txid = 1;</code>\n     * @return The txid.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getTxid() {\n      return txid_;\n    }\n    /**\n     * <code>bytes txid = 1;</code>\n     * @param value The txid to set.\n     */\n    private void setTxid(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      txid_ = value;\n    }\n    /**\n     * <code>bytes txid = 1;</code>\n     */\n    private void clearTxid() {\n\n      txid_ = getDefaultInstance().getTxid();\n    }\n\n    public static chronik.Chronik.BroadcastTxResponse parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.BroadcastTxResponse parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.BroadcastTxResponse parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.BroadcastTxResponse parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.BroadcastTxResponse parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.BroadcastTxResponse parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.BroadcastTxResponse parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.BroadcastTxResponse parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.BroadcastTxResponse parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.BroadcastTxResponse parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.BroadcastTxResponse parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.BroadcastTxResponse parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.BroadcastTxResponse prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.BroadcastTxResponse}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.BroadcastTxResponse, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.BroadcastTxResponse)\n        chronik.Chronik.BroadcastTxResponseOrBuilder {\n      // Construct using chronik.Chronik.BroadcastTxResponse.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>bytes txid = 1;</code>\n       * @return The txid.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getTxid() {\n        return instance.getTxid();\n      }\n      /**\n       * <code>bytes txid = 1;</code>\n       * @param value The txid to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTxid(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setTxid(value);\n        return this;\n      }\n      /**\n       * <code>bytes txid = 1;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearTxid() {\n        copyOnWrite();\n        instance.clearTxid();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.BroadcastTxResponse)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.BroadcastTxResponse();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"txid_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0001\\u0000\\u0000\\u0001\\u0001\\u0001\\u0000\\u0000\\u0000\\u0001\\n\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.BroadcastTxResponse> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.BroadcastTxResponse.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.BroadcastTxResponse>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.BroadcastTxResponse)\n    private static final chronik.Chronik.BroadcastTxResponse DEFAULT_INSTANCE;\n    static {\n      BroadcastTxResponse defaultInstance = new BroadcastTxResponse();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        BroadcastTxResponse.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.BroadcastTxResponse getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<BroadcastTxResponse> PARSER;\n\n    public static com.google.protobuf.Parser<BroadcastTxResponse> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface BroadcastTxsRequestOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.BroadcastTxsRequest)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>repeated bytes raw_txs = 1;</code>\n     * @return A list containing the rawTxs.\n     */\n    java.util.List<com.google.protobuf.ByteString> getRawTxsList();\n    /**\n     * <code>repeated bytes raw_txs = 1;</code>\n     * @return The count of rawTxs.\n     */\n    int getRawTxsCount();\n    /**\n     * <code>repeated bytes raw_txs = 1;</code>\n     * @param index The index of the element to return.\n     * @return The rawTxs at the given index.\n     */\n    com.google.protobuf.ByteString getRawTxs(int index);\n\n    /**\n     * <code>bool skip_slp_check = 2;</code>\n     * @return The skipSlpCheck.\n     */\n    boolean getSkipSlpCheck();\n  }\n  /**\n   * Protobuf type {@code chronik.BroadcastTxsRequest}\n   */\n  public  static final class BroadcastTxsRequest extends\n      com.google.protobuf.GeneratedMessageLite<\n          BroadcastTxsRequest, BroadcastTxsRequest.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.BroadcastTxsRequest)\n      BroadcastTxsRequestOrBuilder {\n    private BroadcastTxsRequest() {\n      rawTxs_ = emptyProtobufList();\n    }\n    public static final int RAW_TXS_FIELD_NUMBER = 1;\n    private com.google.protobuf.Internal.ProtobufList<com.google.protobuf.ByteString> rawTxs_;\n    /**\n     * <code>repeated bytes raw_txs = 1;</code>\n     * @return A list containing the rawTxs.\n     */\n    @java.lang.Override\n    public java.util.List<com.google.protobuf.ByteString>\n        getRawTxsList() {\n      return rawTxs_;\n    }\n    /**\n     * <code>repeated bytes raw_txs = 1;</code>\n     * @return The count of rawTxs.\n     */\n    @java.lang.Override\n    public int getRawTxsCount() {\n      return rawTxs_.size();\n    }\n    /**\n     * <code>repeated bytes raw_txs = 1;</code>\n     * @param index The index of the element to return.\n     * @return The rawTxs at the given index.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getRawTxs(int index) {\n      return rawTxs_.get(index);\n    }\n    private void ensureRawTxsIsMutable() {\n      com.google.protobuf.Internal.ProtobufList<com.google.protobuf.ByteString> tmp = rawTxs_;\n      if (!tmp.isModifiable()) {\n        rawTxs_ =\n            com.google.protobuf.GeneratedMessageLite.mutableCopy(tmp);\n       }\n    }\n    /**\n     * <code>repeated bytes raw_txs = 1;</code>\n     * @param index The index to set the value at.\n     * @param value The rawTxs to set.\n     */\n    private void setRawTxs(\n        int index, com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  ensureRawTxsIsMutable();\n      rawTxs_.set(index, value);\n    }\n    /**\n     * <code>repeated bytes raw_txs = 1;</code>\n     * @param value The rawTxs to add.\n     */\n    private void addRawTxs(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  ensureRawTxsIsMutable();\n      rawTxs_.add(value);\n    }\n    /**\n     * <code>repeated bytes raw_txs = 1;</code>\n     * @param values The rawTxs to add.\n     */\n    private void addAllRawTxs(\n        java.lang.Iterable<? extends com.google.protobuf.ByteString> values) {\n      ensureRawTxsIsMutable();\n      com.google.protobuf.AbstractMessageLite.addAll(\n          values, rawTxs_);\n    }\n    /**\n     * <code>repeated bytes raw_txs = 1;</code>\n     */\n    private void clearRawTxs() {\n      rawTxs_ = emptyProtobufList();\n    }\n\n    public static final int SKIP_SLP_CHECK_FIELD_NUMBER = 2;\n    private boolean skipSlpCheck_;\n    /**\n     * <code>bool skip_slp_check = 2;</code>\n     * @return The skipSlpCheck.\n     */\n    @java.lang.Override\n    public boolean getSkipSlpCheck() {\n      return skipSlpCheck_;\n    }\n    /**\n     * <code>bool skip_slp_check = 2;</code>\n     * @param value The skipSlpCheck to set.\n     */\n    private void setSkipSlpCheck(boolean value) {\n      \n      skipSlpCheck_ = value;\n    }\n    /**\n     * <code>bool skip_slp_check = 2;</code>\n     */\n    private void clearSkipSlpCheck() {\n\n      skipSlpCheck_ = false;\n    }\n\n    public static chronik.Chronik.BroadcastTxsRequest parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.BroadcastTxsRequest parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.BroadcastTxsRequest parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.BroadcastTxsRequest parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.BroadcastTxsRequest parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.BroadcastTxsRequest parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.BroadcastTxsRequest parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.BroadcastTxsRequest parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.BroadcastTxsRequest parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.BroadcastTxsRequest parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.BroadcastTxsRequest parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.BroadcastTxsRequest parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.BroadcastTxsRequest prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.BroadcastTxsRequest}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.BroadcastTxsRequest, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.BroadcastTxsRequest)\n        chronik.Chronik.BroadcastTxsRequestOrBuilder {\n      // Construct using chronik.Chronik.BroadcastTxsRequest.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>repeated bytes raw_txs = 1;</code>\n       * @return A list containing the rawTxs.\n       */\n      @java.lang.Override\n      public java.util.List<com.google.protobuf.ByteString>\n          getRawTxsList() {\n        return java.util.Collections.unmodifiableList(\n            instance.getRawTxsList());\n      }\n      /**\n       * <code>repeated bytes raw_txs = 1;</code>\n       * @return The count of rawTxs.\n       */\n      @java.lang.Override\n      public int getRawTxsCount() {\n        return instance.getRawTxsCount();\n      }\n      /**\n       * <code>repeated bytes raw_txs = 1;</code>\n       * @param index The index of the element to return.\n       * @return The rawTxs at the given index.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getRawTxs(int index) {\n        return instance.getRawTxs(index);\n      }\n      /**\n       * <code>repeated bytes raw_txs = 1;</code>\n       * @param value The rawTxs to set.\n       * @return This builder for chaining.\n       */\n      public Builder setRawTxs(\n          int index, com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setRawTxs(index, value);\n        return this;\n      }\n      /**\n       * <code>repeated bytes raw_txs = 1;</code>\n       * @param value The rawTxs to add.\n       * @return This builder for chaining.\n       */\n      public Builder addRawTxs(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.addRawTxs(value);\n        return this;\n      }\n      /**\n       * <code>repeated bytes raw_txs = 1;</code>\n       * @param values The rawTxs to add.\n       * @return This builder for chaining.\n       */\n      public Builder addAllRawTxs(\n          java.lang.Iterable<? extends com.google.protobuf.ByteString> values) {\n        copyOnWrite();\n        instance.addAllRawTxs(values);\n        return this;\n      }\n      /**\n       * <code>repeated bytes raw_txs = 1;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearRawTxs() {\n        copyOnWrite();\n        instance.clearRawTxs();\n        return this;\n      }\n\n      /**\n       * <code>bool skip_slp_check = 2;</code>\n       * @return The skipSlpCheck.\n       */\n      @java.lang.Override\n      public boolean getSkipSlpCheck() {\n        return instance.getSkipSlpCheck();\n      }\n      /**\n       * <code>bool skip_slp_check = 2;</code>\n       * @param value The skipSlpCheck to set.\n       * @return This builder for chaining.\n       */\n      public Builder setSkipSlpCheck(boolean value) {\n        copyOnWrite();\n        instance.setSkipSlpCheck(value);\n        return this;\n      }\n      /**\n       * <code>bool skip_slp_check = 2;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearSkipSlpCheck() {\n        copyOnWrite();\n        instance.clearSkipSlpCheck();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.BroadcastTxsRequest)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.BroadcastTxsRequest();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"rawTxs_\",\n              \"skipSlpCheck_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0002\\u0000\\u0000\\u0001\\u0002\\u0002\\u0000\\u0001\\u0000\\u0001\\u001c\\u0002\\u0007\" +\n                \"\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.BroadcastTxsRequest> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.BroadcastTxsRequest.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.BroadcastTxsRequest>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.BroadcastTxsRequest)\n    private static final chronik.Chronik.BroadcastTxsRequest DEFAULT_INSTANCE;\n    static {\n      BroadcastTxsRequest defaultInstance = new BroadcastTxsRequest();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        BroadcastTxsRequest.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.BroadcastTxsRequest getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<BroadcastTxsRequest> PARSER;\n\n    public static com.google.protobuf.Parser<BroadcastTxsRequest> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface BroadcastTxsResponseOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.BroadcastTxsResponse)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>repeated bytes txids = 1;</code>\n     * @return A list containing the txids.\n     */\n    java.util.List<com.google.protobuf.ByteString> getTxidsList();\n    /**\n     * <code>repeated bytes txids = 1;</code>\n     * @return The count of txids.\n     */\n    int getTxidsCount();\n    /**\n     * <code>repeated bytes txids = 1;</code>\n     * @param index The index of the element to return.\n     * @return The txids at the given index.\n     */\n    com.google.protobuf.ByteString getTxids(int index);\n  }\n  /**\n   * Protobuf type {@code chronik.BroadcastTxsResponse}\n   */\n  public  static final class BroadcastTxsResponse extends\n      com.google.protobuf.GeneratedMessageLite<\n          BroadcastTxsResponse, BroadcastTxsResponse.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.BroadcastTxsResponse)\n      BroadcastTxsResponseOrBuilder {\n    private BroadcastTxsResponse() {\n      txids_ = emptyProtobufList();\n    }\n    public static final int TXIDS_FIELD_NUMBER = 1;\n    private com.google.protobuf.Internal.ProtobufList<com.google.protobuf.ByteString> txids_;\n    /**\n     * <code>repeated bytes txids = 1;</code>\n     * @return A list containing the txids.\n     */\n    @java.lang.Override\n    public java.util.List<com.google.protobuf.ByteString>\n        getTxidsList() {\n      return txids_;\n    }\n    /**\n     * <code>repeated bytes txids = 1;</code>\n     * @return The count of txids.\n     */\n    @java.lang.Override\n    public int getTxidsCount() {\n      return txids_.size();\n    }\n    /**\n     * <code>repeated bytes txids = 1;</code>\n     * @param index The index of the element to return.\n     * @return The txids at the given index.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getTxids(int index) {\n      return txids_.get(index);\n    }\n    private void ensureTxidsIsMutable() {\n      com.google.protobuf.Internal.ProtobufList<com.google.protobuf.ByteString> tmp = txids_;\n      if (!tmp.isModifiable()) {\n        txids_ =\n            com.google.protobuf.GeneratedMessageLite.mutableCopy(tmp);\n       }\n    }\n    /**\n     * <code>repeated bytes txids = 1;</code>\n     * @param index The index to set the value at.\n     * @param value The txids to set.\n     */\n    private void setTxids(\n        int index, com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  ensureTxidsIsMutable();\n      txids_.set(index, value);\n    }\n    /**\n     * <code>repeated bytes txids = 1;</code>\n     * @param value The txids to add.\n     */\n    private void addTxids(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  ensureTxidsIsMutable();\n      txids_.add(value);\n    }\n    /**\n     * <code>repeated bytes txids = 1;</code>\n     * @param values The txids to add.\n     */\n    private void addAllTxids(\n        java.lang.Iterable<? extends com.google.protobuf.ByteString> values) {\n      ensureTxidsIsMutable();\n      com.google.protobuf.AbstractMessageLite.addAll(\n          values, txids_);\n    }\n    /**\n     * <code>repeated bytes txids = 1;</code>\n     */\n    private void clearTxids() {\n      txids_ = emptyProtobufList();\n    }\n\n    public static chronik.Chronik.BroadcastTxsResponse parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.BroadcastTxsResponse parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.BroadcastTxsResponse parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.BroadcastTxsResponse parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.BroadcastTxsResponse parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.BroadcastTxsResponse parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.BroadcastTxsResponse parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.BroadcastTxsResponse parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.BroadcastTxsResponse parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.BroadcastTxsResponse parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.BroadcastTxsResponse parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.BroadcastTxsResponse parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.BroadcastTxsResponse prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.BroadcastTxsResponse}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.BroadcastTxsResponse, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.BroadcastTxsResponse)\n        chronik.Chronik.BroadcastTxsResponseOrBuilder {\n      // Construct using chronik.Chronik.BroadcastTxsResponse.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>repeated bytes txids = 1;</code>\n       * @return A list containing the txids.\n       */\n      @java.lang.Override\n      public java.util.List<com.google.protobuf.ByteString>\n          getTxidsList() {\n        return java.util.Collections.unmodifiableList(\n            instance.getTxidsList());\n      }\n      /**\n       * <code>repeated bytes txids = 1;</code>\n       * @return The count of txids.\n       */\n      @java.lang.Override\n      public int getTxidsCount() {\n        return instance.getTxidsCount();\n      }\n      /**\n       * <code>repeated bytes txids = 1;</code>\n       * @param index The index of the element to return.\n       * @return The txids at the given index.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getTxids(int index) {\n        return instance.getTxids(index);\n      }\n      /**\n       * <code>repeated bytes txids = 1;</code>\n       * @param value The txids to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTxids(\n          int index, com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setTxids(index, value);\n        return this;\n      }\n      /**\n       * <code>repeated bytes txids = 1;</code>\n       * @param value The txids to add.\n       * @return This builder for chaining.\n       */\n      public Builder addTxids(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.addTxids(value);\n        return this;\n      }\n      /**\n       * <code>repeated bytes txids = 1;</code>\n       * @param values The txids to add.\n       * @return This builder for chaining.\n       */\n      public Builder addAllTxids(\n          java.lang.Iterable<? extends com.google.protobuf.ByteString> values) {\n        copyOnWrite();\n        instance.addAllTxids(values);\n        return this;\n      }\n      /**\n       * <code>repeated bytes txids = 1;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearTxids() {\n        copyOnWrite();\n        instance.clearTxids();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.BroadcastTxsResponse)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.BroadcastTxsResponse();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"txids_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0001\\u0000\\u0000\\u0001\\u0001\\u0001\\u0000\\u0001\\u0000\\u0001\\u001c\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.BroadcastTxsResponse> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.BroadcastTxsResponse.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.BroadcastTxsResponse>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.BroadcastTxsResponse)\n    private static final chronik.Chronik.BroadcastTxsResponse DEFAULT_INSTANCE;\n    static {\n      BroadcastTxsResponse defaultInstance = new BroadcastTxsResponse();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        BroadcastTxsResponse.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.BroadcastTxsResponse getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<BroadcastTxsResponse> PARSER;\n\n    public static com.google.protobuf.Parser<BroadcastTxsResponse> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface BlockchainInfoOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.BlockchainInfo)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>bytes tip_hash = 1;</code>\n     * @return The tipHash.\n     */\n    com.google.protobuf.ByteString getTipHash();\n\n    /**\n     * <code>int32 tip_height = 2;</code>\n     * @return The tipHeight.\n     */\n    int getTipHeight();\n  }\n  /**\n   * Protobuf type {@code chronik.BlockchainInfo}\n   */\n  public  static final class BlockchainInfo extends\n      com.google.protobuf.GeneratedMessageLite<\n          BlockchainInfo, BlockchainInfo.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.BlockchainInfo)\n      BlockchainInfoOrBuilder {\n    private BlockchainInfo() {\n      tipHash_ = com.google.protobuf.ByteString.EMPTY;\n    }\n    public static final int TIP_HASH_FIELD_NUMBER = 1;\n    private com.google.protobuf.ByteString tipHash_;\n    /**\n     * <code>bytes tip_hash = 1;</code>\n     * @return The tipHash.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getTipHash() {\n      return tipHash_;\n    }\n    /**\n     * <code>bytes tip_hash = 1;</code>\n     * @param value The tipHash to set.\n     */\n    private void setTipHash(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      tipHash_ = value;\n    }\n    /**\n     * <code>bytes tip_hash = 1;</code>\n     */\n    private void clearTipHash() {\n\n      tipHash_ = getDefaultInstance().getTipHash();\n    }\n\n    public static final int TIP_HEIGHT_FIELD_NUMBER = 2;\n    private int tipHeight_;\n    /**\n     * <code>int32 tip_height = 2;</code>\n     * @return The tipHeight.\n     */\n    @java.lang.Override\n    public int getTipHeight() {\n      return tipHeight_;\n    }\n    /**\n     * <code>int32 tip_height = 2;</code>\n     * @param value The tipHeight to set.\n     */\n    private void setTipHeight(int value) {\n      \n      tipHeight_ = value;\n    }\n    /**\n     * <code>int32 tip_height = 2;</code>\n     */\n    private void clearTipHeight() {\n\n      tipHeight_ = 0;\n    }\n\n    public static chronik.Chronik.BlockchainInfo parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.BlockchainInfo parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.BlockchainInfo parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.BlockchainInfo parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.BlockchainInfo parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.BlockchainInfo parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.BlockchainInfo parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.BlockchainInfo parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.BlockchainInfo parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.BlockchainInfo parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.BlockchainInfo parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.BlockchainInfo parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.BlockchainInfo prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.BlockchainInfo}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.BlockchainInfo, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.BlockchainInfo)\n        chronik.Chronik.BlockchainInfoOrBuilder {\n      // Construct using chronik.Chronik.BlockchainInfo.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>bytes tip_hash = 1;</code>\n       * @return The tipHash.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getTipHash() {\n        return instance.getTipHash();\n      }\n      /**\n       * <code>bytes tip_hash = 1;</code>\n       * @param value The tipHash to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTipHash(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setTipHash(value);\n        return this;\n      }\n      /**\n       * <code>bytes tip_hash = 1;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearTipHash() {\n        copyOnWrite();\n        instance.clearTipHash();\n        return this;\n      }\n\n      /**\n       * <code>int32 tip_height = 2;</code>\n       * @return The tipHeight.\n       */\n      @java.lang.Override\n      public int getTipHeight() {\n        return instance.getTipHeight();\n      }\n      /**\n       * <code>int32 tip_height = 2;</code>\n       * @param value The tipHeight to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTipHeight(int value) {\n        copyOnWrite();\n        instance.setTipHeight(value);\n        return this;\n      }\n      /**\n       * <code>int32 tip_height = 2;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearTipHeight() {\n        copyOnWrite();\n        instance.clearTipHeight();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.BlockchainInfo)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.BlockchainInfo();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"tipHash_\",\n              \"tipHeight_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0002\\u0000\\u0000\\u0001\\u0002\\u0002\\u0000\\u0000\\u0000\\u0001\\n\\u0002\\u0004\" +\n                \"\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.BlockchainInfo> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.BlockchainInfo.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.BlockchainInfo>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.BlockchainInfo)\n    private static final chronik.Chronik.BlockchainInfo DEFAULT_INSTANCE;\n    static {\n      BlockchainInfo defaultInstance = new BlockchainInfo();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        BlockchainInfo.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.BlockchainInfo getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<BlockchainInfo> PARSER;\n\n    public static com.google.protobuf.Parser<BlockchainInfo> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface TxOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.Tx)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>bytes txid = 1;</code>\n     * @return The txid.\n     */\n    com.google.protobuf.ByteString getTxid();\n\n    /**\n     * <code>int32 version = 2;</code>\n     * @return The version.\n     */\n    int getVersion();\n\n    /**\n     * <code>repeated .chronik.TxInput inputs = 3;</code>\n     */\n    java.util.List<chronik.Chronik.TxInput> \n        getInputsList();\n    /**\n     * <code>repeated .chronik.TxInput inputs = 3;</code>\n     */\n    chronik.Chronik.TxInput getInputs(int index);\n    /**\n     * <code>repeated .chronik.TxInput inputs = 3;</code>\n     */\n    int getInputsCount();\n\n    /**\n     * <code>repeated .chronik.TxOutput outputs = 4;</code>\n     */\n    java.util.List<chronik.Chronik.TxOutput> \n        getOutputsList();\n    /**\n     * <code>repeated .chronik.TxOutput outputs = 4;</code>\n     */\n    chronik.Chronik.TxOutput getOutputs(int index);\n    /**\n     * <code>repeated .chronik.TxOutput outputs = 4;</code>\n     */\n    int getOutputsCount();\n\n    /**\n     * <code>uint32 lock_time = 5;</code>\n     * @return The lockTime.\n     */\n    int getLockTime();\n\n    /**\n     * <code>.chronik.SlpTxData slp_tx_data = 6;</code>\n     * @return Whether the slpTxData field is set.\n     */\n    boolean hasSlpTxData();\n    /**\n     * <code>.chronik.SlpTxData slp_tx_data = 6;</code>\n     * @return The slpTxData.\n     */\n    chronik.Chronik.SlpTxData getSlpTxData();\n\n    /**\n     * <code>string slp_error_msg = 7;</code>\n     * @return The slpErrorMsg.\n     */\n    java.lang.String getSlpErrorMsg();\n    /**\n     * <code>string slp_error_msg = 7;</code>\n     * @return The bytes for slpErrorMsg.\n     */\n    com.google.protobuf.ByteString\n        getSlpErrorMsgBytes();\n\n    /**\n     * <code>.chronik.BlockMetadata block = 8;</code>\n     * @return Whether the block field is set.\n     */\n    boolean hasBlock();\n    /**\n     * <code>.chronik.BlockMetadata block = 8;</code>\n     * @return The block.\n     */\n    chronik.Chronik.BlockMetadata getBlock();\n\n    /**\n     * <code>int64 time_first_seen = 9;</code>\n     * @return The timeFirstSeen.\n     */\n    long getTimeFirstSeen();\n\n    /**\n     * <code>uint32 size = 11;</code>\n     * @return The size.\n     */\n    int getSize();\n\n    /**\n     * <code>bool is_coinbase = 12;</code>\n     * @return The isCoinbase.\n     */\n    boolean getIsCoinbase();\n\n    /**\n     * <code>.chronik.Network network = 10;</code>\n     * @return The enum numeric value on the wire for network.\n     */\n    int getNetworkValue();\n    /**\n     * <code>.chronik.Network network = 10;</code>\n     * @return The network.\n     */\n    chronik.Chronik.Network getNetwork();\n  }\n  /**\n   * Protobuf type {@code chronik.Tx}\n   */\n  public  static final class Tx extends\n      com.google.protobuf.GeneratedMessageLite<\n          Tx, Tx.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.Tx)\n      TxOrBuilder {\n    private Tx() {\n      txid_ = com.google.protobuf.ByteString.EMPTY;\n      inputs_ = emptyProtobufList();\n      outputs_ = emptyProtobufList();\n      slpErrorMsg_ = \"\";\n    }\n    public static final int TXID_FIELD_NUMBER = 1;\n    private com.google.protobuf.ByteString txid_;\n    /**\n     * <code>bytes txid = 1;</code>\n     * @return The txid.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getTxid() {\n      return txid_;\n    }\n    /**\n     * <code>bytes txid = 1;</code>\n     * @param value The txid to set.\n     */\n    private void setTxid(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      txid_ = value;\n    }\n    /**\n     * <code>bytes txid = 1;</code>\n     */\n    private void clearTxid() {\n\n      txid_ = getDefaultInstance().getTxid();\n    }\n\n    public static final int VERSION_FIELD_NUMBER = 2;\n    private int version_;\n    /**\n     * <code>int32 version = 2;</code>\n     * @return The version.\n     */\n    @java.lang.Override\n    public int getVersion() {\n      return version_;\n    }\n    /**\n     * <code>int32 version = 2;</code>\n     * @param value The version to set.\n     */\n    private void setVersion(int value) {\n      \n      version_ = value;\n    }\n    /**\n     * <code>int32 version = 2;</code>\n     */\n    private void clearVersion() {\n\n      version_ = 0;\n    }\n\n    public static final int INPUTS_FIELD_NUMBER = 3;\n    private com.google.protobuf.Internal.ProtobufList<chronik.Chronik.TxInput> inputs_;\n    /**\n     * <code>repeated .chronik.TxInput inputs = 3;</code>\n     */\n    @java.lang.Override\n    public java.util.List<chronik.Chronik.TxInput> getInputsList() {\n      return inputs_;\n    }\n    /**\n     * <code>repeated .chronik.TxInput inputs = 3;</code>\n     */\n    public java.util.List<? extends chronik.Chronik.TxInputOrBuilder> \n        getInputsOrBuilderList() {\n      return inputs_;\n    }\n    /**\n     * <code>repeated .chronik.TxInput inputs = 3;</code>\n     */\n    @java.lang.Override\n    public int getInputsCount() {\n      return inputs_.size();\n    }\n    /**\n     * <code>repeated .chronik.TxInput inputs = 3;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.TxInput getInputs(int index) {\n      return inputs_.get(index);\n    }\n    /**\n     * <code>repeated .chronik.TxInput inputs = 3;</code>\n     */\n    public chronik.Chronik.TxInputOrBuilder getInputsOrBuilder(\n        int index) {\n      return inputs_.get(index);\n    }\n    private void ensureInputsIsMutable() {\n      com.google.protobuf.Internal.ProtobufList<chronik.Chronik.TxInput> tmp = inputs_;\n      if (!tmp.isModifiable()) {\n        inputs_ =\n            com.google.protobuf.GeneratedMessageLite.mutableCopy(tmp);\n       }\n    }\n\n    /**\n     * <code>repeated .chronik.TxInput inputs = 3;</code>\n     */\n    private void setInputs(\n        int index, chronik.Chronik.TxInput value) {\n      value.getClass();\n  ensureInputsIsMutable();\n      inputs_.set(index, value);\n    }\n    /**\n     * <code>repeated .chronik.TxInput inputs = 3;</code>\n     */\n    private void addInputs(chronik.Chronik.TxInput value) {\n      value.getClass();\n  ensureInputsIsMutable();\n      inputs_.add(value);\n    }\n    /**\n     * <code>repeated .chronik.TxInput inputs = 3;</code>\n     */\n    private void addInputs(\n        int index, chronik.Chronik.TxInput value) {\n      value.getClass();\n  ensureInputsIsMutable();\n      inputs_.add(index, value);\n    }\n    /**\n     * <code>repeated .chronik.TxInput inputs = 3;</code>\n     */\n    private void addAllInputs(\n        java.lang.Iterable<? extends chronik.Chronik.TxInput> values) {\n      ensureInputsIsMutable();\n      com.google.protobuf.AbstractMessageLite.addAll(\n          values, inputs_);\n    }\n    /**\n     * <code>repeated .chronik.TxInput inputs = 3;</code>\n     */\n    private void clearInputs() {\n      inputs_ = emptyProtobufList();\n    }\n    /**\n     * <code>repeated .chronik.TxInput inputs = 3;</code>\n     */\n    private void removeInputs(int index) {\n      ensureInputsIsMutable();\n      inputs_.remove(index);\n    }\n\n    public static final int OUTPUTS_FIELD_NUMBER = 4;\n    private com.google.protobuf.Internal.ProtobufList<chronik.Chronik.TxOutput> outputs_;\n    /**\n     * <code>repeated .chronik.TxOutput outputs = 4;</code>\n     */\n    @java.lang.Override\n    public java.util.List<chronik.Chronik.TxOutput> getOutputsList() {\n      return outputs_;\n    }\n    /**\n     * <code>repeated .chronik.TxOutput outputs = 4;</code>\n     */\n    public java.util.List<? extends chronik.Chronik.TxOutputOrBuilder> \n        getOutputsOrBuilderList() {\n      return outputs_;\n    }\n    /**\n     * <code>repeated .chronik.TxOutput outputs = 4;</code>\n     */\n    @java.lang.Override\n    public int getOutputsCount() {\n      return outputs_.size();\n    }\n    /**\n     * <code>repeated .chronik.TxOutput outputs = 4;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.TxOutput getOutputs(int index) {\n      return outputs_.get(index);\n    }\n    /**\n     * <code>repeated .chronik.TxOutput outputs = 4;</code>\n     */\n    public chronik.Chronik.TxOutputOrBuilder getOutputsOrBuilder(\n        int index) {\n      return outputs_.get(index);\n    }\n    private void ensureOutputsIsMutable() {\n      com.google.protobuf.Internal.ProtobufList<chronik.Chronik.TxOutput> tmp = outputs_;\n      if (!tmp.isModifiable()) {\n        outputs_ =\n            com.google.protobuf.GeneratedMessageLite.mutableCopy(tmp);\n       }\n    }\n\n    /**\n     * <code>repeated .chronik.TxOutput outputs = 4;</code>\n     */\n    private void setOutputs(\n        int index, chronik.Chronik.TxOutput value) {\n      value.getClass();\n  ensureOutputsIsMutable();\n      outputs_.set(index, value);\n    }\n    /**\n     * <code>repeated .chronik.TxOutput outputs = 4;</code>\n     */\n    private void addOutputs(chronik.Chronik.TxOutput value) {\n      value.getClass();\n  ensureOutputsIsMutable();\n      outputs_.add(value);\n    }\n    /**\n     * <code>repeated .chronik.TxOutput outputs = 4;</code>\n     */\n    private void addOutputs(\n        int index, chronik.Chronik.TxOutput value) {\n      value.getClass();\n  ensureOutputsIsMutable();\n      outputs_.add(index, value);\n    }\n    /**\n     * <code>repeated .chronik.TxOutput outputs = 4;</code>\n     */\n    private void addAllOutputs(\n        java.lang.Iterable<? extends chronik.Chronik.TxOutput> values) {\n      ensureOutputsIsMutable();\n      com.google.protobuf.AbstractMessageLite.addAll(\n          values, outputs_);\n    }\n    /**\n     * <code>repeated .chronik.TxOutput outputs = 4;</code>\n     */\n    private void clearOutputs() {\n      outputs_ = emptyProtobufList();\n    }\n    /**\n     * <code>repeated .chronik.TxOutput outputs = 4;</code>\n     */\n    private void removeOutputs(int index) {\n      ensureOutputsIsMutable();\n      outputs_.remove(index);\n    }\n\n    public static final int LOCK_TIME_FIELD_NUMBER = 5;\n    private int lockTime_;\n    /**\n     * <code>uint32 lock_time = 5;</code>\n     * @return The lockTime.\n     */\n    @java.lang.Override\n    public int getLockTime() {\n      return lockTime_;\n    }\n    /**\n     * <code>uint32 lock_time = 5;</code>\n     * @param value The lockTime to set.\n     */\n    private void setLockTime(int value) {\n      \n      lockTime_ = value;\n    }\n    /**\n     * <code>uint32 lock_time = 5;</code>\n     */\n    private void clearLockTime() {\n\n      lockTime_ = 0;\n    }\n\n    public static final int SLP_TX_DATA_FIELD_NUMBER = 6;\n    private chronik.Chronik.SlpTxData slpTxData_;\n    /**\n     * <code>.chronik.SlpTxData slp_tx_data = 6;</code>\n     */\n    @java.lang.Override\n    public boolean hasSlpTxData() {\n      return slpTxData_ != null;\n    }\n    /**\n     * <code>.chronik.SlpTxData slp_tx_data = 6;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.SlpTxData getSlpTxData() {\n      return slpTxData_ == null ? chronik.Chronik.SlpTxData.getDefaultInstance() : slpTxData_;\n    }\n    /**\n     * <code>.chronik.SlpTxData slp_tx_data = 6;</code>\n     */\n    private void setSlpTxData(chronik.Chronik.SlpTxData value) {\n      value.getClass();\n  slpTxData_ = value;\n\n      }\n    /**\n     * <code>.chronik.SlpTxData slp_tx_data = 6;</code>\n     */\n    @java.lang.SuppressWarnings({\"ReferenceEquality\"})\n    private void mergeSlpTxData(chronik.Chronik.SlpTxData value) {\n      value.getClass();\n  if (slpTxData_ != null &&\n          slpTxData_ != chronik.Chronik.SlpTxData.getDefaultInstance()) {\n        slpTxData_ =\n          chronik.Chronik.SlpTxData.newBuilder(slpTxData_).mergeFrom(value).buildPartial();\n      } else {\n        slpTxData_ = value;\n      }\n\n    }\n    /**\n     * <code>.chronik.SlpTxData slp_tx_data = 6;</code>\n     */\n    private void clearSlpTxData() {  slpTxData_ = null;\n\n    }\n\n    public static final int SLP_ERROR_MSG_FIELD_NUMBER = 7;\n    private java.lang.String slpErrorMsg_;\n    /**\n     * <code>string slp_error_msg = 7;</code>\n     * @return The slpErrorMsg.\n     */\n    @java.lang.Override\n    public java.lang.String getSlpErrorMsg() {\n      return slpErrorMsg_;\n    }\n    /**\n     * <code>string slp_error_msg = 7;</code>\n     * @return The bytes for slpErrorMsg.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString\n        getSlpErrorMsgBytes() {\n      return com.google.protobuf.ByteString.copyFromUtf8(slpErrorMsg_);\n    }\n    /**\n     * <code>string slp_error_msg = 7;</code>\n     * @param value The slpErrorMsg to set.\n     */\n    private void setSlpErrorMsg(\n        java.lang.String value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      slpErrorMsg_ = value;\n    }\n    /**\n     * <code>string slp_error_msg = 7;</code>\n     */\n    private void clearSlpErrorMsg() {\n\n      slpErrorMsg_ = getDefaultInstance().getSlpErrorMsg();\n    }\n    /**\n     * <code>string slp_error_msg = 7;</code>\n     * @param value The bytes for slpErrorMsg to set.\n     */\n    private void setSlpErrorMsgBytes(\n        com.google.protobuf.ByteString value) {\n      checkByteStringIsUtf8(value);\n      slpErrorMsg_ = value.toStringUtf8();\n\n    }\n\n    public static final int BLOCK_FIELD_NUMBER = 8;\n    private chronik.Chronik.BlockMetadata block_;\n    /**\n     * <code>.chronik.BlockMetadata block = 8;</code>\n     */\n    @java.lang.Override\n    public boolean hasBlock() {\n      return block_ != null;\n    }\n    /**\n     * <code>.chronik.BlockMetadata block = 8;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.BlockMetadata getBlock() {\n      return block_ == null ? chronik.Chronik.BlockMetadata.getDefaultInstance() : block_;\n    }\n    /**\n     * <code>.chronik.BlockMetadata block = 8;</code>\n     */\n    private void setBlock(chronik.Chronik.BlockMetadata value) {\n      value.getClass();\n  block_ = value;\n\n      }\n    /**\n     * <code>.chronik.BlockMetadata block = 8;</code>\n     */\n    @java.lang.SuppressWarnings({\"ReferenceEquality\"})\n    private void mergeBlock(chronik.Chronik.BlockMetadata value) {\n      value.getClass();\n  if (block_ != null &&\n          block_ != chronik.Chronik.BlockMetadata.getDefaultInstance()) {\n        block_ =\n          chronik.Chronik.BlockMetadata.newBuilder(block_).mergeFrom(value).buildPartial();\n      } else {\n        block_ = value;\n      }\n\n    }\n    /**\n     * <code>.chronik.BlockMetadata block = 8;</code>\n     */\n    private void clearBlock() {  block_ = null;\n\n    }\n\n    public static final int TIME_FIRST_SEEN_FIELD_NUMBER = 9;\n    private long timeFirstSeen_;\n    /**\n     * <code>int64 time_first_seen = 9;</code>\n     * @return The timeFirstSeen.\n     */\n    @java.lang.Override\n    public long getTimeFirstSeen() {\n      return timeFirstSeen_;\n    }\n    /**\n     * <code>int64 time_first_seen = 9;</code>\n     * @param value The timeFirstSeen to set.\n     */\n    private void setTimeFirstSeen(long value) {\n      \n      timeFirstSeen_ = value;\n    }\n    /**\n     * <code>int64 time_first_seen = 9;</code>\n     */\n    private void clearTimeFirstSeen() {\n\n      timeFirstSeen_ = 0L;\n    }\n\n    public static final int SIZE_FIELD_NUMBER = 11;\n    private int size_;\n    /**\n     * <code>uint32 size = 11;</code>\n     * @return The size.\n     */\n    @java.lang.Override\n    public int getSize() {\n      return size_;\n    }\n    /**\n     * <code>uint32 size = 11;</code>\n     * @param value The size to set.\n     */\n    private void setSize(int value) {\n      \n      size_ = value;\n    }\n    /**\n     * <code>uint32 size = 11;</code>\n     */\n    private void clearSize() {\n\n      size_ = 0;\n    }\n\n    public static final int IS_COINBASE_FIELD_NUMBER = 12;\n    private boolean isCoinbase_;\n    /**\n     * <code>bool is_coinbase = 12;</code>\n     * @return The isCoinbase.\n     */\n    @java.lang.Override\n    public boolean getIsCoinbase() {\n      return isCoinbase_;\n    }\n    /**\n     * <code>bool is_coinbase = 12;</code>\n     * @param value The isCoinbase to set.\n     */\n    private void setIsCoinbase(boolean value) {\n      \n      isCoinbase_ = value;\n    }\n    /**\n     * <code>bool is_coinbase = 12;</code>\n     */\n    private void clearIsCoinbase() {\n\n      isCoinbase_ = false;\n    }\n\n    public static final int NETWORK_FIELD_NUMBER = 10;\n    private int network_;\n    /**\n     * <code>.chronik.Network network = 10;</code>\n     * @return The enum numeric value on the wire for network.\n     */\n    @java.lang.Override\n    public int getNetworkValue() {\n      return network_;\n    }\n    /**\n     * <code>.chronik.Network network = 10;</code>\n     * @return The network.\n     */\n    @java.lang.Override\n    public chronik.Chronik.Network getNetwork() {\n      chronik.Chronik.Network result = chronik.Chronik.Network.forNumber(network_);\n      return result == null ? chronik.Chronik.Network.UNRECOGNIZED : result;\n    }\n    /**\n     * <code>.chronik.Network network = 10;</code>\n     * @param value The enum numeric value on the wire for network to set.\n     */\n    private void setNetworkValue(int value) {\n        network_ = value;\n    }\n    /**\n     * <code>.chronik.Network network = 10;</code>\n     * @param value The network to set.\n     */\n    private void setNetwork(chronik.Chronik.Network value) {\n      network_ = value.getNumber();\n\n    }\n    /**\n     * <code>.chronik.Network network = 10;</code>\n     */\n    private void clearNetwork() {\n\n      network_ = 0;\n    }\n\n    public static chronik.Chronik.Tx parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.Tx parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.Tx parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.Tx parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.Tx parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.Tx parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.Tx parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.Tx parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.Tx parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.Tx parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.Tx parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.Tx parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.Tx prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.Tx}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.Tx, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.Tx)\n        chronik.Chronik.TxOrBuilder {\n      // Construct using chronik.Chronik.Tx.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>bytes txid = 1;</code>\n       * @return The txid.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getTxid() {\n        return instance.getTxid();\n      }\n      /**\n       * <code>bytes txid = 1;</code>\n       * @param value The txid to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTxid(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setTxid(value);\n        return this;\n      }\n      /**\n       * <code>bytes txid = 1;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearTxid() {\n        copyOnWrite();\n        instance.clearTxid();\n        return this;\n      }\n\n      /**\n       * <code>int32 version = 2;</code>\n       * @return The version.\n       */\n      @java.lang.Override\n      public int getVersion() {\n        return instance.getVersion();\n      }\n      /**\n       * <code>int32 version = 2;</code>\n       * @param value The version to set.\n       * @return This builder for chaining.\n       */\n      public Builder setVersion(int value) {\n        copyOnWrite();\n        instance.setVersion(value);\n        return this;\n      }\n      /**\n       * <code>int32 version = 2;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearVersion() {\n        copyOnWrite();\n        instance.clearVersion();\n        return this;\n      }\n\n      /**\n       * <code>repeated .chronik.TxInput inputs = 3;</code>\n       */\n      @java.lang.Override\n      public java.util.List<chronik.Chronik.TxInput> getInputsList() {\n        return java.util.Collections.unmodifiableList(\n            instance.getInputsList());\n      }\n      /**\n       * <code>repeated .chronik.TxInput inputs = 3;</code>\n       */\n      @java.lang.Override\n      public int getInputsCount() {\n        return instance.getInputsCount();\n      }/**\n       * <code>repeated .chronik.TxInput inputs = 3;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.TxInput getInputs(int index) {\n        return instance.getInputs(index);\n      }\n      /**\n       * <code>repeated .chronik.TxInput inputs = 3;</code>\n       */\n      public Builder setInputs(\n          int index, chronik.Chronik.TxInput value) {\n        copyOnWrite();\n        instance.setInputs(index, value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.TxInput inputs = 3;</code>\n       */\n      public Builder setInputs(\n          int index, chronik.Chronik.TxInput.Builder builderForValue) {\n        copyOnWrite();\n        instance.setInputs(index,\n            builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.TxInput inputs = 3;</code>\n       */\n      public Builder addInputs(chronik.Chronik.TxInput value) {\n        copyOnWrite();\n        instance.addInputs(value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.TxInput inputs = 3;</code>\n       */\n      public Builder addInputs(\n          int index, chronik.Chronik.TxInput value) {\n        copyOnWrite();\n        instance.addInputs(index, value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.TxInput inputs = 3;</code>\n       */\n      public Builder addInputs(\n          chronik.Chronik.TxInput.Builder builderForValue) {\n        copyOnWrite();\n        instance.addInputs(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.TxInput inputs = 3;</code>\n       */\n      public Builder addInputs(\n          int index, chronik.Chronik.TxInput.Builder builderForValue) {\n        copyOnWrite();\n        instance.addInputs(index,\n            builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.TxInput inputs = 3;</code>\n       */\n      public Builder addAllInputs(\n          java.lang.Iterable<? extends chronik.Chronik.TxInput> values) {\n        copyOnWrite();\n        instance.addAllInputs(values);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.TxInput inputs = 3;</code>\n       */\n      public Builder clearInputs() {\n        copyOnWrite();\n        instance.clearInputs();\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.TxInput inputs = 3;</code>\n       */\n      public Builder removeInputs(int index) {\n        copyOnWrite();\n        instance.removeInputs(index);\n        return this;\n      }\n\n      /**\n       * <code>repeated .chronik.TxOutput outputs = 4;</code>\n       */\n      @java.lang.Override\n      public java.util.List<chronik.Chronik.TxOutput> getOutputsList() {\n        return java.util.Collections.unmodifiableList(\n            instance.getOutputsList());\n      }\n      /**\n       * <code>repeated .chronik.TxOutput outputs = 4;</code>\n       */\n      @java.lang.Override\n      public int getOutputsCount() {\n        return instance.getOutputsCount();\n      }/**\n       * <code>repeated .chronik.TxOutput outputs = 4;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.TxOutput getOutputs(int index) {\n        return instance.getOutputs(index);\n      }\n      /**\n       * <code>repeated .chronik.TxOutput outputs = 4;</code>\n       */\n      public Builder setOutputs(\n          int index, chronik.Chronik.TxOutput value) {\n        copyOnWrite();\n        instance.setOutputs(index, value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.TxOutput outputs = 4;</code>\n       */\n      public Builder setOutputs(\n          int index, chronik.Chronik.TxOutput.Builder builderForValue) {\n        copyOnWrite();\n        instance.setOutputs(index,\n            builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.TxOutput outputs = 4;</code>\n       */\n      public Builder addOutputs(chronik.Chronik.TxOutput value) {\n        copyOnWrite();\n        instance.addOutputs(value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.TxOutput outputs = 4;</code>\n       */\n      public Builder addOutputs(\n          int index, chronik.Chronik.TxOutput value) {\n        copyOnWrite();\n        instance.addOutputs(index, value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.TxOutput outputs = 4;</code>\n       */\n      public Builder addOutputs(\n          chronik.Chronik.TxOutput.Builder builderForValue) {\n        copyOnWrite();\n        instance.addOutputs(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.TxOutput outputs = 4;</code>\n       */\n      public Builder addOutputs(\n          int index, chronik.Chronik.TxOutput.Builder builderForValue) {\n        copyOnWrite();\n        instance.addOutputs(index,\n            builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.TxOutput outputs = 4;</code>\n       */\n      public Builder addAllOutputs(\n          java.lang.Iterable<? extends chronik.Chronik.TxOutput> values) {\n        copyOnWrite();\n        instance.addAllOutputs(values);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.TxOutput outputs = 4;</code>\n       */\n      public Builder clearOutputs() {\n        copyOnWrite();\n        instance.clearOutputs();\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.TxOutput outputs = 4;</code>\n       */\n      public Builder removeOutputs(int index) {\n        copyOnWrite();\n        instance.removeOutputs(index);\n        return this;\n      }\n\n      /**\n       * <code>uint32 lock_time = 5;</code>\n       * @return The lockTime.\n       */\n      @java.lang.Override\n      public int getLockTime() {\n        return instance.getLockTime();\n      }\n      /**\n       * <code>uint32 lock_time = 5;</code>\n       * @param value The lockTime to set.\n       * @return This builder for chaining.\n       */\n      public Builder setLockTime(int value) {\n        copyOnWrite();\n        instance.setLockTime(value);\n        return this;\n      }\n      /**\n       * <code>uint32 lock_time = 5;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearLockTime() {\n        copyOnWrite();\n        instance.clearLockTime();\n        return this;\n      }\n\n      /**\n       * <code>.chronik.SlpTxData slp_tx_data = 6;</code>\n       */\n      @java.lang.Override\n      public boolean hasSlpTxData() {\n        return instance.hasSlpTxData();\n      }\n      /**\n       * <code>.chronik.SlpTxData slp_tx_data = 6;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.SlpTxData getSlpTxData() {\n        return instance.getSlpTxData();\n      }\n      /**\n       * <code>.chronik.SlpTxData slp_tx_data = 6;</code>\n       */\n      public Builder setSlpTxData(chronik.Chronik.SlpTxData value) {\n        copyOnWrite();\n        instance.setSlpTxData(value);\n        return this;\n        }\n      /**\n       * <code>.chronik.SlpTxData slp_tx_data = 6;</code>\n       */\n      public Builder setSlpTxData(\n          chronik.Chronik.SlpTxData.Builder builderForValue) {\n        copyOnWrite();\n        instance.setSlpTxData(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>.chronik.SlpTxData slp_tx_data = 6;</code>\n       */\n      public Builder mergeSlpTxData(chronik.Chronik.SlpTxData value) {\n        copyOnWrite();\n        instance.mergeSlpTxData(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.SlpTxData slp_tx_data = 6;</code>\n       */\n      public Builder clearSlpTxData() {  copyOnWrite();\n        instance.clearSlpTxData();\n        return this;\n      }\n\n      /**\n       * <code>string slp_error_msg = 7;</code>\n       * @return The slpErrorMsg.\n       */\n      @java.lang.Override\n      public java.lang.String getSlpErrorMsg() {\n        return instance.getSlpErrorMsg();\n      }\n      /**\n       * <code>string slp_error_msg = 7;</code>\n       * @return The bytes for slpErrorMsg.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString\n          getSlpErrorMsgBytes() {\n        return instance.getSlpErrorMsgBytes();\n      }\n      /**\n       * <code>string slp_error_msg = 7;</code>\n       * @param value The slpErrorMsg to set.\n       * @return This builder for chaining.\n       */\n      public Builder setSlpErrorMsg(\n          java.lang.String value) {\n        copyOnWrite();\n        instance.setSlpErrorMsg(value);\n        return this;\n      }\n      /**\n       * <code>string slp_error_msg = 7;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearSlpErrorMsg() {\n        copyOnWrite();\n        instance.clearSlpErrorMsg();\n        return this;\n      }\n      /**\n       * <code>string slp_error_msg = 7;</code>\n       * @param value The bytes for slpErrorMsg to set.\n       * @return This builder for chaining.\n       */\n      public Builder setSlpErrorMsgBytes(\n          com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setSlpErrorMsgBytes(value);\n        return this;\n      }\n\n      /**\n       * <code>.chronik.BlockMetadata block = 8;</code>\n       */\n      @java.lang.Override\n      public boolean hasBlock() {\n        return instance.hasBlock();\n      }\n      /**\n       * <code>.chronik.BlockMetadata block = 8;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.BlockMetadata getBlock() {\n        return instance.getBlock();\n      }\n      /**\n       * <code>.chronik.BlockMetadata block = 8;</code>\n       */\n      public Builder setBlock(chronik.Chronik.BlockMetadata value) {\n        copyOnWrite();\n        instance.setBlock(value);\n        return this;\n        }\n      /**\n       * <code>.chronik.BlockMetadata block = 8;</code>\n       */\n      public Builder setBlock(\n          chronik.Chronik.BlockMetadata.Builder builderForValue) {\n        copyOnWrite();\n        instance.setBlock(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>.chronik.BlockMetadata block = 8;</code>\n       */\n      public Builder mergeBlock(chronik.Chronik.BlockMetadata value) {\n        copyOnWrite();\n        instance.mergeBlock(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.BlockMetadata block = 8;</code>\n       */\n      public Builder clearBlock() {  copyOnWrite();\n        instance.clearBlock();\n        return this;\n      }\n\n      /**\n       * <code>int64 time_first_seen = 9;</code>\n       * @return The timeFirstSeen.\n       */\n      @java.lang.Override\n      public long getTimeFirstSeen() {\n        return instance.getTimeFirstSeen();\n      }\n      /**\n       * <code>int64 time_first_seen = 9;</code>\n       * @param value The timeFirstSeen to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTimeFirstSeen(long value) {\n        copyOnWrite();\n        instance.setTimeFirstSeen(value);\n        return this;\n      }\n      /**\n       * <code>int64 time_first_seen = 9;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearTimeFirstSeen() {\n        copyOnWrite();\n        instance.clearTimeFirstSeen();\n        return this;\n      }\n\n      /**\n       * <code>uint32 size = 11;</code>\n       * @return The size.\n       */\n      @java.lang.Override\n      public int getSize() {\n        return instance.getSize();\n      }\n      /**\n       * <code>uint32 size = 11;</code>\n       * @param value The size to set.\n       * @return This builder for chaining.\n       */\n      public Builder setSize(int value) {\n        copyOnWrite();\n        instance.setSize(value);\n        return this;\n      }\n      /**\n       * <code>uint32 size = 11;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearSize() {\n        copyOnWrite();\n        instance.clearSize();\n        return this;\n      }\n\n      /**\n       * <code>bool is_coinbase = 12;</code>\n       * @return The isCoinbase.\n       */\n      @java.lang.Override\n      public boolean getIsCoinbase() {\n        return instance.getIsCoinbase();\n      }\n      /**\n       * <code>bool is_coinbase = 12;</code>\n       * @param value The isCoinbase to set.\n       * @return This builder for chaining.\n       */\n      public Builder setIsCoinbase(boolean value) {\n        copyOnWrite();\n        instance.setIsCoinbase(value);\n        return this;\n      }\n      /**\n       * <code>bool is_coinbase = 12;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearIsCoinbase() {\n        copyOnWrite();\n        instance.clearIsCoinbase();\n        return this;\n      }\n\n      /**\n       * <code>.chronik.Network network = 10;</code>\n       * @return The enum numeric value on the wire for network.\n       */\n      @java.lang.Override\n      public int getNetworkValue() {\n        return instance.getNetworkValue();\n      }\n      /**\n       * <code>.chronik.Network network = 10;</code>\n       * @param value The network to set.\n       * @return This builder for chaining.\n       */\n      public Builder setNetworkValue(int value) {\n        copyOnWrite();\n        instance.setNetworkValue(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.Network network = 10;</code>\n       * @return The network.\n       */\n      @java.lang.Override\n      public chronik.Chronik.Network getNetwork() {\n        return instance.getNetwork();\n      }\n      /**\n       * <code>.chronik.Network network = 10;</code>\n       * @param value The enum numeric value on the wire for network to set.\n       * @return This builder for chaining.\n       */\n      public Builder setNetwork(chronik.Chronik.Network value) {\n        copyOnWrite();\n        instance.setNetwork(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.Network network = 10;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearNetwork() {\n        copyOnWrite();\n        instance.clearNetwork();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.Tx)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.Tx();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"txid_\",\n              \"version_\",\n              \"inputs_\",\n              chronik.Chronik.TxInput.class,\n              \"outputs_\",\n              chronik.Chronik.TxOutput.class,\n              \"lockTime_\",\n              \"slpTxData_\",\n              \"slpErrorMsg_\",\n              \"block_\",\n              \"timeFirstSeen_\",\n              \"network_\",\n              \"size_\",\n              \"isCoinbase_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\f\\u0000\\u0000\\u0001\\f\\f\\u0000\\u0002\\u0000\\u0001\\n\\u0002\\u0004\\u0003\\u001b\" +\n                \"\\u0004\\u001b\\u0005\\u000b\\u0006\\t\\u0007\\u0208\\b\\t\\t\\u0002\\n\\f\\u000b\\u000b\\f\\u0007\" +\n                \"\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.Tx> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.Tx.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.Tx>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.Tx)\n    private static final chronik.Chronik.Tx DEFAULT_INSTANCE;\n    static {\n      Tx defaultInstance = new Tx();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        Tx.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.Tx getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<Tx> PARSER;\n\n    public static com.google.protobuf.Parser<Tx> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface UtxoOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.Utxo)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>.chronik.OutPoint outpoint = 1;</code>\n     * @return Whether the outpoint field is set.\n     */\n    boolean hasOutpoint();\n    /**\n     * <code>.chronik.OutPoint outpoint = 1;</code>\n     * @return The outpoint.\n     */\n    chronik.Chronik.OutPoint getOutpoint();\n\n    /**\n     * <code>int32 block_height = 2;</code>\n     * @return The blockHeight.\n     */\n    int getBlockHeight();\n\n    /**\n     * <code>bool is_coinbase = 3;</code>\n     * @return The isCoinbase.\n     */\n    boolean getIsCoinbase();\n\n    /**\n     * <code>int64 value = 5;</code>\n     * @return The value.\n     */\n    long getValue();\n\n    /**\n     * <code>.chronik.SlpMeta slp_meta = 6;</code>\n     * @return Whether the slpMeta field is set.\n     */\n    boolean hasSlpMeta();\n    /**\n     * <code>.chronik.SlpMeta slp_meta = 6;</code>\n     * @return The slpMeta.\n     */\n    chronik.Chronik.SlpMeta getSlpMeta();\n\n    /**\n     * <code>.chronik.SlpToken slp_token = 7;</code>\n     * @return Whether the slpToken field is set.\n     */\n    boolean hasSlpToken();\n    /**\n     * <code>.chronik.SlpToken slp_token = 7;</code>\n     * @return The slpToken.\n     */\n    chronik.Chronik.SlpToken getSlpToken();\n\n    /**\n     * <code>.chronik.Network network = 9;</code>\n     * @return The enum numeric value on the wire for network.\n     */\n    int getNetworkValue();\n    /**\n     * <code>.chronik.Network network = 9;</code>\n     * @return The network.\n     */\n    chronik.Chronik.Network getNetwork();\n  }\n  /**\n   * Protobuf type {@code chronik.Utxo}\n   */\n  public  static final class Utxo extends\n      com.google.protobuf.GeneratedMessageLite<\n          Utxo, Utxo.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.Utxo)\n      UtxoOrBuilder {\n    private Utxo() {\n    }\n    public static final int OUTPOINT_FIELD_NUMBER = 1;\n    private chronik.Chronik.OutPoint outpoint_;\n    /**\n     * <code>.chronik.OutPoint outpoint = 1;</code>\n     */\n    @java.lang.Override\n    public boolean hasOutpoint() {\n      return outpoint_ != null;\n    }\n    /**\n     * <code>.chronik.OutPoint outpoint = 1;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.OutPoint getOutpoint() {\n      return outpoint_ == null ? chronik.Chronik.OutPoint.getDefaultInstance() : outpoint_;\n    }\n    /**\n     * <code>.chronik.OutPoint outpoint = 1;</code>\n     */\n    private void setOutpoint(chronik.Chronik.OutPoint value) {\n      value.getClass();\n  outpoint_ = value;\n\n      }\n    /**\n     * <code>.chronik.OutPoint outpoint = 1;</code>\n     */\n    @java.lang.SuppressWarnings({\"ReferenceEquality\"})\n    private void mergeOutpoint(chronik.Chronik.OutPoint value) {\n      value.getClass();\n  if (outpoint_ != null &&\n          outpoint_ != chronik.Chronik.OutPoint.getDefaultInstance()) {\n        outpoint_ =\n          chronik.Chronik.OutPoint.newBuilder(outpoint_).mergeFrom(value).buildPartial();\n      } else {\n        outpoint_ = value;\n      }\n\n    }\n    /**\n     * <code>.chronik.OutPoint outpoint = 1;</code>\n     */\n    private void clearOutpoint() {  outpoint_ = null;\n\n    }\n\n    public static final int BLOCK_HEIGHT_FIELD_NUMBER = 2;\n    private int blockHeight_;\n    /**\n     * <code>int32 block_height = 2;</code>\n     * @return The blockHeight.\n     */\n    @java.lang.Override\n    public int getBlockHeight() {\n      return blockHeight_;\n    }\n    /**\n     * <code>int32 block_height = 2;</code>\n     * @param value The blockHeight to set.\n     */\n    private void setBlockHeight(int value) {\n      \n      blockHeight_ = value;\n    }\n    /**\n     * <code>int32 block_height = 2;</code>\n     */\n    private void clearBlockHeight() {\n\n      blockHeight_ = 0;\n    }\n\n    public static final int IS_COINBASE_FIELD_NUMBER = 3;\n    private boolean isCoinbase_;\n    /**\n     * <code>bool is_coinbase = 3;</code>\n     * @return The isCoinbase.\n     */\n    @java.lang.Override\n    public boolean getIsCoinbase() {\n      return isCoinbase_;\n    }\n    /**\n     * <code>bool is_coinbase = 3;</code>\n     * @param value The isCoinbase to set.\n     */\n    private void setIsCoinbase(boolean value) {\n      \n      isCoinbase_ = value;\n    }\n    /**\n     * <code>bool is_coinbase = 3;</code>\n     */\n    private void clearIsCoinbase() {\n\n      isCoinbase_ = false;\n    }\n\n    public static final int VALUE_FIELD_NUMBER = 5;\n    private long value_;\n    /**\n     * <code>int64 value = 5;</code>\n     * @return The value.\n     */\n    @java.lang.Override\n    public long getValue() {\n      return value_;\n    }\n    /**\n     * <code>int64 value = 5;</code>\n     * @param value The value to set.\n     */\n    private void setValue(long value) {\n      \n      value_ = value;\n    }\n    /**\n     * <code>int64 value = 5;</code>\n     */\n    private void clearValue() {\n\n      value_ = 0L;\n    }\n\n    public static final int SLP_META_FIELD_NUMBER = 6;\n    private chronik.Chronik.SlpMeta slpMeta_;\n    /**\n     * <code>.chronik.SlpMeta slp_meta = 6;</code>\n     */\n    @java.lang.Override\n    public boolean hasSlpMeta() {\n      return slpMeta_ != null;\n    }\n    /**\n     * <code>.chronik.SlpMeta slp_meta = 6;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.SlpMeta getSlpMeta() {\n      return slpMeta_ == null ? chronik.Chronik.SlpMeta.getDefaultInstance() : slpMeta_;\n    }\n    /**\n     * <code>.chronik.SlpMeta slp_meta = 6;</code>\n     */\n    private void setSlpMeta(chronik.Chronik.SlpMeta value) {\n      value.getClass();\n  slpMeta_ = value;\n\n      }\n    /**\n     * <code>.chronik.SlpMeta slp_meta = 6;</code>\n     */\n    @java.lang.SuppressWarnings({\"ReferenceEquality\"})\n    private void mergeSlpMeta(chronik.Chronik.SlpMeta value) {\n      value.getClass();\n  if (slpMeta_ != null &&\n          slpMeta_ != chronik.Chronik.SlpMeta.getDefaultInstance()) {\n        slpMeta_ =\n          chronik.Chronik.SlpMeta.newBuilder(slpMeta_).mergeFrom(value).buildPartial();\n      } else {\n        slpMeta_ = value;\n      }\n\n    }\n    /**\n     * <code>.chronik.SlpMeta slp_meta = 6;</code>\n     */\n    private void clearSlpMeta() {  slpMeta_ = null;\n\n    }\n\n    public static final int SLP_TOKEN_FIELD_NUMBER = 7;\n    private chronik.Chronik.SlpToken slpToken_;\n    /**\n     * <code>.chronik.SlpToken slp_token = 7;</code>\n     */\n    @java.lang.Override\n    public boolean hasSlpToken() {\n      return slpToken_ != null;\n    }\n    /**\n     * <code>.chronik.SlpToken slp_token = 7;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.SlpToken getSlpToken() {\n      return slpToken_ == null ? chronik.Chronik.SlpToken.getDefaultInstance() : slpToken_;\n    }\n    /**\n     * <code>.chronik.SlpToken slp_token = 7;</code>\n     */\n    private void setSlpToken(chronik.Chronik.SlpToken value) {\n      value.getClass();\n  slpToken_ = value;\n\n      }\n    /**\n     * <code>.chronik.SlpToken slp_token = 7;</code>\n     */\n    @java.lang.SuppressWarnings({\"ReferenceEquality\"})\n    private void mergeSlpToken(chronik.Chronik.SlpToken value) {\n      value.getClass();\n  if (slpToken_ != null &&\n          slpToken_ != chronik.Chronik.SlpToken.getDefaultInstance()) {\n        slpToken_ =\n          chronik.Chronik.SlpToken.newBuilder(slpToken_).mergeFrom(value).buildPartial();\n      } else {\n        slpToken_ = value;\n      }\n\n    }\n    /**\n     * <code>.chronik.SlpToken slp_token = 7;</code>\n     */\n    private void clearSlpToken() {  slpToken_ = null;\n\n    }\n\n    public static final int NETWORK_FIELD_NUMBER = 9;\n    private int network_;\n    /**\n     * <code>.chronik.Network network = 9;</code>\n     * @return The enum numeric value on the wire for network.\n     */\n    @java.lang.Override\n    public int getNetworkValue() {\n      return network_;\n    }\n    /**\n     * <code>.chronik.Network network = 9;</code>\n     * @return The network.\n     */\n    @java.lang.Override\n    public chronik.Chronik.Network getNetwork() {\n      chronik.Chronik.Network result = chronik.Chronik.Network.forNumber(network_);\n      return result == null ? chronik.Chronik.Network.UNRECOGNIZED : result;\n    }\n    /**\n     * <code>.chronik.Network network = 9;</code>\n     * @param value The enum numeric value on the wire for network to set.\n     */\n    private void setNetworkValue(int value) {\n        network_ = value;\n    }\n    /**\n     * <code>.chronik.Network network = 9;</code>\n     * @param value The network to set.\n     */\n    private void setNetwork(chronik.Chronik.Network value) {\n      network_ = value.getNumber();\n\n    }\n    /**\n     * <code>.chronik.Network network = 9;</code>\n     */\n    private void clearNetwork() {\n\n      network_ = 0;\n    }\n\n    public static chronik.Chronik.Utxo parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.Utxo parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.Utxo parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.Utxo parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.Utxo parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.Utxo parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.Utxo parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.Utxo parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.Utxo parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.Utxo parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.Utxo parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.Utxo parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.Utxo prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.Utxo}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.Utxo, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.Utxo)\n        chronik.Chronik.UtxoOrBuilder {\n      // Construct using chronik.Chronik.Utxo.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>.chronik.OutPoint outpoint = 1;</code>\n       */\n      @java.lang.Override\n      public boolean hasOutpoint() {\n        return instance.hasOutpoint();\n      }\n      /**\n       * <code>.chronik.OutPoint outpoint = 1;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.OutPoint getOutpoint() {\n        return instance.getOutpoint();\n      }\n      /**\n       * <code>.chronik.OutPoint outpoint = 1;</code>\n       */\n      public Builder setOutpoint(chronik.Chronik.OutPoint value) {\n        copyOnWrite();\n        instance.setOutpoint(value);\n        return this;\n        }\n      /**\n       * <code>.chronik.OutPoint outpoint = 1;</code>\n       */\n      public Builder setOutpoint(\n          chronik.Chronik.OutPoint.Builder builderForValue) {\n        copyOnWrite();\n        instance.setOutpoint(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>.chronik.OutPoint outpoint = 1;</code>\n       */\n      public Builder mergeOutpoint(chronik.Chronik.OutPoint value) {\n        copyOnWrite();\n        instance.mergeOutpoint(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.OutPoint outpoint = 1;</code>\n       */\n      public Builder clearOutpoint() {  copyOnWrite();\n        instance.clearOutpoint();\n        return this;\n      }\n\n      /**\n       * <code>int32 block_height = 2;</code>\n       * @return The blockHeight.\n       */\n      @java.lang.Override\n      public int getBlockHeight() {\n        return instance.getBlockHeight();\n      }\n      /**\n       * <code>int32 block_height = 2;</code>\n       * @param value The blockHeight to set.\n       * @return This builder for chaining.\n       */\n      public Builder setBlockHeight(int value) {\n        copyOnWrite();\n        instance.setBlockHeight(value);\n        return this;\n      }\n      /**\n       * <code>int32 block_height = 2;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearBlockHeight() {\n        copyOnWrite();\n        instance.clearBlockHeight();\n        return this;\n      }\n\n      /**\n       * <code>bool is_coinbase = 3;</code>\n       * @return The isCoinbase.\n       */\n      @java.lang.Override\n      public boolean getIsCoinbase() {\n        return instance.getIsCoinbase();\n      }\n      /**\n       * <code>bool is_coinbase = 3;</code>\n       * @param value The isCoinbase to set.\n       * @return This builder for chaining.\n       */\n      public Builder setIsCoinbase(boolean value) {\n        copyOnWrite();\n        instance.setIsCoinbase(value);\n        return this;\n      }\n      /**\n       * <code>bool is_coinbase = 3;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearIsCoinbase() {\n        copyOnWrite();\n        instance.clearIsCoinbase();\n        return this;\n      }\n\n      /**\n       * <code>int64 value = 5;</code>\n       * @return The value.\n       */\n      @java.lang.Override\n      public long getValue() {\n        return instance.getValue();\n      }\n      /**\n       * <code>int64 value = 5;</code>\n       * @param value The value to set.\n       * @return This builder for chaining.\n       */\n      public Builder setValue(long value) {\n        copyOnWrite();\n        instance.setValue(value);\n        return this;\n      }\n      /**\n       * <code>int64 value = 5;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearValue() {\n        copyOnWrite();\n        instance.clearValue();\n        return this;\n      }\n\n      /**\n       * <code>.chronik.SlpMeta slp_meta = 6;</code>\n       */\n      @java.lang.Override\n      public boolean hasSlpMeta() {\n        return instance.hasSlpMeta();\n      }\n      /**\n       * <code>.chronik.SlpMeta slp_meta = 6;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.SlpMeta getSlpMeta() {\n        return instance.getSlpMeta();\n      }\n      /**\n       * <code>.chronik.SlpMeta slp_meta = 6;</code>\n       */\n      public Builder setSlpMeta(chronik.Chronik.SlpMeta value) {\n        copyOnWrite();\n        instance.setSlpMeta(value);\n        return this;\n        }\n      /**\n       * <code>.chronik.SlpMeta slp_meta = 6;</code>\n       */\n      public Builder setSlpMeta(\n          chronik.Chronik.SlpMeta.Builder builderForValue) {\n        copyOnWrite();\n        instance.setSlpMeta(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>.chronik.SlpMeta slp_meta = 6;</code>\n       */\n      public Builder mergeSlpMeta(chronik.Chronik.SlpMeta value) {\n        copyOnWrite();\n        instance.mergeSlpMeta(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.SlpMeta slp_meta = 6;</code>\n       */\n      public Builder clearSlpMeta() {  copyOnWrite();\n        instance.clearSlpMeta();\n        return this;\n      }\n\n      /**\n       * <code>.chronik.SlpToken slp_token = 7;</code>\n       */\n      @java.lang.Override\n      public boolean hasSlpToken() {\n        return instance.hasSlpToken();\n      }\n      /**\n       * <code>.chronik.SlpToken slp_token = 7;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.SlpToken getSlpToken() {\n        return instance.getSlpToken();\n      }\n      /**\n       * <code>.chronik.SlpToken slp_token = 7;</code>\n       */\n      public Builder setSlpToken(chronik.Chronik.SlpToken value) {\n        copyOnWrite();\n        instance.setSlpToken(value);\n        return this;\n        }\n      /**\n       * <code>.chronik.SlpToken slp_token = 7;</code>\n       */\n      public Builder setSlpToken(\n          chronik.Chronik.SlpToken.Builder builderForValue) {\n        copyOnWrite();\n        instance.setSlpToken(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>.chronik.SlpToken slp_token = 7;</code>\n       */\n      public Builder mergeSlpToken(chronik.Chronik.SlpToken value) {\n        copyOnWrite();\n        instance.mergeSlpToken(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.SlpToken slp_token = 7;</code>\n       */\n      public Builder clearSlpToken() {  copyOnWrite();\n        instance.clearSlpToken();\n        return this;\n      }\n\n      /**\n       * <code>.chronik.Network network = 9;</code>\n       * @return The enum numeric value on the wire for network.\n       */\n      @java.lang.Override\n      public int getNetworkValue() {\n        return instance.getNetworkValue();\n      }\n      /**\n       * <code>.chronik.Network network = 9;</code>\n       * @param value The network to set.\n       * @return This builder for chaining.\n       */\n      public Builder setNetworkValue(int value) {\n        copyOnWrite();\n        instance.setNetworkValue(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.Network network = 9;</code>\n       * @return The network.\n       */\n      @java.lang.Override\n      public chronik.Chronik.Network getNetwork() {\n        return instance.getNetwork();\n      }\n      /**\n       * <code>.chronik.Network network = 9;</code>\n       * @param value The enum numeric value on the wire for network to set.\n       * @return This builder for chaining.\n       */\n      public Builder setNetwork(chronik.Chronik.Network value) {\n        copyOnWrite();\n        instance.setNetwork(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.Network network = 9;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearNetwork() {\n        copyOnWrite();\n        instance.clearNetwork();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.Utxo)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.Utxo();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"outpoint_\",\n              \"blockHeight_\",\n              \"isCoinbase_\",\n              \"value_\",\n              \"slpMeta_\",\n              \"slpToken_\",\n              \"network_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0007\\u0000\\u0000\\u0001\\t\\u0007\\u0000\\u0000\\u0000\\u0001\\t\\u0002\\u0004\\u0003\" +\n                \"\\u0007\\u0005\\u0002\\u0006\\t\\u0007\\t\\t\\f\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.Utxo> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.Utxo.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.Utxo>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.Utxo)\n    private static final chronik.Chronik.Utxo DEFAULT_INSTANCE;\n    static {\n      Utxo defaultInstance = new Utxo();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        Utxo.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.Utxo getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<Utxo> PARSER;\n\n    public static com.google.protobuf.Parser<Utxo> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface TokenOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.Token)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>.chronik.SlpTxData slp_tx_data = 1;</code>\n     * @return Whether the slpTxData field is set.\n     */\n    boolean hasSlpTxData();\n    /**\n     * <code>.chronik.SlpTxData slp_tx_data = 1;</code>\n     * @return The slpTxData.\n     */\n    chronik.Chronik.SlpTxData getSlpTxData();\n\n    /**\n     * <code>.chronik.TokenStats token_stats = 2;</code>\n     * @return Whether the tokenStats field is set.\n     */\n    boolean hasTokenStats();\n    /**\n     * <code>.chronik.TokenStats token_stats = 2;</code>\n     * @return The tokenStats.\n     */\n    chronik.Chronik.TokenStats getTokenStats();\n\n    /**\n     * <code>.chronik.BlockMetadata block = 3;</code>\n     * @return Whether the block field is set.\n     */\n    boolean hasBlock();\n    /**\n     * <code>.chronik.BlockMetadata block = 3;</code>\n     * @return The block.\n     */\n    chronik.Chronik.BlockMetadata getBlock();\n\n    /**\n     * <code>int64 time_first_seen = 4;</code>\n     * @return The timeFirstSeen.\n     */\n    long getTimeFirstSeen();\n\n    /**\n     * <code>uint64 initial_token_quantity = 5;</code>\n     * @return The initialTokenQuantity.\n     */\n    long getInitialTokenQuantity();\n\n    /**\n     * <code>bool contains_baton = 6;</code>\n     * @return The containsBaton.\n     */\n    boolean getContainsBaton();\n\n    /**\n     * <code>.chronik.Network network = 7;</code>\n     * @return The enum numeric value on the wire for network.\n     */\n    int getNetworkValue();\n    /**\n     * <code>.chronik.Network network = 7;</code>\n     * @return The network.\n     */\n    chronik.Chronik.Network getNetwork();\n  }\n  /**\n   * Protobuf type {@code chronik.Token}\n   */\n  public  static final class Token extends\n      com.google.protobuf.GeneratedMessageLite<\n          Token, Token.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.Token)\n      TokenOrBuilder {\n    private Token() {\n    }\n    public static final int SLP_TX_DATA_FIELD_NUMBER = 1;\n    private chronik.Chronik.SlpTxData slpTxData_;\n    /**\n     * <code>.chronik.SlpTxData slp_tx_data = 1;</code>\n     */\n    @java.lang.Override\n    public boolean hasSlpTxData() {\n      return slpTxData_ != null;\n    }\n    /**\n     * <code>.chronik.SlpTxData slp_tx_data = 1;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.SlpTxData getSlpTxData() {\n      return slpTxData_ == null ? chronik.Chronik.SlpTxData.getDefaultInstance() : slpTxData_;\n    }\n    /**\n     * <code>.chronik.SlpTxData slp_tx_data = 1;</code>\n     */\n    private void setSlpTxData(chronik.Chronik.SlpTxData value) {\n      value.getClass();\n  slpTxData_ = value;\n\n      }\n    /**\n     * <code>.chronik.SlpTxData slp_tx_data = 1;</code>\n     */\n    @java.lang.SuppressWarnings({\"ReferenceEquality\"})\n    private void mergeSlpTxData(chronik.Chronik.SlpTxData value) {\n      value.getClass();\n  if (slpTxData_ != null &&\n          slpTxData_ != chronik.Chronik.SlpTxData.getDefaultInstance()) {\n        slpTxData_ =\n          chronik.Chronik.SlpTxData.newBuilder(slpTxData_).mergeFrom(value).buildPartial();\n      } else {\n        slpTxData_ = value;\n      }\n\n    }\n    /**\n     * <code>.chronik.SlpTxData slp_tx_data = 1;</code>\n     */\n    private void clearSlpTxData() {  slpTxData_ = null;\n\n    }\n\n    public static final int TOKEN_STATS_FIELD_NUMBER = 2;\n    private chronik.Chronik.TokenStats tokenStats_;\n    /**\n     * <code>.chronik.TokenStats token_stats = 2;</code>\n     */\n    @java.lang.Override\n    public boolean hasTokenStats() {\n      return tokenStats_ != null;\n    }\n    /**\n     * <code>.chronik.TokenStats token_stats = 2;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.TokenStats getTokenStats() {\n      return tokenStats_ == null ? chronik.Chronik.TokenStats.getDefaultInstance() : tokenStats_;\n    }\n    /**\n     * <code>.chronik.TokenStats token_stats = 2;</code>\n     */\n    private void setTokenStats(chronik.Chronik.TokenStats value) {\n      value.getClass();\n  tokenStats_ = value;\n\n      }\n    /**\n     * <code>.chronik.TokenStats token_stats = 2;</code>\n     */\n    @java.lang.SuppressWarnings({\"ReferenceEquality\"})\n    private void mergeTokenStats(chronik.Chronik.TokenStats value) {\n      value.getClass();\n  if (tokenStats_ != null &&\n          tokenStats_ != chronik.Chronik.TokenStats.getDefaultInstance()) {\n        tokenStats_ =\n          chronik.Chronik.TokenStats.newBuilder(tokenStats_).mergeFrom(value).buildPartial();\n      } else {\n        tokenStats_ = value;\n      }\n\n    }\n    /**\n     * <code>.chronik.TokenStats token_stats = 2;</code>\n     */\n    private void clearTokenStats() {  tokenStats_ = null;\n\n    }\n\n    public static final int BLOCK_FIELD_NUMBER = 3;\n    private chronik.Chronik.BlockMetadata block_;\n    /**\n     * <code>.chronik.BlockMetadata block = 3;</code>\n     */\n    @java.lang.Override\n    public boolean hasBlock() {\n      return block_ != null;\n    }\n    /**\n     * <code>.chronik.BlockMetadata block = 3;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.BlockMetadata getBlock() {\n      return block_ == null ? chronik.Chronik.BlockMetadata.getDefaultInstance() : block_;\n    }\n    /**\n     * <code>.chronik.BlockMetadata block = 3;</code>\n     */\n    private void setBlock(chronik.Chronik.BlockMetadata value) {\n      value.getClass();\n  block_ = value;\n\n      }\n    /**\n     * <code>.chronik.BlockMetadata block = 3;</code>\n     */\n    @java.lang.SuppressWarnings({\"ReferenceEquality\"})\n    private void mergeBlock(chronik.Chronik.BlockMetadata value) {\n      value.getClass();\n  if (block_ != null &&\n          block_ != chronik.Chronik.BlockMetadata.getDefaultInstance()) {\n        block_ =\n          chronik.Chronik.BlockMetadata.newBuilder(block_).mergeFrom(value).buildPartial();\n      } else {\n        block_ = value;\n      }\n\n    }\n    /**\n     * <code>.chronik.BlockMetadata block = 3;</code>\n     */\n    private void clearBlock() {  block_ = null;\n\n    }\n\n    public static final int TIME_FIRST_SEEN_FIELD_NUMBER = 4;\n    private long timeFirstSeen_;\n    /**\n     * <code>int64 time_first_seen = 4;</code>\n     * @return The timeFirstSeen.\n     */\n    @java.lang.Override\n    public long getTimeFirstSeen() {\n      return timeFirstSeen_;\n    }\n    /**\n     * <code>int64 time_first_seen = 4;</code>\n     * @param value The timeFirstSeen to set.\n     */\n    private void setTimeFirstSeen(long value) {\n      \n      timeFirstSeen_ = value;\n    }\n    /**\n     * <code>int64 time_first_seen = 4;</code>\n     */\n    private void clearTimeFirstSeen() {\n\n      timeFirstSeen_ = 0L;\n    }\n\n    public static final int INITIAL_TOKEN_QUANTITY_FIELD_NUMBER = 5;\n    private long initialTokenQuantity_;\n    /**\n     * <code>uint64 initial_token_quantity = 5;</code>\n     * @return The initialTokenQuantity.\n     */\n    @java.lang.Override\n    public long getInitialTokenQuantity() {\n      return initialTokenQuantity_;\n    }\n    /**\n     * <code>uint64 initial_token_quantity = 5;</code>\n     * @param value The initialTokenQuantity to set.\n     */\n    private void setInitialTokenQuantity(long value) {\n      \n      initialTokenQuantity_ = value;\n    }\n    /**\n     * <code>uint64 initial_token_quantity = 5;</code>\n     */\n    private void clearInitialTokenQuantity() {\n\n      initialTokenQuantity_ = 0L;\n    }\n\n    public static final int CONTAINS_BATON_FIELD_NUMBER = 6;\n    private boolean containsBaton_;\n    /**\n     * <code>bool contains_baton = 6;</code>\n     * @return The containsBaton.\n     */\n    @java.lang.Override\n    public boolean getContainsBaton() {\n      return containsBaton_;\n    }\n    /**\n     * <code>bool contains_baton = 6;</code>\n     * @param value The containsBaton to set.\n     */\n    private void setContainsBaton(boolean value) {\n      \n      containsBaton_ = value;\n    }\n    /**\n     * <code>bool contains_baton = 6;</code>\n     */\n    private void clearContainsBaton() {\n\n      containsBaton_ = false;\n    }\n\n    public static final int NETWORK_FIELD_NUMBER = 7;\n    private int network_;\n    /**\n     * <code>.chronik.Network network = 7;</code>\n     * @return The enum numeric value on the wire for network.\n     */\n    @java.lang.Override\n    public int getNetworkValue() {\n      return network_;\n    }\n    /**\n     * <code>.chronik.Network network = 7;</code>\n     * @return The network.\n     */\n    @java.lang.Override\n    public chronik.Chronik.Network getNetwork() {\n      chronik.Chronik.Network result = chronik.Chronik.Network.forNumber(network_);\n      return result == null ? chronik.Chronik.Network.UNRECOGNIZED : result;\n    }\n    /**\n     * <code>.chronik.Network network = 7;</code>\n     * @param value The enum numeric value on the wire for network to set.\n     */\n    private void setNetworkValue(int value) {\n        network_ = value;\n    }\n    /**\n     * <code>.chronik.Network network = 7;</code>\n     * @param value The network to set.\n     */\n    private void setNetwork(chronik.Chronik.Network value) {\n      network_ = value.getNumber();\n\n    }\n    /**\n     * <code>.chronik.Network network = 7;</code>\n     */\n    private void clearNetwork() {\n\n      network_ = 0;\n    }\n\n    public static chronik.Chronik.Token parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.Token parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.Token parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.Token parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.Token parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.Token parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.Token parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.Token parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.Token parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.Token parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.Token parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.Token parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.Token prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.Token}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.Token, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.Token)\n        chronik.Chronik.TokenOrBuilder {\n      // Construct using chronik.Chronik.Token.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>.chronik.SlpTxData slp_tx_data = 1;</code>\n       */\n      @java.lang.Override\n      public boolean hasSlpTxData() {\n        return instance.hasSlpTxData();\n      }\n      /**\n       * <code>.chronik.SlpTxData slp_tx_data = 1;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.SlpTxData getSlpTxData() {\n        return instance.getSlpTxData();\n      }\n      /**\n       * <code>.chronik.SlpTxData slp_tx_data = 1;</code>\n       */\n      public Builder setSlpTxData(chronik.Chronik.SlpTxData value) {\n        copyOnWrite();\n        instance.setSlpTxData(value);\n        return this;\n        }\n      /**\n       * <code>.chronik.SlpTxData slp_tx_data = 1;</code>\n       */\n      public Builder setSlpTxData(\n          chronik.Chronik.SlpTxData.Builder builderForValue) {\n        copyOnWrite();\n        instance.setSlpTxData(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>.chronik.SlpTxData slp_tx_data = 1;</code>\n       */\n      public Builder mergeSlpTxData(chronik.Chronik.SlpTxData value) {\n        copyOnWrite();\n        instance.mergeSlpTxData(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.SlpTxData slp_tx_data = 1;</code>\n       */\n      public Builder clearSlpTxData() {  copyOnWrite();\n        instance.clearSlpTxData();\n        return this;\n      }\n\n      /**\n       * <code>.chronik.TokenStats token_stats = 2;</code>\n       */\n      @java.lang.Override\n      public boolean hasTokenStats() {\n        return instance.hasTokenStats();\n      }\n      /**\n       * <code>.chronik.TokenStats token_stats = 2;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.TokenStats getTokenStats() {\n        return instance.getTokenStats();\n      }\n      /**\n       * <code>.chronik.TokenStats token_stats = 2;</code>\n       */\n      public Builder setTokenStats(chronik.Chronik.TokenStats value) {\n        copyOnWrite();\n        instance.setTokenStats(value);\n        return this;\n        }\n      /**\n       * <code>.chronik.TokenStats token_stats = 2;</code>\n       */\n      public Builder setTokenStats(\n          chronik.Chronik.TokenStats.Builder builderForValue) {\n        copyOnWrite();\n        instance.setTokenStats(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>.chronik.TokenStats token_stats = 2;</code>\n       */\n      public Builder mergeTokenStats(chronik.Chronik.TokenStats value) {\n        copyOnWrite();\n        instance.mergeTokenStats(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.TokenStats token_stats = 2;</code>\n       */\n      public Builder clearTokenStats() {  copyOnWrite();\n        instance.clearTokenStats();\n        return this;\n      }\n\n      /**\n       * <code>.chronik.BlockMetadata block = 3;</code>\n       */\n      @java.lang.Override\n      public boolean hasBlock() {\n        return instance.hasBlock();\n      }\n      /**\n       * <code>.chronik.BlockMetadata block = 3;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.BlockMetadata getBlock() {\n        return instance.getBlock();\n      }\n      /**\n       * <code>.chronik.BlockMetadata block = 3;</code>\n       */\n      public Builder setBlock(chronik.Chronik.BlockMetadata value) {\n        copyOnWrite();\n        instance.setBlock(value);\n        return this;\n        }\n      /**\n       * <code>.chronik.BlockMetadata block = 3;</code>\n       */\n      public Builder setBlock(\n          chronik.Chronik.BlockMetadata.Builder builderForValue) {\n        copyOnWrite();\n        instance.setBlock(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>.chronik.BlockMetadata block = 3;</code>\n       */\n      public Builder mergeBlock(chronik.Chronik.BlockMetadata value) {\n        copyOnWrite();\n        instance.mergeBlock(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.BlockMetadata block = 3;</code>\n       */\n      public Builder clearBlock() {  copyOnWrite();\n        instance.clearBlock();\n        return this;\n      }\n\n      /**\n       * <code>int64 time_first_seen = 4;</code>\n       * @return The timeFirstSeen.\n       */\n      @java.lang.Override\n      public long getTimeFirstSeen() {\n        return instance.getTimeFirstSeen();\n      }\n      /**\n       * <code>int64 time_first_seen = 4;</code>\n       * @param value The timeFirstSeen to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTimeFirstSeen(long value) {\n        copyOnWrite();\n        instance.setTimeFirstSeen(value);\n        return this;\n      }\n      /**\n       * <code>int64 time_first_seen = 4;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearTimeFirstSeen() {\n        copyOnWrite();\n        instance.clearTimeFirstSeen();\n        return this;\n      }\n\n      /**\n       * <code>uint64 initial_token_quantity = 5;</code>\n       * @return The initialTokenQuantity.\n       */\n      @java.lang.Override\n      public long getInitialTokenQuantity() {\n        return instance.getInitialTokenQuantity();\n      }\n      /**\n       * <code>uint64 initial_token_quantity = 5;</code>\n       * @param value The initialTokenQuantity to set.\n       * @return This builder for chaining.\n       */\n      public Builder setInitialTokenQuantity(long value) {\n        copyOnWrite();\n        instance.setInitialTokenQuantity(value);\n        return this;\n      }\n      /**\n       * <code>uint64 initial_token_quantity = 5;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearInitialTokenQuantity() {\n        copyOnWrite();\n        instance.clearInitialTokenQuantity();\n        return this;\n      }\n\n      /**\n       * <code>bool contains_baton = 6;</code>\n       * @return The containsBaton.\n       */\n      @java.lang.Override\n      public boolean getContainsBaton() {\n        return instance.getContainsBaton();\n      }\n      /**\n       * <code>bool contains_baton = 6;</code>\n       * @param value The containsBaton to set.\n       * @return This builder for chaining.\n       */\n      public Builder setContainsBaton(boolean value) {\n        copyOnWrite();\n        instance.setContainsBaton(value);\n        return this;\n      }\n      /**\n       * <code>bool contains_baton = 6;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearContainsBaton() {\n        copyOnWrite();\n        instance.clearContainsBaton();\n        return this;\n      }\n\n      /**\n       * <code>.chronik.Network network = 7;</code>\n       * @return The enum numeric value on the wire for network.\n       */\n      @java.lang.Override\n      public int getNetworkValue() {\n        return instance.getNetworkValue();\n      }\n      /**\n       * <code>.chronik.Network network = 7;</code>\n       * @param value The network to set.\n       * @return This builder for chaining.\n       */\n      public Builder setNetworkValue(int value) {\n        copyOnWrite();\n        instance.setNetworkValue(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.Network network = 7;</code>\n       * @return The network.\n       */\n      @java.lang.Override\n      public chronik.Chronik.Network getNetwork() {\n        return instance.getNetwork();\n      }\n      /**\n       * <code>.chronik.Network network = 7;</code>\n       * @param value The enum numeric value on the wire for network to set.\n       * @return This builder for chaining.\n       */\n      public Builder setNetwork(chronik.Chronik.Network value) {\n        copyOnWrite();\n        instance.setNetwork(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.Network network = 7;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearNetwork() {\n        copyOnWrite();\n        instance.clearNetwork();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.Token)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.Token();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"slpTxData_\",\n              \"tokenStats_\",\n              \"block_\",\n              \"timeFirstSeen_\",\n              \"initialTokenQuantity_\",\n              \"containsBaton_\",\n              \"network_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0007\\u0000\\u0000\\u0001\\u0007\\u0007\\u0000\\u0000\\u0000\\u0001\\t\\u0002\\t\\u0003\" +\n                \"\\t\\u0004\\u0002\\u0005\\u0003\\u0006\\u0007\\u0007\\f\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.Token> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.Token.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.Token>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.Token)\n    private static final chronik.Chronik.Token DEFAULT_INSTANCE;\n    static {\n      Token defaultInstance = new Token();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        Token.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.Token getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<Token> PARSER;\n\n    public static com.google.protobuf.Parser<Token> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface BlockInfoOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.BlockInfo)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>bytes hash = 1;</code>\n     * @return The hash.\n     */\n    com.google.protobuf.ByteString getHash();\n\n    /**\n     * <code>bytes prev_hash = 2;</code>\n     * @return The prevHash.\n     */\n    com.google.protobuf.ByteString getPrevHash();\n\n    /**\n     * <code>int32 height = 3;</code>\n     * @return The height.\n     */\n    int getHeight();\n\n    /**\n     * <code>uint32 n_bits = 4;</code>\n     * @return The nBits.\n     */\n    int getNBits();\n\n    /**\n     * <code>int64 timestamp = 5;</code>\n     * @return The timestamp.\n     */\n    long getTimestamp();\n\n    /**\n     * <pre>\n     * Block size of this block in bytes (including headers etc.)\n     * </pre>\n     *\n     * <code>uint64 block_size = 6;</code>\n     * @return The blockSize.\n     */\n    long getBlockSize();\n\n    /**\n     * <pre>\n     * Number of txs in this block\n     * </pre>\n     *\n     * <code>uint64 num_txs = 7;</code>\n     * @return The numTxs.\n     */\n    long getNumTxs();\n\n    /**\n     * <pre>\n     * Total number of tx inputs in block (including coinbase)\n     * </pre>\n     *\n     * <code>uint64 num_inputs = 8;</code>\n     * @return The numInputs.\n     */\n    long getNumInputs();\n\n    /**\n     * <pre>\n     * Total number of tx output in block (including coinbase)\n     * </pre>\n     *\n     * <code>uint64 num_outputs = 9;</code>\n     * @return The numOutputs.\n     */\n    long getNumOutputs();\n\n    /**\n     * <pre>\n     * Total number of satoshis spent by tx inputs\n     * </pre>\n     *\n     * <code>int64 sum_input_sats = 10;</code>\n     * @return The sumInputSats.\n     */\n    long getSumInputSats();\n\n    /**\n     * <pre>\n     * Block reward for this block\n     * </pre>\n     *\n     * <code>int64 sum_coinbase_output_sats = 11;</code>\n     * @return The sumCoinbaseOutputSats.\n     */\n    long getSumCoinbaseOutputSats();\n\n    /**\n     * <pre>\n     * Total number of satoshis in non-coinbase tx outputs\n     * </pre>\n     *\n     * <code>int64 sum_normal_output_sats = 12;</code>\n     * @return The sumNormalOutputSats.\n     */\n    long getSumNormalOutputSats();\n\n    /**\n     * <pre>\n     * Total number of satoshis burned using OP_RETURN\n     * </pre>\n     *\n     * <code>int64 sum_burned_sats = 13;</code>\n     * @return The sumBurnedSats.\n     */\n    long getSumBurnedSats();\n  }\n  /**\n   * Protobuf type {@code chronik.BlockInfo}\n   */\n  public  static final class BlockInfo extends\n      com.google.protobuf.GeneratedMessageLite<\n          BlockInfo, BlockInfo.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.BlockInfo)\n      BlockInfoOrBuilder {\n    private BlockInfo() {\n      hash_ = com.google.protobuf.ByteString.EMPTY;\n      prevHash_ = com.google.protobuf.ByteString.EMPTY;\n    }\n    public static final int HASH_FIELD_NUMBER = 1;\n    private com.google.protobuf.ByteString hash_;\n    /**\n     * <code>bytes hash = 1;</code>\n     * @return The hash.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getHash() {\n      return hash_;\n    }\n    /**\n     * <code>bytes hash = 1;</code>\n     * @param value The hash to set.\n     */\n    private void setHash(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      hash_ = value;\n    }\n    /**\n     * <code>bytes hash = 1;</code>\n     */\n    private void clearHash() {\n\n      hash_ = getDefaultInstance().getHash();\n    }\n\n    public static final int PREV_HASH_FIELD_NUMBER = 2;\n    private com.google.protobuf.ByteString prevHash_;\n    /**\n     * <code>bytes prev_hash = 2;</code>\n     * @return The prevHash.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getPrevHash() {\n      return prevHash_;\n    }\n    /**\n     * <code>bytes prev_hash = 2;</code>\n     * @param value The prevHash to set.\n     */\n    private void setPrevHash(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      prevHash_ = value;\n    }\n    /**\n     * <code>bytes prev_hash = 2;</code>\n     */\n    private void clearPrevHash() {\n\n      prevHash_ = getDefaultInstance().getPrevHash();\n    }\n\n    public static final int HEIGHT_FIELD_NUMBER = 3;\n    private int height_;\n    /**\n     * <code>int32 height = 3;</code>\n     * @return The height.\n     */\n    @java.lang.Override\n    public int getHeight() {\n      return height_;\n    }\n    /**\n     * <code>int32 height = 3;</code>\n     * @param value The height to set.\n     */\n    private void setHeight(int value) {\n      \n      height_ = value;\n    }\n    /**\n     * <code>int32 height = 3;</code>\n     */\n    private void clearHeight() {\n\n      height_ = 0;\n    }\n\n    public static final int N_BITS_FIELD_NUMBER = 4;\n    private int nBits_;\n    /**\n     * <code>uint32 n_bits = 4;</code>\n     * @return The nBits.\n     */\n    @java.lang.Override\n    public int getNBits() {\n      return nBits_;\n    }\n    /**\n     * <code>uint32 n_bits = 4;</code>\n     * @param value The nBits to set.\n     */\n    private void setNBits(int value) {\n      \n      nBits_ = value;\n    }\n    /**\n     * <code>uint32 n_bits = 4;</code>\n     */\n    private void clearNBits() {\n\n      nBits_ = 0;\n    }\n\n    public static final int TIMESTAMP_FIELD_NUMBER = 5;\n    private long timestamp_;\n    /**\n     * <code>int64 timestamp = 5;</code>\n     * @return The timestamp.\n     */\n    @java.lang.Override\n    public long getTimestamp() {\n      return timestamp_;\n    }\n    /**\n     * <code>int64 timestamp = 5;</code>\n     * @param value The timestamp to set.\n     */\n    private void setTimestamp(long value) {\n      \n      timestamp_ = value;\n    }\n    /**\n     * <code>int64 timestamp = 5;</code>\n     */\n    private void clearTimestamp() {\n\n      timestamp_ = 0L;\n    }\n\n    public static final int BLOCK_SIZE_FIELD_NUMBER = 6;\n    private long blockSize_;\n    /**\n     * <pre>\n     * Block size of this block in bytes (including headers etc.)\n     * </pre>\n     *\n     * <code>uint64 block_size = 6;</code>\n     * @return The blockSize.\n     */\n    @java.lang.Override\n    public long getBlockSize() {\n      return blockSize_;\n    }\n    /**\n     * <pre>\n     * Block size of this block in bytes (including headers etc.)\n     * </pre>\n     *\n     * <code>uint64 block_size = 6;</code>\n     * @param value The blockSize to set.\n     */\n    private void setBlockSize(long value) {\n      \n      blockSize_ = value;\n    }\n    /**\n     * <pre>\n     * Block size of this block in bytes (including headers etc.)\n     * </pre>\n     *\n     * <code>uint64 block_size = 6;</code>\n     */\n    private void clearBlockSize() {\n\n      blockSize_ = 0L;\n    }\n\n    public static final int NUM_TXS_FIELD_NUMBER = 7;\n    private long numTxs_;\n    /**\n     * <pre>\n     * Number of txs in this block\n     * </pre>\n     *\n     * <code>uint64 num_txs = 7;</code>\n     * @return The numTxs.\n     */\n    @java.lang.Override\n    public long getNumTxs() {\n      return numTxs_;\n    }\n    /**\n     * <pre>\n     * Number of txs in this block\n     * </pre>\n     *\n     * <code>uint64 num_txs = 7;</code>\n     * @param value The numTxs to set.\n     */\n    private void setNumTxs(long value) {\n      \n      numTxs_ = value;\n    }\n    /**\n     * <pre>\n     * Number of txs in this block\n     * </pre>\n     *\n     * <code>uint64 num_txs = 7;</code>\n     */\n    private void clearNumTxs() {\n\n      numTxs_ = 0L;\n    }\n\n    public static final int NUM_INPUTS_FIELD_NUMBER = 8;\n    private long numInputs_;\n    /**\n     * <pre>\n     * Total number of tx inputs in block (including coinbase)\n     * </pre>\n     *\n     * <code>uint64 num_inputs = 8;</code>\n     * @return The numInputs.\n     */\n    @java.lang.Override\n    public long getNumInputs() {\n      return numInputs_;\n    }\n    /**\n     * <pre>\n     * Total number of tx inputs in block (including coinbase)\n     * </pre>\n     *\n     * <code>uint64 num_inputs = 8;</code>\n     * @param value The numInputs to set.\n     */\n    private void setNumInputs(long value) {\n      \n      numInputs_ = value;\n    }\n    /**\n     * <pre>\n     * Total number of tx inputs in block (including coinbase)\n     * </pre>\n     *\n     * <code>uint64 num_inputs = 8;</code>\n     */\n    private void clearNumInputs() {\n\n      numInputs_ = 0L;\n    }\n\n    public static final int NUM_OUTPUTS_FIELD_NUMBER = 9;\n    private long numOutputs_;\n    /**\n     * <pre>\n     * Total number of tx output in block (including coinbase)\n     * </pre>\n     *\n     * <code>uint64 num_outputs = 9;</code>\n     * @return The numOutputs.\n     */\n    @java.lang.Override\n    public long getNumOutputs() {\n      return numOutputs_;\n    }\n    /**\n     * <pre>\n     * Total number of tx output in block (including coinbase)\n     * </pre>\n     *\n     * <code>uint64 num_outputs = 9;</code>\n     * @param value The numOutputs to set.\n     */\n    private void setNumOutputs(long value) {\n      \n      numOutputs_ = value;\n    }\n    /**\n     * <pre>\n     * Total number of tx output in block (including coinbase)\n     * </pre>\n     *\n     * <code>uint64 num_outputs = 9;</code>\n     */\n    private void clearNumOutputs() {\n\n      numOutputs_ = 0L;\n    }\n\n    public static final int SUM_INPUT_SATS_FIELD_NUMBER = 10;\n    private long sumInputSats_;\n    /**\n     * <pre>\n     * Total number of satoshis spent by tx inputs\n     * </pre>\n     *\n     * <code>int64 sum_input_sats = 10;</code>\n     * @return The sumInputSats.\n     */\n    @java.lang.Override\n    public long getSumInputSats() {\n      return sumInputSats_;\n    }\n    /**\n     * <pre>\n     * Total number of satoshis spent by tx inputs\n     * </pre>\n     *\n     * <code>int64 sum_input_sats = 10;</code>\n     * @param value The sumInputSats to set.\n     */\n    private void setSumInputSats(long value) {\n      \n      sumInputSats_ = value;\n    }\n    /**\n     * <pre>\n     * Total number of satoshis spent by tx inputs\n     * </pre>\n     *\n     * <code>int64 sum_input_sats = 10;</code>\n     */\n    private void clearSumInputSats() {\n\n      sumInputSats_ = 0L;\n    }\n\n    public static final int SUM_COINBASE_OUTPUT_SATS_FIELD_NUMBER = 11;\n    private long sumCoinbaseOutputSats_;\n    /**\n     * <pre>\n     * Block reward for this block\n     * </pre>\n     *\n     * <code>int64 sum_coinbase_output_sats = 11;</code>\n     * @return The sumCoinbaseOutputSats.\n     */\n    @java.lang.Override\n    public long getSumCoinbaseOutputSats() {\n      return sumCoinbaseOutputSats_;\n    }\n    /**\n     * <pre>\n     * Block reward for this block\n     * </pre>\n     *\n     * <code>int64 sum_coinbase_output_sats = 11;</code>\n     * @param value The sumCoinbaseOutputSats to set.\n     */\n    private void setSumCoinbaseOutputSats(long value) {\n      \n      sumCoinbaseOutputSats_ = value;\n    }\n    /**\n     * <pre>\n     * Block reward for this block\n     * </pre>\n     *\n     * <code>int64 sum_coinbase_output_sats = 11;</code>\n     */\n    private void clearSumCoinbaseOutputSats() {\n\n      sumCoinbaseOutputSats_ = 0L;\n    }\n\n    public static final int SUM_NORMAL_OUTPUT_SATS_FIELD_NUMBER = 12;\n    private long sumNormalOutputSats_;\n    /**\n     * <pre>\n     * Total number of satoshis in non-coinbase tx outputs\n     * </pre>\n     *\n     * <code>int64 sum_normal_output_sats = 12;</code>\n     * @return The sumNormalOutputSats.\n     */\n    @java.lang.Override\n    public long getSumNormalOutputSats() {\n      return sumNormalOutputSats_;\n    }\n    /**\n     * <pre>\n     * Total number of satoshis in non-coinbase tx outputs\n     * </pre>\n     *\n     * <code>int64 sum_normal_output_sats = 12;</code>\n     * @param value The sumNormalOutputSats to set.\n     */\n    private void setSumNormalOutputSats(long value) {\n      \n      sumNormalOutputSats_ = value;\n    }\n    /**\n     * <pre>\n     * Total number of satoshis in non-coinbase tx outputs\n     * </pre>\n     *\n     * <code>int64 sum_normal_output_sats = 12;</code>\n     */\n    private void clearSumNormalOutputSats() {\n\n      sumNormalOutputSats_ = 0L;\n    }\n\n    public static final int SUM_BURNED_SATS_FIELD_NUMBER = 13;\n    private long sumBurnedSats_;\n    /**\n     * <pre>\n     * Total number of satoshis burned using OP_RETURN\n     * </pre>\n     *\n     * <code>int64 sum_burned_sats = 13;</code>\n     * @return The sumBurnedSats.\n     */\n    @java.lang.Override\n    public long getSumBurnedSats() {\n      return sumBurnedSats_;\n    }\n    /**\n     * <pre>\n     * Total number of satoshis burned using OP_RETURN\n     * </pre>\n     *\n     * <code>int64 sum_burned_sats = 13;</code>\n     * @param value The sumBurnedSats to set.\n     */\n    private void setSumBurnedSats(long value) {\n      \n      sumBurnedSats_ = value;\n    }\n    /**\n     * <pre>\n     * Total number of satoshis burned using OP_RETURN\n     * </pre>\n     *\n     * <code>int64 sum_burned_sats = 13;</code>\n     */\n    private void clearSumBurnedSats() {\n\n      sumBurnedSats_ = 0L;\n    }\n\n    public static chronik.Chronik.BlockInfo parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.BlockInfo parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.BlockInfo parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.BlockInfo parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.BlockInfo parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.BlockInfo parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.BlockInfo parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.BlockInfo parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.BlockInfo parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.BlockInfo parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.BlockInfo parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.BlockInfo parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.BlockInfo prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.BlockInfo}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.BlockInfo, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.BlockInfo)\n        chronik.Chronik.BlockInfoOrBuilder {\n      // Construct using chronik.Chronik.BlockInfo.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>bytes hash = 1;</code>\n       * @return The hash.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getHash() {\n        return instance.getHash();\n      }\n      /**\n       * <code>bytes hash = 1;</code>\n       * @param value The hash to set.\n       * @return This builder for chaining.\n       */\n      public Builder setHash(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setHash(value);\n        return this;\n      }\n      /**\n       * <code>bytes hash = 1;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearHash() {\n        copyOnWrite();\n        instance.clearHash();\n        return this;\n      }\n\n      /**\n       * <code>bytes prev_hash = 2;</code>\n       * @return The prevHash.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getPrevHash() {\n        return instance.getPrevHash();\n      }\n      /**\n       * <code>bytes prev_hash = 2;</code>\n       * @param value The prevHash to set.\n       * @return This builder for chaining.\n       */\n      public Builder setPrevHash(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setPrevHash(value);\n        return this;\n      }\n      /**\n       * <code>bytes prev_hash = 2;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearPrevHash() {\n        copyOnWrite();\n        instance.clearPrevHash();\n        return this;\n      }\n\n      /**\n       * <code>int32 height = 3;</code>\n       * @return The height.\n       */\n      @java.lang.Override\n      public int getHeight() {\n        return instance.getHeight();\n      }\n      /**\n       * <code>int32 height = 3;</code>\n       * @param value The height to set.\n       * @return This builder for chaining.\n       */\n      public Builder setHeight(int value) {\n        copyOnWrite();\n        instance.setHeight(value);\n        return this;\n      }\n      /**\n       * <code>int32 height = 3;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearHeight() {\n        copyOnWrite();\n        instance.clearHeight();\n        return this;\n      }\n\n      /**\n       * <code>uint32 n_bits = 4;</code>\n       * @return The nBits.\n       */\n      @java.lang.Override\n      public int getNBits() {\n        return instance.getNBits();\n      }\n      /**\n       * <code>uint32 n_bits = 4;</code>\n       * @param value The nBits to set.\n       * @return This builder for chaining.\n       */\n      public Builder setNBits(int value) {\n        copyOnWrite();\n        instance.setNBits(value);\n        return this;\n      }\n      /**\n       * <code>uint32 n_bits = 4;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearNBits() {\n        copyOnWrite();\n        instance.clearNBits();\n        return this;\n      }\n\n      /**\n       * <code>int64 timestamp = 5;</code>\n       * @return The timestamp.\n       */\n      @java.lang.Override\n      public long getTimestamp() {\n        return instance.getTimestamp();\n      }\n      /**\n       * <code>int64 timestamp = 5;</code>\n       * @param value The timestamp to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTimestamp(long value) {\n        copyOnWrite();\n        instance.setTimestamp(value);\n        return this;\n      }\n      /**\n       * <code>int64 timestamp = 5;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearTimestamp() {\n        copyOnWrite();\n        instance.clearTimestamp();\n        return this;\n      }\n\n      /**\n       * <pre>\n       * Block size of this block in bytes (including headers etc.)\n       * </pre>\n       *\n       * <code>uint64 block_size = 6;</code>\n       * @return The blockSize.\n       */\n      @java.lang.Override\n      public long getBlockSize() {\n        return instance.getBlockSize();\n      }\n      /**\n       * <pre>\n       * Block size of this block in bytes (including headers etc.)\n       * </pre>\n       *\n       * <code>uint64 block_size = 6;</code>\n       * @param value The blockSize to set.\n       * @return This builder for chaining.\n       */\n      public Builder setBlockSize(long value) {\n        copyOnWrite();\n        instance.setBlockSize(value);\n        return this;\n      }\n      /**\n       * <pre>\n       * Block size of this block in bytes (including headers etc.)\n       * </pre>\n       *\n       * <code>uint64 block_size = 6;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearBlockSize() {\n        copyOnWrite();\n        instance.clearBlockSize();\n        return this;\n      }\n\n      /**\n       * <pre>\n       * Number of txs in this block\n       * </pre>\n       *\n       * <code>uint64 num_txs = 7;</code>\n       * @return The numTxs.\n       */\n      @java.lang.Override\n      public long getNumTxs() {\n        return instance.getNumTxs();\n      }\n      /**\n       * <pre>\n       * Number of txs in this block\n       * </pre>\n       *\n       * <code>uint64 num_txs = 7;</code>\n       * @param value The numTxs to set.\n       * @return This builder for chaining.\n       */\n      public Builder setNumTxs(long value) {\n        copyOnWrite();\n        instance.setNumTxs(value);\n        return this;\n      }\n      /**\n       * <pre>\n       * Number of txs in this block\n       * </pre>\n       *\n       * <code>uint64 num_txs = 7;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearNumTxs() {\n        copyOnWrite();\n        instance.clearNumTxs();\n        return this;\n      }\n\n      /**\n       * <pre>\n       * Total number of tx inputs in block (including coinbase)\n       * </pre>\n       *\n       * <code>uint64 num_inputs = 8;</code>\n       * @return The numInputs.\n       */\n      @java.lang.Override\n      public long getNumInputs() {\n        return instance.getNumInputs();\n      }\n      /**\n       * <pre>\n       * Total number of tx inputs in block (including coinbase)\n       * </pre>\n       *\n       * <code>uint64 num_inputs = 8;</code>\n       * @param value The numInputs to set.\n       * @return This builder for chaining.\n       */\n      public Builder setNumInputs(long value) {\n        copyOnWrite();\n        instance.setNumInputs(value);\n        return this;\n      }\n      /**\n       * <pre>\n       * Total number of tx inputs in block (including coinbase)\n       * </pre>\n       *\n       * <code>uint64 num_inputs = 8;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearNumInputs() {\n        copyOnWrite();\n        instance.clearNumInputs();\n        return this;\n      }\n\n      /**\n       * <pre>\n       * Total number of tx output in block (including coinbase)\n       * </pre>\n       *\n       * <code>uint64 num_outputs = 9;</code>\n       * @return The numOutputs.\n       */\n      @java.lang.Override\n      public long getNumOutputs() {\n        return instance.getNumOutputs();\n      }\n      /**\n       * <pre>\n       * Total number of tx output in block (including coinbase)\n       * </pre>\n       *\n       * <code>uint64 num_outputs = 9;</code>\n       * @param value The numOutputs to set.\n       * @return This builder for chaining.\n       */\n      public Builder setNumOutputs(long value) {\n        copyOnWrite();\n        instance.setNumOutputs(value);\n        return this;\n      }\n      /**\n       * <pre>\n       * Total number of tx output in block (including coinbase)\n       * </pre>\n       *\n       * <code>uint64 num_outputs = 9;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearNumOutputs() {\n        copyOnWrite();\n        instance.clearNumOutputs();\n        return this;\n      }\n\n      /**\n       * <pre>\n       * Total number of satoshis spent by tx inputs\n       * </pre>\n       *\n       * <code>int64 sum_input_sats = 10;</code>\n       * @return The sumInputSats.\n       */\n      @java.lang.Override\n      public long getSumInputSats() {\n        return instance.getSumInputSats();\n      }\n      /**\n       * <pre>\n       * Total number of satoshis spent by tx inputs\n       * </pre>\n       *\n       * <code>int64 sum_input_sats = 10;</code>\n       * @param value The sumInputSats to set.\n       * @return This builder for chaining.\n       */\n      public Builder setSumInputSats(long value) {\n        copyOnWrite();\n        instance.setSumInputSats(value);\n        return this;\n      }\n      /**\n       * <pre>\n       * Total number of satoshis spent by tx inputs\n       * </pre>\n       *\n       * <code>int64 sum_input_sats = 10;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearSumInputSats() {\n        copyOnWrite();\n        instance.clearSumInputSats();\n        return this;\n      }\n\n      /**\n       * <pre>\n       * Block reward for this block\n       * </pre>\n       *\n       * <code>int64 sum_coinbase_output_sats = 11;</code>\n       * @return The sumCoinbaseOutputSats.\n       */\n      @java.lang.Override\n      public long getSumCoinbaseOutputSats() {\n        return instance.getSumCoinbaseOutputSats();\n      }\n      /**\n       * <pre>\n       * Block reward for this block\n       * </pre>\n       *\n       * <code>int64 sum_coinbase_output_sats = 11;</code>\n       * @param value The sumCoinbaseOutputSats to set.\n       * @return This builder for chaining.\n       */\n      public Builder setSumCoinbaseOutputSats(long value) {\n        copyOnWrite();\n        instance.setSumCoinbaseOutputSats(value);\n        return this;\n      }\n      /**\n       * <pre>\n       * Block reward for this block\n       * </pre>\n       *\n       * <code>int64 sum_coinbase_output_sats = 11;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearSumCoinbaseOutputSats() {\n        copyOnWrite();\n        instance.clearSumCoinbaseOutputSats();\n        return this;\n      }\n\n      /**\n       * <pre>\n       * Total number of satoshis in non-coinbase tx outputs\n       * </pre>\n       *\n       * <code>int64 sum_normal_output_sats = 12;</code>\n       * @return The sumNormalOutputSats.\n       */\n      @java.lang.Override\n      public long getSumNormalOutputSats() {\n        return instance.getSumNormalOutputSats();\n      }\n      /**\n       * <pre>\n       * Total number of satoshis in non-coinbase tx outputs\n       * </pre>\n       *\n       * <code>int64 sum_normal_output_sats = 12;</code>\n       * @param value The sumNormalOutputSats to set.\n       * @return This builder for chaining.\n       */\n      public Builder setSumNormalOutputSats(long value) {\n        copyOnWrite();\n        instance.setSumNormalOutputSats(value);\n        return this;\n      }\n      /**\n       * <pre>\n       * Total number of satoshis in non-coinbase tx outputs\n       * </pre>\n       *\n       * <code>int64 sum_normal_output_sats = 12;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearSumNormalOutputSats() {\n        copyOnWrite();\n        instance.clearSumNormalOutputSats();\n        return this;\n      }\n\n      /**\n       * <pre>\n       * Total number of satoshis burned using OP_RETURN\n       * </pre>\n       *\n       * <code>int64 sum_burned_sats = 13;</code>\n       * @return The sumBurnedSats.\n       */\n      @java.lang.Override\n      public long getSumBurnedSats() {\n        return instance.getSumBurnedSats();\n      }\n      /**\n       * <pre>\n       * Total number of satoshis burned using OP_RETURN\n       * </pre>\n       *\n       * <code>int64 sum_burned_sats = 13;</code>\n       * @param value The sumBurnedSats to set.\n       * @return This builder for chaining.\n       */\n      public Builder setSumBurnedSats(long value) {\n        copyOnWrite();\n        instance.setSumBurnedSats(value);\n        return this;\n      }\n      /**\n       * <pre>\n       * Total number of satoshis burned using OP_RETURN\n       * </pre>\n       *\n       * <code>int64 sum_burned_sats = 13;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearSumBurnedSats() {\n        copyOnWrite();\n        instance.clearSumBurnedSats();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.BlockInfo)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.BlockInfo();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"hash_\",\n              \"prevHash_\",\n              \"height_\",\n              \"nBits_\",\n              \"timestamp_\",\n              \"blockSize_\",\n              \"numTxs_\",\n              \"numInputs_\",\n              \"numOutputs_\",\n              \"sumInputSats_\",\n              \"sumCoinbaseOutputSats_\",\n              \"sumNormalOutputSats_\",\n              \"sumBurnedSats_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\r\\u0000\\u0000\\u0001\\r\\r\\u0000\\u0000\\u0000\\u0001\\n\\u0002\\n\\u0003\\u0004\\u0004\" +\n                \"\\u000b\\u0005\\u0002\\u0006\\u0003\\u0007\\u0003\\b\\u0003\\t\\u0003\\n\\u0002\\u000b\\u0002\\f\" +\n                \"\\u0002\\r\\u0002\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.BlockInfo> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.BlockInfo.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.BlockInfo>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.BlockInfo)\n    private static final chronik.Chronik.BlockInfo DEFAULT_INSTANCE;\n    static {\n      BlockInfo defaultInstance = new BlockInfo();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        BlockInfo.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.BlockInfo getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<BlockInfo> PARSER;\n\n    public static com.google.protobuf.Parser<BlockInfo> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface BlockDetailsOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.BlockDetails)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>int32 version = 1;</code>\n     * @return The version.\n     */\n    int getVersion();\n\n    /**\n     * <code>bytes merkle_root = 2;</code>\n     * @return The merkleRoot.\n     */\n    com.google.protobuf.ByteString getMerkleRoot();\n\n    /**\n     * <code>uint64 nonce = 3;</code>\n     * @return The nonce.\n     */\n    long getNonce();\n\n    /**\n     * <code>int64 median_timestamp = 4;</code>\n     * @return The medianTimestamp.\n     */\n    long getMedianTimestamp();\n  }\n  /**\n   * Protobuf type {@code chronik.BlockDetails}\n   */\n  public  static final class BlockDetails extends\n      com.google.protobuf.GeneratedMessageLite<\n          BlockDetails, BlockDetails.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.BlockDetails)\n      BlockDetailsOrBuilder {\n    private BlockDetails() {\n      merkleRoot_ = com.google.protobuf.ByteString.EMPTY;\n    }\n    public static final int VERSION_FIELD_NUMBER = 1;\n    private int version_;\n    /**\n     * <code>int32 version = 1;</code>\n     * @return The version.\n     */\n    @java.lang.Override\n    public int getVersion() {\n      return version_;\n    }\n    /**\n     * <code>int32 version = 1;</code>\n     * @param value The version to set.\n     */\n    private void setVersion(int value) {\n      \n      version_ = value;\n    }\n    /**\n     * <code>int32 version = 1;</code>\n     */\n    private void clearVersion() {\n\n      version_ = 0;\n    }\n\n    public static final int MERKLE_ROOT_FIELD_NUMBER = 2;\n    private com.google.protobuf.ByteString merkleRoot_;\n    /**\n     * <code>bytes merkle_root = 2;</code>\n     * @return The merkleRoot.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getMerkleRoot() {\n      return merkleRoot_;\n    }\n    /**\n     * <code>bytes merkle_root = 2;</code>\n     * @param value The merkleRoot to set.\n     */\n    private void setMerkleRoot(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      merkleRoot_ = value;\n    }\n    /**\n     * <code>bytes merkle_root = 2;</code>\n     */\n    private void clearMerkleRoot() {\n\n      merkleRoot_ = getDefaultInstance().getMerkleRoot();\n    }\n\n    public static final int NONCE_FIELD_NUMBER = 3;\n    private long nonce_;\n    /**\n     * <code>uint64 nonce = 3;</code>\n     * @return The nonce.\n     */\n    @java.lang.Override\n    public long getNonce() {\n      return nonce_;\n    }\n    /**\n     * <code>uint64 nonce = 3;</code>\n     * @param value The nonce to set.\n     */\n    private void setNonce(long value) {\n      \n      nonce_ = value;\n    }\n    /**\n     * <code>uint64 nonce = 3;</code>\n     */\n    private void clearNonce() {\n\n      nonce_ = 0L;\n    }\n\n    public static final int MEDIAN_TIMESTAMP_FIELD_NUMBER = 4;\n    private long medianTimestamp_;\n    /**\n     * <code>int64 median_timestamp = 4;</code>\n     * @return The medianTimestamp.\n     */\n    @java.lang.Override\n    public long getMedianTimestamp() {\n      return medianTimestamp_;\n    }\n    /**\n     * <code>int64 median_timestamp = 4;</code>\n     * @param value The medianTimestamp to set.\n     */\n    private void setMedianTimestamp(long value) {\n      \n      medianTimestamp_ = value;\n    }\n    /**\n     * <code>int64 median_timestamp = 4;</code>\n     */\n    private void clearMedianTimestamp() {\n\n      medianTimestamp_ = 0L;\n    }\n\n    public static chronik.Chronik.BlockDetails parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.BlockDetails parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.BlockDetails parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.BlockDetails parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.BlockDetails parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.BlockDetails parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.BlockDetails parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.BlockDetails parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.BlockDetails parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.BlockDetails parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.BlockDetails parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.BlockDetails parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.BlockDetails prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.BlockDetails}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.BlockDetails, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.BlockDetails)\n        chronik.Chronik.BlockDetailsOrBuilder {\n      // Construct using chronik.Chronik.BlockDetails.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>int32 version = 1;</code>\n       * @return The version.\n       */\n      @java.lang.Override\n      public int getVersion() {\n        return instance.getVersion();\n      }\n      /**\n       * <code>int32 version = 1;</code>\n       * @param value The version to set.\n       * @return This builder for chaining.\n       */\n      public Builder setVersion(int value) {\n        copyOnWrite();\n        instance.setVersion(value);\n        return this;\n      }\n      /**\n       * <code>int32 version = 1;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearVersion() {\n        copyOnWrite();\n        instance.clearVersion();\n        return this;\n      }\n\n      /**\n       * <code>bytes merkle_root = 2;</code>\n       * @return The merkleRoot.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getMerkleRoot() {\n        return instance.getMerkleRoot();\n      }\n      /**\n       * <code>bytes merkle_root = 2;</code>\n       * @param value The merkleRoot to set.\n       * @return This builder for chaining.\n       */\n      public Builder setMerkleRoot(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setMerkleRoot(value);\n        return this;\n      }\n      /**\n       * <code>bytes merkle_root = 2;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearMerkleRoot() {\n        copyOnWrite();\n        instance.clearMerkleRoot();\n        return this;\n      }\n\n      /**\n       * <code>uint64 nonce = 3;</code>\n       * @return The nonce.\n       */\n      @java.lang.Override\n      public long getNonce() {\n        return instance.getNonce();\n      }\n      /**\n       * <code>uint64 nonce = 3;</code>\n       * @param value The nonce to set.\n       * @return This builder for chaining.\n       */\n      public Builder setNonce(long value) {\n        copyOnWrite();\n        instance.setNonce(value);\n        return this;\n      }\n      /**\n       * <code>uint64 nonce = 3;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearNonce() {\n        copyOnWrite();\n        instance.clearNonce();\n        return this;\n      }\n\n      /**\n       * <code>int64 median_timestamp = 4;</code>\n       * @return The medianTimestamp.\n       */\n      @java.lang.Override\n      public long getMedianTimestamp() {\n        return instance.getMedianTimestamp();\n      }\n      /**\n       * <code>int64 median_timestamp = 4;</code>\n       * @param value The medianTimestamp to set.\n       * @return This builder for chaining.\n       */\n      public Builder setMedianTimestamp(long value) {\n        copyOnWrite();\n        instance.setMedianTimestamp(value);\n        return this;\n      }\n      /**\n       * <code>int64 median_timestamp = 4;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearMedianTimestamp() {\n        copyOnWrite();\n        instance.clearMedianTimestamp();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.BlockDetails)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.BlockDetails();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"version_\",\n              \"merkleRoot_\",\n              \"nonce_\",\n              \"medianTimestamp_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0004\\u0000\\u0000\\u0001\\u0004\\u0004\\u0000\\u0000\\u0000\\u0001\\u0004\\u0002\\n\" +\n                \"\\u0003\\u0003\\u0004\\u0002\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.BlockDetails> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.BlockDetails.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.BlockDetails>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.BlockDetails)\n    private static final chronik.Chronik.BlockDetails DEFAULT_INSTANCE;\n    static {\n      BlockDetails defaultInstance = new BlockDetails();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        BlockDetails.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.BlockDetails getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<BlockDetails> PARSER;\n\n    public static com.google.protobuf.Parser<BlockDetails> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface BlockOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.Block)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>.chronik.BlockInfo block_info = 1;</code>\n     * @return Whether the blockInfo field is set.\n     */\n    boolean hasBlockInfo();\n    /**\n     * <code>.chronik.BlockInfo block_info = 1;</code>\n     * @return The blockInfo.\n     */\n    chronik.Chronik.BlockInfo getBlockInfo();\n\n    /**\n     * <code>.chronik.BlockDetails block_details = 3;</code>\n     * @return Whether the blockDetails field is set.\n     */\n    boolean hasBlockDetails();\n    /**\n     * <code>.chronik.BlockDetails block_details = 3;</code>\n     * @return The blockDetails.\n     */\n    chronik.Chronik.BlockDetails getBlockDetails();\n\n    /**\n     * <code>bytes raw_header = 4;</code>\n     * @return The rawHeader.\n     */\n    com.google.protobuf.ByteString getRawHeader();\n\n    /**\n     * <code>repeated .chronik.Tx txs = 2;</code>\n     */\n    java.util.List<chronik.Chronik.Tx> \n        getTxsList();\n    /**\n     * <code>repeated .chronik.Tx txs = 2;</code>\n     */\n    chronik.Chronik.Tx getTxs(int index);\n    /**\n     * <code>repeated .chronik.Tx txs = 2;</code>\n     */\n    int getTxsCount();\n  }\n  /**\n   * Protobuf type {@code chronik.Block}\n   */\n  public  static final class Block extends\n      com.google.protobuf.GeneratedMessageLite<\n          Block, Block.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.Block)\n      BlockOrBuilder {\n    private Block() {\n      rawHeader_ = com.google.protobuf.ByteString.EMPTY;\n      txs_ = emptyProtobufList();\n    }\n    public static final int BLOCK_INFO_FIELD_NUMBER = 1;\n    private chronik.Chronik.BlockInfo blockInfo_;\n    /**\n     * <code>.chronik.BlockInfo block_info = 1;</code>\n     */\n    @java.lang.Override\n    public boolean hasBlockInfo() {\n      return blockInfo_ != null;\n    }\n    /**\n     * <code>.chronik.BlockInfo block_info = 1;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.BlockInfo getBlockInfo() {\n      return blockInfo_ == null ? chronik.Chronik.BlockInfo.getDefaultInstance() : blockInfo_;\n    }\n    /**\n     * <code>.chronik.BlockInfo block_info = 1;</code>\n     */\n    private void setBlockInfo(chronik.Chronik.BlockInfo value) {\n      value.getClass();\n  blockInfo_ = value;\n\n      }\n    /**\n     * <code>.chronik.BlockInfo block_info = 1;</code>\n     */\n    @java.lang.SuppressWarnings({\"ReferenceEquality\"})\n    private void mergeBlockInfo(chronik.Chronik.BlockInfo value) {\n      value.getClass();\n  if (blockInfo_ != null &&\n          blockInfo_ != chronik.Chronik.BlockInfo.getDefaultInstance()) {\n        blockInfo_ =\n          chronik.Chronik.BlockInfo.newBuilder(blockInfo_).mergeFrom(value).buildPartial();\n      } else {\n        blockInfo_ = value;\n      }\n\n    }\n    /**\n     * <code>.chronik.BlockInfo block_info = 1;</code>\n     */\n    private void clearBlockInfo() {  blockInfo_ = null;\n\n    }\n\n    public static final int BLOCK_DETAILS_FIELD_NUMBER = 3;\n    private chronik.Chronik.BlockDetails blockDetails_;\n    /**\n     * <code>.chronik.BlockDetails block_details = 3;</code>\n     */\n    @java.lang.Override\n    public boolean hasBlockDetails() {\n      return blockDetails_ != null;\n    }\n    /**\n     * <code>.chronik.BlockDetails block_details = 3;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.BlockDetails getBlockDetails() {\n      return blockDetails_ == null ? chronik.Chronik.BlockDetails.getDefaultInstance() : blockDetails_;\n    }\n    /**\n     * <code>.chronik.BlockDetails block_details = 3;</code>\n     */\n    private void setBlockDetails(chronik.Chronik.BlockDetails value) {\n      value.getClass();\n  blockDetails_ = value;\n\n      }\n    /**\n     * <code>.chronik.BlockDetails block_details = 3;</code>\n     */\n    @java.lang.SuppressWarnings({\"ReferenceEquality\"})\n    private void mergeBlockDetails(chronik.Chronik.BlockDetails value) {\n      value.getClass();\n  if (blockDetails_ != null &&\n          blockDetails_ != chronik.Chronik.BlockDetails.getDefaultInstance()) {\n        blockDetails_ =\n          chronik.Chronik.BlockDetails.newBuilder(blockDetails_).mergeFrom(value).buildPartial();\n      } else {\n        blockDetails_ = value;\n      }\n\n    }\n    /**\n     * <code>.chronik.BlockDetails block_details = 3;</code>\n     */\n    private void clearBlockDetails() {  blockDetails_ = null;\n\n    }\n\n    public static final int RAW_HEADER_FIELD_NUMBER = 4;\n    private com.google.protobuf.ByteString rawHeader_;\n    /**\n     * <code>bytes raw_header = 4;</code>\n     * @return The rawHeader.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getRawHeader() {\n      return rawHeader_;\n    }\n    /**\n     * <code>bytes raw_header = 4;</code>\n     * @param value The rawHeader to set.\n     */\n    private void setRawHeader(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      rawHeader_ = value;\n    }\n    /**\n     * <code>bytes raw_header = 4;</code>\n     */\n    private void clearRawHeader() {\n\n      rawHeader_ = getDefaultInstance().getRawHeader();\n    }\n\n    public static final int TXS_FIELD_NUMBER = 2;\n    private com.google.protobuf.Internal.ProtobufList<chronik.Chronik.Tx> txs_;\n    /**\n     * <code>repeated .chronik.Tx txs = 2;</code>\n     */\n    @java.lang.Override\n    public java.util.List<chronik.Chronik.Tx> getTxsList() {\n      return txs_;\n    }\n    /**\n     * <code>repeated .chronik.Tx txs = 2;</code>\n     */\n    public java.util.List<? extends chronik.Chronik.TxOrBuilder> \n        getTxsOrBuilderList() {\n      return txs_;\n    }\n    /**\n     * <code>repeated .chronik.Tx txs = 2;</code>\n     */\n    @java.lang.Override\n    public int getTxsCount() {\n      return txs_.size();\n    }\n    /**\n     * <code>repeated .chronik.Tx txs = 2;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.Tx getTxs(int index) {\n      return txs_.get(index);\n    }\n    /**\n     * <code>repeated .chronik.Tx txs = 2;</code>\n     */\n    public chronik.Chronik.TxOrBuilder getTxsOrBuilder(\n        int index) {\n      return txs_.get(index);\n    }\n    private void ensureTxsIsMutable() {\n      com.google.protobuf.Internal.ProtobufList<chronik.Chronik.Tx> tmp = txs_;\n      if (!tmp.isModifiable()) {\n        txs_ =\n            com.google.protobuf.GeneratedMessageLite.mutableCopy(tmp);\n       }\n    }\n\n    /**\n     * <code>repeated .chronik.Tx txs = 2;</code>\n     */\n    private void setTxs(\n        int index, chronik.Chronik.Tx value) {\n      value.getClass();\n  ensureTxsIsMutable();\n      txs_.set(index, value);\n    }\n    /**\n     * <code>repeated .chronik.Tx txs = 2;</code>\n     */\n    private void addTxs(chronik.Chronik.Tx value) {\n      value.getClass();\n  ensureTxsIsMutable();\n      txs_.add(value);\n    }\n    /**\n     * <code>repeated .chronik.Tx txs = 2;</code>\n     */\n    private void addTxs(\n        int index, chronik.Chronik.Tx value) {\n      value.getClass();\n  ensureTxsIsMutable();\n      txs_.add(index, value);\n    }\n    /**\n     * <code>repeated .chronik.Tx txs = 2;</code>\n     */\n    private void addAllTxs(\n        java.lang.Iterable<? extends chronik.Chronik.Tx> values) {\n      ensureTxsIsMutable();\n      com.google.protobuf.AbstractMessageLite.addAll(\n          values, txs_);\n    }\n    /**\n     * <code>repeated .chronik.Tx txs = 2;</code>\n     */\n    private void clearTxs() {\n      txs_ = emptyProtobufList();\n    }\n    /**\n     * <code>repeated .chronik.Tx txs = 2;</code>\n     */\n    private void removeTxs(int index) {\n      ensureTxsIsMutable();\n      txs_.remove(index);\n    }\n\n    public static chronik.Chronik.Block parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.Block parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.Block parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.Block parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.Block parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.Block parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.Block parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.Block parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.Block parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.Block parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.Block parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.Block parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.Block prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.Block}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.Block, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.Block)\n        chronik.Chronik.BlockOrBuilder {\n      // Construct using chronik.Chronik.Block.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>.chronik.BlockInfo block_info = 1;</code>\n       */\n      @java.lang.Override\n      public boolean hasBlockInfo() {\n        return instance.hasBlockInfo();\n      }\n      /**\n       * <code>.chronik.BlockInfo block_info = 1;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.BlockInfo getBlockInfo() {\n        return instance.getBlockInfo();\n      }\n      /**\n       * <code>.chronik.BlockInfo block_info = 1;</code>\n       */\n      public Builder setBlockInfo(chronik.Chronik.BlockInfo value) {\n        copyOnWrite();\n        instance.setBlockInfo(value);\n        return this;\n        }\n      /**\n       * <code>.chronik.BlockInfo block_info = 1;</code>\n       */\n      public Builder setBlockInfo(\n          chronik.Chronik.BlockInfo.Builder builderForValue) {\n        copyOnWrite();\n        instance.setBlockInfo(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>.chronik.BlockInfo block_info = 1;</code>\n       */\n      public Builder mergeBlockInfo(chronik.Chronik.BlockInfo value) {\n        copyOnWrite();\n        instance.mergeBlockInfo(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.BlockInfo block_info = 1;</code>\n       */\n      public Builder clearBlockInfo() {  copyOnWrite();\n        instance.clearBlockInfo();\n        return this;\n      }\n\n      /**\n       * <code>.chronik.BlockDetails block_details = 3;</code>\n       */\n      @java.lang.Override\n      public boolean hasBlockDetails() {\n        return instance.hasBlockDetails();\n      }\n      /**\n       * <code>.chronik.BlockDetails block_details = 3;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.BlockDetails getBlockDetails() {\n        return instance.getBlockDetails();\n      }\n      /**\n       * <code>.chronik.BlockDetails block_details = 3;</code>\n       */\n      public Builder setBlockDetails(chronik.Chronik.BlockDetails value) {\n        copyOnWrite();\n        instance.setBlockDetails(value);\n        return this;\n        }\n      /**\n       * <code>.chronik.BlockDetails block_details = 3;</code>\n       */\n      public Builder setBlockDetails(\n          chronik.Chronik.BlockDetails.Builder builderForValue) {\n        copyOnWrite();\n        instance.setBlockDetails(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>.chronik.BlockDetails block_details = 3;</code>\n       */\n      public Builder mergeBlockDetails(chronik.Chronik.BlockDetails value) {\n        copyOnWrite();\n        instance.mergeBlockDetails(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.BlockDetails block_details = 3;</code>\n       */\n      public Builder clearBlockDetails() {  copyOnWrite();\n        instance.clearBlockDetails();\n        return this;\n      }\n\n      /**\n       * <code>bytes raw_header = 4;</code>\n       * @return The rawHeader.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getRawHeader() {\n        return instance.getRawHeader();\n      }\n      /**\n       * <code>bytes raw_header = 4;</code>\n       * @param value The rawHeader to set.\n       * @return This builder for chaining.\n       */\n      public Builder setRawHeader(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setRawHeader(value);\n        return this;\n      }\n      /**\n       * <code>bytes raw_header = 4;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearRawHeader() {\n        copyOnWrite();\n        instance.clearRawHeader();\n        return this;\n      }\n\n      /**\n       * <code>repeated .chronik.Tx txs = 2;</code>\n       */\n      @java.lang.Override\n      public java.util.List<chronik.Chronik.Tx> getTxsList() {\n        return java.util.Collections.unmodifiableList(\n            instance.getTxsList());\n      }\n      /**\n       * <code>repeated .chronik.Tx txs = 2;</code>\n       */\n      @java.lang.Override\n      public int getTxsCount() {\n        return instance.getTxsCount();\n      }/**\n       * <code>repeated .chronik.Tx txs = 2;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.Tx getTxs(int index) {\n        return instance.getTxs(index);\n      }\n      /**\n       * <code>repeated .chronik.Tx txs = 2;</code>\n       */\n      public Builder setTxs(\n          int index, chronik.Chronik.Tx value) {\n        copyOnWrite();\n        instance.setTxs(index, value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.Tx txs = 2;</code>\n       */\n      public Builder setTxs(\n          int index, chronik.Chronik.Tx.Builder builderForValue) {\n        copyOnWrite();\n        instance.setTxs(index,\n            builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.Tx txs = 2;</code>\n       */\n      public Builder addTxs(chronik.Chronik.Tx value) {\n        copyOnWrite();\n        instance.addTxs(value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.Tx txs = 2;</code>\n       */\n      public Builder addTxs(\n          int index, chronik.Chronik.Tx value) {\n        copyOnWrite();\n        instance.addTxs(index, value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.Tx txs = 2;</code>\n       */\n      public Builder addTxs(\n          chronik.Chronik.Tx.Builder builderForValue) {\n        copyOnWrite();\n        instance.addTxs(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.Tx txs = 2;</code>\n       */\n      public Builder addTxs(\n          int index, chronik.Chronik.Tx.Builder builderForValue) {\n        copyOnWrite();\n        instance.addTxs(index,\n            builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.Tx txs = 2;</code>\n       */\n      public Builder addAllTxs(\n          java.lang.Iterable<? extends chronik.Chronik.Tx> values) {\n        copyOnWrite();\n        instance.addAllTxs(values);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.Tx txs = 2;</code>\n       */\n      public Builder clearTxs() {\n        copyOnWrite();\n        instance.clearTxs();\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.Tx txs = 2;</code>\n       */\n      public Builder removeTxs(int index) {\n        copyOnWrite();\n        instance.removeTxs(index);\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.Block)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.Block();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"blockInfo_\",\n              \"txs_\",\n              chronik.Chronik.Tx.class,\n              \"blockDetails_\",\n              \"rawHeader_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0004\\u0000\\u0000\\u0001\\u0004\\u0004\\u0000\\u0001\\u0000\\u0001\\t\\u0002\\u001b\" +\n                \"\\u0003\\t\\u0004\\n\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.Block> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.Block.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.Block>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.Block)\n    private static final chronik.Chronik.Block DEFAULT_INSTANCE;\n    static {\n      Block defaultInstance = new Block();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        Block.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.Block getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<Block> PARSER;\n\n    public static com.google.protobuf.Parser<Block> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface ScriptUtxosOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.ScriptUtxos)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>bytes output_script = 1;</code>\n     * @return The outputScript.\n     */\n    com.google.protobuf.ByteString getOutputScript();\n\n    /**\n     * <code>repeated .chronik.Utxo utxos = 2;</code>\n     */\n    java.util.List<chronik.Chronik.Utxo> \n        getUtxosList();\n    /**\n     * <code>repeated .chronik.Utxo utxos = 2;</code>\n     */\n    chronik.Chronik.Utxo getUtxos(int index);\n    /**\n     * <code>repeated .chronik.Utxo utxos = 2;</code>\n     */\n    int getUtxosCount();\n  }\n  /**\n   * Protobuf type {@code chronik.ScriptUtxos}\n   */\n  public  static final class ScriptUtxos extends\n      com.google.protobuf.GeneratedMessageLite<\n          ScriptUtxos, ScriptUtxos.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.ScriptUtxos)\n      ScriptUtxosOrBuilder {\n    private ScriptUtxos() {\n      outputScript_ = com.google.protobuf.ByteString.EMPTY;\n      utxos_ = emptyProtobufList();\n    }\n    public static final int OUTPUT_SCRIPT_FIELD_NUMBER = 1;\n    private com.google.protobuf.ByteString outputScript_;\n    /**\n     * <code>bytes output_script = 1;</code>\n     * @return The outputScript.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getOutputScript() {\n      return outputScript_;\n    }\n    /**\n     * <code>bytes output_script = 1;</code>\n     * @param value The outputScript to set.\n     */\n    private void setOutputScript(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      outputScript_ = value;\n    }\n    /**\n     * <code>bytes output_script = 1;</code>\n     */\n    private void clearOutputScript() {\n\n      outputScript_ = getDefaultInstance().getOutputScript();\n    }\n\n    public static final int UTXOS_FIELD_NUMBER = 2;\n    private com.google.protobuf.Internal.ProtobufList<chronik.Chronik.Utxo> utxos_;\n    /**\n     * <code>repeated .chronik.Utxo utxos = 2;</code>\n     */\n    @java.lang.Override\n    public java.util.List<chronik.Chronik.Utxo> getUtxosList() {\n      return utxos_;\n    }\n    /**\n     * <code>repeated .chronik.Utxo utxos = 2;</code>\n     */\n    public java.util.List<? extends chronik.Chronik.UtxoOrBuilder> \n        getUtxosOrBuilderList() {\n      return utxos_;\n    }\n    /**\n     * <code>repeated .chronik.Utxo utxos = 2;</code>\n     */\n    @java.lang.Override\n    public int getUtxosCount() {\n      return utxos_.size();\n    }\n    /**\n     * <code>repeated .chronik.Utxo utxos = 2;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.Utxo getUtxos(int index) {\n      return utxos_.get(index);\n    }\n    /**\n     * <code>repeated .chronik.Utxo utxos = 2;</code>\n     */\n    public chronik.Chronik.UtxoOrBuilder getUtxosOrBuilder(\n        int index) {\n      return utxos_.get(index);\n    }\n    private void ensureUtxosIsMutable() {\n      com.google.protobuf.Internal.ProtobufList<chronik.Chronik.Utxo> tmp = utxos_;\n      if (!tmp.isModifiable()) {\n        utxos_ =\n            com.google.protobuf.GeneratedMessageLite.mutableCopy(tmp);\n       }\n    }\n\n    /**\n     * <code>repeated .chronik.Utxo utxos = 2;</code>\n     */\n    private void setUtxos(\n        int index, chronik.Chronik.Utxo value) {\n      value.getClass();\n  ensureUtxosIsMutable();\n      utxos_.set(index, value);\n    }\n    /**\n     * <code>repeated .chronik.Utxo utxos = 2;</code>\n     */\n    private void addUtxos(chronik.Chronik.Utxo value) {\n      value.getClass();\n  ensureUtxosIsMutable();\n      utxos_.add(value);\n    }\n    /**\n     * <code>repeated .chronik.Utxo utxos = 2;</code>\n     */\n    private void addUtxos(\n        int index, chronik.Chronik.Utxo value) {\n      value.getClass();\n  ensureUtxosIsMutable();\n      utxos_.add(index, value);\n    }\n    /**\n     * <code>repeated .chronik.Utxo utxos = 2;</code>\n     */\n    private void addAllUtxos(\n        java.lang.Iterable<? extends chronik.Chronik.Utxo> values) {\n      ensureUtxosIsMutable();\n      com.google.protobuf.AbstractMessageLite.addAll(\n          values, utxos_);\n    }\n    /**\n     * <code>repeated .chronik.Utxo utxos = 2;</code>\n     */\n    private void clearUtxos() {\n      utxos_ = emptyProtobufList();\n    }\n    /**\n     * <code>repeated .chronik.Utxo utxos = 2;</code>\n     */\n    private void removeUtxos(int index) {\n      ensureUtxosIsMutable();\n      utxos_.remove(index);\n    }\n\n    public static chronik.Chronik.ScriptUtxos parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.ScriptUtxos parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.ScriptUtxos parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.ScriptUtxos parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.ScriptUtxos parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.ScriptUtxos parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.ScriptUtxos parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.ScriptUtxos parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.ScriptUtxos parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.ScriptUtxos parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.ScriptUtxos parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.ScriptUtxos parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.ScriptUtxos prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.ScriptUtxos}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.ScriptUtxos, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.ScriptUtxos)\n        chronik.Chronik.ScriptUtxosOrBuilder {\n      // Construct using chronik.Chronik.ScriptUtxos.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>bytes output_script = 1;</code>\n       * @return The outputScript.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getOutputScript() {\n        return instance.getOutputScript();\n      }\n      /**\n       * <code>bytes output_script = 1;</code>\n       * @param value The outputScript to set.\n       * @return This builder for chaining.\n       */\n      public Builder setOutputScript(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setOutputScript(value);\n        return this;\n      }\n      /**\n       * <code>bytes output_script = 1;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearOutputScript() {\n        copyOnWrite();\n        instance.clearOutputScript();\n        return this;\n      }\n\n      /**\n       * <code>repeated .chronik.Utxo utxos = 2;</code>\n       */\n      @java.lang.Override\n      public java.util.List<chronik.Chronik.Utxo> getUtxosList() {\n        return java.util.Collections.unmodifiableList(\n            instance.getUtxosList());\n      }\n      /**\n       * <code>repeated .chronik.Utxo utxos = 2;</code>\n       */\n      @java.lang.Override\n      public int getUtxosCount() {\n        return instance.getUtxosCount();\n      }/**\n       * <code>repeated .chronik.Utxo utxos = 2;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.Utxo getUtxos(int index) {\n        return instance.getUtxos(index);\n      }\n      /**\n       * <code>repeated .chronik.Utxo utxos = 2;</code>\n       */\n      public Builder setUtxos(\n          int index, chronik.Chronik.Utxo value) {\n        copyOnWrite();\n        instance.setUtxos(index, value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.Utxo utxos = 2;</code>\n       */\n      public Builder setUtxos(\n          int index, chronik.Chronik.Utxo.Builder builderForValue) {\n        copyOnWrite();\n        instance.setUtxos(index,\n            builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.Utxo utxos = 2;</code>\n       */\n      public Builder addUtxos(chronik.Chronik.Utxo value) {\n        copyOnWrite();\n        instance.addUtxos(value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.Utxo utxos = 2;</code>\n       */\n      public Builder addUtxos(\n          int index, chronik.Chronik.Utxo value) {\n        copyOnWrite();\n        instance.addUtxos(index, value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.Utxo utxos = 2;</code>\n       */\n      public Builder addUtxos(\n          chronik.Chronik.Utxo.Builder builderForValue) {\n        copyOnWrite();\n        instance.addUtxos(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.Utxo utxos = 2;</code>\n       */\n      public Builder addUtxos(\n          int index, chronik.Chronik.Utxo.Builder builderForValue) {\n        copyOnWrite();\n        instance.addUtxos(index,\n            builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.Utxo utxos = 2;</code>\n       */\n      public Builder addAllUtxos(\n          java.lang.Iterable<? extends chronik.Chronik.Utxo> values) {\n        copyOnWrite();\n        instance.addAllUtxos(values);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.Utxo utxos = 2;</code>\n       */\n      public Builder clearUtxos() {\n        copyOnWrite();\n        instance.clearUtxos();\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.Utxo utxos = 2;</code>\n       */\n      public Builder removeUtxos(int index) {\n        copyOnWrite();\n        instance.removeUtxos(index);\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.ScriptUtxos)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.ScriptUtxos();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"outputScript_\",\n              \"utxos_\",\n              chronik.Chronik.Utxo.class,\n            };\n            java.lang.String info =\n                \"\\u0000\\u0002\\u0000\\u0000\\u0001\\u0002\\u0002\\u0000\\u0001\\u0000\\u0001\\n\\u0002\\u001b\" +\n                \"\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.ScriptUtxos> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.ScriptUtxos.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.ScriptUtxos>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.ScriptUtxos)\n    private static final chronik.Chronik.ScriptUtxos DEFAULT_INSTANCE;\n    static {\n      ScriptUtxos defaultInstance = new ScriptUtxos();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        ScriptUtxos.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.ScriptUtxos getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<ScriptUtxos> PARSER;\n\n    public static com.google.protobuf.Parser<ScriptUtxos> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface TxHistoryPageOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.TxHistoryPage)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>repeated .chronik.Tx txs = 1;</code>\n     */\n    java.util.List<chronik.Chronik.Tx> \n        getTxsList();\n    /**\n     * <code>repeated .chronik.Tx txs = 1;</code>\n     */\n    chronik.Chronik.Tx getTxs(int index);\n    /**\n     * <code>repeated .chronik.Tx txs = 1;</code>\n     */\n    int getTxsCount();\n\n    /**\n     * <code>uint32 num_pages = 2;</code>\n     * @return The numPages.\n     */\n    int getNumPages();\n  }\n  /**\n   * Protobuf type {@code chronik.TxHistoryPage}\n   */\n  public  static final class TxHistoryPage extends\n      com.google.protobuf.GeneratedMessageLite<\n          TxHistoryPage, TxHistoryPage.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.TxHistoryPage)\n      TxHistoryPageOrBuilder {\n    private TxHistoryPage() {\n      txs_ = emptyProtobufList();\n    }\n    public static final int TXS_FIELD_NUMBER = 1;\n    private com.google.protobuf.Internal.ProtobufList<chronik.Chronik.Tx> txs_;\n    /**\n     * <code>repeated .chronik.Tx txs = 1;</code>\n     */\n    @java.lang.Override\n    public java.util.List<chronik.Chronik.Tx> getTxsList() {\n      return txs_;\n    }\n    /**\n     * <code>repeated .chronik.Tx txs = 1;</code>\n     */\n    public java.util.List<? extends chronik.Chronik.TxOrBuilder> \n        getTxsOrBuilderList() {\n      return txs_;\n    }\n    /**\n     * <code>repeated .chronik.Tx txs = 1;</code>\n     */\n    @java.lang.Override\n    public int getTxsCount() {\n      return txs_.size();\n    }\n    /**\n     * <code>repeated .chronik.Tx txs = 1;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.Tx getTxs(int index) {\n      return txs_.get(index);\n    }\n    /**\n     * <code>repeated .chronik.Tx txs = 1;</code>\n     */\n    public chronik.Chronik.TxOrBuilder getTxsOrBuilder(\n        int index) {\n      return txs_.get(index);\n    }\n    private void ensureTxsIsMutable() {\n      com.google.protobuf.Internal.ProtobufList<chronik.Chronik.Tx> tmp = txs_;\n      if (!tmp.isModifiable()) {\n        txs_ =\n            com.google.protobuf.GeneratedMessageLite.mutableCopy(tmp);\n       }\n    }\n\n    /**\n     * <code>repeated .chronik.Tx txs = 1;</code>\n     */\n    private void setTxs(\n        int index, chronik.Chronik.Tx value) {\n      value.getClass();\n  ensureTxsIsMutable();\n      txs_.set(index, value);\n    }\n    /**\n     * <code>repeated .chronik.Tx txs = 1;</code>\n     */\n    private void addTxs(chronik.Chronik.Tx value) {\n      value.getClass();\n  ensureTxsIsMutable();\n      txs_.add(value);\n    }\n    /**\n     * <code>repeated .chronik.Tx txs = 1;</code>\n     */\n    private void addTxs(\n        int index, chronik.Chronik.Tx value) {\n      value.getClass();\n  ensureTxsIsMutable();\n      txs_.add(index, value);\n    }\n    /**\n     * <code>repeated .chronik.Tx txs = 1;</code>\n     */\n    private void addAllTxs(\n        java.lang.Iterable<? extends chronik.Chronik.Tx> values) {\n      ensureTxsIsMutable();\n      com.google.protobuf.AbstractMessageLite.addAll(\n          values, txs_);\n    }\n    /**\n     * <code>repeated .chronik.Tx txs = 1;</code>\n     */\n    private void clearTxs() {\n      txs_ = emptyProtobufList();\n    }\n    /**\n     * <code>repeated .chronik.Tx txs = 1;</code>\n     */\n    private void removeTxs(int index) {\n      ensureTxsIsMutable();\n      txs_.remove(index);\n    }\n\n    public static final int NUM_PAGES_FIELD_NUMBER = 2;\n    private int numPages_;\n    /**\n     * <code>uint32 num_pages = 2;</code>\n     * @return The numPages.\n     */\n    @java.lang.Override\n    public int getNumPages() {\n      return numPages_;\n    }\n    /**\n     * <code>uint32 num_pages = 2;</code>\n     * @param value The numPages to set.\n     */\n    private void setNumPages(int value) {\n      \n      numPages_ = value;\n    }\n    /**\n     * <code>uint32 num_pages = 2;</code>\n     */\n    private void clearNumPages() {\n\n      numPages_ = 0;\n    }\n\n    public static chronik.Chronik.TxHistoryPage parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.TxHistoryPage parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.TxHistoryPage parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.TxHistoryPage parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.TxHistoryPage parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.TxHistoryPage parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.TxHistoryPage parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.TxHistoryPage parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.TxHistoryPage parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.TxHistoryPage parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.TxHistoryPage parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.TxHistoryPage parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.TxHistoryPage prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.TxHistoryPage}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.TxHistoryPage, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.TxHistoryPage)\n        chronik.Chronik.TxHistoryPageOrBuilder {\n      // Construct using chronik.Chronik.TxHistoryPage.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>repeated .chronik.Tx txs = 1;</code>\n       */\n      @java.lang.Override\n      public java.util.List<chronik.Chronik.Tx> getTxsList() {\n        return java.util.Collections.unmodifiableList(\n            instance.getTxsList());\n      }\n      /**\n       * <code>repeated .chronik.Tx txs = 1;</code>\n       */\n      @java.lang.Override\n      public int getTxsCount() {\n        return instance.getTxsCount();\n      }/**\n       * <code>repeated .chronik.Tx txs = 1;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.Tx getTxs(int index) {\n        return instance.getTxs(index);\n      }\n      /**\n       * <code>repeated .chronik.Tx txs = 1;</code>\n       */\n      public Builder setTxs(\n          int index, chronik.Chronik.Tx value) {\n        copyOnWrite();\n        instance.setTxs(index, value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.Tx txs = 1;</code>\n       */\n      public Builder setTxs(\n          int index, chronik.Chronik.Tx.Builder builderForValue) {\n        copyOnWrite();\n        instance.setTxs(index,\n            builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.Tx txs = 1;</code>\n       */\n      public Builder addTxs(chronik.Chronik.Tx value) {\n        copyOnWrite();\n        instance.addTxs(value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.Tx txs = 1;</code>\n       */\n      public Builder addTxs(\n          int index, chronik.Chronik.Tx value) {\n        copyOnWrite();\n        instance.addTxs(index, value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.Tx txs = 1;</code>\n       */\n      public Builder addTxs(\n          chronik.Chronik.Tx.Builder builderForValue) {\n        copyOnWrite();\n        instance.addTxs(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.Tx txs = 1;</code>\n       */\n      public Builder addTxs(\n          int index, chronik.Chronik.Tx.Builder builderForValue) {\n        copyOnWrite();\n        instance.addTxs(index,\n            builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.Tx txs = 1;</code>\n       */\n      public Builder addAllTxs(\n          java.lang.Iterable<? extends chronik.Chronik.Tx> values) {\n        copyOnWrite();\n        instance.addAllTxs(values);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.Tx txs = 1;</code>\n       */\n      public Builder clearTxs() {\n        copyOnWrite();\n        instance.clearTxs();\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.Tx txs = 1;</code>\n       */\n      public Builder removeTxs(int index) {\n        copyOnWrite();\n        instance.removeTxs(index);\n        return this;\n      }\n\n      /**\n       * <code>uint32 num_pages = 2;</code>\n       * @return The numPages.\n       */\n      @java.lang.Override\n      public int getNumPages() {\n        return instance.getNumPages();\n      }\n      /**\n       * <code>uint32 num_pages = 2;</code>\n       * @param value The numPages to set.\n       * @return This builder for chaining.\n       */\n      public Builder setNumPages(int value) {\n        copyOnWrite();\n        instance.setNumPages(value);\n        return this;\n      }\n      /**\n       * <code>uint32 num_pages = 2;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearNumPages() {\n        copyOnWrite();\n        instance.clearNumPages();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.TxHistoryPage)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.TxHistoryPage();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"txs_\",\n              chronik.Chronik.Tx.class,\n              \"numPages_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0002\\u0000\\u0000\\u0001\\u0002\\u0002\\u0000\\u0001\\u0000\\u0001\\u001b\\u0002\\u000b\" +\n                \"\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.TxHistoryPage> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.TxHistoryPage.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.TxHistoryPage>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.TxHistoryPage)\n    private static final chronik.Chronik.TxHistoryPage DEFAULT_INSTANCE;\n    static {\n      TxHistoryPage defaultInstance = new TxHistoryPage();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        TxHistoryPage.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.TxHistoryPage getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<TxHistoryPage> PARSER;\n\n    public static com.google.protobuf.Parser<TxHistoryPage> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface UtxosOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.Utxos)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n     */\n    java.util.List<chronik.Chronik.ScriptUtxos> \n        getScriptUtxosList();\n    /**\n     * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n     */\n    chronik.Chronik.ScriptUtxos getScriptUtxos(int index);\n    /**\n     * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n     */\n    int getScriptUtxosCount();\n  }\n  /**\n   * Protobuf type {@code chronik.Utxos}\n   */\n  public  static final class Utxos extends\n      com.google.protobuf.GeneratedMessageLite<\n          Utxos, Utxos.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.Utxos)\n      UtxosOrBuilder {\n    private Utxos() {\n      scriptUtxos_ = emptyProtobufList();\n    }\n    public static final int SCRIPT_UTXOS_FIELD_NUMBER = 1;\n    private com.google.protobuf.Internal.ProtobufList<chronik.Chronik.ScriptUtxos> scriptUtxos_;\n    /**\n     * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n     */\n    @java.lang.Override\n    public java.util.List<chronik.Chronik.ScriptUtxos> getScriptUtxosList() {\n      return scriptUtxos_;\n    }\n    /**\n     * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n     */\n    public java.util.List<? extends chronik.Chronik.ScriptUtxosOrBuilder> \n        getScriptUtxosOrBuilderList() {\n      return scriptUtxos_;\n    }\n    /**\n     * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n     */\n    @java.lang.Override\n    public int getScriptUtxosCount() {\n      return scriptUtxos_.size();\n    }\n    /**\n     * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.ScriptUtxos getScriptUtxos(int index) {\n      return scriptUtxos_.get(index);\n    }\n    /**\n     * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n     */\n    public chronik.Chronik.ScriptUtxosOrBuilder getScriptUtxosOrBuilder(\n        int index) {\n      return scriptUtxos_.get(index);\n    }\n    private void ensureScriptUtxosIsMutable() {\n      com.google.protobuf.Internal.ProtobufList<chronik.Chronik.ScriptUtxos> tmp = scriptUtxos_;\n      if (!tmp.isModifiable()) {\n        scriptUtxos_ =\n            com.google.protobuf.GeneratedMessageLite.mutableCopy(tmp);\n       }\n    }\n\n    /**\n     * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n     */\n    private void setScriptUtxos(\n        int index, chronik.Chronik.ScriptUtxos value) {\n      value.getClass();\n  ensureScriptUtxosIsMutable();\n      scriptUtxos_.set(index, value);\n    }\n    /**\n     * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n     */\n    private void addScriptUtxos(chronik.Chronik.ScriptUtxos value) {\n      value.getClass();\n  ensureScriptUtxosIsMutable();\n      scriptUtxos_.add(value);\n    }\n    /**\n     * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n     */\n    private void addScriptUtxos(\n        int index, chronik.Chronik.ScriptUtxos value) {\n      value.getClass();\n  ensureScriptUtxosIsMutable();\n      scriptUtxos_.add(index, value);\n    }\n    /**\n     * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n     */\n    private void addAllScriptUtxos(\n        java.lang.Iterable<? extends chronik.Chronik.ScriptUtxos> values) {\n      ensureScriptUtxosIsMutable();\n      com.google.protobuf.AbstractMessageLite.addAll(\n          values, scriptUtxos_);\n    }\n    /**\n     * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n     */\n    private void clearScriptUtxos() {\n      scriptUtxos_ = emptyProtobufList();\n    }\n    /**\n     * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n     */\n    private void removeScriptUtxos(int index) {\n      ensureScriptUtxosIsMutable();\n      scriptUtxos_.remove(index);\n    }\n\n    public static chronik.Chronik.Utxos parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.Utxos parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.Utxos parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.Utxos parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.Utxos parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.Utxos parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.Utxos parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.Utxos parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.Utxos parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.Utxos parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.Utxos parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.Utxos parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.Utxos prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.Utxos}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.Utxos, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.Utxos)\n        chronik.Chronik.UtxosOrBuilder {\n      // Construct using chronik.Chronik.Utxos.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n       */\n      @java.lang.Override\n      public java.util.List<chronik.Chronik.ScriptUtxos> getScriptUtxosList() {\n        return java.util.Collections.unmodifiableList(\n            instance.getScriptUtxosList());\n      }\n      /**\n       * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n       */\n      @java.lang.Override\n      public int getScriptUtxosCount() {\n        return instance.getScriptUtxosCount();\n      }/**\n       * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.ScriptUtxos getScriptUtxos(int index) {\n        return instance.getScriptUtxos(index);\n      }\n      /**\n       * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n       */\n      public Builder setScriptUtxos(\n          int index, chronik.Chronik.ScriptUtxos value) {\n        copyOnWrite();\n        instance.setScriptUtxos(index, value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n       */\n      public Builder setScriptUtxos(\n          int index, chronik.Chronik.ScriptUtxos.Builder builderForValue) {\n        copyOnWrite();\n        instance.setScriptUtxos(index,\n            builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n       */\n      public Builder addScriptUtxos(chronik.Chronik.ScriptUtxos value) {\n        copyOnWrite();\n        instance.addScriptUtxos(value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n       */\n      public Builder addScriptUtxos(\n          int index, chronik.Chronik.ScriptUtxos value) {\n        copyOnWrite();\n        instance.addScriptUtxos(index, value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n       */\n      public Builder addScriptUtxos(\n          chronik.Chronik.ScriptUtxos.Builder builderForValue) {\n        copyOnWrite();\n        instance.addScriptUtxos(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n       */\n      public Builder addScriptUtxos(\n          int index, chronik.Chronik.ScriptUtxos.Builder builderForValue) {\n        copyOnWrite();\n        instance.addScriptUtxos(index,\n            builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n       */\n      public Builder addAllScriptUtxos(\n          java.lang.Iterable<? extends chronik.Chronik.ScriptUtxos> values) {\n        copyOnWrite();\n        instance.addAllScriptUtxos(values);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n       */\n      public Builder clearScriptUtxos() {\n        copyOnWrite();\n        instance.clearScriptUtxos();\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.ScriptUtxos script_utxos = 1;</code>\n       */\n      public Builder removeScriptUtxos(int index) {\n        copyOnWrite();\n        instance.removeScriptUtxos(index);\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.Utxos)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.Utxos();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"scriptUtxos_\",\n              chronik.Chronik.ScriptUtxos.class,\n            };\n            java.lang.String info =\n                \"\\u0000\\u0001\\u0000\\u0000\\u0001\\u0001\\u0001\\u0000\\u0001\\u0000\\u0001\\u001b\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.Utxos> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.Utxos.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.Utxos>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.Utxos)\n    private static final chronik.Chronik.Utxos DEFAULT_INSTANCE;\n    static {\n      Utxos defaultInstance = new Utxos();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        Utxos.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.Utxos getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<Utxos> PARSER;\n\n    public static com.google.protobuf.Parser<Utxos> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface BlocksOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.Blocks)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n     */\n    java.util.List<chronik.Chronik.BlockInfo> \n        getBlocksList();\n    /**\n     * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n     */\n    chronik.Chronik.BlockInfo getBlocks(int index);\n    /**\n     * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n     */\n    int getBlocksCount();\n  }\n  /**\n   * Protobuf type {@code chronik.Blocks}\n   */\n  public  static final class Blocks extends\n      com.google.protobuf.GeneratedMessageLite<\n          Blocks, Blocks.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.Blocks)\n      BlocksOrBuilder {\n    private Blocks() {\n      blocks_ = emptyProtobufList();\n    }\n    public static final int BLOCKS_FIELD_NUMBER = 1;\n    private com.google.protobuf.Internal.ProtobufList<chronik.Chronik.BlockInfo> blocks_;\n    /**\n     * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n     */\n    @java.lang.Override\n    public java.util.List<chronik.Chronik.BlockInfo> getBlocksList() {\n      return blocks_;\n    }\n    /**\n     * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n     */\n    public java.util.List<? extends chronik.Chronik.BlockInfoOrBuilder> \n        getBlocksOrBuilderList() {\n      return blocks_;\n    }\n    /**\n     * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n     */\n    @java.lang.Override\n    public int getBlocksCount() {\n      return blocks_.size();\n    }\n    /**\n     * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.BlockInfo getBlocks(int index) {\n      return blocks_.get(index);\n    }\n    /**\n     * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n     */\n    public chronik.Chronik.BlockInfoOrBuilder getBlocksOrBuilder(\n        int index) {\n      return blocks_.get(index);\n    }\n    private void ensureBlocksIsMutable() {\n      com.google.protobuf.Internal.ProtobufList<chronik.Chronik.BlockInfo> tmp = blocks_;\n      if (!tmp.isModifiable()) {\n        blocks_ =\n            com.google.protobuf.GeneratedMessageLite.mutableCopy(tmp);\n       }\n    }\n\n    /**\n     * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n     */\n    private void setBlocks(\n        int index, chronik.Chronik.BlockInfo value) {\n      value.getClass();\n  ensureBlocksIsMutable();\n      blocks_.set(index, value);\n    }\n    /**\n     * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n     */\n    private void addBlocks(chronik.Chronik.BlockInfo value) {\n      value.getClass();\n  ensureBlocksIsMutable();\n      blocks_.add(value);\n    }\n    /**\n     * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n     */\n    private void addBlocks(\n        int index, chronik.Chronik.BlockInfo value) {\n      value.getClass();\n  ensureBlocksIsMutable();\n      blocks_.add(index, value);\n    }\n    /**\n     * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n     */\n    private void addAllBlocks(\n        java.lang.Iterable<? extends chronik.Chronik.BlockInfo> values) {\n      ensureBlocksIsMutable();\n      com.google.protobuf.AbstractMessageLite.addAll(\n          values, blocks_);\n    }\n    /**\n     * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n     */\n    private void clearBlocks() {\n      blocks_ = emptyProtobufList();\n    }\n    /**\n     * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n     */\n    private void removeBlocks(int index) {\n      ensureBlocksIsMutable();\n      blocks_.remove(index);\n    }\n\n    public static chronik.Chronik.Blocks parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.Blocks parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.Blocks parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.Blocks parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.Blocks parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.Blocks parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.Blocks parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.Blocks parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.Blocks parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.Blocks parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.Blocks parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.Blocks parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.Blocks prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.Blocks}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.Blocks, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.Blocks)\n        chronik.Chronik.BlocksOrBuilder {\n      // Construct using chronik.Chronik.Blocks.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n       */\n      @java.lang.Override\n      public java.util.List<chronik.Chronik.BlockInfo> getBlocksList() {\n        return java.util.Collections.unmodifiableList(\n            instance.getBlocksList());\n      }\n      /**\n       * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n       */\n      @java.lang.Override\n      public int getBlocksCount() {\n        return instance.getBlocksCount();\n      }/**\n       * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.BlockInfo getBlocks(int index) {\n        return instance.getBlocks(index);\n      }\n      /**\n       * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n       */\n      public Builder setBlocks(\n          int index, chronik.Chronik.BlockInfo value) {\n        copyOnWrite();\n        instance.setBlocks(index, value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n       */\n      public Builder setBlocks(\n          int index, chronik.Chronik.BlockInfo.Builder builderForValue) {\n        copyOnWrite();\n        instance.setBlocks(index,\n            builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n       */\n      public Builder addBlocks(chronik.Chronik.BlockInfo value) {\n        copyOnWrite();\n        instance.addBlocks(value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n       */\n      public Builder addBlocks(\n          int index, chronik.Chronik.BlockInfo value) {\n        copyOnWrite();\n        instance.addBlocks(index, value);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n       */\n      public Builder addBlocks(\n          chronik.Chronik.BlockInfo.Builder builderForValue) {\n        copyOnWrite();\n        instance.addBlocks(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n       */\n      public Builder addBlocks(\n          int index, chronik.Chronik.BlockInfo.Builder builderForValue) {\n        copyOnWrite();\n        instance.addBlocks(index,\n            builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n       */\n      public Builder addAllBlocks(\n          java.lang.Iterable<? extends chronik.Chronik.BlockInfo> values) {\n        copyOnWrite();\n        instance.addAllBlocks(values);\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n       */\n      public Builder clearBlocks() {\n        copyOnWrite();\n        instance.clearBlocks();\n        return this;\n      }\n      /**\n       * <code>repeated .chronik.BlockInfo blocks = 1;</code>\n       */\n      public Builder removeBlocks(int index) {\n        copyOnWrite();\n        instance.removeBlocks(index);\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.Blocks)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.Blocks();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"blocks_\",\n              chronik.Chronik.BlockInfo.class,\n            };\n            java.lang.String info =\n                \"\\u0000\\u0001\\u0000\\u0000\\u0001\\u0001\\u0001\\u0000\\u0001\\u0000\\u0001\\u001b\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.Blocks> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.Blocks.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.Blocks>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.Blocks)\n    private static final chronik.Chronik.Blocks DEFAULT_INSTANCE;\n    static {\n      Blocks defaultInstance = new Blocks();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        Blocks.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.Blocks getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<Blocks> PARSER;\n\n    public static com.google.protobuf.Parser<Blocks> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface SlpTxDataOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.SlpTxData)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>.chronik.SlpMeta slp_meta = 1;</code>\n     * @return Whether the slpMeta field is set.\n     */\n    boolean hasSlpMeta();\n    /**\n     * <code>.chronik.SlpMeta slp_meta = 1;</code>\n     * @return The slpMeta.\n     */\n    chronik.Chronik.SlpMeta getSlpMeta();\n\n    /**\n     * <code>.chronik.SlpGenesisInfo genesis_info = 2;</code>\n     * @return Whether the genesisInfo field is set.\n     */\n    boolean hasGenesisInfo();\n    /**\n     * <code>.chronik.SlpGenesisInfo genesis_info = 2;</code>\n     * @return The genesisInfo.\n     */\n    chronik.Chronik.SlpGenesisInfo getGenesisInfo();\n  }\n  /**\n   * Protobuf type {@code chronik.SlpTxData}\n   */\n  public  static final class SlpTxData extends\n      com.google.protobuf.GeneratedMessageLite<\n          SlpTxData, SlpTxData.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.SlpTxData)\n      SlpTxDataOrBuilder {\n    private SlpTxData() {\n    }\n    public static final int SLP_META_FIELD_NUMBER = 1;\n    private chronik.Chronik.SlpMeta slpMeta_;\n    /**\n     * <code>.chronik.SlpMeta slp_meta = 1;</code>\n     */\n    @java.lang.Override\n    public boolean hasSlpMeta() {\n      return slpMeta_ != null;\n    }\n    /**\n     * <code>.chronik.SlpMeta slp_meta = 1;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.SlpMeta getSlpMeta() {\n      return slpMeta_ == null ? chronik.Chronik.SlpMeta.getDefaultInstance() : slpMeta_;\n    }\n    /**\n     * <code>.chronik.SlpMeta slp_meta = 1;</code>\n     */\n    private void setSlpMeta(chronik.Chronik.SlpMeta value) {\n      value.getClass();\n  slpMeta_ = value;\n\n      }\n    /**\n     * <code>.chronik.SlpMeta slp_meta = 1;</code>\n     */\n    @java.lang.SuppressWarnings({\"ReferenceEquality\"})\n    private void mergeSlpMeta(chronik.Chronik.SlpMeta value) {\n      value.getClass();\n  if (slpMeta_ != null &&\n          slpMeta_ != chronik.Chronik.SlpMeta.getDefaultInstance()) {\n        slpMeta_ =\n          chronik.Chronik.SlpMeta.newBuilder(slpMeta_).mergeFrom(value).buildPartial();\n      } else {\n        slpMeta_ = value;\n      }\n\n    }\n    /**\n     * <code>.chronik.SlpMeta slp_meta = 1;</code>\n     */\n    private void clearSlpMeta() {  slpMeta_ = null;\n\n    }\n\n    public static final int GENESIS_INFO_FIELD_NUMBER = 2;\n    private chronik.Chronik.SlpGenesisInfo genesisInfo_;\n    /**\n     * <code>.chronik.SlpGenesisInfo genesis_info = 2;</code>\n     */\n    @java.lang.Override\n    public boolean hasGenesisInfo() {\n      return genesisInfo_ != null;\n    }\n    /**\n     * <code>.chronik.SlpGenesisInfo genesis_info = 2;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.SlpGenesisInfo getGenesisInfo() {\n      return genesisInfo_ == null ? chronik.Chronik.SlpGenesisInfo.getDefaultInstance() : genesisInfo_;\n    }\n    /**\n     * <code>.chronik.SlpGenesisInfo genesis_info = 2;</code>\n     */\n    private void setGenesisInfo(chronik.Chronik.SlpGenesisInfo value) {\n      value.getClass();\n  genesisInfo_ = value;\n\n      }\n    /**\n     * <code>.chronik.SlpGenesisInfo genesis_info = 2;</code>\n     */\n    @java.lang.SuppressWarnings({\"ReferenceEquality\"})\n    private void mergeGenesisInfo(chronik.Chronik.SlpGenesisInfo value) {\n      value.getClass();\n  if (genesisInfo_ != null &&\n          genesisInfo_ != chronik.Chronik.SlpGenesisInfo.getDefaultInstance()) {\n        genesisInfo_ =\n          chronik.Chronik.SlpGenesisInfo.newBuilder(genesisInfo_).mergeFrom(value).buildPartial();\n      } else {\n        genesisInfo_ = value;\n      }\n\n    }\n    /**\n     * <code>.chronik.SlpGenesisInfo genesis_info = 2;</code>\n     */\n    private void clearGenesisInfo() {  genesisInfo_ = null;\n\n    }\n\n    public static chronik.Chronik.SlpTxData parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.SlpTxData parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.SlpTxData parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.SlpTxData parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.SlpTxData parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.SlpTxData parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.SlpTxData parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.SlpTxData parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.SlpTxData parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.SlpTxData parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.SlpTxData parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.SlpTxData parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.SlpTxData prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.SlpTxData}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.SlpTxData, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.SlpTxData)\n        chronik.Chronik.SlpTxDataOrBuilder {\n      // Construct using chronik.Chronik.SlpTxData.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>.chronik.SlpMeta slp_meta = 1;</code>\n       */\n      @java.lang.Override\n      public boolean hasSlpMeta() {\n        return instance.hasSlpMeta();\n      }\n      /**\n       * <code>.chronik.SlpMeta slp_meta = 1;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.SlpMeta getSlpMeta() {\n        return instance.getSlpMeta();\n      }\n      /**\n       * <code>.chronik.SlpMeta slp_meta = 1;</code>\n       */\n      public Builder setSlpMeta(chronik.Chronik.SlpMeta value) {\n        copyOnWrite();\n        instance.setSlpMeta(value);\n        return this;\n        }\n      /**\n       * <code>.chronik.SlpMeta slp_meta = 1;</code>\n       */\n      public Builder setSlpMeta(\n          chronik.Chronik.SlpMeta.Builder builderForValue) {\n        copyOnWrite();\n        instance.setSlpMeta(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>.chronik.SlpMeta slp_meta = 1;</code>\n       */\n      public Builder mergeSlpMeta(chronik.Chronik.SlpMeta value) {\n        copyOnWrite();\n        instance.mergeSlpMeta(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.SlpMeta slp_meta = 1;</code>\n       */\n      public Builder clearSlpMeta() {  copyOnWrite();\n        instance.clearSlpMeta();\n        return this;\n      }\n\n      /**\n       * <code>.chronik.SlpGenesisInfo genesis_info = 2;</code>\n       */\n      @java.lang.Override\n      public boolean hasGenesisInfo() {\n        return instance.hasGenesisInfo();\n      }\n      /**\n       * <code>.chronik.SlpGenesisInfo genesis_info = 2;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.SlpGenesisInfo getGenesisInfo() {\n        return instance.getGenesisInfo();\n      }\n      /**\n       * <code>.chronik.SlpGenesisInfo genesis_info = 2;</code>\n       */\n      public Builder setGenesisInfo(chronik.Chronik.SlpGenesisInfo value) {\n        copyOnWrite();\n        instance.setGenesisInfo(value);\n        return this;\n        }\n      /**\n       * <code>.chronik.SlpGenesisInfo genesis_info = 2;</code>\n       */\n      public Builder setGenesisInfo(\n          chronik.Chronik.SlpGenesisInfo.Builder builderForValue) {\n        copyOnWrite();\n        instance.setGenesisInfo(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>.chronik.SlpGenesisInfo genesis_info = 2;</code>\n       */\n      public Builder mergeGenesisInfo(chronik.Chronik.SlpGenesisInfo value) {\n        copyOnWrite();\n        instance.mergeGenesisInfo(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.SlpGenesisInfo genesis_info = 2;</code>\n       */\n      public Builder clearGenesisInfo() {  copyOnWrite();\n        instance.clearGenesisInfo();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.SlpTxData)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.SlpTxData();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"slpMeta_\",\n              \"genesisInfo_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0002\\u0000\\u0000\\u0001\\u0002\\u0002\\u0000\\u0000\\u0000\\u0001\\t\\u0002\\t\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.SlpTxData> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.SlpTxData.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.SlpTxData>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.SlpTxData)\n    private static final chronik.Chronik.SlpTxData DEFAULT_INSTANCE;\n    static {\n      SlpTxData defaultInstance = new SlpTxData();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        SlpTxData.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.SlpTxData getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<SlpTxData> PARSER;\n\n    public static com.google.protobuf.Parser<SlpTxData> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface SlpMetaOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.SlpMeta)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>.chronik.SlpTokenType token_type = 1;</code>\n     * @return The enum numeric value on the wire for tokenType.\n     */\n    int getTokenTypeValue();\n    /**\n     * <code>.chronik.SlpTokenType token_type = 1;</code>\n     * @return The tokenType.\n     */\n    chronik.Chronik.SlpTokenType getTokenType();\n\n    /**\n     * <code>.chronik.SlpTxType tx_type = 2;</code>\n     * @return The enum numeric value on the wire for txType.\n     */\n    int getTxTypeValue();\n    /**\n     * <code>.chronik.SlpTxType tx_type = 2;</code>\n     * @return The txType.\n     */\n    chronik.Chronik.SlpTxType getTxType();\n\n    /**\n     * <code>bytes token_id = 3;</code>\n     * @return The tokenId.\n     */\n    com.google.protobuf.ByteString getTokenId();\n\n    /**\n     * <code>bytes group_token_id = 4;</code>\n     * @return The groupTokenId.\n     */\n    com.google.protobuf.ByteString getGroupTokenId();\n  }\n  /**\n   * Protobuf type {@code chronik.SlpMeta}\n   */\n  public  static final class SlpMeta extends\n      com.google.protobuf.GeneratedMessageLite<\n          SlpMeta, SlpMeta.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.SlpMeta)\n      SlpMetaOrBuilder {\n    private SlpMeta() {\n      tokenId_ = com.google.protobuf.ByteString.EMPTY;\n      groupTokenId_ = com.google.protobuf.ByteString.EMPTY;\n    }\n    public static final int TOKEN_TYPE_FIELD_NUMBER = 1;\n    private int tokenType_;\n    /**\n     * <code>.chronik.SlpTokenType token_type = 1;</code>\n     * @return The enum numeric value on the wire for tokenType.\n     */\n    @java.lang.Override\n    public int getTokenTypeValue() {\n      return tokenType_;\n    }\n    /**\n     * <code>.chronik.SlpTokenType token_type = 1;</code>\n     * @return The tokenType.\n     */\n    @java.lang.Override\n    public chronik.Chronik.SlpTokenType getTokenType() {\n      chronik.Chronik.SlpTokenType result = chronik.Chronik.SlpTokenType.forNumber(tokenType_);\n      return result == null ? chronik.Chronik.SlpTokenType.UNRECOGNIZED : result;\n    }\n    /**\n     * <code>.chronik.SlpTokenType token_type = 1;</code>\n     * @param value The enum numeric value on the wire for tokenType to set.\n     */\n    private void setTokenTypeValue(int value) {\n        tokenType_ = value;\n    }\n    /**\n     * <code>.chronik.SlpTokenType token_type = 1;</code>\n     * @param value The tokenType to set.\n     */\n    private void setTokenType(chronik.Chronik.SlpTokenType value) {\n      tokenType_ = value.getNumber();\n\n    }\n    /**\n     * <code>.chronik.SlpTokenType token_type = 1;</code>\n     */\n    private void clearTokenType() {\n\n      tokenType_ = 0;\n    }\n\n    public static final int TX_TYPE_FIELD_NUMBER = 2;\n    private int txType_;\n    /**\n     * <code>.chronik.SlpTxType tx_type = 2;</code>\n     * @return The enum numeric value on the wire for txType.\n     */\n    @java.lang.Override\n    public int getTxTypeValue() {\n      return txType_;\n    }\n    /**\n     * <code>.chronik.SlpTxType tx_type = 2;</code>\n     * @return The txType.\n     */\n    @java.lang.Override\n    public chronik.Chronik.SlpTxType getTxType() {\n      chronik.Chronik.SlpTxType result = chronik.Chronik.SlpTxType.forNumber(txType_);\n      return result == null ? chronik.Chronik.SlpTxType.UNRECOGNIZED : result;\n    }\n    /**\n     * <code>.chronik.SlpTxType tx_type = 2;</code>\n     * @param value The enum numeric value on the wire for txType to set.\n     */\n    private void setTxTypeValue(int value) {\n        txType_ = value;\n    }\n    /**\n     * <code>.chronik.SlpTxType tx_type = 2;</code>\n     * @param value The txType to set.\n     */\n    private void setTxType(chronik.Chronik.SlpTxType value) {\n      txType_ = value.getNumber();\n\n    }\n    /**\n     * <code>.chronik.SlpTxType tx_type = 2;</code>\n     */\n    private void clearTxType() {\n\n      txType_ = 0;\n    }\n\n    public static final int TOKEN_ID_FIELD_NUMBER = 3;\n    private com.google.protobuf.ByteString tokenId_;\n    /**\n     * <code>bytes token_id = 3;</code>\n     * @return The tokenId.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getTokenId() {\n      return tokenId_;\n    }\n    /**\n     * <code>bytes token_id = 3;</code>\n     * @param value The tokenId to set.\n     */\n    private void setTokenId(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      tokenId_ = value;\n    }\n    /**\n     * <code>bytes token_id = 3;</code>\n     */\n    private void clearTokenId() {\n\n      tokenId_ = getDefaultInstance().getTokenId();\n    }\n\n    public static final int GROUP_TOKEN_ID_FIELD_NUMBER = 4;\n    private com.google.protobuf.ByteString groupTokenId_;\n    /**\n     * <code>bytes group_token_id = 4;</code>\n     * @return The groupTokenId.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getGroupTokenId() {\n      return groupTokenId_;\n    }\n    /**\n     * <code>bytes group_token_id = 4;</code>\n     * @param value The groupTokenId to set.\n     */\n    private void setGroupTokenId(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      groupTokenId_ = value;\n    }\n    /**\n     * <code>bytes group_token_id = 4;</code>\n     */\n    private void clearGroupTokenId() {\n\n      groupTokenId_ = getDefaultInstance().getGroupTokenId();\n    }\n\n    public static chronik.Chronik.SlpMeta parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.SlpMeta parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.SlpMeta parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.SlpMeta parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.SlpMeta parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.SlpMeta parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.SlpMeta parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.SlpMeta parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.SlpMeta parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.SlpMeta parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.SlpMeta parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.SlpMeta parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.SlpMeta prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.SlpMeta}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.SlpMeta, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.SlpMeta)\n        chronik.Chronik.SlpMetaOrBuilder {\n      // Construct using chronik.Chronik.SlpMeta.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>.chronik.SlpTokenType token_type = 1;</code>\n       * @return The enum numeric value on the wire for tokenType.\n       */\n      @java.lang.Override\n      public int getTokenTypeValue() {\n        return instance.getTokenTypeValue();\n      }\n      /**\n       * <code>.chronik.SlpTokenType token_type = 1;</code>\n       * @param value The tokenType to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTokenTypeValue(int value) {\n        copyOnWrite();\n        instance.setTokenTypeValue(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.SlpTokenType token_type = 1;</code>\n       * @return The tokenType.\n       */\n      @java.lang.Override\n      public chronik.Chronik.SlpTokenType getTokenType() {\n        return instance.getTokenType();\n      }\n      /**\n       * <code>.chronik.SlpTokenType token_type = 1;</code>\n       * @param value The enum numeric value on the wire for tokenType to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTokenType(chronik.Chronik.SlpTokenType value) {\n        copyOnWrite();\n        instance.setTokenType(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.SlpTokenType token_type = 1;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearTokenType() {\n        copyOnWrite();\n        instance.clearTokenType();\n        return this;\n      }\n\n      /**\n       * <code>.chronik.SlpTxType tx_type = 2;</code>\n       * @return The enum numeric value on the wire for txType.\n       */\n      @java.lang.Override\n      public int getTxTypeValue() {\n        return instance.getTxTypeValue();\n      }\n      /**\n       * <code>.chronik.SlpTxType tx_type = 2;</code>\n       * @param value The txType to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTxTypeValue(int value) {\n        copyOnWrite();\n        instance.setTxTypeValue(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.SlpTxType tx_type = 2;</code>\n       * @return The txType.\n       */\n      @java.lang.Override\n      public chronik.Chronik.SlpTxType getTxType() {\n        return instance.getTxType();\n      }\n      /**\n       * <code>.chronik.SlpTxType tx_type = 2;</code>\n       * @param value The enum numeric value on the wire for txType to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTxType(chronik.Chronik.SlpTxType value) {\n        copyOnWrite();\n        instance.setTxType(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.SlpTxType tx_type = 2;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearTxType() {\n        copyOnWrite();\n        instance.clearTxType();\n        return this;\n      }\n\n      /**\n       * <code>bytes token_id = 3;</code>\n       * @return The tokenId.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getTokenId() {\n        return instance.getTokenId();\n      }\n      /**\n       * <code>bytes token_id = 3;</code>\n       * @param value The tokenId to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTokenId(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setTokenId(value);\n        return this;\n      }\n      /**\n       * <code>bytes token_id = 3;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearTokenId() {\n        copyOnWrite();\n        instance.clearTokenId();\n        return this;\n      }\n\n      /**\n       * <code>bytes group_token_id = 4;</code>\n       * @return The groupTokenId.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getGroupTokenId() {\n        return instance.getGroupTokenId();\n      }\n      /**\n       * <code>bytes group_token_id = 4;</code>\n       * @param value The groupTokenId to set.\n       * @return This builder for chaining.\n       */\n      public Builder setGroupTokenId(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setGroupTokenId(value);\n        return this;\n      }\n      /**\n       * <code>bytes group_token_id = 4;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearGroupTokenId() {\n        copyOnWrite();\n        instance.clearGroupTokenId();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.SlpMeta)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.SlpMeta();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"tokenType_\",\n              \"txType_\",\n              \"tokenId_\",\n              \"groupTokenId_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0004\\u0000\\u0000\\u0001\\u0004\\u0004\\u0000\\u0000\\u0000\\u0001\\f\\u0002\\f\\u0003\" +\n                \"\\n\\u0004\\n\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.SlpMeta> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.SlpMeta.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.SlpMeta>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.SlpMeta)\n    private static final chronik.Chronik.SlpMeta DEFAULT_INSTANCE;\n    static {\n      SlpMeta defaultInstance = new SlpMeta();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        SlpMeta.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.SlpMeta getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<SlpMeta> PARSER;\n\n    public static com.google.protobuf.Parser<SlpMeta> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface TokenStatsOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.TokenStats)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <pre>\n     * This doesn't fit into uint64, so we use a string with the decimal\n     * representation. If available, use i128 to parse, otherwise some\n     * BigNumber library.\n     * </pre>\n     *\n     * <code>string total_minted = 1;</code>\n     * @return The totalMinted.\n     */\n    java.lang.String getTotalMinted();\n    /**\n     * <pre>\n     * This doesn't fit into uint64, so we use a string with the decimal\n     * representation. If available, use i128 to parse, otherwise some\n     * BigNumber library.\n     * </pre>\n     *\n     * <code>string total_minted = 1;</code>\n     * @return The bytes for totalMinted.\n     */\n    com.google.protobuf.ByteString\n        getTotalMintedBytes();\n\n    /**\n     * <code>string total_burned = 2;</code>\n     * @return The totalBurned.\n     */\n    java.lang.String getTotalBurned();\n    /**\n     * <code>string total_burned = 2;</code>\n     * @return The bytes for totalBurned.\n     */\n    com.google.protobuf.ByteString\n        getTotalBurnedBytes();\n  }\n  /**\n   * Protobuf type {@code chronik.TokenStats}\n   */\n  public  static final class TokenStats extends\n      com.google.protobuf.GeneratedMessageLite<\n          TokenStats, TokenStats.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.TokenStats)\n      TokenStatsOrBuilder {\n    private TokenStats() {\n      totalMinted_ = \"\";\n      totalBurned_ = \"\";\n    }\n    public static final int TOTAL_MINTED_FIELD_NUMBER = 1;\n    private java.lang.String totalMinted_;\n    /**\n     * <pre>\n     * This doesn't fit into uint64, so we use a string with the decimal\n     * representation. If available, use i128 to parse, otherwise some\n     * BigNumber library.\n     * </pre>\n     *\n     * <code>string total_minted = 1;</code>\n     * @return The totalMinted.\n     */\n    @java.lang.Override\n    public java.lang.String getTotalMinted() {\n      return totalMinted_;\n    }\n    /**\n     * <pre>\n     * This doesn't fit into uint64, so we use a string with the decimal\n     * representation. If available, use i128 to parse, otherwise some\n     * BigNumber library.\n     * </pre>\n     *\n     * <code>string total_minted = 1;</code>\n     * @return The bytes for totalMinted.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString\n        getTotalMintedBytes() {\n      return com.google.protobuf.ByteString.copyFromUtf8(totalMinted_);\n    }\n    /**\n     * <pre>\n     * This doesn't fit into uint64, so we use a string with the decimal\n     * representation. If available, use i128 to parse, otherwise some\n     * BigNumber library.\n     * </pre>\n     *\n     * <code>string total_minted = 1;</code>\n     * @param value The totalMinted to set.\n     */\n    private void setTotalMinted(\n        java.lang.String value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      totalMinted_ = value;\n    }\n    /**\n     * <pre>\n     * This doesn't fit into uint64, so we use a string with the decimal\n     * representation. If available, use i128 to parse, otherwise some\n     * BigNumber library.\n     * </pre>\n     *\n     * <code>string total_minted = 1;</code>\n     */\n    private void clearTotalMinted() {\n\n      totalMinted_ = getDefaultInstance().getTotalMinted();\n    }\n    /**\n     * <pre>\n     * This doesn't fit into uint64, so we use a string with the decimal\n     * representation. If available, use i128 to parse, otherwise some\n     * BigNumber library.\n     * </pre>\n     *\n     * <code>string total_minted = 1;</code>\n     * @param value The bytes for totalMinted to set.\n     */\n    private void setTotalMintedBytes(\n        com.google.protobuf.ByteString value) {\n      checkByteStringIsUtf8(value);\n      totalMinted_ = value.toStringUtf8();\n\n    }\n\n    public static final int TOTAL_BURNED_FIELD_NUMBER = 2;\n    private java.lang.String totalBurned_;\n    /**\n     * <code>string total_burned = 2;</code>\n     * @return The totalBurned.\n     */\n    @java.lang.Override\n    public java.lang.String getTotalBurned() {\n      return totalBurned_;\n    }\n    /**\n     * <code>string total_burned = 2;</code>\n     * @return The bytes for totalBurned.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString\n        getTotalBurnedBytes() {\n      return com.google.protobuf.ByteString.copyFromUtf8(totalBurned_);\n    }\n    /**\n     * <code>string total_burned = 2;</code>\n     * @param value The totalBurned to set.\n     */\n    private void setTotalBurned(\n        java.lang.String value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      totalBurned_ = value;\n    }\n    /**\n     * <code>string total_burned = 2;</code>\n     */\n    private void clearTotalBurned() {\n\n      totalBurned_ = getDefaultInstance().getTotalBurned();\n    }\n    /**\n     * <code>string total_burned = 2;</code>\n     * @param value The bytes for totalBurned to set.\n     */\n    private void setTotalBurnedBytes(\n        com.google.protobuf.ByteString value) {\n      checkByteStringIsUtf8(value);\n      totalBurned_ = value.toStringUtf8();\n\n    }\n\n    public static chronik.Chronik.TokenStats parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.TokenStats parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.TokenStats parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.TokenStats parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.TokenStats parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.TokenStats parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.TokenStats parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.TokenStats parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.TokenStats parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.TokenStats parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.TokenStats parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.TokenStats parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.TokenStats prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.TokenStats}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.TokenStats, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.TokenStats)\n        chronik.Chronik.TokenStatsOrBuilder {\n      // Construct using chronik.Chronik.TokenStats.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <pre>\n       * This doesn't fit into uint64, so we use a string with the decimal\n       * representation. If available, use i128 to parse, otherwise some\n       * BigNumber library.\n       * </pre>\n       *\n       * <code>string total_minted = 1;</code>\n       * @return The totalMinted.\n       */\n      @java.lang.Override\n      public java.lang.String getTotalMinted() {\n        return instance.getTotalMinted();\n      }\n      /**\n       * <pre>\n       * This doesn't fit into uint64, so we use a string with the decimal\n       * representation. If available, use i128 to parse, otherwise some\n       * BigNumber library.\n       * </pre>\n       *\n       * <code>string total_minted = 1;</code>\n       * @return The bytes for totalMinted.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString\n          getTotalMintedBytes() {\n        return instance.getTotalMintedBytes();\n      }\n      /**\n       * <pre>\n       * This doesn't fit into uint64, so we use a string with the decimal\n       * representation. If available, use i128 to parse, otherwise some\n       * BigNumber library.\n       * </pre>\n       *\n       * <code>string total_minted = 1;</code>\n       * @param value The totalMinted to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTotalMinted(\n          java.lang.String value) {\n        copyOnWrite();\n        instance.setTotalMinted(value);\n        return this;\n      }\n      /**\n       * <pre>\n       * This doesn't fit into uint64, so we use a string with the decimal\n       * representation. If available, use i128 to parse, otherwise some\n       * BigNumber library.\n       * </pre>\n       *\n       * <code>string total_minted = 1;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearTotalMinted() {\n        copyOnWrite();\n        instance.clearTotalMinted();\n        return this;\n      }\n      /**\n       * <pre>\n       * This doesn't fit into uint64, so we use a string with the decimal\n       * representation. If available, use i128 to parse, otherwise some\n       * BigNumber library.\n       * </pre>\n       *\n       * <code>string total_minted = 1;</code>\n       * @param value The bytes for totalMinted to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTotalMintedBytes(\n          com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setTotalMintedBytes(value);\n        return this;\n      }\n\n      /**\n       * <code>string total_burned = 2;</code>\n       * @return The totalBurned.\n       */\n      @java.lang.Override\n      public java.lang.String getTotalBurned() {\n        return instance.getTotalBurned();\n      }\n      /**\n       * <code>string total_burned = 2;</code>\n       * @return The bytes for totalBurned.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString\n          getTotalBurnedBytes() {\n        return instance.getTotalBurnedBytes();\n      }\n      /**\n       * <code>string total_burned = 2;</code>\n       * @param value The totalBurned to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTotalBurned(\n          java.lang.String value) {\n        copyOnWrite();\n        instance.setTotalBurned(value);\n        return this;\n      }\n      /**\n       * <code>string total_burned = 2;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearTotalBurned() {\n        copyOnWrite();\n        instance.clearTotalBurned();\n        return this;\n      }\n      /**\n       * <code>string total_burned = 2;</code>\n       * @param value The bytes for totalBurned to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTotalBurnedBytes(\n          com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setTotalBurnedBytes(value);\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.TokenStats)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.TokenStats();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"totalMinted_\",\n              \"totalBurned_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0002\\u0000\\u0000\\u0001\\u0002\\u0002\\u0000\\u0000\\u0000\\u0001\\u0208\\u0002\\u0208\" +\n                \"\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.TokenStats> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.TokenStats.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.TokenStats>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.TokenStats)\n    private static final chronik.Chronik.TokenStats DEFAULT_INSTANCE;\n    static {\n      TokenStats defaultInstance = new TokenStats();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        TokenStats.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.TokenStats getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<TokenStats> PARSER;\n\n    public static com.google.protobuf.Parser<TokenStats> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface TxInputOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.TxInput)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>.chronik.OutPoint prev_out = 1;</code>\n     * @return Whether the prevOut field is set.\n     */\n    boolean hasPrevOut();\n    /**\n     * <code>.chronik.OutPoint prev_out = 1;</code>\n     * @return The prevOut.\n     */\n    chronik.Chronik.OutPoint getPrevOut();\n\n    /**\n     * <code>bytes input_script = 2;</code>\n     * @return The inputScript.\n     */\n    com.google.protobuf.ByteString getInputScript();\n\n    /**\n     * <code>bytes output_script = 3;</code>\n     * @return The outputScript.\n     */\n    com.google.protobuf.ByteString getOutputScript();\n\n    /**\n     * <code>int64 value = 4;</code>\n     * @return The value.\n     */\n    long getValue();\n\n    /**\n     * <code>uint32 sequence_no = 5;</code>\n     * @return The sequenceNo.\n     */\n    int getSequenceNo();\n\n    /**\n     * <code>.chronik.SlpBurn slp_burn = 6;</code>\n     * @return Whether the slpBurn field is set.\n     */\n    boolean hasSlpBurn();\n    /**\n     * <code>.chronik.SlpBurn slp_burn = 6;</code>\n     * @return The slpBurn.\n     */\n    chronik.Chronik.SlpBurn getSlpBurn();\n\n    /**\n     * <code>.chronik.SlpToken slp_token = 7;</code>\n     * @return Whether the slpToken field is set.\n     */\n    boolean hasSlpToken();\n    /**\n     * <code>.chronik.SlpToken slp_token = 7;</code>\n     * @return The slpToken.\n     */\n    chronik.Chronik.SlpToken getSlpToken();\n  }\n  /**\n   * Protobuf type {@code chronik.TxInput}\n   */\n  public  static final class TxInput extends\n      com.google.protobuf.GeneratedMessageLite<\n          TxInput, TxInput.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.TxInput)\n      TxInputOrBuilder {\n    private TxInput() {\n      inputScript_ = com.google.protobuf.ByteString.EMPTY;\n      outputScript_ = com.google.protobuf.ByteString.EMPTY;\n    }\n    public static final int PREV_OUT_FIELD_NUMBER = 1;\n    private chronik.Chronik.OutPoint prevOut_;\n    /**\n     * <code>.chronik.OutPoint prev_out = 1;</code>\n     */\n    @java.lang.Override\n    public boolean hasPrevOut() {\n      return prevOut_ != null;\n    }\n    /**\n     * <code>.chronik.OutPoint prev_out = 1;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.OutPoint getPrevOut() {\n      return prevOut_ == null ? chronik.Chronik.OutPoint.getDefaultInstance() : prevOut_;\n    }\n    /**\n     * <code>.chronik.OutPoint prev_out = 1;</code>\n     */\n    private void setPrevOut(chronik.Chronik.OutPoint value) {\n      value.getClass();\n  prevOut_ = value;\n\n      }\n    /**\n     * <code>.chronik.OutPoint prev_out = 1;</code>\n     */\n    @java.lang.SuppressWarnings({\"ReferenceEquality\"})\n    private void mergePrevOut(chronik.Chronik.OutPoint value) {\n      value.getClass();\n  if (prevOut_ != null &&\n          prevOut_ != chronik.Chronik.OutPoint.getDefaultInstance()) {\n        prevOut_ =\n          chronik.Chronik.OutPoint.newBuilder(prevOut_).mergeFrom(value).buildPartial();\n      } else {\n        prevOut_ = value;\n      }\n\n    }\n    /**\n     * <code>.chronik.OutPoint prev_out = 1;</code>\n     */\n    private void clearPrevOut() {  prevOut_ = null;\n\n    }\n\n    public static final int INPUT_SCRIPT_FIELD_NUMBER = 2;\n    private com.google.protobuf.ByteString inputScript_;\n    /**\n     * <code>bytes input_script = 2;</code>\n     * @return The inputScript.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getInputScript() {\n      return inputScript_;\n    }\n    /**\n     * <code>bytes input_script = 2;</code>\n     * @param value The inputScript to set.\n     */\n    private void setInputScript(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      inputScript_ = value;\n    }\n    /**\n     * <code>bytes input_script = 2;</code>\n     */\n    private void clearInputScript() {\n\n      inputScript_ = getDefaultInstance().getInputScript();\n    }\n\n    public static final int OUTPUT_SCRIPT_FIELD_NUMBER = 3;\n    private com.google.protobuf.ByteString outputScript_;\n    /**\n     * <code>bytes output_script = 3;</code>\n     * @return The outputScript.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getOutputScript() {\n      return outputScript_;\n    }\n    /**\n     * <code>bytes output_script = 3;</code>\n     * @param value The outputScript to set.\n     */\n    private void setOutputScript(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      outputScript_ = value;\n    }\n    /**\n     * <code>bytes output_script = 3;</code>\n     */\n    private void clearOutputScript() {\n\n      outputScript_ = getDefaultInstance().getOutputScript();\n    }\n\n    public static final int VALUE_FIELD_NUMBER = 4;\n    private long value_;\n    /**\n     * <code>int64 value = 4;</code>\n     * @return The value.\n     */\n    @java.lang.Override\n    public long getValue() {\n      return value_;\n    }\n    /**\n     * <code>int64 value = 4;</code>\n     * @param value The value to set.\n     */\n    private void setValue(long value) {\n      \n      value_ = value;\n    }\n    /**\n     * <code>int64 value = 4;</code>\n     */\n    private void clearValue() {\n\n      value_ = 0L;\n    }\n\n    public static final int SEQUENCE_NO_FIELD_NUMBER = 5;\n    private int sequenceNo_;\n    /**\n     * <code>uint32 sequence_no = 5;</code>\n     * @return The sequenceNo.\n     */\n    @java.lang.Override\n    public int getSequenceNo() {\n      return sequenceNo_;\n    }\n    /**\n     * <code>uint32 sequence_no = 5;</code>\n     * @param value The sequenceNo to set.\n     */\n    private void setSequenceNo(int value) {\n      \n      sequenceNo_ = value;\n    }\n    /**\n     * <code>uint32 sequence_no = 5;</code>\n     */\n    private void clearSequenceNo() {\n\n      sequenceNo_ = 0;\n    }\n\n    public static final int SLP_BURN_FIELD_NUMBER = 6;\n    private chronik.Chronik.SlpBurn slpBurn_;\n    /**\n     * <code>.chronik.SlpBurn slp_burn = 6;</code>\n     */\n    @java.lang.Override\n    public boolean hasSlpBurn() {\n      return slpBurn_ != null;\n    }\n    /**\n     * <code>.chronik.SlpBurn slp_burn = 6;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.SlpBurn getSlpBurn() {\n      return slpBurn_ == null ? chronik.Chronik.SlpBurn.getDefaultInstance() : slpBurn_;\n    }\n    /**\n     * <code>.chronik.SlpBurn slp_burn = 6;</code>\n     */\n    private void setSlpBurn(chronik.Chronik.SlpBurn value) {\n      value.getClass();\n  slpBurn_ = value;\n\n      }\n    /**\n     * <code>.chronik.SlpBurn slp_burn = 6;</code>\n     */\n    @java.lang.SuppressWarnings({\"ReferenceEquality\"})\n    private void mergeSlpBurn(chronik.Chronik.SlpBurn value) {\n      value.getClass();\n  if (slpBurn_ != null &&\n          slpBurn_ != chronik.Chronik.SlpBurn.getDefaultInstance()) {\n        slpBurn_ =\n          chronik.Chronik.SlpBurn.newBuilder(slpBurn_).mergeFrom(value).buildPartial();\n      } else {\n        slpBurn_ = value;\n      }\n\n    }\n    /**\n     * <code>.chronik.SlpBurn slp_burn = 6;</code>\n     */\n    private void clearSlpBurn() {  slpBurn_ = null;\n\n    }\n\n    public static final int SLP_TOKEN_FIELD_NUMBER = 7;\n    private chronik.Chronik.SlpToken slpToken_;\n    /**\n     * <code>.chronik.SlpToken slp_token = 7;</code>\n     */\n    @java.lang.Override\n    public boolean hasSlpToken() {\n      return slpToken_ != null;\n    }\n    /**\n     * <code>.chronik.SlpToken slp_token = 7;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.SlpToken getSlpToken() {\n      return slpToken_ == null ? chronik.Chronik.SlpToken.getDefaultInstance() : slpToken_;\n    }\n    /**\n     * <code>.chronik.SlpToken slp_token = 7;</code>\n     */\n    private void setSlpToken(chronik.Chronik.SlpToken value) {\n      value.getClass();\n  slpToken_ = value;\n\n      }\n    /**\n     * <code>.chronik.SlpToken slp_token = 7;</code>\n     */\n    @java.lang.SuppressWarnings({\"ReferenceEquality\"})\n    private void mergeSlpToken(chronik.Chronik.SlpToken value) {\n      value.getClass();\n  if (slpToken_ != null &&\n          slpToken_ != chronik.Chronik.SlpToken.getDefaultInstance()) {\n        slpToken_ =\n          chronik.Chronik.SlpToken.newBuilder(slpToken_).mergeFrom(value).buildPartial();\n      } else {\n        slpToken_ = value;\n      }\n\n    }\n    /**\n     * <code>.chronik.SlpToken slp_token = 7;</code>\n     */\n    private void clearSlpToken() {  slpToken_ = null;\n\n    }\n\n    public static chronik.Chronik.TxInput parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.TxInput parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.TxInput parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.TxInput parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.TxInput parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.TxInput parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.TxInput parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.TxInput parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.TxInput parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.TxInput parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.TxInput parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.TxInput parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.TxInput prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.TxInput}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.TxInput, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.TxInput)\n        chronik.Chronik.TxInputOrBuilder {\n      // Construct using chronik.Chronik.TxInput.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>.chronik.OutPoint prev_out = 1;</code>\n       */\n      @java.lang.Override\n      public boolean hasPrevOut() {\n        return instance.hasPrevOut();\n      }\n      /**\n       * <code>.chronik.OutPoint prev_out = 1;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.OutPoint getPrevOut() {\n        return instance.getPrevOut();\n      }\n      /**\n       * <code>.chronik.OutPoint prev_out = 1;</code>\n       */\n      public Builder setPrevOut(chronik.Chronik.OutPoint value) {\n        copyOnWrite();\n        instance.setPrevOut(value);\n        return this;\n        }\n      /**\n       * <code>.chronik.OutPoint prev_out = 1;</code>\n       */\n      public Builder setPrevOut(\n          chronik.Chronik.OutPoint.Builder builderForValue) {\n        copyOnWrite();\n        instance.setPrevOut(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>.chronik.OutPoint prev_out = 1;</code>\n       */\n      public Builder mergePrevOut(chronik.Chronik.OutPoint value) {\n        copyOnWrite();\n        instance.mergePrevOut(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.OutPoint prev_out = 1;</code>\n       */\n      public Builder clearPrevOut() {  copyOnWrite();\n        instance.clearPrevOut();\n        return this;\n      }\n\n      /**\n       * <code>bytes input_script = 2;</code>\n       * @return The inputScript.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getInputScript() {\n        return instance.getInputScript();\n      }\n      /**\n       * <code>bytes input_script = 2;</code>\n       * @param value The inputScript to set.\n       * @return This builder for chaining.\n       */\n      public Builder setInputScript(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setInputScript(value);\n        return this;\n      }\n      /**\n       * <code>bytes input_script = 2;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearInputScript() {\n        copyOnWrite();\n        instance.clearInputScript();\n        return this;\n      }\n\n      /**\n       * <code>bytes output_script = 3;</code>\n       * @return The outputScript.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getOutputScript() {\n        return instance.getOutputScript();\n      }\n      /**\n       * <code>bytes output_script = 3;</code>\n       * @param value The outputScript to set.\n       * @return This builder for chaining.\n       */\n      public Builder setOutputScript(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setOutputScript(value);\n        return this;\n      }\n      /**\n       * <code>bytes output_script = 3;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearOutputScript() {\n        copyOnWrite();\n        instance.clearOutputScript();\n        return this;\n      }\n\n      /**\n       * <code>int64 value = 4;</code>\n       * @return The value.\n       */\n      @java.lang.Override\n      public long getValue() {\n        return instance.getValue();\n      }\n      /**\n       * <code>int64 value = 4;</code>\n       * @param value The value to set.\n       * @return This builder for chaining.\n       */\n      public Builder setValue(long value) {\n        copyOnWrite();\n        instance.setValue(value);\n        return this;\n      }\n      /**\n       * <code>int64 value = 4;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearValue() {\n        copyOnWrite();\n        instance.clearValue();\n        return this;\n      }\n\n      /**\n       * <code>uint32 sequence_no = 5;</code>\n       * @return The sequenceNo.\n       */\n      @java.lang.Override\n      public int getSequenceNo() {\n        return instance.getSequenceNo();\n      }\n      /**\n       * <code>uint32 sequence_no = 5;</code>\n       * @param value The sequenceNo to set.\n       * @return This builder for chaining.\n       */\n      public Builder setSequenceNo(int value) {\n        copyOnWrite();\n        instance.setSequenceNo(value);\n        return this;\n      }\n      /**\n       * <code>uint32 sequence_no = 5;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearSequenceNo() {\n        copyOnWrite();\n        instance.clearSequenceNo();\n        return this;\n      }\n\n      /**\n       * <code>.chronik.SlpBurn slp_burn = 6;</code>\n       */\n      @java.lang.Override\n      public boolean hasSlpBurn() {\n        return instance.hasSlpBurn();\n      }\n      /**\n       * <code>.chronik.SlpBurn slp_burn = 6;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.SlpBurn getSlpBurn() {\n        return instance.getSlpBurn();\n      }\n      /**\n       * <code>.chronik.SlpBurn slp_burn = 6;</code>\n       */\n      public Builder setSlpBurn(chronik.Chronik.SlpBurn value) {\n        copyOnWrite();\n        instance.setSlpBurn(value);\n        return this;\n        }\n      /**\n       * <code>.chronik.SlpBurn slp_burn = 6;</code>\n       */\n      public Builder setSlpBurn(\n          chronik.Chronik.SlpBurn.Builder builderForValue) {\n        copyOnWrite();\n        instance.setSlpBurn(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>.chronik.SlpBurn slp_burn = 6;</code>\n       */\n      public Builder mergeSlpBurn(chronik.Chronik.SlpBurn value) {\n        copyOnWrite();\n        instance.mergeSlpBurn(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.SlpBurn slp_burn = 6;</code>\n       */\n      public Builder clearSlpBurn() {  copyOnWrite();\n        instance.clearSlpBurn();\n        return this;\n      }\n\n      /**\n       * <code>.chronik.SlpToken slp_token = 7;</code>\n       */\n      @java.lang.Override\n      public boolean hasSlpToken() {\n        return instance.hasSlpToken();\n      }\n      /**\n       * <code>.chronik.SlpToken slp_token = 7;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.SlpToken getSlpToken() {\n        return instance.getSlpToken();\n      }\n      /**\n       * <code>.chronik.SlpToken slp_token = 7;</code>\n       */\n      public Builder setSlpToken(chronik.Chronik.SlpToken value) {\n        copyOnWrite();\n        instance.setSlpToken(value);\n        return this;\n        }\n      /**\n       * <code>.chronik.SlpToken slp_token = 7;</code>\n       */\n      public Builder setSlpToken(\n          chronik.Chronik.SlpToken.Builder builderForValue) {\n        copyOnWrite();\n        instance.setSlpToken(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>.chronik.SlpToken slp_token = 7;</code>\n       */\n      public Builder mergeSlpToken(chronik.Chronik.SlpToken value) {\n        copyOnWrite();\n        instance.mergeSlpToken(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.SlpToken slp_token = 7;</code>\n       */\n      public Builder clearSlpToken() {  copyOnWrite();\n        instance.clearSlpToken();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.TxInput)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.TxInput();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"prevOut_\",\n              \"inputScript_\",\n              \"outputScript_\",\n              \"value_\",\n              \"sequenceNo_\",\n              \"slpBurn_\",\n              \"slpToken_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0007\\u0000\\u0000\\u0001\\u0007\\u0007\\u0000\\u0000\\u0000\\u0001\\t\\u0002\\n\\u0003\" +\n                \"\\n\\u0004\\u0002\\u0005\\u000b\\u0006\\t\\u0007\\t\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.TxInput> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.TxInput.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.TxInput>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.TxInput)\n    private static final chronik.Chronik.TxInput DEFAULT_INSTANCE;\n    static {\n      TxInput defaultInstance = new TxInput();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        TxInput.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.TxInput getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<TxInput> PARSER;\n\n    public static com.google.protobuf.Parser<TxInput> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface TxOutputOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.TxOutput)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>int64 value = 1;</code>\n     * @return The value.\n     */\n    long getValue();\n\n    /**\n     * <code>bytes output_script = 2;</code>\n     * @return The outputScript.\n     */\n    com.google.protobuf.ByteString getOutputScript();\n\n    /**\n     * <code>.chronik.SlpToken slp_token = 3;</code>\n     * @return Whether the slpToken field is set.\n     */\n    boolean hasSlpToken();\n    /**\n     * <code>.chronik.SlpToken slp_token = 3;</code>\n     * @return The slpToken.\n     */\n    chronik.Chronik.SlpToken getSlpToken();\n\n    /**\n     * <code>.chronik.OutPoint spent_by = 4;</code>\n     * @return Whether the spentBy field is set.\n     */\n    boolean hasSpentBy();\n    /**\n     * <code>.chronik.OutPoint spent_by = 4;</code>\n     * @return The spentBy.\n     */\n    chronik.Chronik.OutPoint getSpentBy();\n  }\n  /**\n   * Protobuf type {@code chronik.TxOutput}\n   */\n  public  static final class TxOutput extends\n      com.google.protobuf.GeneratedMessageLite<\n          TxOutput, TxOutput.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.TxOutput)\n      TxOutputOrBuilder {\n    private TxOutput() {\n      outputScript_ = com.google.protobuf.ByteString.EMPTY;\n    }\n    public static final int VALUE_FIELD_NUMBER = 1;\n    private long value_;\n    /**\n     * <code>int64 value = 1;</code>\n     * @return The value.\n     */\n    @java.lang.Override\n    public long getValue() {\n      return value_;\n    }\n    /**\n     * <code>int64 value = 1;</code>\n     * @param value The value to set.\n     */\n    private void setValue(long value) {\n      \n      value_ = value;\n    }\n    /**\n     * <code>int64 value = 1;</code>\n     */\n    private void clearValue() {\n\n      value_ = 0L;\n    }\n\n    public static final int OUTPUT_SCRIPT_FIELD_NUMBER = 2;\n    private com.google.protobuf.ByteString outputScript_;\n    /**\n     * <code>bytes output_script = 2;</code>\n     * @return The outputScript.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getOutputScript() {\n      return outputScript_;\n    }\n    /**\n     * <code>bytes output_script = 2;</code>\n     * @param value The outputScript to set.\n     */\n    private void setOutputScript(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      outputScript_ = value;\n    }\n    /**\n     * <code>bytes output_script = 2;</code>\n     */\n    private void clearOutputScript() {\n\n      outputScript_ = getDefaultInstance().getOutputScript();\n    }\n\n    public static final int SLP_TOKEN_FIELD_NUMBER = 3;\n    private chronik.Chronik.SlpToken slpToken_;\n    /**\n     * <code>.chronik.SlpToken slp_token = 3;</code>\n     */\n    @java.lang.Override\n    public boolean hasSlpToken() {\n      return slpToken_ != null;\n    }\n    /**\n     * <code>.chronik.SlpToken slp_token = 3;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.SlpToken getSlpToken() {\n      return slpToken_ == null ? chronik.Chronik.SlpToken.getDefaultInstance() : slpToken_;\n    }\n    /**\n     * <code>.chronik.SlpToken slp_token = 3;</code>\n     */\n    private void setSlpToken(chronik.Chronik.SlpToken value) {\n      value.getClass();\n  slpToken_ = value;\n\n      }\n    /**\n     * <code>.chronik.SlpToken slp_token = 3;</code>\n     */\n    @java.lang.SuppressWarnings({\"ReferenceEquality\"})\n    private void mergeSlpToken(chronik.Chronik.SlpToken value) {\n      value.getClass();\n  if (slpToken_ != null &&\n          slpToken_ != chronik.Chronik.SlpToken.getDefaultInstance()) {\n        slpToken_ =\n          chronik.Chronik.SlpToken.newBuilder(slpToken_).mergeFrom(value).buildPartial();\n      } else {\n        slpToken_ = value;\n      }\n\n    }\n    /**\n     * <code>.chronik.SlpToken slp_token = 3;</code>\n     */\n    private void clearSlpToken() {  slpToken_ = null;\n\n    }\n\n    public static final int SPENT_BY_FIELD_NUMBER = 4;\n    private chronik.Chronik.OutPoint spentBy_;\n    /**\n     * <code>.chronik.OutPoint spent_by = 4;</code>\n     */\n    @java.lang.Override\n    public boolean hasSpentBy() {\n      return spentBy_ != null;\n    }\n    /**\n     * <code>.chronik.OutPoint spent_by = 4;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.OutPoint getSpentBy() {\n      return spentBy_ == null ? chronik.Chronik.OutPoint.getDefaultInstance() : spentBy_;\n    }\n    /**\n     * <code>.chronik.OutPoint spent_by = 4;</code>\n     */\n    private void setSpentBy(chronik.Chronik.OutPoint value) {\n      value.getClass();\n  spentBy_ = value;\n\n      }\n    /**\n     * <code>.chronik.OutPoint spent_by = 4;</code>\n     */\n    @java.lang.SuppressWarnings({\"ReferenceEquality\"})\n    private void mergeSpentBy(chronik.Chronik.OutPoint value) {\n      value.getClass();\n  if (spentBy_ != null &&\n          spentBy_ != chronik.Chronik.OutPoint.getDefaultInstance()) {\n        spentBy_ =\n          chronik.Chronik.OutPoint.newBuilder(spentBy_).mergeFrom(value).buildPartial();\n      } else {\n        spentBy_ = value;\n      }\n\n    }\n    /**\n     * <code>.chronik.OutPoint spent_by = 4;</code>\n     */\n    private void clearSpentBy() {  spentBy_ = null;\n\n    }\n\n    public static chronik.Chronik.TxOutput parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.TxOutput parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.TxOutput parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.TxOutput parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.TxOutput parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.TxOutput parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.TxOutput parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.TxOutput parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.TxOutput parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.TxOutput parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.TxOutput parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.TxOutput parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.TxOutput prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.TxOutput}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.TxOutput, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.TxOutput)\n        chronik.Chronik.TxOutputOrBuilder {\n      // Construct using chronik.Chronik.TxOutput.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>int64 value = 1;</code>\n       * @return The value.\n       */\n      @java.lang.Override\n      public long getValue() {\n        return instance.getValue();\n      }\n      /**\n       * <code>int64 value = 1;</code>\n       * @param value The value to set.\n       * @return This builder for chaining.\n       */\n      public Builder setValue(long value) {\n        copyOnWrite();\n        instance.setValue(value);\n        return this;\n      }\n      /**\n       * <code>int64 value = 1;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearValue() {\n        copyOnWrite();\n        instance.clearValue();\n        return this;\n      }\n\n      /**\n       * <code>bytes output_script = 2;</code>\n       * @return The outputScript.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getOutputScript() {\n        return instance.getOutputScript();\n      }\n      /**\n       * <code>bytes output_script = 2;</code>\n       * @param value The outputScript to set.\n       * @return This builder for chaining.\n       */\n      public Builder setOutputScript(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setOutputScript(value);\n        return this;\n      }\n      /**\n       * <code>bytes output_script = 2;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearOutputScript() {\n        copyOnWrite();\n        instance.clearOutputScript();\n        return this;\n      }\n\n      /**\n       * <code>.chronik.SlpToken slp_token = 3;</code>\n       */\n      @java.lang.Override\n      public boolean hasSlpToken() {\n        return instance.hasSlpToken();\n      }\n      /**\n       * <code>.chronik.SlpToken slp_token = 3;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.SlpToken getSlpToken() {\n        return instance.getSlpToken();\n      }\n      /**\n       * <code>.chronik.SlpToken slp_token = 3;</code>\n       */\n      public Builder setSlpToken(chronik.Chronik.SlpToken value) {\n        copyOnWrite();\n        instance.setSlpToken(value);\n        return this;\n        }\n      /**\n       * <code>.chronik.SlpToken slp_token = 3;</code>\n       */\n      public Builder setSlpToken(\n          chronik.Chronik.SlpToken.Builder builderForValue) {\n        copyOnWrite();\n        instance.setSlpToken(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>.chronik.SlpToken slp_token = 3;</code>\n       */\n      public Builder mergeSlpToken(chronik.Chronik.SlpToken value) {\n        copyOnWrite();\n        instance.mergeSlpToken(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.SlpToken slp_token = 3;</code>\n       */\n      public Builder clearSlpToken() {  copyOnWrite();\n        instance.clearSlpToken();\n        return this;\n      }\n\n      /**\n       * <code>.chronik.OutPoint spent_by = 4;</code>\n       */\n      @java.lang.Override\n      public boolean hasSpentBy() {\n        return instance.hasSpentBy();\n      }\n      /**\n       * <code>.chronik.OutPoint spent_by = 4;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.OutPoint getSpentBy() {\n        return instance.getSpentBy();\n      }\n      /**\n       * <code>.chronik.OutPoint spent_by = 4;</code>\n       */\n      public Builder setSpentBy(chronik.Chronik.OutPoint value) {\n        copyOnWrite();\n        instance.setSpentBy(value);\n        return this;\n        }\n      /**\n       * <code>.chronik.OutPoint spent_by = 4;</code>\n       */\n      public Builder setSpentBy(\n          chronik.Chronik.OutPoint.Builder builderForValue) {\n        copyOnWrite();\n        instance.setSpentBy(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>.chronik.OutPoint spent_by = 4;</code>\n       */\n      public Builder mergeSpentBy(chronik.Chronik.OutPoint value) {\n        copyOnWrite();\n        instance.mergeSpentBy(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.OutPoint spent_by = 4;</code>\n       */\n      public Builder clearSpentBy() {  copyOnWrite();\n        instance.clearSpentBy();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.TxOutput)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.TxOutput();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"value_\",\n              \"outputScript_\",\n              \"slpToken_\",\n              \"spentBy_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0004\\u0000\\u0000\\u0001\\u0004\\u0004\\u0000\\u0000\\u0000\\u0001\\u0002\\u0002\\n\" +\n                \"\\u0003\\t\\u0004\\t\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.TxOutput> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.TxOutput.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.TxOutput>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.TxOutput)\n    private static final chronik.Chronik.TxOutput DEFAULT_INSTANCE;\n    static {\n      TxOutput defaultInstance = new TxOutput();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        TxOutput.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.TxOutput getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<TxOutput> PARSER;\n\n    public static com.google.protobuf.Parser<TxOutput> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface BlockMetadataOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.BlockMetadata)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>int32 height = 1;</code>\n     * @return The height.\n     */\n    int getHeight();\n\n    /**\n     * <code>bytes hash = 2;</code>\n     * @return The hash.\n     */\n    com.google.protobuf.ByteString getHash();\n\n    /**\n     * <code>int64 timestamp = 3;</code>\n     * @return The timestamp.\n     */\n    long getTimestamp();\n  }\n  /**\n   * Protobuf type {@code chronik.BlockMetadata}\n   */\n  public  static final class BlockMetadata extends\n      com.google.protobuf.GeneratedMessageLite<\n          BlockMetadata, BlockMetadata.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.BlockMetadata)\n      BlockMetadataOrBuilder {\n    private BlockMetadata() {\n      hash_ = com.google.protobuf.ByteString.EMPTY;\n    }\n    public static final int HEIGHT_FIELD_NUMBER = 1;\n    private int height_;\n    /**\n     * <code>int32 height = 1;</code>\n     * @return The height.\n     */\n    @java.lang.Override\n    public int getHeight() {\n      return height_;\n    }\n    /**\n     * <code>int32 height = 1;</code>\n     * @param value The height to set.\n     */\n    private void setHeight(int value) {\n      \n      height_ = value;\n    }\n    /**\n     * <code>int32 height = 1;</code>\n     */\n    private void clearHeight() {\n\n      height_ = 0;\n    }\n\n    public static final int HASH_FIELD_NUMBER = 2;\n    private com.google.protobuf.ByteString hash_;\n    /**\n     * <code>bytes hash = 2;</code>\n     * @return The hash.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getHash() {\n      return hash_;\n    }\n    /**\n     * <code>bytes hash = 2;</code>\n     * @param value The hash to set.\n     */\n    private void setHash(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      hash_ = value;\n    }\n    /**\n     * <code>bytes hash = 2;</code>\n     */\n    private void clearHash() {\n\n      hash_ = getDefaultInstance().getHash();\n    }\n\n    public static final int TIMESTAMP_FIELD_NUMBER = 3;\n    private long timestamp_;\n    /**\n     * <code>int64 timestamp = 3;</code>\n     * @return The timestamp.\n     */\n    @java.lang.Override\n    public long getTimestamp() {\n      return timestamp_;\n    }\n    /**\n     * <code>int64 timestamp = 3;</code>\n     * @param value The timestamp to set.\n     */\n    private void setTimestamp(long value) {\n      \n      timestamp_ = value;\n    }\n    /**\n     * <code>int64 timestamp = 3;</code>\n     */\n    private void clearTimestamp() {\n\n      timestamp_ = 0L;\n    }\n\n    public static chronik.Chronik.BlockMetadata parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.BlockMetadata parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.BlockMetadata parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.BlockMetadata parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.BlockMetadata parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.BlockMetadata parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.BlockMetadata parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.BlockMetadata parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.BlockMetadata parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.BlockMetadata parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.BlockMetadata parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.BlockMetadata parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.BlockMetadata prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.BlockMetadata}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.BlockMetadata, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.BlockMetadata)\n        chronik.Chronik.BlockMetadataOrBuilder {\n      // Construct using chronik.Chronik.BlockMetadata.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>int32 height = 1;</code>\n       * @return The height.\n       */\n      @java.lang.Override\n      public int getHeight() {\n        return instance.getHeight();\n      }\n      /**\n       * <code>int32 height = 1;</code>\n       * @param value The height to set.\n       * @return This builder for chaining.\n       */\n      public Builder setHeight(int value) {\n        copyOnWrite();\n        instance.setHeight(value);\n        return this;\n      }\n      /**\n       * <code>int32 height = 1;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearHeight() {\n        copyOnWrite();\n        instance.clearHeight();\n        return this;\n      }\n\n      /**\n       * <code>bytes hash = 2;</code>\n       * @return The hash.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getHash() {\n        return instance.getHash();\n      }\n      /**\n       * <code>bytes hash = 2;</code>\n       * @param value The hash to set.\n       * @return This builder for chaining.\n       */\n      public Builder setHash(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setHash(value);\n        return this;\n      }\n      /**\n       * <code>bytes hash = 2;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearHash() {\n        copyOnWrite();\n        instance.clearHash();\n        return this;\n      }\n\n      /**\n       * <code>int64 timestamp = 3;</code>\n       * @return The timestamp.\n       */\n      @java.lang.Override\n      public long getTimestamp() {\n        return instance.getTimestamp();\n      }\n      /**\n       * <code>int64 timestamp = 3;</code>\n       * @param value The timestamp to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTimestamp(long value) {\n        copyOnWrite();\n        instance.setTimestamp(value);\n        return this;\n      }\n      /**\n       * <code>int64 timestamp = 3;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearTimestamp() {\n        copyOnWrite();\n        instance.clearTimestamp();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.BlockMetadata)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.BlockMetadata();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"height_\",\n              \"hash_\",\n              \"timestamp_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0003\\u0000\\u0000\\u0001\\u0003\\u0003\\u0000\\u0000\\u0000\\u0001\\u0004\\u0002\\n\" +\n                \"\\u0003\\u0002\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.BlockMetadata> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.BlockMetadata.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.BlockMetadata>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.BlockMetadata)\n    private static final chronik.Chronik.BlockMetadata DEFAULT_INSTANCE;\n    static {\n      BlockMetadata defaultInstance = new BlockMetadata();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        BlockMetadata.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.BlockMetadata getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<BlockMetadata> PARSER;\n\n    public static com.google.protobuf.Parser<BlockMetadata> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface OutPointOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.OutPoint)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>bytes txid = 1;</code>\n     * @return The txid.\n     */\n    com.google.protobuf.ByteString getTxid();\n\n    /**\n     * <code>uint32 out_idx = 2;</code>\n     * @return The outIdx.\n     */\n    int getOutIdx();\n  }\n  /**\n   * Protobuf type {@code chronik.OutPoint}\n   */\n  public  static final class OutPoint extends\n      com.google.protobuf.GeneratedMessageLite<\n          OutPoint, OutPoint.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.OutPoint)\n      OutPointOrBuilder {\n    private OutPoint() {\n      txid_ = com.google.protobuf.ByteString.EMPTY;\n    }\n    public static final int TXID_FIELD_NUMBER = 1;\n    private com.google.protobuf.ByteString txid_;\n    /**\n     * <code>bytes txid = 1;</code>\n     * @return The txid.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getTxid() {\n      return txid_;\n    }\n    /**\n     * <code>bytes txid = 1;</code>\n     * @param value The txid to set.\n     */\n    private void setTxid(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      txid_ = value;\n    }\n    /**\n     * <code>bytes txid = 1;</code>\n     */\n    private void clearTxid() {\n\n      txid_ = getDefaultInstance().getTxid();\n    }\n\n    public static final int OUT_IDX_FIELD_NUMBER = 2;\n    private int outIdx_;\n    /**\n     * <code>uint32 out_idx = 2;</code>\n     * @return The outIdx.\n     */\n    @java.lang.Override\n    public int getOutIdx() {\n      return outIdx_;\n    }\n    /**\n     * <code>uint32 out_idx = 2;</code>\n     * @param value The outIdx to set.\n     */\n    private void setOutIdx(int value) {\n      \n      outIdx_ = value;\n    }\n    /**\n     * <code>uint32 out_idx = 2;</code>\n     */\n    private void clearOutIdx() {\n\n      outIdx_ = 0;\n    }\n\n    public static chronik.Chronik.OutPoint parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.OutPoint parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.OutPoint parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.OutPoint parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.OutPoint parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.OutPoint parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.OutPoint parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.OutPoint parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.OutPoint parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.OutPoint parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.OutPoint parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.OutPoint parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.OutPoint prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.OutPoint}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.OutPoint, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.OutPoint)\n        chronik.Chronik.OutPointOrBuilder {\n      // Construct using chronik.Chronik.OutPoint.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>bytes txid = 1;</code>\n       * @return The txid.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getTxid() {\n        return instance.getTxid();\n      }\n      /**\n       * <code>bytes txid = 1;</code>\n       * @param value The txid to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTxid(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setTxid(value);\n        return this;\n      }\n      /**\n       * <code>bytes txid = 1;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearTxid() {\n        copyOnWrite();\n        instance.clearTxid();\n        return this;\n      }\n\n      /**\n       * <code>uint32 out_idx = 2;</code>\n       * @return The outIdx.\n       */\n      @java.lang.Override\n      public int getOutIdx() {\n        return instance.getOutIdx();\n      }\n      /**\n       * <code>uint32 out_idx = 2;</code>\n       * @param value The outIdx to set.\n       * @return This builder for chaining.\n       */\n      public Builder setOutIdx(int value) {\n        copyOnWrite();\n        instance.setOutIdx(value);\n        return this;\n      }\n      /**\n       * <code>uint32 out_idx = 2;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearOutIdx() {\n        copyOnWrite();\n        instance.clearOutIdx();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.OutPoint)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.OutPoint();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"txid_\",\n              \"outIdx_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0002\\u0000\\u0000\\u0001\\u0002\\u0002\\u0000\\u0000\\u0000\\u0001\\n\\u0002\\u000b\" +\n                \"\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.OutPoint> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.OutPoint.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.OutPoint>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.OutPoint)\n    private static final chronik.Chronik.OutPoint DEFAULT_INSTANCE;\n    static {\n      OutPoint defaultInstance = new OutPoint();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        OutPoint.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.OutPoint getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<OutPoint> PARSER;\n\n    public static com.google.protobuf.Parser<OutPoint> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface SlpTokenOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.SlpToken)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>uint64 amount = 1;</code>\n     * @return The amount.\n     */\n    long getAmount();\n\n    /**\n     * <code>bool is_mint_baton = 2;</code>\n     * @return The isMintBaton.\n     */\n    boolean getIsMintBaton();\n  }\n  /**\n   * Protobuf type {@code chronik.SlpToken}\n   */\n  public  static final class SlpToken extends\n      com.google.protobuf.GeneratedMessageLite<\n          SlpToken, SlpToken.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.SlpToken)\n      SlpTokenOrBuilder {\n    private SlpToken() {\n    }\n    public static final int AMOUNT_FIELD_NUMBER = 1;\n    private long amount_;\n    /**\n     * <code>uint64 amount = 1;</code>\n     * @return The amount.\n     */\n    @java.lang.Override\n    public long getAmount() {\n      return amount_;\n    }\n    /**\n     * <code>uint64 amount = 1;</code>\n     * @param value The amount to set.\n     */\n    private void setAmount(long value) {\n      \n      amount_ = value;\n    }\n    /**\n     * <code>uint64 amount = 1;</code>\n     */\n    private void clearAmount() {\n\n      amount_ = 0L;\n    }\n\n    public static final int IS_MINT_BATON_FIELD_NUMBER = 2;\n    private boolean isMintBaton_;\n    /**\n     * <code>bool is_mint_baton = 2;</code>\n     * @return The isMintBaton.\n     */\n    @java.lang.Override\n    public boolean getIsMintBaton() {\n      return isMintBaton_;\n    }\n    /**\n     * <code>bool is_mint_baton = 2;</code>\n     * @param value The isMintBaton to set.\n     */\n    private void setIsMintBaton(boolean value) {\n      \n      isMintBaton_ = value;\n    }\n    /**\n     * <code>bool is_mint_baton = 2;</code>\n     */\n    private void clearIsMintBaton() {\n\n      isMintBaton_ = false;\n    }\n\n    public static chronik.Chronik.SlpToken parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.SlpToken parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.SlpToken parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.SlpToken parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.SlpToken parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.SlpToken parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.SlpToken parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.SlpToken parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.SlpToken parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.SlpToken parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.SlpToken parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.SlpToken parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.SlpToken prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.SlpToken}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.SlpToken, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.SlpToken)\n        chronik.Chronik.SlpTokenOrBuilder {\n      // Construct using chronik.Chronik.SlpToken.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>uint64 amount = 1;</code>\n       * @return The amount.\n       */\n      @java.lang.Override\n      public long getAmount() {\n        return instance.getAmount();\n      }\n      /**\n       * <code>uint64 amount = 1;</code>\n       * @param value The amount to set.\n       * @return This builder for chaining.\n       */\n      public Builder setAmount(long value) {\n        copyOnWrite();\n        instance.setAmount(value);\n        return this;\n      }\n      /**\n       * <code>uint64 amount = 1;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearAmount() {\n        copyOnWrite();\n        instance.clearAmount();\n        return this;\n      }\n\n      /**\n       * <code>bool is_mint_baton = 2;</code>\n       * @return The isMintBaton.\n       */\n      @java.lang.Override\n      public boolean getIsMintBaton() {\n        return instance.getIsMintBaton();\n      }\n      /**\n       * <code>bool is_mint_baton = 2;</code>\n       * @param value The isMintBaton to set.\n       * @return This builder for chaining.\n       */\n      public Builder setIsMintBaton(boolean value) {\n        copyOnWrite();\n        instance.setIsMintBaton(value);\n        return this;\n      }\n      /**\n       * <code>bool is_mint_baton = 2;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearIsMintBaton() {\n        copyOnWrite();\n        instance.clearIsMintBaton();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.SlpToken)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.SlpToken();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"amount_\",\n              \"isMintBaton_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0002\\u0000\\u0000\\u0001\\u0002\\u0002\\u0000\\u0000\\u0000\\u0001\\u0003\\u0002\\u0007\" +\n                \"\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.SlpToken> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.SlpToken.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.SlpToken>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.SlpToken)\n    private static final chronik.Chronik.SlpToken DEFAULT_INSTANCE;\n    static {\n      SlpToken defaultInstance = new SlpToken();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        SlpToken.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.SlpToken getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<SlpToken> PARSER;\n\n    public static com.google.protobuf.Parser<SlpToken> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface SlpBurnOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.SlpBurn)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>.chronik.SlpToken token = 1;</code>\n     * @return Whether the token field is set.\n     */\n    boolean hasToken();\n    /**\n     * <code>.chronik.SlpToken token = 1;</code>\n     * @return The token.\n     */\n    chronik.Chronik.SlpToken getToken();\n\n    /**\n     * <code>bytes token_id = 2;</code>\n     * @return The tokenId.\n     */\n    com.google.protobuf.ByteString getTokenId();\n  }\n  /**\n   * Protobuf type {@code chronik.SlpBurn}\n   */\n  public  static final class SlpBurn extends\n      com.google.protobuf.GeneratedMessageLite<\n          SlpBurn, SlpBurn.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.SlpBurn)\n      SlpBurnOrBuilder {\n    private SlpBurn() {\n      tokenId_ = com.google.protobuf.ByteString.EMPTY;\n    }\n    public static final int TOKEN_FIELD_NUMBER = 1;\n    private chronik.Chronik.SlpToken token_;\n    /**\n     * <code>.chronik.SlpToken token = 1;</code>\n     */\n    @java.lang.Override\n    public boolean hasToken() {\n      return token_ != null;\n    }\n    /**\n     * <code>.chronik.SlpToken token = 1;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.SlpToken getToken() {\n      return token_ == null ? chronik.Chronik.SlpToken.getDefaultInstance() : token_;\n    }\n    /**\n     * <code>.chronik.SlpToken token = 1;</code>\n     */\n    private void setToken(chronik.Chronik.SlpToken value) {\n      value.getClass();\n  token_ = value;\n\n      }\n    /**\n     * <code>.chronik.SlpToken token = 1;</code>\n     */\n    @java.lang.SuppressWarnings({\"ReferenceEquality\"})\n    private void mergeToken(chronik.Chronik.SlpToken value) {\n      value.getClass();\n  if (token_ != null &&\n          token_ != chronik.Chronik.SlpToken.getDefaultInstance()) {\n        token_ =\n          chronik.Chronik.SlpToken.newBuilder(token_).mergeFrom(value).buildPartial();\n      } else {\n        token_ = value;\n      }\n\n    }\n    /**\n     * <code>.chronik.SlpToken token = 1;</code>\n     */\n    private void clearToken() {  token_ = null;\n\n    }\n\n    public static final int TOKEN_ID_FIELD_NUMBER = 2;\n    private com.google.protobuf.ByteString tokenId_;\n    /**\n     * <code>bytes token_id = 2;</code>\n     * @return The tokenId.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getTokenId() {\n      return tokenId_;\n    }\n    /**\n     * <code>bytes token_id = 2;</code>\n     * @param value The tokenId to set.\n     */\n    private void setTokenId(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      tokenId_ = value;\n    }\n    /**\n     * <code>bytes token_id = 2;</code>\n     */\n    private void clearTokenId() {\n\n      tokenId_ = getDefaultInstance().getTokenId();\n    }\n\n    public static chronik.Chronik.SlpBurn parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.SlpBurn parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.SlpBurn parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.SlpBurn parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.SlpBurn parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.SlpBurn parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.SlpBurn parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.SlpBurn parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.SlpBurn parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.SlpBurn parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.SlpBurn parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.SlpBurn parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.SlpBurn prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.SlpBurn}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.SlpBurn, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.SlpBurn)\n        chronik.Chronik.SlpBurnOrBuilder {\n      // Construct using chronik.Chronik.SlpBurn.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>.chronik.SlpToken token = 1;</code>\n       */\n      @java.lang.Override\n      public boolean hasToken() {\n        return instance.hasToken();\n      }\n      /**\n       * <code>.chronik.SlpToken token = 1;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.SlpToken getToken() {\n        return instance.getToken();\n      }\n      /**\n       * <code>.chronik.SlpToken token = 1;</code>\n       */\n      public Builder setToken(chronik.Chronik.SlpToken value) {\n        copyOnWrite();\n        instance.setToken(value);\n        return this;\n        }\n      /**\n       * <code>.chronik.SlpToken token = 1;</code>\n       */\n      public Builder setToken(\n          chronik.Chronik.SlpToken.Builder builderForValue) {\n        copyOnWrite();\n        instance.setToken(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>.chronik.SlpToken token = 1;</code>\n       */\n      public Builder mergeToken(chronik.Chronik.SlpToken value) {\n        copyOnWrite();\n        instance.mergeToken(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.SlpToken token = 1;</code>\n       */\n      public Builder clearToken() {  copyOnWrite();\n        instance.clearToken();\n        return this;\n      }\n\n      /**\n       * <code>bytes token_id = 2;</code>\n       * @return The tokenId.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getTokenId() {\n        return instance.getTokenId();\n      }\n      /**\n       * <code>bytes token_id = 2;</code>\n       * @param value The tokenId to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTokenId(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setTokenId(value);\n        return this;\n      }\n      /**\n       * <code>bytes token_id = 2;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearTokenId() {\n        copyOnWrite();\n        instance.clearTokenId();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.SlpBurn)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.SlpBurn();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"token_\",\n              \"tokenId_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0002\\u0000\\u0000\\u0001\\u0002\\u0002\\u0000\\u0000\\u0000\\u0001\\t\\u0002\\n\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.SlpBurn> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.SlpBurn.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.SlpBurn>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.SlpBurn)\n    private static final chronik.Chronik.SlpBurn DEFAULT_INSTANCE;\n    static {\n      SlpBurn defaultInstance = new SlpBurn();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        SlpBurn.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.SlpBurn getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<SlpBurn> PARSER;\n\n    public static com.google.protobuf.Parser<SlpBurn> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface SlpGenesisInfoOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.SlpGenesisInfo)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>bytes token_ticker = 1;</code>\n     * @return The tokenTicker.\n     */\n    com.google.protobuf.ByteString getTokenTicker();\n\n    /**\n     * <code>bytes token_name = 2;</code>\n     * @return The tokenName.\n     */\n    com.google.protobuf.ByteString getTokenName();\n\n    /**\n     * <code>bytes token_document_url = 3;</code>\n     * @return The tokenDocumentUrl.\n     */\n    com.google.protobuf.ByteString getTokenDocumentUrl();\n\n    /**\n     * <code>bytes token_document_hash = 4;</code>\n     * @return The tokenDocumentHash.\n     */\n    com.google.protobuf.ByteString getTokenDocumentHash();\n\n    /**\n     * <code>uint32 decimals = 5;</code>\n     * @return The decimals.\n     */\n    int getDecimals();\n  }\n  /**\n   * Protobuf type {@code chronik.SlpGenesisInfo}\n   */\n  public  static final class SlpGenesisInfo extends\n      com.google.protobuf.GeneratedMessageLite<\n          SlpGenesisInfo, SlpGenesisInfo.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.SlpGenesisInfo)\n      SlpGenesisInfoOrBuilder {\n    private SlpGenesisInfo() {\n      tokenTicker_ = com.google.protobuf.ByteString.EMPTY;\n      tokenName_ = com.google.protobuf.ByteString.EMPTY;\n      tokenDocumentUrl_ = com.google.protobuf.ByteString.EMPTY;\n      tokenDocumentHash_ = com.google.protobuf.ByteString.EMPTY;\n    }\n    public static final int TOKEN_TICKER_FIELD_NUMBER = 1;\n    private com.google.protobuf.ByteString tokenTicker_;\n    /**\n     * <code>bytes token_ticker = 1;</code>\n     * @return The tokenTicker.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getTokenTicker() {\n      return tokenTicker_;\n    }\n    /**\n     * <code>bytes token_ticker = 1;</code>\n     * @param value The tokenTicker to set.\n     */\n    private void setTokenTicker(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      tokenTicker_ = value;\n    }\n    /**\n     * <code>bytes token_ticker = 1;</code>\n     */\n    private void clearTokenTicker() {\n\n      tokenTicker_ = getDefaultInstance().getTokenTicker();\n    }\n\n    public static final int TOKEN_NAME_FIELD_NUMBER = 2;\n    private com.google.protobuf.ByteString tokenName_;\n    /**\n     * <code>bytes token_name = 2;</code>\n     * @return The tokenName.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getTokenName() {\n      return tokenName_;\n    }\n    /**\n     * <code>bytes token_name = 2;</code>\n     * @param value The tokenName to set.\n     */\n    private void setTokenName(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      tokenName_ = value;\n    }\n    /**\n     * <code>bytes token_name = 2;</code>\n     */\n    private void clearTokenName() {\n\n      tokenName_ = getDefaultInstance().getTokenName();\n    }\n\n    public static final int TOKEN_DOCUMENT_URL_FIELD_NUMBER = 3;\n    private com.google.protobuf.ByteString tokenDocumentUrl_;\n    /**\n     * <code>bytes token_document_url = 3;</code>\n     * @return The tokenDocumentUrl.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getTokenDocumentUrl() {\n      return tokenDocumentUrl_;\n    }\n    /**\n     * <code>bytes token_document_url = 3;</code>\n     * @param value The tokenDocumentUrl to set.\n     */\n    private void setTokenDocumentUrl(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      tokenDocumentUrl_ = value;\n    }\n    /**\n     * <code>bytes token_document_url = 3;</code>\n     */\n    private void clearTokenDocumentUrl() {\n\n      tokenDocumentUrl_ = getDefaultInstance().getTokenDocumentUrl();\n    }\n\n    public static final int TOKEN_DOCUMENT_HASH_FIELD_NUMBER = 4;\n    private com.google.protobuf.ByteString tokenDocumentHash_;\n    /**\n     * <code>bytes token_document_hash = 4;</code>\n     * @return The tokenDocumentHash.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getTokenDocumentHash() {\n      return tokenDocumentHash_;\n    }\n    /**\n     * <code>bytes token_document_hash = 4;</code>\n     * @param value The tokenDocumentHash to set.\n     */\n    private void setTokenDocumentHash(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      tokenDocumentHash_ = value;\n    }\n    /**\n     * <code>bytes token_document_hash = 4;</code>\n     */\n    private void clearTokenDocumentHash() {\n\n      tokenDocumentHash_ = getDefaultInstance().getTokenDocumentHash();\n    }\n\n    public static final int DECIMALS_FIELD_NUMBER = 5;\n    private int decimals_;\n    /**\n     * <code>uint32 decimals = 5;</code>\n     * @return The decimals.\n     */\n    @java.lang.Override\n    public int getDecimals() {\n      return decimals_;\n    }\n    /**\n     * <code>uint32 decimals = 5;</code>\n     * @param value The decimals to set.\n     */\n    private void setDecimals(int value) {\n      \n      decimals_ = value;\n    }\n    /**\n     * <code>uint32 decimals = 5;</code>\n     */\n    private void clearDecimals() {\n\n      decimals_ = 0;\n    }\n\n    public static chronik.Chronik.SlpGenesisInfo parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.SlpGenesisInfo parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.SlpGenesisInfo parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.SlpGenesisInfo parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.SlpGenesisInfo parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.SlpGenesisInfo parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.SlpGenesisInfo parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.SlpGenesisInfo parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.SlpGenesisInfo parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.SlpGenesisInfo parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.SlpGenesisInfo parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.SlpGenesisInfo parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.SlpGenesisInfo prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.SlpGenesisInfo}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.SlpGenesisInfo, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.SlpGenesisInfo)\n        chronik.Chronik.SlpGenesisInfoOrBuilder {\n      // Construct using chronik.Chronik.SlpGenesisInfo.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>bytes token_ticker = 1;</code>\n       * @return The tokenTicker.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getTokenTicker() {\n        return instance.getTokenTicker();\n      }\n      /**\n       * <code>bytes token_ticker = 1;</code>\n       * @param value The tokenTicker to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTokenTicker(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setTokenTicker(value);\n        return this;\n      }\n      /**\n       * <code>bytes token_ticker = 1;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearTokenTicker() {\n        copyOnWrite();\n        instance.clearTokenTicker();\n        return this;\n      }\n\n      /**\n       * <code>bytes token_name = 2;</code>\n       * @return The tokenName.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getTokenName() {\n        return instance.getTokenName();\n      }\n      /**\n       * <code>bytes token_name = 2;</code>\n       * @param value The tokenName to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTokenName(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setTokenName(value);\n        return this;\n      }\n      /**\n       * <code>bytes token_name = 2;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearTokenName() {\n        copyOnWrite();\n        instance.clearTokenName();\n        return this;\n      }\n\n      /**\n       * <code>bytes token_document_url = 3;</code>\n       * @return The tokenDocumentUrl.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getTokenDocumentUrl() {\n        return instance.getTokenDocumentUrl();\n      }\n      /**\n       * <code>bytes token_document_url = 3;</code>\n       * @param value The tokenDocumentUrl to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTokenDocumentUrl(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setTokenDocumentUrl(value);\n        return this;\n      }\n      /**\n       * <code>bytes token_document_url = 3;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearTokenDocumentUrl() {\n        copyOnWrite();\n        instance.clearTokenDocumentUrl();\n        return this;\n      }\n\n      /**\n       * <code>bytes token_document_hash = 4;</code>\n       * @return The tokenDocumentHash.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getTokenDocumentHash() {\n        return instance.getTokenDocumentHash();\n      }\n      /**\n       * <code>bytes token_document_hash = 4;</code>\n       * @param value The tokenDocumentHash to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTokenDocumentHash(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setTokenDocumentHash(value);\n        return this;\n      }\n      /**\n       * <code>bytes token_document_hash = 4;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearTokenDocumentHash() {\n        copyOnWrite();\n        instance.clearTokenDocumentHash();\n        return this;\n      }\n\n      /**\n       * <code>uint32 decimals = 5;</code>\n       * @return The decimals.\n       */\n      @java.lang.Override\n      public int getDecimals() {\n        return instance.getDecimals();\n      }\n      /**\n       * <code>uint32 decimals = 5;</code>\n       * @param value The decimals to set.\n       * @return This builder for chaining.\n       */\n      public Builder setDecimals(int value) {\n        copyOnWrite();\n        instance.setDecimals(value);\n        return this;\n      }\n      /**\n       * <code>uint32 decimals = 5;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearDecimals() {\n        copyOnWrite();\n        instance.clearDecimals();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.SlpGenesisInfo)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.SlpGenesisInfo();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"tokenTicker_\",\n              \"tokenName_\",\n              \"tokenDocumentUrl_\",\n              \"tokenDocumentHash_\",\n              \"decimals_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0005\\u0000\\u0000\\u0001\\u0005\\u0005\\u0000\\u0000\\u0000\\u0001\\n\\u0002\\n\\u0003\" +\n                \"\\n\\u0004\\n\\u0005\\u000b\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.SlpGenesisInfo> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.SlpGenesisInfo.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.SlpGenesisInfo>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.SlpGenesisInfo)\n    private static final chronik.Chronik.SlpGenesisInfo DEFAULT_INSTANCE;\n    static {\n      SlpGenesisInfo defaultInstance = new SlpGenesisInfo();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        SlpGenesisInfo.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.SlpGenesisInfo getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<SlpGenesisInfo> PARSER;\n\n    public static com.google.protobuf.Parser<SlpGenesisInfo> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface UtxoStateOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.UtxoState)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>int32 height = 1;</code>\n     * @return The height.\n     */\n    int getHeight();\n\n    /**\n     * <code>bool is_confirmed = 2;</code>\n     * @return The isConfirmed.\n     */\n    boolean getIsConfirmed();\n\n    /**\n     * <code>.chronik.UtxoStateVariant state = 3;</code>\n     * @return The enum numeric value on the wire for state.\n     */\n    int getStateValue();\n    /**\n     * <code>.chronik.UtxoStateVariant state = 3;</code>\n     * @return The state.\n     */\n    chronik.Chronik.UtxoStateVariant getState();\n  }\n  /**\n   * Protobuf type {@code chronik.UtxoState}\n   */\n  public  static final class UtxoState extends\n      com.google.protobuf.GeneratedMessageLite<\n          UtxoState, UtxoState.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.UtxoState)\n      UtxoStateOrBuilder {\n    private UtxoState() {\n    }\n    public static final int HEIGHT_FIELD_NUMBER = 1;\n    private int height_;\n    /**\n     * <code>int32 height = 1;</code>\n     * @return The height.\n     */\n    @java.lang.Override\n    public int getHeight() {\n      return height_;\n    }\n    /**\n     * <code>int32 height = 1;</code>\n     * @param value The height to set.\n     */\n    private void setHeight(int value) {\n      \n      height_ = value;\n    }\n    /**\n     * <code>int32 height = 1;</code>\n     */\n    private void clearHeight() {\n\n      height_ = 0;\n    }\n\n    public static final int IS_CONFIRMED_FIELD_NUMBER = 2;\n    private boolean isConfirmed_;\n    /**\n     * <code>bool is_confirmed = 2;</code>\n     * @return The isConfirmed.\n     */\n    @java.lang.Override\n    public boolean getIsConfirmed() {\n      return isConfirmed_;\n    }\n    /**\n     * <code>bool is_confirmed = 2;</code>\n     * @param value The isConfirmed to set.\n     */\n    private void setIsConfirmed(boolean value) {\n      \n      isConfirmed_ = value;\n    }\n    /**\n     * <code>bool is_confirmed = 2;</code>\n     */\n    private void clearIsConfirmed() {\n\n      isConfirmed_ = false;\n    }\n\n    public static final int STATE_FIELD_NUMBER = 3;\n    private int state_;\n    /**\n     * <code>.chronik.UtxoStateVariant state = 3;</code>\n     * @return The enum numeric value on the wire for state.\n     */\n    @java.lang.Override\n    public int getStateValue() {\n      return state_;\n    }\n    /**\n     * <code>.chronik.UtxoStateVariant state = 3;</code>\n     * @return The state.\n     */\n    @java.lang.Override\n    public chronik.Chronik.UtxoStateVariant getState() {\n      chronik.Chronik.UtxoStateVariant result = chronik.Chronik.UtxoStateVariant.forNumber(state_);\n      return result == null ? chronik.Chronik.UtxoStateVariant.UNRECOGNIZED : result;\n    }\n    /**\n     * <code>.chronik.UtxoStateVariant state = 3;</code>\n     * @param value The enum numeric value on the wire for state to set.\n     */\n    private void setStateValue(int value) {\n        state_ = value;\n    }\n    /**\n     * <code>.chronik.UtxoStateVariant state = 3;</code>\n     * @param value The state to set.\n     */\n    private void setState(chronik.Chronik.UtxoStateVariant value) {\n      state_ = value.getNumber();\n\n    }\n    /**\n     * <code>.chronik.UtxoStateVariant state = 3;</code>\n     */\n    private void clearState() {\n\n      state_ = 0;\n    }\n\n    public static chronik.Chronik.UtxoState parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.UtxoState parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.UtxoState parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.UtxoState parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.UtxoState parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.UtxoState parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.UtxoState parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.UtxoState parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.UtxoState parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.UtxoState parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.UtxoState parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.UtxoState parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.UtxoState prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.UtxoState}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.UtxoState, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.UtxoState)\n        chronik.Chronik.UtxoStateOrBuilder {\n      // Construct using chronik.Chronik.UtxoState.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>int32 height = 1;</code>\n       * @return The height.\n       */\n      @java.lang.Override\n      public int getHeight() {\n        return instance.getHeight();\n      }\n      /**\n       * <code>int32 height = 1;</code>\n       * @param value The height to set.\n       * @return This builder for chaining.\n       */\n      public Builder setHeight(int value) {\n        copyOnWrite();\n        instance.setHeight(value);\n        return this;\n      }\n      /**\n       * <code>int32 height = 1;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearHeight() {\n        copyOnWrite();\n        instance.clearHeight();\n        return this;\n      }\n\n      /**\n       * <code>bool is_confirmed = 2;</code>\n       * @return The isConfirmed.\n       */\n      @java.lang.Override\n      public boolean getIsConfirmed() {\n        return instance.getIsConfirmed();\n      }\n      /**\n       * <code>bool is_confirmed = 2;</code>\n       * @param value The isConfirmed to set.\n       * @return This builder for chaining.\n       */\n      public Builder setIsConfirmed(boolean value) {\n        copyOnWrite();\n        instance.setIsConfirmed(value);\n        return this;\n      }\n      /**\n       * <code>bool is_confirmed = 2;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearIsConfirmed() {\n        copyOnWrite();\n        instance.clearIsConfirmed();\n        return this;\n      }\n\n      /**\n       * <code>.chronik.UtxoStateVariant state = 3;</code>\n       * @return The enum numeric value on the wire for state.\n       */\n      @java.lang.Override\n      public int getStateValue() {\n        return instance.getStateValue();\n      }\n      /**\n       * <code>.chronik.UtxoStateVariant state = 3;</code>\n       * @param value The state to set.\n       * @return This builder for chaining.\n       */\n      public Builder setStateValue(int value) {\n        copyOnWrite();\n        instance.setStateValue(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.UtxoStateVariant state = 3;</code>\n       * @return The state.\n       */\n      @java.lang.Override\n      public chronik.Chronik.UtxoStateVariant getState() {\n        return instance.getState();\n      }\n      /**\n       * <code>.chronik.UtxoStateVariant state = 3;</code>\n       * @param value The enum numeric value on the wire for state to set.\n       * @return This builder for chaining.\n       */\n      public Builder setState(chronik.Chronik.UtxoStateVariant value) {\n        copyOnWrite();\n        instance.setState(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.UtxoStateVariant state = 3;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearState() {\n        copyOnWrite();\n        instance.clearState();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.UtxoState)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.UtxoState();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"height_\",\n              \"isConfirmed_\",\n              \"state_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0003\\u0000\\u0000\\u0001\\u0003\\u0003\\u0000\\u0000\\u0000\\u0001\\u0004\\u0002\\u0007\" +\n                \"\\u0003\\f\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.UtxoState> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.UtxoState.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.UtxoState>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.UtxoState)\n    private static final chronik.Chronik.UtxoState DEFAULT_INSTANCE;\n    static {\n      UtxoState defaultInstance = new UtxoState();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        UtxoState.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.UtxoState getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<UtxoState> PARSER;\n\n    public static com.google.protobuf.Parser<UtxoState> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface SubscriptionOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.Subscription)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>string script_type = 1;</code>\n     * @return The scriptType.\n     */\n    java.lang.String getScriptType();\n    /**\n     * <code>string script_type = 1;</code>\n     * @return The bytes for scriptType.\n     */\n    com.google.protobuf.ByteString\n        getScriptTypeBytes();\n\n    /**\n     * <code>bytes payload = 2;</code>\n     * @return The payload.\n     */\n    com.google.protobuf.ByteString getPayload();\n\n    /**\n     * <code>bool is_subscribe = 3;</code>\n     * @return The isSubscribe.\n     */\n    boolean getIsSubscribe();\n  }\n  /**\n   * Protobuf type {@code chronik.Subscription}\n   */\n  public  static final class Subscription extends\n      com.google.protobuf.GeneratedMessageLite<\n          Subscription, Subscription.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.Subscription)\n      SubscriptionOrBuilder {\n    private Subscription() {\n      scriptType_ = \"\";\n      payload_ = com.google.protobuf.ByteString.EMPTY;\n    }\n    public static final int SCRIPT_TYPE_FIELD_NUMBER = 1;\n    private java.lang.String scriptType_;\n    /**\n     * <code>string script_type = 1;</code>\n     * @return The scriptType.\n     */\n    @java.lang.Override\n    public java.lang.String getScriptType() {\n      return scriptType_;\n    }\n    /**\n     * <code>string script_type = 1;</code>\n     * @return The bytes for scriptType.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString\n        getScriptTypeBytes() {\n      return com.google.protobuf.ByteString.copyFromUtf8(scriptType_);\n    }\n    /**\n     * <code>string script_type = 1;</code>\n     * @param value The scriptType to set.\n     */\n    private void setScriptType(\n        java.lang.String value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      scriptType_ = value;\n    }\n    /**\n     * <code>string script_type = 1;</code>\n     */\n    private void clearScriptType() {\n\n      scriptType_ = getDefaultInstance().getScriptType();\n    }\n    /**\n     * <code>string script_type = 1;</code>\n     * @param value The bytes for scriptType to set.\n     */\n    private void setScriptTypeBytes(\n        com.google.protobuf.ByteString value) {\n      checkByteStringIsUtf8(value);\n      scriptType_ = value.toStringUtf8();\n\n    }\n\n    public static final int PAYLOAD_FIELD_NUMBER = 2;\n    private com.google.protobuf.ByteString payload_;\n    /**\n     * <code>bytes payload = 2;</code>\n     * @return The payload.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getPayload() {\n      return payload_;\n    }\n    /**\n     * <code>bytes payload = 2;</code>\n     * @param value The payload to set.\n     */\n    private void setPayload(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      payload_ = value;\n    }\n    /**\n     * <code>bytes payload = 2;</code>\n     */\n    private void clearPayload() {\n\n      payload_ = getDefaultInstance().getPayload();\n    }\n\n    public static final int IS_SUBSCRIBE_FIELD_NUMBER = 3;\n    private boolean isSubscribe_;\n    /**\n     * <code>bool is_subscribe = 3;</code>\n     * @return The isSubscribe.\n     */\n    @java.lang.Override\n    public boolean getIsSubscribe() {\n      return isSubscribe_;\n    }\n    /**\n     * <code>bool is_subscribe = 3;</code>\n     * @param value The isSubscribe to set.\n     */\n    private void setIsSubscribe(boolean value) {\n      \n      isSubscribe_ = value;\n    }\n    /**\n     * <code>bool is_subscribe = 3;</code>\n     */\n    private void clearIsSubscribe() {\n\n      isSubscribe_ = false;\n    }\n\n    public static chronik.Chronik.Subscription parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.Subscription parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.Subscription parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.Subscription parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.Subscription parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.Subscription parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.Subscription parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.Subscription parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.Subscription parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.Subscription parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.Subscription parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.Subscription parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.Subscription prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.Subscription}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.Subscription, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.Subscription)\n        chronik.Chronik.SubscriptionOrBuilder {\n      // Construct using chronik.Chronik.Subscription.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>string script_type = 1;</code>\n       * @return The scriptType.\n       */\n      @java.lang.Override\n      public java.lang.String getScriptType() {\n        return instance.getScriptType();\n      }\n      /**\n       * <code>string script_type = 1;</code>\n       * @return The bytes for scriptType.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString\n          getScriptTypeBytes() {\n        return instance.getScriptTypeBytes();\n      }\n      /**\n       * <code>string script_type = 1;</code>\n       * @param value The scriptType to set.\n       * @return This builder for chaining.\n       */\n      public Builder setScriptType(\n          java.lang.String value) {\n        copyOnWrite();\n        instance.setScriptType(value);\n        return this;\n      }\n      /**\n       * <code>string script_type = 1;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearScriptType() {\n        copyOnWrite();\n        instance.clearScriptType();\n        return this;\n      }\n      /**\n       * <code>string script_type = 1;</code>\n       * @param value The bytes for scriptType to set.\n       * @return This builder for chaining.\n       */\n      public Builder setScriptTypeBytes(\n          com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setScriptTypeBytes(value);\n        return this;\n      }\n\n      /**\n       * <code>bytes payload = 2;</code>\n       * @return The payload.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getPayload() {\n        return instance.getPayload();\n      }\n      /**\n       * <code>bytes payload = 2;</code>\n       * @param value The payload to set.\n       * @return This builder for chaining.\n       */\n      public Builder setPayload(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setPayload(value);\n        return this;\n      }\n      /**\n       * <code>bytes payload = 2;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearPayload() {\n        copyOnWrite();\n        instance.clearPayload();\n        return this;\n      }\n\n      /**\n       * <code>bool is_subscribe = 3;</code>\n       * @return The isSubscribe.\n       */\n      @java.lang.Override\n      public boolean getIsSubscribe() {\n        return instance.getIsSubscribe();\n      }\n      /**\n       * <code>bool is_subscribe = 3;</code>\n       * @param value The isSubscribe to set.\n       * @return This builder for chaining.\n       */\n      public Builder setIsSubscribe(boolean value) {\n        copyOnWrite();\n        instance.setIsSubscribe(value);\n        return this;\n      }\n      /**\n       * <code>bool is_subscribe = 3;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearIsSubscribe() {\n        copyOnWrite();\n        instance.clearIsSubscribe();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.Subscription)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.Subscription();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"scriptType_\",\n              \"payload_\",\n              \"isSubscribe_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0003\\u0000\\u0000\\u0001\\u0003\\u0003\\u0000\\u0000\\u0000\\u0001\\u0208\\u0002\\n\" +\n                \"\\u0003\\u0007\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.Subscription> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.Subscription.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.Subscription>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.Subscription)\n    private static final chronik.Chronik.Subscription DEFAULT_INSTANCE;\n    static {\n      Subscription defaultInstance = new Subscription();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        Subscription.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.Subscription getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<Subscription> PARSER;\n\n    public static com.google.protobuf.Parser<Subscription> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface SubscribeMsgOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.SubscribeMsg)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>.chronik.Error error = 1;</code>\n     * @return Whether the error field is set.\n     */\n    boolean hasError();\n    /**\n     * <code>.chronik.Error error = 1;</code>\n     * @return The error.\n     */\n    chronik.Chronik.Error getError();\n\n    /**\n     * <code>.chronik.MsgAddedToMempool AddedToMempool = 2;</code>\n     * @return Whether the addedToMempool field is set.\n     */\n    boolean hasAddedToMempool();\n    /**\n     * <code>.chronik.MsgAddedToMempool AddedToMempool = 2;</code>\n     * @return The addedToMempool.\n     */\n    chronik.Chronik.MsgAddedToMempool getAddedToMempool();\n\n    /**\n     * <code>.chronik.MsgRemovedFromMempool RemovedFromMempool = 3;</code>\n     * @return Whether the removedFromMempool field is set.\n     */\n    boolean hasRemovedFromMempool();\n    /**\n     * <code>.chronik.MsgRemovedFromMempool RemovedFromMempool = 3;</code>\n     * @return The removedFromMempool.\n     */\n    chronik.Chronik.MsgRemovedFromMempool getRemovedFromMempool();\n\n    /**\n     * <code>.chronik.MsgConfirmed Confirmed = 4;</code>\n     * @return Whether the confirmed field is set.\n     */\n    boolean hasConfirmed();\n    /**\n     * <code>.chronik.MsgConfirmed Confirmed = 4;</code>\n     * @return The confirmed.\n     */\n    chronik.Chronik.MsgConfirmed getConfirmed();\n\n    /**\n     * <code>.chronik.MsgReorg Reorg = 5;</code>\n     * @return Whether the reorg field is set.\n     */\n    boolean hasReorg();\n    /**\n     * <code>.chronik.MsgReorg Reorg = 5;</code>\n     * @return The reorg.\n     */\n    chronik.Chronik.MsgReorg getReorg();\n\n    /**\n     * <code>.chronik.MsgBlockConnected BlockConnected = 6;</code>\n     * @return Whether the blockConnected field is set.\n     */\n    boolean hasBlockConnected();\n    /**\n     * <code>.chronik.MsgBlockConnected BlockConnected = 6;</code>\n     * @return The blockConnected.\n     */\n    chronik.Chronik.MsgBlockConnected getBlockConnected();\n\n    /**\n     * <code>.chronik.MsgBlockDisconnected BlockDisconnected = 7;</code>\n     * @return Whether the blockDisconnected field is set.\n     */\n    boolean hasBlockDisconnected();\n    /**\n     * <code>.chronik.MsgBlockDisconnected BlockDisconnected = 7;</code>\n     * @return The blockDisconnected.\n     */\n    chronik.Chronik.MsgBlockDisconnected getBlockDisconnected();\n\n    public chronik.Chronik.SubscribeMsg.MsgTypeCase getMsgTypeCase();\n  }\n  /**\n   * Protobuf type {@code chronik.SubscribeMsg}\n   */\n  public  static final class SubscribeMsg extends\n      com.google.protobuf.GeneratedMessageLite<\n          SubscribeMsg, SubscribeMsg.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.SubscribeMsg)\n      SubscribeMsgOrBuilder {\n    private SubscribeMsg() {\n    }\n    private int msgTypeCase_ = 0;\n    private java.lang.Object msgType_;\n    public enum MsgTypeCase {\n      ERROR(1),\n      ADDEDTOMEMPOOL(2),\n      REMOVEDFROMMEMPOOL(3),\n      CONFIRMED(4),\n      REORG(5),\n      BLOCKCONNECTED(6),\n      BLOCKDISCONNECTED(7),\n      MSGTYPE_NOT_SET(0);\n      private final int value;\n      private MsgTypeCase(int value) {\n        this.value = value;\n      }\n      /**\n       * @deprecated Use {@link #forNumber(int)} instead.\n       */\n      @java.lang.Deprecated\n      public static MsgTypeCase valueOf(int value) {\n        return forNumber(value);\n      }\n\n      public static MsgTypeCase forNumber(int value) {\n        switch (value) {\n          case 1: return ERROR;\n          case 2: return ADDEDTOMEMPOOL;\n          case 3: return REMOVEDFROMMEMPOOL;\n          case 4: return CONFIRMED;\n          case 5: return REORG;\n          case 6: return BLOCKCONNECTED;\n          case 7: return BLOCKDISCONNECTED;\n          case 0: return MSGTYPE_NOT_SET;\n          default: return null;\n        }\n      }\n      public int getNumber() {\n        return this.value;\n      }\n    };\n\n    @java.lang.Override\n    public MsgTypeCase\n    getMsgTypeCase() {\n      return MsgTypeCase.forNumber(\n          msgTypeCase_);\n    }\n\n    private void clearMsgType() {\n      msgTypeCase_ = 0;\n      msgType_ = null;\n    }\n\n    public static final int ERROR_FIELD_NUMBER = 1;\n    /**\n     * <code>.chronik.Error error = 1;</code>\n     */\n    @java.lang.Override\n    public boolean hasError() {\n      return msgTypeCase_ == 1;\n    }\n    /**\n     * <code>.chronik.Error error = 1;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.Error getError() {\n      if (msgTypeCase_ == 1) {\n         return (chronik.Chronik.Error) msgType_;\n      }\n      return chronik.Chronik.Error.getDefaultInstance();\n    }\n    /**\n     * <code>.chronik.Error error = 1;</code>\n     */\n    private void setError(chronik.Chronik.Error value) {\n      value.getClass();\n  msgType_ = value;\n      msgTypeCase_ = 1;\n    }\n    /**\n     * <code>.chronik.Error error = 1;</code>\n     */\n    private void mergeError(chronik.Chronik.Error value) {\n      value.getClass();\n  if (msgTypeCase_ == 1 &&\n          msgType_ != chronik.Chronik.Error.getDefaultInstance()) {\n        msgType_ = chronik.Chronik.Error.newBuilder((chronik.Chronik.Error) msgType_)\n            .mergeFrom(value).buildPartial();\n      } else {\n        msgType_ = value;\n      }\n      msgTypeCase_ = 1;\n    }\n    /**\n     * <code>.chronik.Error error = 1;</code>\n     */\n    private void clearError() {\n      if (msgTypeCase_ == 1) {\n        msgTypeCase_ = 0;\n        msgType_ = null;\n      }\n    }\n\n    public static final int ADDEDTOMEMPOOL_FIELD_NUMBER = 2;\n    /**\n     * <code>.chronik.MsgAddedToMempool AddedToMempool = 2;</code>\n     */\n    @java.lang.Override\n    public boolean hasAddedToMempool() {\n      return msgTypeCase_ == 2;\n    }\n    /**\n     * <code>.chronik.MsgAddedToMempool AddedToMempool = 2;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.MsgAddedToMempool getAddedToMempool() {\n      if (msgTypeCase_ == 2) {\n         return (chronik.Chronik.MsgAddedToMempool) msgType_;\n      }\n      return chronik.Chronik.MsgAddedToMempool.getDefaultInstance();\n    }\n    /**\n     * <code>.chronik.MsgAddedToMempool AddedToMempool = 2;</code>\n     */\n    private void setAddedToMempool(chronik.Chronik.MsgAddedToMempool value) {\n      value.getClass();\n  msgType_ = value;\n      msgTypeCase_ = 2;\n    }\n    /**\n     * <code>.chronik.MsgAddedToMempool AddedToMempool = 2;</code>\n     */\n    private void mergeAddedToMempool(chronik.Chronik.MsgAddedToMempool value) {\n      value.getClass();\n  if (msgTypeCase_ == 2 &&\n          msgType_ != chronik.Chronik.MsgAddedToMempool.getDefaultInstance()) {\n        msgType_ = chronik.Chronik.MsgAddedToMempool.newBuilder((chronik.Chronik.MsgAddedToMempool) msgType_)\n            .mergeFrom(value).buildPartial();\n      } else {\n        msgType_ = value;\n      }\n      msgTypeCase_ = 2;\n    }\n    /**\n     * <code>.chronik.MsgAddedToMempool AddedToMempool = 2;</code>\n     */\n    private void clearAddedToMempool() {\n      if (msgTypeCase_ == 2) {\n        msgTypeCase_ = 0;\n        msgType_ = null;\n      }\n    }\n\n    public static final int REMOVEDFROMMEMPOOL_FIELD_NUMBER = 3;\n    /**\n     * <code>.chronik.MsgRemovedFromMempool RemovedFromMempool = 3;</code>\n     */\n    @java.lang.Override\n    public boolean hasRemovedFromMempool() {\n      return msgTypeCase_ == 3;\n    }\n    /**\n     * <code>.chronik.MsgRemovedFromMempool RemovedFromMempool = 3;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.MsgRemovedFromMempool getRemovedFromMempool() {\n      if (msgTypeCase_ == 3) {\n         return (chronik.Chronik.MsgRemovedFromMempool) msgType_;\n      }\n      return chronik.Chronik.MsgRemovedFromMempool.getDefaultInstance();\n    }\n    /**\n     * <code>.chronik.MsgRemovedFromMempool RemovedFromMempool = 3;</code>\n     */\n    private void setRemovedFromMempool(chronik.Chronik.MsgRemovedFromMempool value) {\n      value.getClass();\n  msgType_ = value;\n      msgTypeCase_ = 3;\n    }\n    /**\n     * <code>.chronik.MsgRemovedFromMempool RemovedFromMempool = 3;</code>\n     */\n    private void mergeRemovedFromMempool(chronik.Chronik.MsgRemovedFromMempool value) {\n      value.getClass();\n  if (msgTypeCase_ == 3 &&\n          msgType_ != chronik.Chronik.MsgRemovedFromMempool.getDefaultInstance()) {\n        msgType_ = chronik.Chronik.MsgRemovedFromMempool.newBuilder((chronik.Chronik.MsgRemovedFromMempool) msgType_)\n            .mergeFrom(value).buildPartial();\n      } else {\n        msgType_ = value;\n      }\n      msgTypeCase_ = 3;\n    }\n    /**\n     * <code>.chronik.MsgRemovedFromMempool RemovedFromMempool = 3;</code>\n     */\n    private void clearRemovedFromMempool() {\n      if (msgTypeCase_ == 3) {\n        msgTypeCase_ = 0;\n        msgType_ = null;\n      }\n    }\n\n    public static final int CONFIRMED_FIELD_NUMBER = 4;\n    /**\n     * <code>.chronik.MsgConfirmed Confirmed = 4;</code>\n     */\n    @java.lang.Override\n    public boolean hasConfirmed() {\n      return msgTypeCase_ == 4;\n    }\n    /**\n     * <code>.chronik.MsgConfirmed Confirmed = 4;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.MsgConfirmed getConfirmed() {\n      if (msgTypeCase_ == 4) {\n         return (chronik.Chronik.MsgConfirmed) msgType_;\n      }\n      return chronik.Chronik.MsgConfirmed.getDefaultInstance();\n    }\n    /**\n     * <code>.chronik.MsgConfirmed Confirmed = 4;</code>\n     */\n    private void setConfirmed(chronik.Chronik.MsgConfirmed value) {\n      value.getClass();\n  msgType_ = value;\n      msgTypeCase_ = 4;\n    }\n    /**\n     * <code>.chronik.MsgConfirmed Confirmed = 4;</code>\n     */\n    private void mergeConfirmed(chronik.Chronik.MsgConfirmed value) {\n      value.getClass();\n  if (msgTypeCase_ == 4 &&\n          msgType_ != chronik.Chronik.MsgConfirmed.getDefaultInstance()) {\n        msgType_ = chronik.Chronik.MsgConfirmed.newBuilder((chronik.Chronik.MsgConfirmed) msgType_)\n            .mergeFrom(value).buildPartial();\n      } else {\n        msgType_ = value;\n      }\n      msgTypeCase_ = 4;\n    }\n    /**\n     * <code>.chronik.MsgConfirmed Confirmed = 4;</code>\n     */\n    private void clearConfirmed() {\n      if (msgTypeCase_ == 4) {\n        msgTypeCase_ = 0;\n        msgType_ = null;\n      }\n    }\n\n    public static final int REORG_FIELD_NUMBER = 5;\n    /**\n     * <code>.chronik.MsgReorg Reorg = 5;</code>\n     */\n    @java.lang.Override\n    public boolean hasReorg() {\n      return msgTypeCase_ == 5;\n    }\n    /**\n     * <code>.chronik.MsgReorg Reorg = 5;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.MsgReorg getReorg() {\n      if (msgTypeCase_ == 5) {\n         return (chronik.Chronik.MsgReorg) msgType_;\n      }\n      return chronik.Chronik.MsgReorg.getDefaultInstance();\n    }\n    /**\n     * <code>.chronik.MsgReorg Reorg = 5;</code>\n     */\n    private void setReorg(chronik.Chronik.MsgReorg value) {\n      value.getClass();\n  msgType_ = value;\n      msgTypeCase_ = 5;\n    }\n    /**\n     * <code>.chronik.MsgReorg Reorg = 5;</code>\n     */\n    private void mergeReorg(chronik.Chronik.MsgReorg value) {\n      value.getClass();\n  if (msgTypeCase_ == 5 &&\n          msgType_ != chronik.Chronik.MsgReorg.getDefaultInstance()) {\n        msgType_ = chronik.Chronik.MsgReorg.newBuilder((chronik.Chronik.MsgReorg) msgType_)\n            .mergeFrom(value).buildPartial();\n      } else {\n        msgType_ = value;\n      }\n      msgTypeCase_ = 5;\n    }\n    /**\n     * <code>.chronik.MsgReorg Reorg = 5;</code>\n     */\n    private void clearReorg() {\n      if (msgTypeCase_ == 5) {\n        msgTypeCase_ = 0;\n        msgType_ = null;\n      }\n    }\n\n    public static final int BLOCKCONNECTED_FIELD_NUMBER = 6;\n    /**\n     * <code>.chronik.MsgBlockConnected BlockConnected = 6;</code>\n     */\n    @java.lang.Override\n    public boolean hasBlockConnected() {\n      return msgTypeCase_ == 6;\n    }\n    /**\n     * <code>.chronik.MsgBlockConnected BlockConnected = 6;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.MsgBlockConnected getBlockConnected() {\n      if (msgTypeCase_ == 6) {\n         return (chronik.Chronik.MsgBlockConnected) msgType_;\n      }\n      return chronik.Chronik.MsgBlockConnected.getDefaultInstance();\n    }\n    /**\n     * <code>.chronik.MsgBlockConnected BlockConnected = 6;</code>\n     */\n    private void setBlockConnected(chronik.Chronik.MsgBlockConnected value) {\n      value.getClass();\n  msgType_ = value;\n      msgTypeCase_ = 6;\n    }\n    /**\n     * <code>.chronik.MsgBlockConnected BlockConnected = 6;</code>\n     */\n    private void mergeBlockConnected(chronik.Chronik.MsgBlockConnected value) {\n      value.getClass();\n  if (msgTypeCase_ == 6 &&\n          msgType_ != chronik.Chronik.MsgBlockConnected.getDefaultInstance()) {\n        msgType_ = chronik.Chronik.MsgBlockConnected.newBuilder((chronik.Chronik.MsgBlockConnected) msgType_)\n            .mergeFrom(value).buildPartial();\n      } else {\n        msgType_ = value;\n      }\n      msgTypeCase_ = 6;\n    }\n    /**\n     * <code>.chronik.MsgBlockConnected BlockConnected = 6;</code>\n     */\n    private void clearBlockConnected() {\n      if (msgTypeCase_ == 6) {\n        msgTypeCase_ = 0;\n        msgType_ = null;\n      }\n    }\n\n    public static final int BLOCKDISCONNECTED_FIELD_NUMBER = 7;\n    /**\n     * <code>.chronik.MsgBlockDisconnected BlockDisconnected = 7;</code>\n     */\n    @java.lang.Override\n    public boolean hasBlockDisconnected() {\n      return msgTypeCase_ == 7;\n    }\n    /**\n     * <code>.chronik.MsgBlockDisconnected BlockDisconnected = 7;</code>\n     */\n    @java.lang.Override\n    public chronik.Chronik.MsgBlockDisconnected getBlockDisconnected() {\n      if (msgTypeCase_ == 7) {\n         return (chronik.Chronik.MsgBlockDisconnected) msgType_;\n      }\n      return chronik.Chronik.MsgBlockDisconnected.getDefaultInstance();\n    }\n    /**\n     * <code>.chronik.MsgBlockDisconnected BlockDisconnected = 7;</code>\n     */\n    private void setBlockDisconnected(chronik.Chronik.MsgBlockDisconnected value) {\n      value.getClass();\n  msgType_ = value;\n      msgTypeCase_ = 7;\n    }\n    /**\n     * <code>.chronik.MsgBlockDisconnected BlockDisconnected = 7;</code>\n     */\n    private void mergeBlockDisconnected(chronik.Chronik.MsgBlockDisconnected value) {\n      value.getClass();\n  if (msgTypeCase_ == 7 &&\n          msgType_ != chronik.Chronik.MsgBlockDisconnected.getDefaultInstance()) {\n        msgType_ = chronik.Chronik.MsgBlockDisconnected.newBuilder((chronik.Chronik.MsgBlockDisconnected) msgType_)\n            .mergeFrom(value).buildPartial();\n      } else {\n        msgType_ = value;\n      }\n      msgTypeCase_ = 7;\n    }\n    /**\n     * <code>.chronik.MsgBlockDisconnected BlockDisconnected = 7;</code>\n     */\n    private void clearBlockDisconnected() {\n      if (msgTypeCase_ == 7) {\n        msgTypeCase_ = 0;\n        msgType_ = null;\n      }\n    }\n\n    public static chronik.Chronik.SubscribeMsg parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.SubscribeMsg parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.SubscribeMsg parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.SubscribeMsg parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.SubscribeMsg parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.SubscribeMsg parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.SubscribeMsg parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.SubscribeMsg parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.SubscribeMsg parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.SubscribeMsg parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.SubscribeMsg parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.SubscribeMsg parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.SubscribeMsg prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.SubscribeMsg}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.SubscribeMsg, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.SubscribeMsg)\n        chronik.Chronik.SubscribeMsgOrBuilder {\n      // Construct using chronik.Chronik.SubscribeMsg.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n      @java.lang.Override\n      public MsgTypeCase\n          getMsgTypeCase() {\n        return instance.getMsgTypeCase();\n      }\n\n      public Builder clearMsgType() {\n        copyOnWrite();\n        instance.clearMsgType();\n        return this;\n      }\n\n\n      /**\n       * <code>.chronik.Error error = 1;</code>\n       */\n      @java.lang.Override\n      public boolean hasError() {\n        return instance.hasError();\n      }\n      /**\n       * <code>.chronik.Error error = 1;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.Error getError() {\n        return instance.getError();\n      }\n      /**\n       * <code>.chronik.Error error = 1;</code>\n       */\n      public Builder setError(chronik.Chronik.Error value) {\n        copyOnWrite();\n        instance.setError(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.Error error = 1;</code>\n       */\n      public Builder setError(\n          chronik.Chronik.Error.Builder builderForValue) {\n        copyOnWrite();\n        instance.setError(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>.chronik.Error error = 1;</code>\n       */\n      public Builder mergeError(chronik.Chronik.Error value) {\n        copyOnWrite();\n        instance.mergeError(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.Error error = 1;</code>\n       */\n      public Builder clearError() {\n        copyOnWrite();\n        instance.clearError();\n        return this;\n      }\n\n      /**\n       * <code>.chronik.MsgAddedToMempool AddedToMempool = 2;</code>\n       */\n      @java.lang.Override\n      public boolean hasAddedToMempool() {\n        return instance.hasAddedToMempool();\n      }\n      /**\n       * <code>.chronik.MsgAddedToMempool AddedToMempool = 2;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.MsgAddedToMempool getAddedToMempool() {\n        return instance.getAddedToMempool();\n      }\n      /**\n       * <code>.chronik.MsgAddedToMempool AddedToMempool = 2;</code>\n       */\n      public Builder setAddedToMempool(chronik.Chronik.MsgAddedToMempool value) {\n        copyOnWrite();\n        instance.setAddedToMempool(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.MsgAddedToMempool AddedToMempool = 2;</code>\n       */\n      public Builder setAddedToMempool(\n          chronik.Chronik.MsgAddedToMempool.Builder builderForValue) {\n        copyOnWrite();\n        instance.setAddedToMempool(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>.chronik.MsgAddedToMempool AddedToMempool = 2;</code>\n       */\n      public Builder mergeAddedToMempool(chronik.Chronik.MsgAddedToMempool value) {\n        copyOnWrite();\n        instance.mergeAddedToMempool(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.MsgAddedToMempool AddedToMempool = 2;</code>\n       */\n      public Builder clearAddedToMempool() {\n        copyOnWrite();\n        instance.clearAddedToMempool();\n        return this;\n      }\n\n      /**\n       * <code>.chronik.MsgRemovedFromMempool RemovedFromMempool = 3;</code>\n       */\n      @java.lang.Override\n      public boolean hasRemovedFromMempool() {\n        return instance.hasRemovedFromMempool();\n      }\n      /**\n       * <code>.chronik.MsgRemovedFromMempool RemovedFromMempool = 3;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.MsgRemovedFromMempool getRemovedFromMempool() {\n        return instance.getRemovedFromMempool();\n      }\n      /**\n       * <code>.chronik.MsgRemovedFromMempool RemovedFromMempool = 3;</code>\n       */\n      public Builder setRemovedFromMempool(chronik.Chronik.MsgRemovedFromMempool value) {\n        copyOnWrite();\n        instance.setRemovedFromMempool(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.MsgRemovedFromMempool RemovedFromMempool = 3;</code>\n       */\n      public Builder setRemovedFromMempool(\n          chronik.Chronik.MsgRemovedFromMempool.Builder builderForValue) {\n        copyOnWrite();\n        instance.setRemovedFromMempool(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>.chronik.MsgRemovedFromMempool RemovedFromMempool = 3;</code>\n       */\n      public Builder mergeRemovedFromMempool(chronik.Chronik.MsgRemovedFromMempool value) {\n        copyOnWrite();\n        instance.mergeRemovedFromMempool(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.MsgRemovedFromMempool RemovedFromMempool = 3;</code>\n       */\n      public Builder clearRemovedFromMempool() {\n        copyOnWrite();\n        instance.clearRemovedFromMempool();\n        return this;\n      }\n\n      /**\n       * <code>.chronik.MsgConfirmed Confirmed = 4;</code>\n       */\n      @java.lang.Override\n      public boolean hasConfirmed() {\n        return instance.hasConfirmed();\n      }\n      /**\n       * <code>.chronik.MsgConfirmed Confirmed = 4;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.MsgConfirmed getConfirmed() {\n        return instance.getConfirmed();\n      }\n      /**\n       * <code>.chronik.MsgConfirmed Confirmed = 4;</code>\n       */\n      public Builder setConfirmed(chronik.Chronik.MsgConfirmed value) {\n        copyOnWrite();\n        instance.setConfirmed(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.MsgConfirmed Confirmed = 4;</code>\n       */\n      public Builder setConfirmed(\n          chronik.Chronik.MsgConfirmed.Builder builderForValue) {\n        copyOnWrite();\n        instance.setConfirmed(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>.chronik.MsgConfirmed Confirmed = 4;</code>\n       */\n      public Builder mergeConfirmed(chronik.Chronik.MsgConfirmed value) {\n        copyOnWrite();\n        instance.mergeConfirmed(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.MsgConfirmed Confirmed = 4;</code>\n       */\n      public Builder clearConfirmed() {\n        copyOnWrite();\n        instance.clearConfirmed();\n        return this;\n      }\n\n      /**\n       * <code>.chronik.MsgReorg Reorg = 5;</code>\n       */\n      @java.lang.Override\n      public boolean hasReorg() {\n        return instance.hasReorg();\n      }\n      /**\n       * <code>.chronik.MsgReorg Reorg = 5;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.MsgReorg getReorg() {\n        return instance.getReorg();\n      }\n      /**\n       * <code>.chronik.MsgReorg Reorg = 5;</code>\n       */\n      public Builder setReorg(chronik.Chronik.MsgReorg value) {\n        copyOnWrite();\n        instance.setReorg(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.MsgReorg Reorg = 5;</code>\n       */\n      public Builder setReorg(\n          chronik.Chronik.MsgReorg.Builder builderForValue) {\n        copyOnWrite();\n        instance.setReorg(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>.chronik.MsgReorg Reorg = 5;</code>\n       */\n      public Builder mergeReorg(chronik.Chronik.MsgReorg value) {\n        copyOnWrite();\n        instance.mergeReorg(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.MsgReorg Reorg = 5;</code>\n       */\n      public Builder clearReorg() {\n        copyOnWrite();\n        instance.clearReorg();\n        return this;\n      }\n\n      /**\n       * <code>.chronik.MsgBlockConnected BlockConnected = 6;</code>\n       */\n      @java.lang.Override\n      public boolean hasBlockConnected() {\n        return instance.hasBlockConnected();\n      }\n      /**\n       * <code>.chronik.MsgBlockConnected BlockConnected = 6;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.MsgBlockConnected getBlockConnected() {\n        return instance.getBlockConnected();\n      }\n      /**\n       * <code>.chronik.MsgBlockConnected BlockConnected = 6;</code>\n       */\n      public Builder setBlockConnected(chronik.Chronik.MsgBlockConnected value) {\n        copyOnWrite();\n        instance.setBlockConnected(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.MsgBlockConnected BlockConnected = 6;</code>\n       */\n      public Builder setBlockConnected(\n          chronik.Chronik.MsgBlockConnected.Builder builderForValue) {\n        copyOnWrite();\n        instance.setBlockConnected(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>.chronik.MsgBlockConnected BlockConnected = 6;</code>\n       */\n      public Builder mergeBlockConnected(chronik.Chronik.MsgBlockConnected value) {\n        copyOnWrite();\n        instance.mergeBlockConnected(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.MsgBlockConnected BlockConnected = 6;</code>\n       */\n      public Builder clearBlockConnected() {\n        copyOnWrite();\n        instance.clearBlockConnected();\n        return this;\n      }\n\n      /**\n       * <code>.chronik.MsgBlockDisconnected BlockDisconnected = 7;</code>\n       */\n      @java.lang.Override\n      public boolean hasBlockDisconnected() {\n        return instance.hasBlockDisconnected();\n      }\n      /**\n       * <code>.chronik.MsgBlockDisconnected BlockDisconnected = 7;</code>\n       */\n      @java.lang.Override\n      public chronik.Chronik.MsgBlockDisconnected getBlockDisconnected() {\n        return instance.getBlockDisconnected();\n      }\n      /**\n       * <code>.chronik.MsgBlockDisconnected BlockDisconnected = 7;</code>\n       */\n      public Builder setBlockDisconnected(chronik.Chronik.MsgBlockDisconnected value) {\n        copyOnWrite();\n        instance.setBlockDisconnected(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.MsgBlockDisconnected BlockDisconnected = 7;</code>\n       */\n      public Builder setBlockDisconnected(\n          chronik.Chronik.MsgBlockDisconnected.Builder builderForValue) {\n        copyOnWrite();\n        instance.setBlockDisconnected(builderForValue.build());\n        return this;\n      }\n      /**\n       * <code>.chronik.MsgBlockDisconnected BlockDisconnected = 7;</code>\n       */\n      public Builder mergeBlockDisconnected(chronik.Chronik.MsgBlockDisconnected value) {\n        copyOnWrite();\n        instance.mergeBlockDisconnected(value);\n        return this;\n      }\n      /**\n       * <code>.chronik.MsgBlockDisconnected BlockDisconnected = 7;</code>\n       */\n      public Builder clearBlockDisconnected() {\n        copyOnWrite();\n        instance.clearBlockDisconnected();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.SubscribeMsg)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.SubscribeMsg();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"msgType_\",\n              \"msgTypeCase_\",\n              chronik.Chronik.Error.class,\n              chronik.Chronik.MsgAddedToMempool.class,\n              chronik.Chronik.MsgRemovedFromMempool.class,\n              chronik.Chronik.MsgConfirmed.class,\n              chronik.Chronik.MsgReorg.class,\n              chronik.Chronik.MsgBlockConnected.class,\n              chronik.Chronik.MsgBlockDisconnected.class,\n            };\n            java.lang.String info =\n                \"\\u0000\\u0007\\u0001\\u0000\\u0001\\u0007\\u0007\\u0000\\u0000\\u0000\\u0001<\\u0000\\u0002<\" +\n                \"\\u0000\\u0003<\\u0000\\u0004<\\u0000\\u0005<\\u0000\\u0006<\\u0000\\u0007<\\u0000\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.SubscribeMsg> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.SubscribeMsg.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.SubscribeMsg>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.SubscribeMsg)\n    private static final chronik.Chronik.SubscribeMsg DEFAULT_INSTANCE;\n    static {\n      SubscribeMsg defaultInstance = new SubscribeMsg();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        SubscribeMsg.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.SubscribeMsg getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<SubscribeMsg> PARSER;\n\n    public static com.google.protobuf.Parser<SubscribeMsg> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface MsgAddedToMempoolOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.MsgAddedToMempool)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>bytes txid = 1;</code>\n     * @return The txid.\n     */\n    com.google.protobuf.ByteString getTxid();\n  }\n  /**\n   * Protobuf type {@code chronik.MsgAddedToMempool}\n   */\n  public  static final class MsgAddedToMempool extends\n      com.google.protobuf.GeneratedMessageLite<\n          MsgAddedToMempool, MsgAddedToMempool.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.MsgAddedToMempool)\n      MsgAddedToMempoolOrBuilder {\n    private MsgAddedToMempool() {\n      txid_ = com.google.protobuf.ByteString.EMPTY;\n    }\n    public static final int TXID_FIELD_NUMBER = 1;\n    private com.google.protobuf.ByteString txid_;\n    /**\n     * <code>bytes txid = 1;</code>\n     * @return The txid.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getTxid() {\n      return txid_;\n    }\n    /**\n     * <code>bytes txid = 1;</code>\n     * @param value The txid to set.\n     */\n    private void setTxid(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      txid_ = value;\n    }\n    /**\n     * <code>bytes txid = 1;</code>\n     */\n    private void clearTxid() {\n\n      txid_ = getDefaultInstance().getTxid();\n    }\n\n    public static chronik.Chronik.MsgAddedToMempool parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.MsgAddedToMempool parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgAddedToMempool parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.MsgAddedToMempool parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgAddedToMempool parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.MsgAddedToMempool parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgAddedToMempool parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.MsgAddedToMempool parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgAddedToMempool parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.MsgAddedToMempool parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgAddedToMempool parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.MsgAddedToMempool parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.MsgAddedToMempool prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.MsgAddedToMempool}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.MsgAddedToMempool, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.MsgAddedToMempool)\n        chronik.Chronik.MsgAddedToMempoolOrBuilder {\n      // Construct using chronik.Chronik.MsgAddedToMempool.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>bytes txid = 1;</code>\n       * @return The txid.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getTxid() {\n        return instance.getTxid();\n      }\n      /**\n       * <code>bytes txid = 1;</code>\n       * @param value The txid to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTxid(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setTxid(value);\n        return this;\n      }\n      /**\n       * <code>bytes txid = 1;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearTxid() {\n        copyOnWrite();\n        instance.clearTxid();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.MsgAddedToMempool)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.MsgAddedToMempool();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"txid_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0001\\u0000\\u0000\\u0001\\u0001\\u0001\\u0000\\u0000\\u0000\\u0001\\n\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.MsgAddedToMempool> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.MsgAddedToMempool.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.MsgAddedToMempool>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.MsgAddedToMempool)\n    private static final chronik.Chronik.MsgAddedToMempool DEFAULT_INSTANCE;\n    static {\n      MsgAddedToMempool defaultInstance = new MsgAddedToMempool();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        MsgAddedToMempool.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.MsgAddedToMempool getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<MsgAddedToMempool> PARSER;\n\n    public static com.google.protobuf.Parser<MsgAddedToMempool> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface MsgRemovedFromMempoolOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.MsgRemovedFromMempool)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>bytes txid = 1;</code>\n     * @return The txid.\n     */\n    com.google.protobuf.ByteString getTxid();\n  }\n  /**\n   * Protobuf type {@code chronik.MsgRemovedFromMempool}\n   */\n  public  static final class MsgRemovedFromMempool extends\n      com.google.protobuf.GeneratedMessageLite<\n          MsgRemovedFromMempool, MsgRemovedFromMempool.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.MsgRemovedFromMempool)\n      MsgRemovedFromMempoolOrBuilder {\n    private MsgRemovedFromMempool() {\n      txid_ = com.google.protobuf.ByteString.EMPTY;\n    }\n    public static final int TXID_FIELD_NUMBER = 1;\n    private com.google.protobuf.ByteString txid_;\n    /**\n     * <code>bytes txid = 1;</code>\n     * @return The txid.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getTxid() {\n      return txid_;\n    }\n    /**\n     * <code>bytes txid = 1;</code>\n     * @param value The txid to set.\n     */\n    private void setTxid(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      txid_ = value;\n    }\n    /**\n     * <code>bytes txid = 1;</code>\n     */\n    private void clearTxid() {\n\n      txid_ = getDefaultInstance().getTxid();\n    }\n\n    public static chronik.Chronik.MsgRemovedFromMempool parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.MsgRemovedFromMempool parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgRemovedFromMempool parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.MsgRemovedFromMempool parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgRemovedFromMempool parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.MsgRemovedFromMempool parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgRemovedFromMempool parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.MsgRemovedFromMempool parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgRemovedFromMempool parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.MsgRemovedFromMempool parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgRemovedFromMempool parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.MsgRemovedFromMempool parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.MsgRemovedFromMempool prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.MsgRemovedFromMempool}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.MsgRemovedFromMempool, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.MsgRemovedFromMempool)\n        chronik.Chronik.MsgRemovedFromMempoolOrBuilder {\n      // Construct using chronik.Chronik.MsgRemovedFromMempool.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>bytes txid = 1;</code>\n       * @return The txid.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getTxid() {\n        return instance.getTxid();\n      }\n      /**\n       * <code>bytes txid = 1;</code>\n       * @param value The txid to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTxid(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setTxid(value);\n        return this;\n      }\n      /**\n       * <code>bytes txid = 1;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearTxid() {\n        copyOnWrite();\n        instance.clearTxid();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.MsgRemovedFromMempool)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.MsgRemovedFromMempool();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"txid_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0001\\u0000\\u0000\\u0001\\u0001\\u0001\\u0000\\u0000\\u0000\\u0001\\n\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.MsgRemovedFromMempool> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.MsgRemovedFromMempool.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.MsgRemovedFromMempool>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.MsgRemovedFromMempool)\n    private static final chronik.Chronik.MsgRemovedFromMempool DEFAULT_INSTANCE;\n    static {\n      MsgRemovedFromMempool defaultInstance = new MsgRemovedFromMempool();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        MsgRemovedFromMempool.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.MsgRemovedFromMempool getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<MsgRemovedFromMempool> PARSER;\n\n    public static com.google.protobuf.Parser<MsgRemovedFromMempool> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface MsgConfirmedOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.MsgConfirmed)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>bytes txid = 1;</code>\n     * @return The txid.\n     */\n    com.google.protobuf.ByteString getTxid();\n  }\n  /**\n   * Protobuf type {@code chronik.MsgConfirmed}\n   */\n  public  static final class MsgConfirmed extends\n      com.google.protobuf.GeneratedMessageLite<\n          MsgConfirmed, MsgConfirmed.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.MsgConfirmed)\n      MsgConfirmedOrBuilder {\n    private MsgConfirmed() {\n      txid_ = com.google.protobuf.ByteString.EMPTY;\n    }\n    public static final int TXID_FIELD_NUMBER = 1;\n    private com.google.protobuf.ByteString txid_;\n    /**\n     * <code>bytes txid = 1;</code>\n     * @return The txid.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getTxid() {\n      return txid_;\n    }\n    /**\n     * <code>bytes txid = 1;</code>\n     * @param value The txid to set.\n     */\n    private void setTxid(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      txid_ = value;\n    }\n    /**\n     * <code>bytes txid = 1;</code>\n     */\n    private void clearTxid() {\n\n      txid_ = getDefaultInstance().getTxid();\n    }\n\n    public static chronik.Chronik.MsgConfirmed parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.MsgConfirmed parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgConfirmed parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.MsgConfirmed parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgConfirmed parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.MsgConfirmed parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgConfirmed parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.MsgConfirmed parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgConfirmed parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.MsgConfirmed parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgConfirmed parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.MsgConfirmed parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.MsgConfirmed prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.MsgConfirmed}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.MsgConfirmed, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.MsgConfirmed)\n        chronik.Chronik.MsgConfirmedOrBuilder {\n      // Construct using chronik.Chronik.MsgConfirmed.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>bytes txid = 1;</code>\n       * @return The txid.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getTxid() {\n        return instance.getTxid();\n      }\n      /**\n       * <code>bytes txid = 1;</code>\n       * @param value The txid to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTxid(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setTxid(value);\n        return this;\n      }\n      /**\n       * <code>bytes txid = 1;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearTxid() {\n        copyOnWrite();\n        instance.clearTxid();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.MsgConfirmed)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.MsgConfirmed();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"txid_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0001\\u0000\\u0000\\u0001\\u0001\\u0001\\u0000\\u0000\\u0000\\u0001\\n\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.MsgConfirmed> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.MsgConfirmed.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.MsgConfirmed>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.MsgConfirmed)\n    private static final chronik.Chronik.MsgConfirmed DEFAULT_INSTANCE;\n    static {\n      MsgConfirmed defaultInstance = new MsgConfirmed();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        MsgConfirmed.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.MsgConfirmed getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<MsgConfirmed> PARSER;\n\n    public static com.google.protobuf.Parser<MsgConfirmed> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface MsgReorgOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.MsgReorg)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>bytes txid = 1;</code>\n     * @return The txid.\n     */\n    com.google.protobuf.ByteString getTxid();\n  }\n  /**\n   * Protobuf type {@code chronik.MsgReorg}\n   */\n  public  static final class MsgReorg extends\n      com.google.protobuf.GeneratedMessageLite<\n          MsgReorg, MsgReorg.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.MsgReorg)\n      MsgReorgOrBuilder {\n    private MsgReorg() {\n      txid_ = com.google.protobuf.ByteString.EMPTY;\n    }\n    public static final int TXID_FIELD_NUMBER = 1;\n    private com.google.protobuf.ByteString txid_;\n    /**\n     * <code>bytes txid = 1;</code>\n     * @return The txid.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getTxid() {\n      return txid_;\n    }\n    /**\n     * <code>bytes txid = 1;</code>\n     * @param value The txid to set.\n     */\n    private void setTxid(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      txid_ = value;\n    }\n    /**\n     * <code>bytes txid = 1;</code>\n     */\n    private void clearTxid() {\n\n      txid_ = getDefaultInstance().getTxid();\n    }\n\n    public static chronik.Chronik.MsgReorg parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.MsgReorg parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgReorg parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.MsgReorg parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgReorg parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.MsgReorg parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgReorg parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.MsgReorg parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgReorg parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.MsgReorg parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgReorg parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.MsgReorg parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.MsgReorg prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.MsgReorg}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.MsgReorg, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.MsgReorg)\n        chronik.Chronik.MsgReorgOrBuilder {\n      // Construct using chronik.Chronik.MsgReorg.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>bytes txid = 1;</code>\n       * @return The txid.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getTxid() {\n        return instance.getTxid();\n      }\n      /**\n       * <code>bytes txid = 1;</code>\n       * @param value The txid to set.\n       * @return This builder for chaining.\n       */\n      public Builder setTxid(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setTxid(value);\n        return this;\n      }\n      /**\n       * <code>bytes txid = 1;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearTxid() {\n        copyOnWrite();\n        instance.clearTxid();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.MsgReorg)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.MsgReorg();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"txid_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0001\\u0000\\u0000\\u0001\\u0001\\u0001\\u0000\\u0000\\u0000\\u0001\\n\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.MsgReorg> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.MsgReorg.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.MsgReorg>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.MsgReorg)\n    private static final chronik.Chronik.MsgReorg DEFAULT_INSTANCE;\n    static {\n      MsgReorg defaultInstance = new MsgReorg();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        MsgReorg.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.MsgReorg getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<MsgReorg> PARSER;\n\n    public static com.google.protobuf.Parser<MsgReorg> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface MsgBlockConnectedOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.MsgBlockConnected)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>bytes block_hash = 1;</code>\n     * @return The blockHash.\n     */\n    com.google.protobuf.ByteString getBlockHash();\n  }\n  /**\n   * Protobuf type {@code chronik.MsgBlockConnected}\n   */\n  public  static final class MsgBlockConnected extends\n      com.google.protobuf.GeneratedMessageLite<\n          MsgBlockConnected, MsgBlockConnected.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.MsgBlockConnected)\n      MsgBlockConnectedOrBuilder {\n    private MsgBlockConnected() {\n      blockHash_ = com.google.protobuf.ByteString.EMPTY;\n    }\n    public static final int BLOCK_HASH_FIELD_NUMBER = 1;\n    private com.google.protobuf.ByteString blockHash_;\n    /**\n     * <code>bytes block_hash = 1;</code>\n     * @return The blockHash.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getBlockHash() {\n      return blockHash_;\n    }\n    /**\n     * <code>bytes block_hash = 1;</code>\n     * @param value The blockHash to set.\n     */\n    private void setBlockHash(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      blockHash_ = value;\n    }\n    /**\n     * <code>bytes block_hash = 1;</code>\n     */\n    private void clearBlockHash() {\n\n      blockHash_ = getDefaultInstance().getBlockHash();\n    }\n\n    public static chronik.Chronik.MsgBlockConnected parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.MsgBlockConnected parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgBlockConnected parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.MsgBlockConnected parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgBlockConnected parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.MsgBlockConnected parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgBlockConnected parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.MsgBlockConnected parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgBlockConnected parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.MsgBlockConnected parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgBlockConnected parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.MsgBlockConnected parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.MsgBlockConnected prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.MsgBlockConnected}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.MsgBlockConnected, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.MsgBlockConnected)\n        chronik.Chronik.MsgBlockConnectedOrBuilder {\n      // Construct using chronik.Chronik.MsgBlockConnected.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>bytes block_hash = 1;</code>\n       * @return The blockHash.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getBlockHash() {\n        return instance.getBlockHash();\n      }\n      /**\n       * <code>bytes block_hash = 1;</code>\n       * @param value The blockHash to set.\n       * @return This builder for chaining.\n       */\n      public Builder setBlockHash(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setBlockHash(value);\n        return this;\n      }\n      /**\n       * <code>bytes block_hash = 1;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearBlockHash() {\n        copyOnWrite();\n        instance.clearBlockHash();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.MsgBlockConnected)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.MsgBlockConnected();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"blockHash_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0001\\u0000\\u0000\\u0001\\u0001\\u0001\\u0000\\u0000\\u0000\\u0001\\n\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.MsgBlockConnected> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.MsgBlockConnected.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.MsgBlockConnected>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.MsgBlockConnected)\n    private static final chronik.Chronik.MsgBlockConnected DEFAULT_INSTANCE;\n    static {\n      MsgBlockConnected defaultInstance = new MsgBlockConnected();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        MsgBlockConnected.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.MsgBlockConnected getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<MsgBlockConnected> PARSER;\n\n    public static com.google.protobuf.Parser<MsgBlockConnected> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface MsgBlockDisconnectedOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.MsgBlockDisconnected)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>bytes block_hash = 1;</code>\n     * @return The blockHash.\n     */\n    com.google.protobuf.ByteString getBlockHash();\n  }\n  /**\n   * Protobuf type {@code chronik.MsgBlockDisconnected}\n   */\n  public  static final class MsgBlockDisconnected extends\n      com.google.protobuf.GeneratedMessageLite<\n          MsgBlockDisconnected, MsgBlockDisconnected.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.MsgBlockDisconnected)\n      MsgBlockDisconnectedOrBuilder {\n    private MsgBlockDisconnected() {\n      blockHash_ = com.google.protobuf.ByteString.EMPTY;\n    }\n    public static final int BLOCK_HASH_FIELD_NUMBER = 1;\n    private com.google.protobuf.ByteString blockHash_;\n    /**\n     * <code>bytes block_hash = 1;</code>\n     * @return The blockHash.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString getBlockHash() {\n      return blockHash_;\n    }\n    /**\n     * <code>bytes block_hash = 1;</code>\n     * @param value The blockHash to set.\n     */\n    private void setBlockHash(com.google.protobuf.ByteString value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      blockHash_ = value;\n    }\n    /**\n     * <code>bytes block_hash = 1;</code>\n     */\n    private void clearBlockHash() {\n\n      blockHash_ = getDefaultInstance().getBlockHash();\n    }\n\n    public static chronik.Chronik.MsgBlockDisconnected parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.MsgBlockDisconnected parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgBlockDisconnected parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.MsgBlockDisconnected parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgBlockDisconnected parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.MsgBlockDisconnected parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgBlockDisconnected parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.MsgBlockDisconnected parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgBlockDisconnected parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.MsgBlockDisconnected parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.MsgBlockDisconnected parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.MsgBlockDisconnected parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.MsgBlockDisconnected prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.MsgBlockDisconnected}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.MsgBlockDisconnected, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.MsgBlockDisconnected)\n        chronik.Chronik.MsgBlockDisconnectedOrBuilder {\n      // Construct using chronik.Chronik.MsgBlockDisconnected.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>bytes block_hash = 1;</code>\n       * @return The blockHash.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString getBlockHash() {\n        return instance.getBlockHash();\n      }\n      /**\n       * <code>bytes block_hash = 1;</code>\n       * @param value The blockHash to set.\n       * @return This builder for chaining.\n       */\n      public Builder setBlockHash(com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setBlockHash(value);\n        return this;\n      }\n      /**\n       * <code>bytes block_hash = 1;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearBlockHash() {\n        copyOnWrite();\n        instance.clearBlockHash();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.MsgBlockDisconnected)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.MsgBlockDisconnected();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"blockHash_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0001\\u0000\\u0000\\u0001\\u0001\\u0001\\u0000\\u0000\\u0000\\u0001\\n\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.MsgBlockDisconnected> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.MsgBlockDisconnected.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.MsgBlockDisconnected>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.MsgBlockDisconnected)\n    private static final chronik.Chronik.MsgBlockDisconnected DEFAULT_INSTANCE;\n    static {\n      MsgBlockDisconnected defaultInstance = new MsgBlockDisconnected();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        MsgBlockDisconnected.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.MsgBlockDisconnected getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<MsgBlockDisconnected> PARSER;\n\n    public static com.google.protobuf.Parser<MsgBlockDisconnected> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n  public interface ErrorOrBuilder extends\n      // @@protoc_insertion_point(interface_extends:chronik.Error)\n      com.google.protobuf.MessageLiteOrBuilder {\n\n    /**\n     * <code>string error_code = 1;</code>\n     * @return The errorCode.\n     */\n    java.lang.String getErrorCode();\n    /**\n     * <code>string error_code = 1;</code>\n     * @return The bytes for errorCode.\n     */\n    com.google.protobuf.ByteString\n        getErrorCodeBytes();\n\n    /**\n     * <code>string msg = 2;</code>\n     * @return The msg.\n     */\n    java.lang.String getMsg();\n    /**\n     * <code>string msg = 2;</code>\n     * @return The bytes for msg.\n     */\n    com.google.protobuf.ByteString\n        getMsgBytes();\n\n    /**\n     * <code>bool is_user_error = 3;</code>\n     * @return The isUserError.\n     */\n    boolean getIsUserError();\n  }\n  /**\n   * Protobuf type {@code chronik.Error}\n   */\n  public  static final class Error extends\n      com.google.protobuf.GeneratedMessageLite<\n          Error, Error.Builder> implements\n      // @@protoc_insertion_point(message_implements:chronik.Error)\n      ErrorOrBuilder {\n    private Error() {\n      errorCode_ = \"\";\n      msg_ = \"\";\n    }\n    public static final int ERROR_CODE_FIELD_NUMBER = 1;\n    private java.lang.String errorCode_;\n    /**\n     * <code>string error_code = 1;</code>\n     * @return The errorCode.\n     */\n    @java.lang.Override\n    public java.lang.String getErrorCode() {\n      return errorCode_;\n    }\n    /**\n     * <code>string error_code = 1;</code>\n     * @return The bytes for errorCode.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString\n        getErrorCodeBytes() {\n      return com.google.protobuf.ByteString.copyFromUtf8(errorCode_);\n    }\n    /**\n     * <code>string error_code = 1;</code>\n     * @param value The errorCode to set.\n     */\n    private void setErrorCode(\n        java.lang.String value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      errorCode_ = value;\n    }\n    /**\n     * <code>string error_code = 1;</code>\n     */\n    private void clearErrorCode() {\n\n      errorCode_ = getDefaultInstance().getErrorCode();\n    }\n    /**\n     * <code>string error_code = 1;</code>\n     * @param value The bytes for errorCode to set.\n     */\n    private void setErrorCodeBytes(\n        com.google.protobuf.ByteString value) {\n      checkByteStringIsUtf8(value);\n      errorCode_ = value.toStringUtf8();\n\n    }\n\n    public static final int MSG_FIELD_NUMBER = 2;\n    private java.lang.String msg_;\n    /**\n     * <code>string msg = 2;</code>\n     * @return The msg.\n     */\n    @java.lang.Override\n    public java.lang.String getMsg() {\n      return msg_;\n    }\n    /**\n     * <code>string msg = 2;</code>\n     * @return The bytes for msg.\n     */\n    @java.lang.Override\n    public com.google.protobuf.ByteString\n        getMsgBytes() {\n      return com.google.protobuf.ByteString.copyFromUtf8(msg_);\n    }\n    /**\n     * <code>string msg = 2;</code>\n     * @param value The msg to set.\n     */\n    private void setMsg(\n        java.lang.String value) {\n      java.lang.Class<?> valueClass = value.getClass();\n  \n      msg_ = value;\n    }\n    /**\n     * <code>string msg = 2;</code>\n     */\n    private void clearMsg() {\n\n      msg_ = getDefaultInstance().getMsg();\n    }\n    /**\n     * <code>string msg = 2;</code>\n     * @param value The bytes for msg to set.\n     */\n    private void setMsgBytes(\n        com.google.protobuf.ByteString value) {\n      checkByteStringIsUtf8(value);\n      msg_ = value.toStringUtf8();\n\n    }\n\n    public static final int IS_USER_ERROR_FIELD_NUMBER = 3;\n    private boolean isUserError_;\n    /**\n     * <code>bool is_user_error = 3;</code>\n     * @return The isUserError.\n     */\n    @java.lang.Override\n    public boolean getIsUserError() {\n      return isUserError_;\n    }\n    /**\n     * <code>bool is_user_error = 3;</code>\n     * @param value The isUserError to set.\n     */\n    private void setIsUserError(boolean value) {\n      \n      isUserError_ = value;\n    }\n    /**\n     * <code>bool is_user_error = 3;</code>\n     */\n    private void clearIsUserError() {\n\n      isUserError_ = false;\n    }\n\n    public static chronik.Chronik.Error parseFrom(\n        java.nio.ByteBuffer data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.Error parseFrom(\n        java.nio.ByteBuffer data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.Error parseFrom(\n        com.google.protobuf.ByteString data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.Error parseFrom(\n        com.google.protobuf.ByteString data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.Error parseFrom(byte[] data)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data);\n    }\n    public static chronik.Chronik.Error parseFrom(\n        byte[] data,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws com.google.protobuf.InvalidProtocolBufferException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, data, extensionRegistry);\n    }\n    public static chronik.Chronik.Error parseFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.Error parseFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.Error parseDelimitedFrom(java.io.InputStream input)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.Error parseDelimitedFrom(\n        java.io.InputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return parseDelimitedFrom(DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n    public static chronik.Chronik.Error parseFrom(\n        com.google.protobuf.CodedInputStream input)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input);\n    }\n    public static chronik.Chronik.Error parseFrom(\n        com.google.protobuf.CodedInputStream input,\n        com.google.protobuf.ExtensionRegistryLite extensionRegistry)\n        throws java.io.IOException {\n      return com.google.protobuf.GeneratedMessageLite.parseFrom(\n          DEFAULT_INSTANCE, input, extensionRegistry);\n    }\n\n    public static Builder newBuilder() {\n      return (Builder) DEFAULT_INSTANCE.createBuilder();\n    }\n    public static Builder newBuilder(chronik.Chronik.Error prototype) {\n      return (Builder) DEFAULT_INSTANCE.createBuilder(prototype);\n    }\n\n    /**\n     * Protobuf type {@code chronik.Error}\n     */\n    public static final class Builder extends\n        com.google.protobuf.GeneratedMessageLite.Builder<\n          chronik.Chronik.Error, Builder> implements\n        // @@protoc_insertion_point(builder_implements:chronik.Error)\n        chronik.Chronik.ErrorOrBuilder {\n      // Construct using chronik.Chronik.Error.newBuilder()\n      private Builder() {\n        super(DEFAULT_INSTANCE);\n      }\n\n\n      /**\n       * <code>string error_code = 1;</code>\n       * @return The errorCode.\n       */\n      @java.lang.Override\n      public java.lang.String getErrorCode() {\n        return instance.getErrorCode();\n      }\n      /**\n       * <code>string error_code = 1;</code>\n       * @return The bytes for errorCode.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString\n          getErrorCodeBytes() {\n        return instance.getErrorCodeBytes();\n      }\n      /**\n       * <code>string error_code = 1;</code>\n       * @param value The errorCode to set.\n       * @return This builder for chaining.\n       */\n      public Builder setErrorCode(\n          java.lang.String value) {\n        copyOnWrite();\n        instance.setErrorCode(value);\n        return this;\n      }\n      /**\n       * <code>string error_code = 1;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearErrorCode() {\n        copyOnWrite();\n        instance.clearErrorCode();\n        return this;\n      }\n      /**\n       * <code>string error_code = 1;</code>\n       * @param value The bytes for errorCode to set.\n       * @return This builder for chaining.\n       */\n      public Builder setErrorCodeBytes(\n          com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setErrorCodeBytes(value);\n        return this;\n      }\n\n      /**\n       * <code>string msg = 2;</code>\n       * @return The msg.\n       */\n      @java.lang.Override\n      public java.lang.String getMsg() {\n        return instance.getMsg();\n      }\n      /**\n       * <code>string msg = 2;</code>\n       * @return The bytes for msg.\n       */\n      @java.lang.Override\n      public com.google.protobuf.ByteString\n          getMsgBytes() {\n        return instance.getMsgBytes();\n      }\n      /**\n       * <code>string msg = 2;</code>\n       * @param value The msg to set.\n       * @return This builder for chaining.\n       */\n      public Builder setMsg(\n          java.lang.String value) {\n        copyOnWrite();\n        instance.setMsg(value);\n        return this;\n      }\n      /**\n       * <code>string msg = 2;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearMsg() {\n        copyOnWrite();\n        instance.clearMsg();\n        return this;\n      }\n      /**\n       * <code>string msg = 2;</code>\n       * @param value The bytes for msg to set.\n       * @return This builder for chaining.\n       */\n      public Builder setMsgBytes(\n          com.google.protobuf.ByteString value) {\n        copyOnWrite();\n        instance.setMsgBytes(value);\n        return this;\n      }\n\n      /**\n       * <code>bool is_user_error = 3;</code>\n       * @return The isUserError.\n       */\n      @java.lang.Override\n      public boolean getIsUserError() {\n        return instance.getIsUserError();\n      }\n      /**\n       * <code>bool is_user_error = 3;</code>\n       * @param value The isUserError to set.\n       * @return This builder for chaining.\n       */\n      public Builder setIsUserError(boolean value) {\n        copyOnWrite();\n        instance.setIsUserError(value);\n        return this;\n      }\n      /**\n       * <code>bool is_user_error = 3;</code>\n       * @return This builder for chaining.\n       */\n      public Builder clearIsUserError() {\n        copyOnWrite();\n        instance.clearIsUserError();\n        return this;\n      }\n\n      // @@protoc_insertion_point(builder_scope:chronik.Error)\n    }\n    @java.lang.Override\n    @java.lang.SuppressWarnings({\"unchecked\", \"fallthrough\"})\n    protected final java.lang.Object dynamicMethod(\n        com.google.protobuf.GeneratedMessageLite.MethodToInvoke method,\n        java.lang.Object arg0, java.lang.Object arg1) {\n      switch (method) {\n        case NEW_MUTABLE_INSTANCE: {\n          return new chronik.Chronik.Error();\n        }\n        case NEW_BUILDER: {\n          return new Builder();\n        }\n        case BUILD_MESSAGE_INFO: {\n            java.lang.Object[] objects = new java.lang.Object[] {\n              \"errorCode_\",\n              \"msg_\",\n              \"isUserError_\",\n            };\n            java.lang.String info =\n                \"\\u0000\\u0003\\u0000\\u0000\\u0001\\u0003\\u0003\\u0000\\u0000\\u0000\\u0001\\u0208\\u0002\\u0208\" +\n                \"\\u0003\\u0007\";\n            return newMessageInfo(DEFAULT_INSTANCE, info, objects);\n        }\n        // fall through\n        case GET_DEFAULT_INSTANCE: {\n          return DEFAULT_INSTANCE;\n        }\n        case GET_PARSER: {\n          com.google.protobuf.Parser<chronik.Chronik.Error> parser = PARSER;\n          if (parser == null) {\n            synchronized (chronik.Chronik.Error.class) {\n              parser = PARSER;\n              if (parser == null) {\n                parser =\n                    new DefaultInstanceBasedParser<chronik.Chronik.Error>(\n                        DEFAULT_INSTANCE);\n                PARSER = parser;\n              }\n            }\n          }\n          return parser;\n      }\n      case GET_MEMOIZED_IS_INITIALIZED: {\n        return (byte) 1;\n      }\n      case SET_MEMOIZED_IS_INITIALIZED: {\n        return null;\n      }\n      }\n      throw new UnsupportedOperationException();\n    }\n\n\n    // @@protoc_insertion_point(class_scope:chronik.Error)\n    private static final chronik.Chronik.Error DEFAULT_INSTANCE;\n    static {\n      Error defaultInstance = new Error();\n      // New instances are implicitly immutable so no need to make\n      // immutable.\n      DEFAULT_INSTANCE = defaultInstance;\n      com.google.protobuf.GeneratedMessageLite.registerDefaultInstance(\n        Error.class, defaultInstance);\n    }\n\n    public static chronik.Chronik.Error getDefaultInstance() {\n      return DEFAULT_INSTANCE;\n    }\n\n    private static volatile com.google.protobuf.Parser<Error> PARSER;\n\n    public static com.google.protobuf.Parser<Error> parser() {\n      return DEFAULT_INSTANCE.getParserForType();\n    }\n  }\n\n\n  static {\n  }\n\n  // @@protoc_insertion_point(outer_class_scope)\n}\n"
  },
  {
    "path": "ecashkit/src/main/kotlin/io/horizontalsystems/ecash/ChronikApi.kt",
    "content": "package io.horizontalsystems.ecash\n\nimport chronik.Chronik\nimport io.horizontalsystems.bitcoincore.core.IApiTransactionProvider\nimport io.horizontalsystems.bitcoincore.extensions.toReversedHex\nimport io.horizontalsystems.bitcoincore.managers.ApiManager\nimport io.horizontalsystems.bitcoincore.apisync.model.TransactionItem\nimport io.horizontalsystems.bitcoincore.apisync.model.AddressItem\n\nclass ChronikApi : IApiTransactionProvider {\n    private val apiManager = ApiManager(\"https://chronik.fabien.cash\")\n\n    override fun transactions(addresses: List<String>, stopHeight: Int?): List<TransactionItem> {\n        val transactionItems = mutableListOf<TransactionItem>()\n\n        for (address in addresses) {\n            try {\n                var page = 0\n\n                while (true) {\n                    Thread.sleep(10)\n                    val get = apiManager.get(\"script/p2pkh/$address/history?page=$page\")\n                    val txHistoryPage = Chronik.TxHistoryPage.parseFrom(get)\n                    transactionItems.addAll(\n                        txHistoryPage.txsList.map {\n                            TransactionItem(\n                                blockHash = it.block.hash.toByteArray().toReversedHex(),\n                                blockHeight = it.block.height,\n                                addressItems = it.outputsList.map { txOutput ->\n                                    AddressItem(\n                                        script = txOutput.outputScript.toByteArray().toReversedHex(),\n                                        address = \"\"\n                                    )\n                                }.toMutableList()\n                            )\n                        }\n                    )\n\n                    page++\n\n                    if (txHistoryPage.numPages < page + 1)\n                        break\n                }\n            } catch (e: Throwable) {\n                continue\n            }\n        }\n\n        return transactionItems\n    }\n}\n"
  },
  {
    "path": "ecashkit/src/main/kotlin/io/horizontalsystems/ecash/ECashKit.kt",
    "content": "package io.horizontalsystems.ecash\n\nimport android.content.Context\nimport android.database.sqlite.SQLiteDatabase\nimport io.horizontalsystems.bitcoincash.blocks.BitcoinCashBlockValidatorHelper\nimport io.horizontalsystems.bitcoincash.blocks.validators.AsertValidator\nimport io.horizontalsystems.bitcoincash.blocks.validators.DAAValidator\nimport io.horizontalsystems.bitcoincash.blocks.validators.EDAValidator\nimport io.horizontalsystems.bitcoincash.blocks.validators.ForkValidator\nimport io.horizontalsystems.bitcoincore.AbstractKit\nimport io.horizontalsystems.bitcoincore.BitcoinCore\nimport io.horizontalsystems.bitcoincore.BitcoinCore.SyncMode\nimport io.horizontalsystems.bitcoincore.BitcoinCoreBuilder\nimport io.horizontalsystems.bitcoincore.apisync.blockchair.BlockchairApi\nimport io.horizontalsystems.bitcoincore.apisync.blockchair.BlockchairBlockHashFetcher\nimport io.horizontalsystems.bitcoincore.apisync.blockchair.BlockchairTransactionProvider\nimport io.horizontalsystems.bitcoincore.blocks.BlockMedianTimeHelper\nimport io.horizontalsystems.bitcoincore.blocks.validators.BlockValidatorChain\nimport io.horizontalsystems.bitcoincore.blocks.validators.BlockValidatorSet\nimport io.horizontalsystems.bitcoincore.blocks.validators.LegacyDifficultyAdjustmentValidator\nimport io.horizontalsystems.bitcoincore.blocks.validators.ProofOfWorkValidator\nimport io.horizontalsystems.bitcoincore.extensions.toReversedByteArray\nimport io.horizontalsystems.bitcoincore.managers.ApiSyncStateManager\nimport io.horizontalsystems.bitcoincore.models.Address\nimport io.horizontalsystems.bitcoincore.models.Checkpoint\nimport io.horizontalsystems.bitcoincore.models.WatchAddressPublicKey\nimport io.horizontalsystems.bitcoincore.network.Network\nimport io.horizontalsystems.bitcoincore.storage.CoreDatabase\nimport io.horizontalsystems.bitcoincore.storage.Storage\nimport io.horizontalsystems.bitcoincore.utils.AddressConverterChain\nimport io.horizontalsystems.bitcoincore.utils.Base58AddressConverter\nimport io.horizontalsystems.bitcoincore.utils.CashAddressConverter\nimport io.horizontalsystems.bitcoincore.utils.PaymentAddressParser\nimport io.horizontalsystems.hdwalletkit.HDExtendedKey\nimport io.horizontalsystems.hdwalletkit.HDWallet.Purpose\nimport io.horizontalsystems.hdwalletkit.Mnemonic\n\nclass ECashKit : AbstractKit {\n    enum class NetworkType {\n        MainNet, TestNet\n\n    }\n\n    interface Listener : BitcoinCore.Listener\n\n    override var bitcoinCore: BitcoinCore\n    override var network: Network\n\n    var listener: Listener? = null\n        set(value) {\n            field = value\n            bitcoinCore.listener = value\n        }\n\n    constructor(\n        context: Context,\n        words: List<String>,\n        passphrase: String,\n        walletId: String,\n        networkType: NetworkType = defaultNetworkType,\n        peerSize: Int = defaultPeerSize,\n        syncMode: SyncMode = defaultSyncMode,\n        confirmationsThreshold: Int = defaultConfirmationsThreshold\n    ) : this(context, Mnemonic().toSeed(words, passphrase), walletId, networkType, peerSize, syncMode, confirmationsThreshold)\n\n    constructor(\n        context: Context,\n        seed: ByteArray,\n        walletId: String,\n        networkType: NetworkType = defaultNetworkType,\n        peerSize: Int = defaultPeerSize,\n        syncMode: SyncMode = defaultSyncMode,\n        confirmationsThreshold: Int = defaultConfirmationsThreshold\n    ) : this(context, HDExtendedKey(seed, Purpose.BIP44), walletId, networkType, peerSize, syncMode, confirmationsThreshold)\n\n    /**\n     * @constructor Creates and initializes the BitcoinKit\n     * @param context The Android context\n     * @param extendedKey HDExtendedKey that contains HDKey and version\n     * @param walletId an arbitrary ID of type String.\n     * @param networkType The network type. The default is MainNet.\n     * @param peerSize The # of peer-nodes required. The default is 10 peers.\n     * @param syncMode How the kit syncs with the blockchain. The default is SyncMode.Api().\n     * @param confirmationsThreshold How many confirmations required to be considered confirmed. The default is 1 confirmation.\n     */\n    constructor(\n        context: Context,\n        extendedKey: HDExtendedKey,\n        walletId: String,\n        networkType: NetworkType = defaultNetworkType,\n        peerSize: Int = defaultPeerSize,\n        syncMode: SyncMode = defaultSyncMode,\n        confirmationsThreshold: Int = defaultConfirmationsThreshold\n    ) {\n        network = network(networkType)\n\n        bitcoinCore = bitcoinCore(\n            context = context,\n            extendedKey = extendedKey,\n            watchAddressPublicKey = null,\n            networkType = networkType,\n            network = network,\n            walletId = walletId,\n            syncMode = syncMode,\n            peerSize = peerSize,\n            confirmationsThreshold = confirmationsThreshold\n        )\n    }\n\n    /**\n     * @constructor Creates and initializes the BitcoinKit\n     * @param context The Android context\n     * @param watchAddress address for watching in read-only mode\n     * @param walletId an arbitrary ID of type String.\n     * @param networkType The network type. The default is MainNet.\n     * @param peerSize The # of peer-nodes required. The default is 10 peers.\n     * @param syncMode How the kit syncs with the blockchain. The default is SyncMode.Api().\n     * @param confirmationsThreshold How many confirmations required to be considered confirmed. The default is 6 confirmations.\n     */\n    constructor(\n        context: Context,\n        watchAddress: String,\n        walletId: String,\n        networkType: NetworkType = defaultNetworkType,\n        peerSize: Int = defaultPeerSize,\n        syncMode: SyncMode = defaultSyncMode,\n        confirmationsThreshold: Int = defaultConfirmationsThreshold\n    ) {\n        network = network(networkType)\n\n        val address = parseAddress(watchAddress, network)\n        val watchAddressPublicKey = WatchAddressPublicKey(address.lockingScriptPayload, address.scriptType)\n\n        bitcoinCore = bitcoinCore(\n            context = context,\n            extendedKey = null,\n            watchAddressPublicKey = watchAddressPublicKey,\n            networkType = networkType,\n            network = network,\n            walletId = walletId,\n            syncMode = syncMode,\n            peerSize = peerSize,\n            confirmationsThreshold = confirmationsThreshold\n        )\n    }\n\n    private fun bitcoinCore(\n        context: Context,\n        extendedKey: HDExtendedKey?,\n        watchAddressPublicKey: WatchAddressPublicKey?,\n        networkType: NetworkType,\n        network: Network,\n        walletId: String,\n        syncMode: SyncMode,\n        peerSize: Int,\n        confirmationsThreshold: Int\n    ): BitcoinCore {\n        val database = CoreDatabase.getInstance(context, getDatabaseName(networkType, walletId, syncMode))\n        val storage = Storage(database)\n\n        val checkpoint = Checkpoint.resolveCheckpoint(syncMode, network, storage)\n        val apiSyncStateManager = ApiSyncStateManager(storage, network.syncableFromApi && syncMode !is SyncMode.Full)\n        val apiTransactionProvider = apiTransactionProvider(networkType)\n        val paymentAddressParser = PaymentAddressParser(\"bitcoincash\", removeScheme = false)\n        val blockValidatorSet = blockValidatorSet(networkType, storage)\n\n        val purpose = Purpose.BIP44\n        val bitcoinCoreBuilder = BitcoinCoreBuilder()\n\n        val bitcoinCore = bitcoinCoreBuilder\n            .setContext(context)\n            .setExtendedKey(extendedKey)\n            .setWatchAddressPublicKey(watchAddressPublicKey)\n            .setPurpose(purpose)\n            .setNetwork(network)\n            .setCheckpoint(checkpoint)\n            .setPaymentAddressParser(paymentAddressParser)\n            .setPeerSize(peerSize)\n            .setSyncMode(syncMode)\n            .setConfirmationThreshold(confirmationsThreshold)\n            .setStorage(storage)\n            .setApiTransactionProvider(apiTransactionProvider)\n            .setApiSyncStateManager(apiSyncStateManager)\n            .setBlockValidator(blockValidatorSet)\n            .build()\n\n        //  extending bitcoinCore\n\n        bitcoinCore.prependAddressConverter(CashAddressConverter(network.addressSegwitHrp))\n        bitcoinCore.addRestoreKeyConverter(\n            ECashRestoreKeyConverter(bitcoinCoreBuilder.addressConverter, purpose)\n        )\n\n        return bitcoinCore\n    }\n\n    private fun parseAddress(address: String, network: Network): Address {\n        val addressConverter = AddressConverterChain().apply {\n            prependConverter(CashAddressConverter(network.addressSegwitHrp))\n            prependConverter(Base58AddressConverter(network.addressVersion, network.addressScriptVersion))\n        }\n        return addressConverter.convert(address)\n    }\n\n    private fun network(networkType: NetworkType) = when (networkType) {\n        NetworkType.MainNet -> MainNetECash()\n        NetworkType.TestNet -> TODO()\n    }\n\n    private fun blockValidatorSet(\n        networkType: NetworkType,\n        storage: Storage\n    ): BlockValidatorSet {\n        val blockValidatorSet = BlockValidatorSet()\n        blockValidatorSet.addBlockValidator(ProofOfWorkValidator())\n\n        val blockValidatorChain = BlockValidatorChain()\n        if (networkType == NetworkType.MainNet) {\n            val blockHelper = BitcoinCashBlockValidatorHelper(storage)\n\n            val daaValidator = DAAValidator(targetSpacing, blockHelper)\n            val asertValidator = AsertValidator()\n\n            blockValidatorChain.add(ForkValidator(bchnChainForkHeight, bchaChainForkBlockHash, asertValidator))\n            blockValidatorChain.add(asertValidator)\n\n            blockValidatorChain.add(ForkValidator(svForkHeight, abcForkBlockHash, daaValidator))\n            blockValidatorChain.add(daaValidator)\n\n            blockValidatorChain.add(LegacyDifficultyAdjustmentValidator(blockHelper, heightInterval, targetTimespan, maxTargetBits))\n            blockValidatorChain.add(EDAValidator(maxTargetBits, blockHelper, BlockMedianTimeHelper(storage)))\n        }\n\n        blockValidatorSet.addBlockValidator(blockValidatorChain)\n        return blockValidatorSet\n    }\n\n    private fun apiTransactionProvider(networkType: NetworkType) = when (networkType) {\n        NetworkType.MainNet -> {\n            val blockchairApi = BlockchairApi(network.blockchairChainId)\n            val blockchairBlockHashFetcher = BlockchairBlockHashFetcher(blockchairApi)\n\n            BlockchairTransactionProvider(blockchairApi, blockchairBlockHashFetcher)\n        }\n\n        NetworkType.TestNet -> {\n            TODO()\n        }\n    }\n\n    companion object {\n        const val maxTargetBits: Long = 0x1d00ffff              // Maximum difficulty\n        const val targetSpacing = 10 * 60                       // 10 minutes per block.\n        const val targetTimespan: Long = 14 * 24 * 60 * 60      // 2 weeks per difficulty cycle, on average.\n        var heightInterval = targetTimespan / targetSpacing     // 2016 blocks\n\n        const val svForkHeight = 556767                         // 2018 November 14\n        const val bchnChainForkHeight = 661648                  // 2020 November 15, 14:13 GMT\n\n        val defaultNetworkType: NetworkType = NetworkType.MainNet\n        val defaultSyncMode: SyncMode = SyncMode.Api()\n        const val defaultPeerSize: Int = 10\n        const val defaultConfirmationsThreshold: Int = 1\n\n\n        val abcForkBlockHash = \"0000000000000000004626ff6e3b936941d341c5932ece4357eeccac44e6d56c\".toReversedByteArray()\n        val bchaChainForkBlockHash = \"000000000000000004284c9d8b2c8ff731efeaec6be50729bdc9bd07f910757d\".toReversedByteArray()\n\n        private fun getDatabaseName(networkType: NetworkType, walletId: String, syncMode: SyncMode): String =\n            \"ECash-${networkType.name}-$walletId-${syncMode.javaClass.simpleName}\"\n\n        fun clear(context: Context, networkType: NetworkType, walletId: String) {\n            for (syncMode in listOf(SyncMode.Api(), SyncMode.Full(), SyncMode.Blockchair())) {\n                try {\n                    SQLiteDatabase.deleteDatabase(context.getDatabasePath(getDatabaseName(networkType, walletId, syncMode)))\n                } catch (ex: Exception) {\n                    continue\n                }\n            }\n        }\n\n        private fun network(networkType: NetworkType) = when (networkType) {\n            NetworkType.MainNet -> MainNetECash()\n            NetworkType.TestNet -> TODO()\n        }\n\n        private fun addressConverter(network: Network): AddressConverterChain {\n            val addressConverter = AddressConverterChain()\n\n            val bech32 = CashAddressConverter(network.addressSegwitHrp)\n            addressConverter.prependConverter(bech32)\n\n            return addressConverter\n        }\n\n        fun firstAddress(\n            seed: ByteArray,\n            networkType: NetworkType,\n        ): Address {\n            return BitcoinCore.firstAddress(\n                seed,\n                Purpose.BIP44,\n                network(networkType),\n                addressConverter(network(networkType))\n            )\n        }\n\n        fun firstAddress(\n            extendedKey: HDExtendedKey,\n            networkType: NetworkType,\n        ): Address {\n            return BitcoinCore.firstAddress(\n                extendedKey,\n                Purpose.BIP44,\n                network(networkType),\n                addressConverter(network(networkType))\n            )\n        }\n    }\n}\n"
  },
  {
    "path": "ecashkit/src/main/kotlin/io/horizontalsystems/ecash/ECashRestoreKeyConverter.kt",
    "content": "package io.horizontalsystems.ecash\n\nimport io.horizontalsystems.bitcoincore.core.scriptType\nimport io.horizontalsystems.bitcoincore.extensions.toHexString\nimport io.horizontalsystems.bitcoincore.managers.IRestoreKeyConverter\nimport io.horizontalsystems.bitcoincore.models.PublicKey\nimport io.horizontalsystems.bitcoincore.utils.IAddressConverter\nimport io.horizontalsystems.hdwalletkit.HDWallet\n\nclass ECashRestoreKeyConverter(\n    private val addressConverter: IAddressConverter,\n    private val purpose: HDWallet.Purpose\n) : IRestoreKeyConverter {\n    override fun keysForApiRestore(publicKey: PublicKey): List<String> {\n        return listOf(\n            publicKey.publicKeyHash.toHexString(),\n            addressConverter.convert(publicKey, purpose.scriptType).stringValue\n        )\n    }\n\n    override fun bloomFilterElements(publicKey: PublicKey): List<ByteArray> {\n        return listOf(publicKey.publicKeyHash, publicKey.publicKey)\n    }\n}\n"
  },
  {
    "path": "ecashkit/src/main/kotlin/io/horizontalsystems/ecash/MainNetECash.kt",
    "content": "package io.horizontalsystems.ecash\n\nimport io.horizontalsystems.bitcoincore.network.Network\nimport io.horizontalsystems.bitcoincore.transactions.scripts.Sighash\nimport kotlin.experimental.or\n\nclass MainNetECash : Network() {\n\n    override var port: Int = 8333\n\n    override var magic: Long = 0xe8f3e1e3L\n    override var bip32HeaderPub: Int = 0x0488b21e\n    override var bip32HeaderPriv: Int = 0x0488ade4\n    override var addressVersion: Int = 0\n    override var addressSegwitHrp: String = \"ecash\"\n    override var addressScriptVersion: Int = 5\n    override var coinType: Int = 899\n    override val blockchairChainId: String = \"ecash\"\n\n    override val maxBlockSize = 32 * 1024 * 1024\n    override val dustRelayTxFee = 1000 // https://github.com/Bitcoin-ABC/bitcoin-abc/blob/master/src/policy/policy.h#L78\n    override val sigHashForked = true\n    override val sigHashValue = Sighash.FORKID or Sighash.ALL\n\n    override var dnsSeeds = listOf(\n        \"x5.seed.bitcoinabc.org\",                   // Bitcoin ABC seeder\n        \"btccash-seeder.bitcoinunlimited.info\",     // BU backed seeder\n        \"x5.seeder.jasonbcox.com\",                  // Jason B. Cox\n        \"seed.deadalnix.me\",                        // Amaury SÉCHET\n        \"seed.bchd.cash\",                           // BCHD\n        \"x5.seeder.fabien.cash\"                     // Fabien\n    )\n}\n"
  },
  {
    "path": "ecashkit/src/main/resources/MainNetECash-bip44.checkpoint",
    "content": "02000000ba3f2b4208ec0495b2e3743465cae2b44d8f1c778b44cf6b0000000000000000d287e52e8045c060c1cee47d1cc7559c7b8ab8db580539fb55fc579a998ea14efe0e50538c9d001926c0c180a08504003f72e59e0db5b38e5210369dc2fb4831ab1e81f3b5dbec3d0000000000000000\n0200000088c03613a752946272147792ee524af4acbffc3c1a70204b00000000000000005b679afd22900abbf4221db5401ee867c99264e13f754777ccaf1fd92f1fa4fb3a0d5053aab3001931afde449f850400ba3f2b4208ec0495b2e3743465cae2b44d8f1c778b44cf6b0000000000000000\n02000000de6076c4ebaf8a99d1aed6600b702499921d606e5b7df60500000000000000005aedac698f670b8c787f2339575bd4466e558adc6e0f6e8e0373005618c672820a0c5053aab300196bf4edac9e85040088c03613a752946272147792ee524af4acbffc3c1a70204b0000000000000000\n02000000f308b5ad14372c12cd08c584596e0790a7fed9f2846b8e170000000000000000a69485840909da4d1236721b721f2638a7d1c716f085f099cc21c705bdeef8494f085053aab300190efbed749d850400de6076c4ebaf8a99d1aed6600b702499921d606e5b7df6050000000000000000\n02000000ebb127579225c0b7c9556364ee197eff8d84c2c474cbe0090000000000000000bf62b8c209d82909d5883e8095e8cbabab5545ad29839b25c973944fd161ed44a5055053aab30019058e37689c850400f308b5ad14372c12cd08c584596e0790a7fed9f2846b8e170000000000000000\n02000000a8ed0256dd77435126cffa9d9d6550046a45ff63643c492200000000000000002546a265189fd9caf283386a90dc53a15d7a9caf8daff4b4b741851bb8a6421173015053aab30019effe96599b850400ebb127579225c0b7c9556364ee197eff8d84c2c474cbe0090000000000000000\n0200000012a3a9812e4b2f1311576e9abf834e7b247c139453da7b4300000000000000003a54f400aee6cef8fc999858ca83ea110b2a71da91c53061e736dc0f8fe5438fca005053aab3001951fab8bd9a850400a8ed0256dd77435126cffa9d9d6550046a45ff63643c49220000000000000000\n020000001bdf3c4a0aaf48ad2b6fe321f9ca2687fc77b7dcffe0c54b00000000000000005e1932b8b416ffb592c6c6a900016eb76a5ff1ef264823a8dbea2a5506ae5aa472ff4f53aab3001951c856699985040012a3a9812e4b2f1311576e9abf834e7b247c139453da7b430000000000000000\n0200000045c081445f0c886c0e9c06100a032f899a0ca02981c7242c00000000000000007404f4844f9ea3bf199e9b0879359a682442805f36df07f07705a52e818479dc57fc4f53aab30019b4de0dde988504001bdf3c4a0aaf48ad2b6fe321f9ca2687fc77b7dcffe0c54b0000000000000000\n02000000edeafbbb92af050ce4fdff9b2e42c7c7e09a1fb7b3420e81000000000000000061b816aeadc6d8060be33252375f320f73f6b2c7385b517525574dde8bfd1770e2fa4f53aab30019a29707c59785040045c081445f0c886c0e9c06100a032f899a0ca02981c7242c0000000000000000\n02000000223f307aadd3b7f79b72f224a5b5b5df31722cec5f48c51e000000000000000047ea9aa89e6ecc12e573daa8efb6727cfeda897455bbc0a6b2f69c2d209f4b71f5f94f53aab3001907fad61a96850400edeafbbb92af050ce4fdff9b2e42c7c7e09a1fb7b3420e810000000000000000\n02000000b09566bca15f31a35345a5bc315b2957b3efdd75bc7c4c6400000000000000006f5d1c5a30362de6e2bcb95aab111149a77cba7f9f72c925a82b0348500d217748f64f53aab30019f6a067c695850400223f307aadd3b7f79b72f224a5b5b5df31722cec5f48c51e0000000000000000\n020000003209dff479cfc22384c25fe241f907e24ba8e29e103d3c390000000000000000c0ebec8579d6a66721537ec2425c7083b9da1ed863585725b7680b1f6d2072dce1f34f53aab30019365408bc94850400b09566bca15f31a35345a5bc315b2957b3efdd75bc7c4c640000000000000000\n02000000e589a04528b496bb1bd163424583ec8c3a3eebd64decbcaa000000000000000092c84273d629837a781f62df4ff1e98735bae5e69d6680421d3d106a501add60d5f24f53aab300190d2711dc938504003209dff479cfc22384c25fe241f907e24ba8e29e103d3c390000000000000000\n0200000031ad2c1506d203293dd6348d5f54ff5b14629186933f58620000000000000000227058a7137843a516996ce7ce984beab4fd5e9099709bdbb1bc98f3517c3c56e3ee4f53aab3001992a4e3f792850400e589a04528b496bb1bd163424583ec8c3a3eebd64decbcaa0000000000000000\n02000000bc7498364b22f646653e7753fdd49013e51bc7c5be5b8a4e0000000000000000d98447ac6d93c629fc81af9a49a074d7db60b90c5790fce9990f9114a493194affed4f53aab30019d81bf0829185040031ad2c1506d203293dd6348d5f54ff5b14629186933f58620000000000000000\n02000000910bd6e05b76fb5ed472b76c07a28313da789d778ad2e57700000000000000008380c76e3c4b8f48c2965c4c4c16653e85e99329780b92dc12bea667f041e3164aed4f53aab30019aa7b9ff690850400bc7498364b22f646653e7753fdd49013e51bc7c5be5b8a4e0000000000000000\n02000000ba34128aadc2e73a6d9bbe328cd660b517752fa57ee411600000000000000000f1042e7234a288d31ef80f8893481272bcd6eea0fa513dfaebf3ac5a92f37e7c12ec4f53aab300190c5f41688f850400910bd6e05b76fb5ed472b76c07a28313da789d778ad2e5770000000000000000\n02000000bd6fd95daaccf77098024073cc5dededffad394ae025844b00000000000000005d77b6ee34ad3a5363f58cfafb015e3d72efd8e918cde7029345b385f30d16513ae94f53aab30019bb8afec88e850400ba34128aadc2e73a6d9bbe328cd660b517752fa57ee411600000000000000000\n02000000ae7c78592678f6ba5065bd9a0662271013134a89434fd8a900000000000000004759f0afc2f484916fb6a082338323be7c2cd2803b76abc375153df5d808733966e74f53aab300195dcfb6778d850400bd6fd95daaccf77098024073cc5dededffad394ae025844b0000000000000000\n020000009ee688465fe1174c750089d5195f8348cd439e60c930c74a000000000000000006a33880503c7f14c144f037f2ac1819b27160bde2722c995e39be1218d3ecc79ee54f53aab30019a193cd438c850400ae7c78592678f6ba5065bd9a0662271013134a89434fd8a90000000000000000\n02000000e077c2bab49992492d535b1b60549fc97593011717c03580000000000000000065abe8c39e5bf51cf5d87373f6366b87e1974802dd801f1af5516f2cd8bfbddce5e34f53aab30019e34ea8c38b8504009ee688465fe1174c750089d5195f8348cd439e60c930c74a0000000000000000\n020000007a8cb9e2ea2f7fcc1d0195ddb2175965bf45f2812cacea3c0000000000000000304629e4aa6d3c307a719db55ee29bb073048b003e759118a3acbbe02497ca5848df4f53aab30019dbf02e988a850400e077c2bab49992492d535b1b60549fc97593011717c035800000000000000000\n02000000578569a85a532af78d9f2bb448a439c376e916782b212d0f00000000000000006f504b91547c7fd4cf0a99b3ac4fdcee30a779cce3c099819d43be85b838bd5168dd4f53aab30019c54e259b898504007a8cb9e2ea2f7fcc1d0195ddb2175965bf45f2812cacea3c0000000000000000\n02000000f0f00b72042032823151b265c854cced592cf73d8638b94c0000000000000000a34029fd1f9dfe44947178ae0d4491289ddacf75989b06b90d0142eb3d86536c2fd94f53aab30019f08e0c4688850400578569a85a532af78d9f2bb448a439c376e916782b212d0f0000000000000000\n0200000038375a7969c498dab5b388afbc1c3d374bd9210425f4c074000000000000000008b8a73fbb942d2e35f7faaacbe76aa63193b9c36a2f39d0698b1e23a924363333d64f53aab30019a07c2e2b87850400f0f00b72042032823151b265c854cced592cf73d8638b94c0000000000000000\n0200000011355cd4abdbce81ff0583e7317f35329acbf14a57214b85000000000000000070702c32c6a515e0b167e642db4409f537b5addd63c2e41630635aae8499456fb0d54f53aab30019f3b8fbf48685040038375a7969c498dab5b388afbc1c3d374bd9210425f4c0740000000000000000\n02000000a091e9e5205df3c26a27b8499c26b7bfd1e75b7bdb8522a60000000000000000c4d57b53d1ee807ffee287f0a11fe1b24c3324442863ddae9036a8951558d4acf6d34f53aab300194cb573318585040011355cd4abdbce81ff0583e7317f35329acbf14a57214b850000000000000000\n020000007ac6087351f59c8730e7109cef79a2118d4e9788cb09732e0000000000000000b09c72103aa7e6cb81faef18027c57e700aa33a9f02b95013b605605763d02b2bdce4f53aab300190e0c313e84850400a091e9e5205df3c26a27b8499c26b7bfd1e75b7bdb8522a60000000000000000\n02000000543a88c9ead76ff8c031d558b6214f37d2258a057288a98200000000000000004a7420c2953accf8630c75fca3821ade63aafc1d99b476aeb78f1f6c7044044f33cd4f53aab30019cfdad2b6838504007ac6087351f59c8730e7109cef79a2118d4e9788cb09732e0000000000000000\n02000000c281b9672067629d19873f9e50b52e5c6d713f1535cdc6770000000000000000ece1a30937fe7a6478df3283167980b6440a36bfc79fd5ed876aec9a718527d4a1cc4f53aab30019b5a9e54c82850400543a88c9ead76ff8c031d558b6214f37d2258a057288a9820000000000000000\n02000000148699aa20a44895895985d5e3d9f31f0e59e626540b7d840000000000000000dcad236ea06fd34e30ae519f477dc084bf75552f85578e0a8b9c6cc804dcab8e08c94f53aab3001997fd95e881850400c281b9672067629d19873f9e50b52e5c6d713f1535cdc6770000000000000000\n020000008b4317c07870ee8b16e1c279a0a9405e539249d12032b02a00000000000000000b9b5d0f7351b5bc05f2bdc9557963d80a50660dc3aec57fb6fe2432b7ce4b5dacc84f53aab30019486b48f680850400148699aa20a44895895985d5e3d9f31f0e59e626540b7d840000000000000000\n02000000a35cbb341f4dc369daf1db4aa6c7b6deb906f21431fade65000000000000000064014a614d54ad4f58430a53336480ca53fee63b3a24032708c7ca41887f92b07dc84f53aab30019770e1ccb7f8504008b4317c07870ee8b16e1c279a0a9405e539249d12032b02a0000000000000000\n020000000f217937f5acb6f5ee6b1046391fc8dafc7d14f45b543b190000000000000000bc3dadbb38272b5516159c21f3254d4535b9de7e45d142f932295953b953e9788ec54f53aab300197d5dd9e07e850400a35cbb341f4dc369daf1db4aa6c7b6deb906f21431fade650000000000000000\n020000003dfecf95a637d583ed7d83e60c337cce28184b369c8055190000000000000000ed988bc76a1ace4d5af85c915b6b6247fcadb679daeba41e871852899a646ced13c54f53aab3001913012e107d8504000f217937f5acb6f5ee6b1046391fc8dafc7d14f45b543b190000000000000000\n0200000042feba1f0689217040e87a2d5c8003f62054ca70526020870000000000000000854c2a9323d372c80ccd60b8fe93bbf1335e5596dae48aa9a8e85f988c6ab68607c54f53aab30019625b285a7c8504003dfecf95a637d583ed7d83e60c337cce28184b369c8055190000000000000000\n0200000006eed96748eb886c2dd80a66929b6d436d88f8178ce89ab00000000000000000e76ca180a82080b3a12ab75ff1349fb5291ff66e3e061c3bcd2af0937a32cf4c55c24f53aab300190711fa907b85040042feba1f0689217040e87a2d5c8003f62054ca70526020870000000000000000\n0200000060f84899b9dc1829026b42c8a869c3f3cbfe48f44c5fe5740000000000000000f19c0030b60d269e1c1eb2c8c5663b5d848e3e3a8cdb95c40e6cc807cb29aab855bd4f53aab30019cd5a218a7a85040006eed96748eb886c2dd80a66929b6d436d88f8178ce89ab00000000000000000\n020000003874f8b15e3878126e2ab6ada58c4ccb5e9f7ae66006b78700000000000000001a9e63d440c243638fa43d841a6556244b071c51400eaa35f58ab897052bdf5cc6bd4f53aab300195338dba17985040060f84899b9dc1829026b42c8a869c3f3cbfe48f44c5fe5740000000000000000\n0200000048833a11adfe8ca520774d04c30b9c1145d0a0ef7e9d392d0000000000000000f80f0c720479c629e2d8ded2e1835d4871d6efe6489e80a2c7b6b599dd5faa4b35bb4f53aab3001927672991788504003874f8b15e3878126e2ab6ada58c4ccb5e9f7ae66006b7870000000000000000\n0200000027ab9802f9fca4a65bb4ed509f475ec85dced21f5d0337920000000000000000f91e0f223667c1872506d8a658e24d5374b0cc802b2fa552d2fc68f72343aa2e28ba4f53aab300191894e0a87785040048833a11adfe8ca520774d04c30b9c1145d0a0ef7e9d392d0000000000000000\n020000009afe656e0f5922610f0ce98ef4c28e0787674a55189b3619000000000000000039886ac4637bfb353647625edf6b0a6726fa8879c72abb5505022c0fef7bec3c21b94f53aab300197ace04de7685040027ab9802f9fca4a65bb4ed509f475ec85dced21f5d0337920000000000000000\n020000009dd8612854be8ca84cd2cd26de8b43cf5339428a261aed8800000000000000003668922c235558f89ad6a4f0e8b5a20e29aebfdf693df11b302fe523729af35dcfb34f53aab30019337ed7ea758504009afe656e0f5922610f0ce98ef4c28e0787674a55189b36190000000000000000\n02000000b93523be114558c5d172c44d9d1c3f420c9dd4e49d1467a7000000000000000069d1718c45b6cf2fcb253cd14efc6690b5d9afd89f7246daf0e3be736579a94ac4b14f53aab30019024a2f9e748504009dd8612854be8ca84cd2cd26de8b43cf5339428a261aed880000000000000000\n0200000075cbd526a5327ec10cd9bceae74892b180d27a689f153682000000000000000056e857744cc3fb9ac15423b9d23daf69603975ee83876e020fefd946ed6e7bc75bb14f53aab30019ef2968c073850400b93523be114558c5d172c44d9d1c3f420c9dd4e49d1467a70000000000000000\n02000000cb0eddbea32985fc9ee1be87ec23ece886358219536adf440000000000000000653d36f8ffc0a89d2e12358b580cd6e1b0156389fd2d27b54d4107bb9d858f325cb04f53aab300191177f5287285040075cbd526a5327ec10cd9bceae74892b180d27a689f1536820000000000000000\n0200000017fa90299ce9d234cf73c46cb8c4e7480454ec9167b4bd50000000000000000047b38999f9af6fee382fb1b098fe469fc0ab777ab1bc7a73b14bde7673932688deaf4f53aab300194a3cc36671850400cb0eddbea32985fc9ee1be87ec23ece886358219536adf440000000000000000\n020000003268f352f71040e8a15f9c8062d3d5005715436536ba293d00000000000000009e3f8bc6691440d64a681c0dfc9ec2fd459797d7e3e6259362d656daa7374628b8ac4f53aab300197987a3617085040017fa90299ce9d234cf73c46cb8c4e7480454ec9167b4bd500000000000000000\n02000000d2a652ae1b7e57e072f5e3743136dd46ada4d073a3a06039000000000000000079e27bc7faaa03a719d1491ad6dad19ba0b0cb17f443de7c461247c3f47ec45982ab4f53aab30019e804deb86f8504003268f352f71040e8a15f9c8062d3d5005715436536ba293d0000000000000000\n020000006ce2aba8861ec16c546d23fa8af538ce6112f109a9f412360000000000000000497e72f1c1fad86b7e35e230c11e76f635e5c6e809223423f478c39b685b0c615da94f53aab30019f29795776e850400d2a652ae1b7e57e072f5e3743136dd46ada4d073a3a060390000000000000000\n02000000cd27f3686735c946a32f079fea0aff1dc7f8dfe9ada6ecb00000000000000000dd504f552b7f71b6685a8d2dbff92d7c2a62344ce87b801ba4f3c274ca547f7851a94f53aab300194ddb49a06d8504006ce2aba8861ec16c546d23fa8af538ce6112f109a9f412360000000000000000\n0200000089b96fd7b5009cbfdd98322574e1c160f89a1cb7a9bc7fb30000000000000000e3c124151985dd04e2c32ba5e241abd5b22ffd479869259246e29c26a3a9164809a54f53aab30019e177d7866c850400cd27f3686735c946a32f079fea0aff1dc7f8dfe9ada6ecb00000000000000000\n02000000cc8480971555c86209a87f72f51f407ca4dad13fa00079020000000000000000d1ecb901afaaf70e7725f25b444bae44071b6219d50a9c940a556091577a35a50ea44f53aab30019aca3f9ce6b85040089b96fd7b5009cbfdd98322574e1c160f89a1cb7a9bc7fb30000000000000000\n020000004b5b861369e67a4d87657dfffe85253f23fe13815f8e07080000000000000000850ac13a6bf15fe700a8da6f0ec852039a6630a4d474b1b1929db160086ebd1edfa14f53aab30019ce0868a96a850400cc8480971555c86209a87f72f51f407ca4dad13fa00079020000000000000000\n0200000026ba9cc1ff8180ffabee37b7f7682e2dbe38a21762edf53e0000000000000000887b0db8b15a3275165919cdc23ebcfc46b758ce96ecd1ac8c7c8c93a9ec445b88a04f53aab3001973a47edc698504004b5b861369e67a4d87657dfffe85253f23fe13815f8e07080000000000000000\n0200000090bcbfd0a472558833ce464368e939768752e568a93c343500000000000000000716a874daf6c450b277db0baf34463aebb14a4e0e79270e21d4e6c583d33ef0129f4f53aab30019ed605ce76885040026ba9cc1ff8180ffabee37b7f7682e2dbe38a21762edf53e0000000000000000\n020000005f9c242c6b61d0782af687009b48ef873961c8190a052d310000000000000000ed13b2a231aa9fad7154f558202dd7d3ea40105f6d8a1a351043e5a130e8fb2f889e4f53aab30019d8a34f2e6785040090bcbfd0a472558833ce464368e939768752e568a93c34350000000000000000\n02000000be15ccaa2406d7ce2190b4afcb338c49fa3faaa5b6fcd7060000000000000000b2ac4861093a0d319985089c50739d4e8d3e54d0adfc16b4e60f200bcaf8ea43d59d4f53aab30019cdf6810c668504005f9c242c6b61d0782af687009b48ef873961c8190a052d310000000000000000\n02000000c77835534d0a487d00de30f7b40370c958eaa50f1a21089d0000000000000000c26a86958b0dbebe7f7f91e51c0f59486864214b56a1a9c01628a3dad832ca88e09c4f53aab300195365730965850400be15ccaa2406d7ce2190b4afcb338c49fa3faaa5b6fcd7060000000000000000\n02000000429c00b13583ac149843442aa129cf2d44ce396f518e186b00000000000000002796c110ead15a0a3b6b6e84048c821d0edc94a1aaf9eaf08d1ddd6c285927fa4d9b4f53aab30019d8a2f7ff64850400c77835534d0a487d00de30f7b40370c958eaa50f1a21089d0000000000000000\n0200000057415e3cf946d26fad4a69de1832a90937a361a9e8e2b94700000000000000003979bdd7c50be7a00c0d5af930dc11fd21bff44342c09a4d959f60a268f38b5838964f53aab30019c13a8d5663850400429c00b13583ac149843442aa129cf2d44ce396f518e186b0000000000000000\n02000000a1423eae0bbdf430c71516721db80190448f1a9a97e2e2600000000000000000c594080c3ca9c1b1d960025ee20da0e9530cec194c811f728e61bfdb74c308f907944f53aab30019d57d9c066285040057415e3cf946d26fad4a69de1832a90937a361a9e8e2b9470000000000000000\n02000000233b174e68901ebfae050b981ae24d40390d61d26cb7ff720000000000000000b09410313b3c343d16240637ec98993f5c266d1d30a82801a874499b68605bb673934f53aab3001970c6192861850400a1423eae0bbdf430c71516721db80190448f1a9a97e2e2600000000000000000\n02000000f490c3382fac48e779350c67e10072983f5faae76cebaa960000000000000000fb3ed4c465d037d7e0f320702eb52586e95ed052c241266e5a00f04d8d46e013f3924f53aab3001994e10afb60850400233b174e68901ebfae050b981ae24d40390d61d26cb7ff720000000000000000\n02000000a72f696a171bd2c58c7e3eea3ff05be15808d7d9d6860e7b0000000000000000ef70e9f7a49418e55b471de2c416f5d42bc8053972c21b5071e28363bc7085c128904f53aab3001990682ba15f850400f490c3382fac48e779350c67e10072983f5faae76cebaa960000000000000000\n02000000a3f72083fa8cfc8883ef1fb9cf33e719db6c03046f323d6a0000000000000000b213c8c36e257ac08c71f1c11c4b3bbb8e278e3f4f6d96413660a75736fa9ff2018e4f53aab3001925b485255e850400a72f696a171bd2c58c7e3eea3ff05be15808d7d9d6860e7b0000000000000000\n02000000ff3728a8389cd1c1a2ca8a2e825fc1803fd41fca1b41712400000000000000001010056c33aa93d7fe00344d33a90d8c250aaa8cbb7d8a197135a8392fe75c441e8d4f53aab300194839ccd85d850400a3f72083fa8cfc8883ef1fb9cf33e719db6c03046f323d6a0000000000000000\n02000000e1e4f9bfff2512284a9fc44bd35cd2fe9e3deb6d179903660000000000000000f0ae9d62b2e6489d07ab442123e79ebaf87016edf7165f667a1386d1af28bd277d894f53aab300197e4bb89b5c850400ff3728a8389cd1c1a2ca8a2e825fc1803fd41fca1b4171240000000000000000\n020000006b6b77ad6ca3ca34259d6788fdadeef1547a0fa84d731845000000000000000024dcb54597ce76f1d34053d1173092e1a66959c5fc46ddd61e6d1cef2336ac7e27874f53aab3001903c4d70f5b850400e1e4f9bfff2512284a9fc44bd35cd2fe9e3deb6d179903660000000000000000\n020000000df888a842be963a78d897be945d3b6b13c337bd3d99645c0000000000000000d695710e85c2d259b073efc5505d8a33d3065044fe2420263fafd7e165f0accd89874f53aab30019b192482a5a8504006b6b77ad6ca3ca34259d6788fdadeef1547a0fa84d7318450000000000000000\n020000002d420e24ba7ec1fea33a8c317c0f5fc3cb601e28398d219d000000000000000060b3be45558b170a3c5bba932a058f4dab252a2f679021613713eeb80779629386874f53aab3001998924e3f598504000df888a842be963a78d897be945d3b6b13c337bd3d99645c0000000000000000\n02000000764b23552e51c6cfda870eac15f990dac9413932015cc066000000000000000036d520c80b481c1ab56e93466cc6afe2aea1724744ef1bbd7ce09f5f0050a9555e874f53aab300192c47769e588504002d420e24ba7ec1fea33a8c317c0f5fc3cb601e28398d219d0000000000000000\n020000000d1805f9cd06b1a95d282eb025d5df977d95fd48ae6b6d1d0000000000000000beb93f892f198a8a68eec93fe96895c212e78b7f395e1dfeb878a59bbe9562702e864f53aab30019b0cc06b457850400764b23552e51c6cfda870eac15f990dac9413932015cc0660000000000000000\n02000000e3690d5b97db4837340c4b868fa67c9ef0fbd455c05676060000000000000000cd037bce20c7fec31ac599c5ae9b8f585d2cfb722dff5cc6e2a564335f558c1ce2834f53aab300194cfd7f34568504000d1805f9cd06b1a95d282eb025d5df977d95fd48ae6b6d1d0000000000000000\n020000002f7007a253d1c19df4ba90e727bc2074b5a86d19055128730000000000000000fb4a40c6ebe74bdb5ce8bf10360d041995bfc5ded3e9fe775b8b2ee0df2751e3df824f53aab300199dc17fb655850400e3690d5b97db4837340c4b868fa67c9ef0fbd455c05676060000000000000000\n0200000065ce6aee748fa5cf93e4f58a5e27efbf36e808ffdf7f89b100000000000000003918448585c6c551ef5cc3718aa05920832799ab2139e7b1bb27a1952b38f6ba4f814f53aab30019cf84687f548504002f7007a253d1c19df4ba90e727bc2074b5a86d19055128730000000000000000\n0200000050f5d898a6863e578b33cdbf4e0a450ddb5974e6e7cc87a10000000000000000c9bd0e56b8d6ae7ed84cba97b154d14a225b57d494865a398b4cd7df3ae0887893804f53aab300192ca5a1ba5385040065ce6aee748fa5cf93e4f58a5e27efbf36e808ffdf7f89b10000000000000000\n020000001885ce9ecedd1ad9a984a99cb0892e05e76ce4f9a4b0959100000000000000009f99224474cf0aafb9c896ffc8ad292efb26138ff8d3d205401111bdf093c70aed7d4f53aab30019322dad085285040050f5d898a6863e578b33cdbf4e0a450ddb5974e6e7cc87a10000000000000000\n020000003dc4683a79dbebfae6fae6dab9485332c2b4e3104582ee2800000000000000007585606dc0e260afcf37207053d9d20a227052b9c4c08250e365892147aa6704097d4f53aab30019d05817a7518504001885ce9ecedd1ad9a984a99cb0892e05e76ce4f9a4b095910000000000000000\n02000000d4aa5b768011c87fb2988beefc1dfc3c0c27b5445d4ae8060000000000000000b4c2433ae9881920487f975725beabd79399cdb6acd6a57933e9e1dec0261dc5117f4f53aab300191240ee49508504003dc4683a79dbebfae6fae6dab9485332c2b4e3104582ee280000000000000000\n020000001a593e49d585a5987f78c5b141d74238f953dcb25ce2eb680000000000000000bd54cff7a4d185ccad60eb31cf7a9b83256ec0742ebc6e2682f1ada5977a22e69d794f53aab3001916a2d12e4f850400d4aa5b768011c87fb2988beefc1dfc3c0c27b5445d4ae8060000000000000000\n02000000bee844fc7a75db08426459904d2a223bd3c80252ebd18c1f0000000000000000adb779bbc3e7af5f724290c5e102a4d8eae315f8d0b5de6240fdbe7372fdbbed69794f53aab30019d603a6674e8504001a593e49d585a5987f78c5b141d74238f953dcb25ce2eb680000000000000000\n02000000caef89dccbfd7e8a6e14ef44f43f8c16201fb8cbb72a797c0000000000000000770552d3ffa346313864ad81af4e179bb2c6e502a51f34db4c30edb3847de8d441774f53aab30019a642fc8b4d850400bee844fc7a75db08426459904d2a223bd3c80252ebd18c1f0000000000000000\n0200000033a8f19953f1c6b6ece262e4d26a9a262cb598e852cef99a0000000000000000bdf4f023f783249f866adc74e8b633022f1afe71e15f47c4ad03d20bdf79ab9f0a774f53aab30019eb8b93754c850400caef89dccbfd7e8a6e14ef44f43f8c16201fb8cbb72a797c0000000000000000\n0200000034f5b6c08a27e092aeb4adb5370c016cc79a88b09412db4500000000000000003ac289bfdec3d121cc8f2e31404d3c2da154238dbcd424a71cc007b9eff13c3e99754f53aab300195bcd03924b85040033a8f19953f1c6b6ece262e4d26a9a262cb598e852cef99a0000000000000000\n020000005b6639ac9d2483341c991d6c999e415961502bc5c31a168a000000000000000026fc89c24402c97a8cdc70d202014b3f02585b7fedb514a5e4db6a7fa81cd5d9b7744f53aab300198130df8d4a85040034f5b6c08a27e092aeb4adb5370c016cc79a88b09412db450000000000000000\n02000000a64a8d644080e215b4007f64acf883c1877a4e912c83e69e0000000000000000b589f128dd342e7d2acdb1c9471dcbe0c7b9e184ac232b8d3cc4a00bff8042d2766f4f53aab300194ee5562b498504005b6639ac9d2483341c991d6c999e415961502bc5c31a168a0000000000000000\n020000003d0fe23a5aa47fae62dede20794661fba6943c9fe25764a30000000000000000e264374685cafd7b6e4aa6ec65fee7d5f2f2bed349fbaa2b6529823df816bcc1016f4f53aab30019bb976a1048850400a64a8d644080e215b4007f64acf883c1877a4e912c83e69e0000000000000000\n02000000d6daa7600fbeb2e44e30962670ec38573dc4f96e7bda02950000000000000000cb95ae12c3e77829e9eb65f184997ff269a246ef3adf77629b4c467236e20624e86e4f53aab30019be628613478504003d0fe23a5aa47fae62dede20794661fba6943c9fe25764a30000000000000000\n0200000029ed27f5cc527daacf035aa17c6814fd936d9ddf9723d03200000000000000008f836090c6d0b4bb8c3e0d8e79717018e8b9bf0d3613ead3fc003120a82e10324d6e4f53aab30019481e0bc146850400d6daa7600fbeb2e44e30962670ec38573dc4f96e7bda02950000000000000000\n020000009d85a7a41cf63d3a14e0c02b22df37afdf4dafd68578a26a0000000000000000bdeceb9a02027fafd09ce755e1f5f87b31a0bbf27106c6adc990ea40dfeef9e54f694f53aab300196640e5564585040029ed27f5cc527daacf035aa17c6814fd936d9ddf9723d0320000000000000000\n02000000a5cbf81c4d5d3e89669ca406fd9b8fdde99c63e67a7759aa00000000000000007d2107d5f8603e077561f0e9b5eac5936e3919ed12d13afdc3e4eecb9e22c4b2ba694f53aab3001963041b86448504009d85a7a41cf63d3a14e0c02b22df37afdf4dafd68578a26a0000000000000000\n02000000606ded4b084da4f712e562b081408b28584c0ebc05f7bd210000000000000000f4ef310e4649682bc300d2e53d137dc7c2a78c89c6b312d68ba7b2a8a170ddab05694f53aab30019c8dbb3e543850400a5cbf81c4d5d3e89669ca406fd9b8fdde99c63e67a7759aa0000000000000000\n020000002e8da06450c35d020ab2a4d9e510ee89fca196e77f12f0560000000000000000184797be67e17ad2e9fe4eb5d6cb2dd6cb593e28cac759f0048ecd1f1a80600b87694f53aab30019b8a802a842850400606ded4b084da4f712e562b081408b28584c0ebc05f7bd210000000000000000\n02000000ccc36e54810b1412ced7a9a00b37223074c6080ea0dd189c00000000000000000b0cced8fe8361aa7f219649df4daffc0044a4f48e002e17ca6624596401acd0e9664f53aab3001935ee7e1f418504002e8da06450c35d020ab2a4d9e510ee89fca196e77f12f0560000000000000000\n0200000038393b82255dfeadb0fac022ce3e1783eec78ac504b50d9c0000000000000000cb8eb7cf71aeeeb94ec62e30cfef0eb203a8231e22b1ed1eb2ef263f1e4892a586604f53aab30019cd94b47140850400ccc36e54810b1412ced7a9a00b37223074c6080ea0dd189c0000000000000000\n020000000d0d2b4e81465971b908d12a4213a9298acfa7e5c64552910000000000000000cbc469f068c7b093db78b4560f87aa426de78c2a7a1dc4e2b3d070d9c71b3fe85d5f4f53aab30019a3008f9c3f85040038393b82255dfeadb0fac022ce3e1783eec78ac504b50d9c0000000000000000\n02000000be5a12d77b6f57d35657352ca97e6f63cf7c96418b85c279000000000000000029ed471aa72e61952ec049a6e017feab2719c2759fb57e8deb5a8cb440e2d3fbb15c4f53aab3001958d29efb3e8504000d0d2b4e81465971b908d12a4213a9298acfa7e5c64552910000000000000000\n0200000081727d1330040849bde55ef61312bd3cc56a5fd25c338f1b0000000000000000e0fe553450d2009fb94e859fa5304c6bdf6e1a5ca504bc29ec00c49920e0a8cc665c4f53aab30019af038f383d850400be5a12d77b6f57d35657352ca97e6f63cf7c96418b85c2790000000000000000\n02000000fb0307d0fb53e09602225ac23bbe7ed41956ecb7d24baa1e000000000000000060d1ded77639c5d5d3206fa93382ff97af2d73f50d30200bb186dbcf3fd9d3bff85a4f53aab300198fadf0773c85040081727d1330040849bde55ef61312bd3cc56a5fd25c338f1b0000000000000000\n02000000f22bef36ce9113bcbcfed19855f41b892368be6ee4a303a20000000000000000df62f043351ec709ac1b9c4c5bc803aad1a200e55e32d98b876bd9ffbf5c11ffed5a4f53aab30019f624ac1d3b850400fb0307d0fb53e09602225ac23bbe7ed41956ecb7d24baa1e0000000000000000\n02000000e6fc23bf68a531067151ac200b6c17f31ef36ea75ebf054800000000000000000b4cc592c0f058cd48c0f8deced0b419167481fac90e7dddc03666bcd5d0bae77e534f53aab30019101824fd3a850400f22bef36ce9113bcbcfed19855f41b892368be6ee4a303a20000000000000000\n02000000cd4e3a43fcf50fa19ca3ca122bd17741ac6f85a6aa961ab10000000000000000e89391c419414cce2df6c0ae00f5b39d04019a112327942a0187451c80dd6e53ea4f4f53aab3001908489b5439850400e6fc23bf68a531067151ac200b6c17f31ef36ea75ebf05480000000000000000\n020000007102b8fd502967b883062d558bbcb1934de7792aec9413720000000000000000c5676ae74f68105d8d671b3990773a0ec8d6ec3bc663a91c5ae4ea64d6d3347f81494f53aab3001923d85ce638850400cd4e3a43fcf50fa19ca3ca122bd17741ac6f85a6aa961ab10000000000000000\n020000002f15862adb55833c2773f570de340c49db7722809fd3f0940000000000000000b3ed418a2282f811e3d2a90b734f1b639a6ed64a98c01e0ba032ce914ab9b73d73484f53aab30019e2163656378504007102b8fd502967b883062d558bbcb1934de7792aec9413720000000000000000\n02000000101d5109c4b77b74f9fa469d665b68b3dd80d1d84dde19170000000000000000512c5c78a4f53886740ddfcd89f1387d419c0be3f97642465628683c830841f70e484f53aab30019de598b35368504002f15862adb55833c2773f570de340c49db7722809fd3f0940000000000000000\n020000001e6ea1cce073efbfcd5fd45232129a7efeca5c5a43ebe02d00000000000000003b24db33c7b9b256f84c5f934d5f504d885c11b6bd5bb1ce7f14a921992dd274c1434f53aab30019012fb91735850400101d5109c4b77b74f9fa469d665b68b3dd80d1d84dde19170000000000000000\n02000000877999b7a3cb95fa7729a4f5e22109cae34ee4e941c2e7a300000000000000000387ffc351b3167742fd1bf42525d8c33e0c45d5117eec38720a3ac17a21729cef414f53aab3001949b1f27b348504001e6ea1cce073efbfcd5fd45232129a7efeca5c5a43ebe02d0000000000000000\n02000000f70e98f57e4af2a8a7deb52792a250e02d30a68d23d793ad00000000000000003fbf0eb8beca9bd4eed150de6f370b8610579a8d22debfffdbb02f9a183483a319404f53aab3001921d9c2c833850400877999b7a3cb95fa7729a4f5e22109cae34ee4e941c2e7a30000000000000000\n02000000ce7ac68dc3123b45436cef787e839b9eedc7c963da4989ab00000000000000000e246ec6bd3d3321242604f6703e87d59c7642daf414fd5e95e1778a125ec0f542404f53aab30019d8e62d8732850400f70e98f57e4af2a8a7deb52792a250e02d30a68d23d793ad0000000000000000\n0200000062122d2b42728d0b320aabf1732b31b55fad13dd32ccf77700000000000000006258cc658d5427533d85a3252d8778d550fdc15c72a7ade86a16c12459a817bc9e3f4f53aab30019e4f5551131850400ce7ac68dc3123b45436cef787e839b9eedc7c963da4989ab0000000000000000\n020000009423cfc6819c7020bd42ac27e80b1c7c4f730aaea42fd013000000000000000067ba626bd952462953728eb9cb471846cbae71327a36e6a613ab8317995f577e9b3e4f53aab30019e0dcbfac3085040062122d2b42728d0b320aabf1732b31b55fad13dd32ccf7770000000000000000\n02000000ccf2187358b9f1eaaa7fcf58ec2d9fb600348dfa6c2bb8350000000000000000e9b32f7cec2ca0499488cd8159d9e1be2794b20ad5d2b97b5ee9bfdd3a7c320bdd3d4f53aab300196dd6199f2f8504009423cfc6819c7020bd42ac27e80b1c7c4f730aaea42fd0130000000000000000\n0200000065a11cfbe57eb93892b8a6a3c14080c483c2e67ae685f1580000000000000000fe252a834d8b62231f9b01ba6a48227a47fbc22f8007e9728636b11dd6b43409343c4f53aab30019a32afb6e2e850400ccf2187358b9f1eaaa7fcf58ec2d9fb600348dfa6c2bb8350000000000000000\n02000000b04fc8a8dc72a839990c1146a8ab753d96598e1802365a8a000000000000000071bcfd14c366a576b3b05500f9e7052f7704696608272f0b61f86f6c6d469874063b4f53aab30019b5578ed32d85040065a11cfbe57eb93892b8a6a3c14080c483c2e67ae685f1580000000000000000\n02000000c0d2fa1356fecf883d9a905a379d71acaa413368f2f4cb3b0000000000000000400253656db80a89b86b8f685be54be1571a4ea40540773d9101dba015f736d4223a4f53aab30019d88a29842c850400b04fc8a8dc72a839990c1146a8ab753d96598e1802365a8a0000000000000000\n02000000ec1242bd5fe1fdd1b9f3736ba2eae1200cb03235c1d927060000000000000000ad21d25ddca5da312acaf1c50fec67bb18c04f2bc212e64f74c2ce0347375bc9c6384f53aab300199d0e666c2b850400c0d2fa1356fecf883d9a905a379d71acaa413368f2f4cb3b0000000000000000\n0200000044e6a3529e8b18c6449f9e3e1efbda878af990fe5e4aa90500000000000000003174e12d9fa1a7aeae40d2cc8af5b2b31223dc3dbb5ea94ca1cc8bb684b908713f384f53aab30019065bd7292a850400ec1242bd5fe1fdd1b9f3736ba2eae1200cb03235c1d927060000000000000000\n020000004105a5c0ea0853a02d20af3cbd042feafb8dc2f79315522f0000000000000000a1a2dea77a9d186e336ade332fc6b011c83fc44add2d83b4bded99946040aba53a384f53aab300195341aa282985040044e6a3529e8b18c6449f9e3e1efbda878af990fe5e4aa9050000000000000000\n020000002d008795f0acd1e7dcdc5aa76afaba1ba31c271aab32f738000000000000000004dbad9b0b50922c369acd1c1e35bd28bb3deb59f20b2221a7ef78d417e01bd3a3324f53aab30019ac9159d5288504004105a5c0ea0853a02d20af3cbd042feafb8dc2f79315522f0000000000000000\n02000000f8cbbfcbd9a5709910c5f79056ff83e9ffc43e03940a96730000000000000000db2e57c047b670986ac8f7f40992003dd711690f9ba3fe6f0967203e1bf76490e62e4f53aab30019558f91fa278504002d008795f0acd1e7dcdc5aa76afaba1ba31c271aab32f7380000000000000000\n020000001e63ba192d148693cd116c484d602d5b60ae4210c9445d5d0000000000000000185caf72267125f82ea2a8d75690a6250d82739a18f8915a55cbe33a3da598d006284f53aab30019a7ee66ad26850400f8cbbfcbd9a5709910c5f79056ff83e9ffc43e03940a96730000000000000000\n0200000021b148c8fc7f61a8cbab86fa917d3c4c7f6183139b034931000000000000000045e0e292469d9911d8f01d97bb83338f1583625382305441dd9bebf694a80f2cad284f53aab30019c4744f67258504001e63ba192d148693cd116c484d602d5b60ae4210c9445d5d0000000000000000\n020000000248cf85be1d7ba49a2013dde6e2ebe3a8a46e342b16fa3d0000000000000000ee1f75947dff4dea62a76b6b0c60e2cc76cac26a2beacb6c5bc6788828dbbe7008254f53aab30019c9ea1c532485040021b148c8fc7f61a8cbab86fa917d3c4c7f6183139b0349310000000000000000\n0200000022a85507651f1129652a25b54a284d43d1d755ee2d2ce17900000000000000005f25d77d06d7fc6aa3bb0cec575a1b9d68ee120ca6a261e0057240eff8acaa78f4244f53aab30019b82048dc238504000248cf85be1d7ba49a2013dde6e2ebe3a8a46e342b16fa3d0000000000000000\n02000000e1c354e61233452e21c7d7419c9a31fbcb4ccc3c9bbf00600000000000000000a103278c41b8d1e5f9ac5f177005529b1f1ad2d56681ddc03003e07a3223ee1981244f53aab30019a95b4c9c2285040022a85507651f1129652a25b54a284d43d1d755ee2d2ce1790000000000000000\n02000000d44d7f012a71a227c5f500e88e67966b9e0f0e104f2af62d000000000000000046b84388d0c0786b398f7a116e480ed9b945989ae8482a2d5c62b39cb2177fa59f204f53aab3001935ab54c821850400e1c354e61233452e21c7d7419c9a31fbcb4ccc3c9bbf00600000000000000000\n0200000017aad17f4acfdc72e3bbd8e9fc885e3ccef06274e86c5b6a0000000000000000adeb010e3be1306361545949e16dfabad1275361550061801de0227470c383886e1b4f53aab30019b5b7442f20850400d44d7f012a71a227c5f500e88e67966b9e0f0e104f2af62d0000000000000000\n02000000981200022c790a6d2da10be7b2e3d057885c57706f9884100000000000000000a592635e157a7ac121e4ebe71a7e2d8baaa40ea06186997a961de473bcc0e43545194f53aab30019001cdd5a1f85040017aad17f4acfdc72e3bbd8e9fc885e3ccef06274e86c5b6a0000000000000000\n020000008c7c954557748ce467a11742bccf733e5df09aef26cb36080000000000000000677e01646e62e466d8a30870638848f5f2682935047da6ad9b5cbef55af050cdbb174f53aab300198d0c43221e850400981200022c790a6d2da10be7b2e3d057885c57706f9884100000000000000000\n02000000bf4c372f972fbd1beeb682bb5f84169fef89cfa1ff9b322700000000000000006d9a0c989b1532ca9ed7cef4ea8c738822604c276339bad06b828a8fd6ed2ce2f2164f53aab30019923733f91d8504008c7c954557748ce467a11742bccf733e5df09aef26cb36080000000000000000\n0200000054fc78066d71e469c398be1525587527302faab49f81979000000000000000004cc800d9ad02a4180a5ef6e96f13bad059c7a7fd7ae49a3e4c51d4d47963d4cfeb144f53aab30019831103731c850400bf4c372f972fbd1beeb682bb5f84169fef89cfa1ff9b32270000000000000000\n02000000739d7509fc558e59272558711e7560dd3eae023f1a3f484700000000000000008981569b49984136e549bfc2af562057b01e53f3977e472e8bf1152671d6c478d1144f53aab3001930a1a7ab1b85040054fc78066d71e469c398be1525587527302faab49f8197900000000000000000\n02000000a30f864fb3282f0bb533bfaf294fa71ea6d24e9a6a9bfd7d0000000000000000fc56b108ee29b70c7eff51f8ae8a31a76ecfde8cec57e32b90d29336bfe9835330114f53aab30019d28c826c1a850400739d7509fc558e59272558711e7560dd3eae023f1a3f48470000000000000000\n020000003bb767b1cccc63d26553d2ffac3bf8d38648b986e0f7c17f00000000000000004af836f012bf26e6cd03c6d844c7c321aeaadf571f1556fad1fd5f9ffc1c5628e70d4f53aab30019714eb9e219850400a30f864fb3282f0bb533bfaf294fa71ea6d24e9a6a9bfd7d0000000000000000\n0200000039a5d05c2762372ddafba55b2f57406cb3e6c874fc770e950000000000000000327ffc418d5daeec95a0bf5e07eb55171636e62ca9d539302d4fb7b4f79a38ecd90c4f53aab300196d1fd401188504003bb767b1cccc63d26553d2ffac3bf8d38648b986e0f7c17f0000000000000000\n020000003b9343ae2bad5197438df09269076707f328d806f3c0d51c00000000000000009fd2c1de375bdfb2ace14b0236a14bef281fd8d404944053a9fda7a4a376b44f2c0a4f53aab30019237a8cf11785040039a5d05c2762372ddafba55b2f57406cb3e6c874fc770e950000000000000000\n02000000a079768528b9ef1b365b313553337d5fc6e403fb5d3de73100000000000000003992d23c20b895f9fe71994bd03f885c13dff7141117dae130a5f7e3f6c0e45725094f53aab300194843f437168504003b9343ae2bad5197438df09269076707f328d806f3c0d51c0000000000000000\n02000000423eb273b0cf153fbe42b6cfe544f48f1c4991f6fc3562660000000000000000cfc95644d97a0e9e2f04b7139fa3ad891f9fc5ce7fc63ff95cf59430623950e369084f53aab300190c5670c415850400a079768528b9ef1b365b313553337d5fc6e403fb5d3de7310000000000000000\n02000000af28abbb9ed1305d9aaa0f6efeec9021b864480b5e7935a40000000000000000bda0ab7dd66310642e6b39f135f5303f1872e3b3b5d0ce4b1d7423af5e95dd150b004f53aab30019e5b545e214850400423eb273b0cf153fbe42b6cfe544f48f1c4991f6fc3562660000000000000000\n02000000331a74f700fefabfdf1f1093bd36576bb00d7c6d9dd8713a0000000000000000482eecd9a424899a7eb6ae85bc0b9d5519f5fb87739ea6c13f9668a40ef17a39e4fd4e53aab300196e5bce5c13850400af28abbb9ed1305d9aaa0f6efeec9021b864480b5e7935a40000000000000000\n02000000a0310d422e8a7f095ef38b461631a772eb8d1f9dc4bcae3a0000000000000000a6a4469562add71b42286a3033f27d8d027c186b1640735ae206536825b79b0152fd4e53aab300190b4a4cbd12850400331a74f700fefabfdf1f1093bd36576bb00d7c6d9dd8713a0000000000000000\n020000005cfeea1fc4b068f75b30b35edd263a8ffeb90778f6de1c20000000000000000069c107d2d425598fc8352a050d2827b6657e02a0eded7bca667b10910ad64a8f30fd4e53aab300192c5b667611850400a0310d422e8a7f095ef38b461631a772eb8d1f9dc4bcae3a0000000000000000\n02000000c495295ebb9eec9333aceb2c124cae92a3b43c3a4b60a28000000000000000008e189eeab9d432eb27d4a35fc84c538031f2397c8df52d6eb242be1c659c03e89efc4e53aab3001994c92316108504005cfeea1fc4b068f75b30b35edd263a8ffeb90778f6de1c200000000000000000\n02000000ad09610c309bd28cc9c9d036f6ece1797bf6f5be681394790000000000000000ae9e1173bd110c3c9abd91f8a078949e221b3bebbddb0070626aa061056ce29d6ef64e53aab3001963bc9bac0f850400c495295ebb9eec9333aceb2c124cae92a3b43c3a4b60a2800000000000000000\n02000000ebe9e64039e239a5c40498e886b0498d6903a8d364df89200000000000000000a15f0a52681c1bc1c7ad2fa1ef32047d60b3d07ae852fd1ea8b73acca9fb1fea51f64e53aab3001923e35fdc0e850400ad09610c309bd28cc9c9d036f6ece1797bf6f5be681394790000000000000000\n"
  },
  {
    "path": "ecashkit/src/main/resources/MainNetECash.checkpoint",
    "content": "00808a3810285c2dbb1588a7cd99b93771b6658d57682e14fa64766300000000000000007d749bc46c08152003be290cd591fe7c2bf6b341581af2f7d5996ec6e4db97d54572cb693a950019d0fb1f8477620e00cf8b965067c27c12f34b5a6c805f14113522f6cadcfa9d650000000000000000\n000004202a93b0b4df2d33537b2ee0632084fbc7e6b04109920493680000000000000000d097be6e616cd12de161f0cb1266b0a0842aa6eca32adbd33e4b802bdb2491070970cb696f950019d1f1282376620e0010285c2dbb1588a7cd99b93771b6658d57682e14fa6476630000000000000000\n0000e020b5cefab3ec42c7c00e9422d96c37bf08bbfeef8d1ff3a61600000000000000008bd0010896e49f7c0c85960dba4b3a8bfbf1ee9a4566a935f7574d1d17747b1c066fcb69d39400195f52428975620e002a93b0b4df2d33537b2ee0632084fbc7e6b04109920493680000000000000000\n00c04c23c851f1616b9733d4b53d77e63ea251d458190ad6a29cea490000000000000000acad0b7bda8171dcd99e6b2db4f57db67cb750ba5ca1e374ed89aca87ee69892b468cb691895001902dd93f774620e00b5cefab3ec42c7c00e9422d96c37bf08bbfeef8d1ff3a6160000000000000000\n0000062052f0615f79a3bf2e17709dbf7099df6004ca109bcbc7c3190000000000000000889db8cc19ec1bf29495cd25a061e2b07eb9563326637568fb8480c6a05fc8cc1e68cb69e594001968e50a2073620e00c851f1616b9733d4b53d77e63ea251d458190ad6a29cea490000000000000000\n0020193e385fe9579d333ae040fcc02c47594c87bca97fe484d72a6f000000000000000022ddfd190b1a8efcdb4c1c84e116701cefd6f50f7ae4ef6adaf993954e0d4eec7664cb690e950019c9f644b272620e0052f0615f79a3bf2e17709dbf7099df6004ca109bcbc7c3190000000000000000\n00e000201cf7dbfe88e236350535b5733a0178468e9d4400547d928a000000000000000095875a163e893877f05b369cf8724ca75ada2f6b2c83652883110703051e08932b63cb69fa940019f34bb13471620e00385fe9579d333ae040fcc02c47594c87bca97fe484d72a6f0000000000000000\n002023201953258263503fa1041954e47bb3aa7eaa3b40ad7fa15e690000000000000000311206b40a8ed9492ade9bcc5ff443186df2f20887f9e7ae4eda98276271e2f65160cb69ed94001981d2c88670620e001cf7dbfe88e236350535b5733a0178468e9d4400547d928a0000000000000000\n000010202a26fba2786a371952a753bb0c9d8c92d476e8d99948ae5b0000000000000000d180a716912e14842b7d93d3b956f6a9621fa7972dd9c8c5c5d6c4f250fa06a4a25dcb69c4940019d5d692956f620e001953258263503fa1041954e47bb3aa7eaa3b40ad7fa15e690000000000000000\n0040002024f6be353ad851f00a260f5b32f66c452ac3b09a7dc19655000000000000000036fa820523578eca34cbb60a15af76d3c8f381d93aba3750e99ce7c96623e060425acb69d8940019074a18736e620e002a26fba2786a371952a753bb0c9d8c92d476e8d99948ae5b0000000000000000\n00205f25e507ae21d469f8eafe5d42c4b79828bd317c05c73c135f5f00000000000000001d0bf9b50b032e01f865da57af206a80dc1de656a54fea89fffaa78cde2116fe6f58cb69fb9400192632296d6d620e0024f6be353ad851f00a260f5b32f66c452ac3b09a7dc196550000000000000000\n00a00f37b6b331bc61b15b37df7cfb02969029aa96492fb60966b5580000000000000000b03932b23ba60f2507258e8a3877b569a6bd98e2952b79286f37c1e1df71898df856cb6925950019ba81dee46c620e00e507ae21d469f8eafe5d42c4b79828bd317c05c73c135f5f0000000000000000\n00e0a825e9b7126caf75d3d4d0aff550fa52b88a30dae74707dddd3100000000000000002518eb3ff1752175d1ce9ca02fcb38e78ee452addaa929573f5db624e5e7df88b455cb695e9500191e04a36e6b620e00b6b331bc61b15b37df7cfb02969029aa96492fb60966b5580000000000000000\n00209a2074958fa8aef2c07952642ff273660c8ca7c9835c24e7a510000000000000000018f115c18ea0ba0c0cd096f711321f8228220d713dc594f90159b308ecf88bb0cd54cb698b93001919c380246a620e00e9b7126caf75d3d4d0aff550fa52b88a30dae74707dddd310000000000000000\n00601131b8d1537757f349ed0b8903e0da0f2e78e533abc5eab78a5e0000000000000000b22b326bc3159cdfd146e348ca3c4d3162d58e2a27f3283f1fa0cb39a887e26f7f46cb6997920019536ebfb469620e0074958fa8aef2c07952642ff273660c8ca7c9835c24e7a5100000000000000000\n00a00020d3dc16fd4ef5e7531303d055038e022c4844e12f8fab4b7b000000000000000062d368a4414ba789a94fc9ee3854fab1585157ea1dd1b31afc7e23bf1efc1346d23dcb695a920019304207e968620e00b8d1537757f349ed0b8903e0da0f2e78e533abc5eab78a5e0000000000000000\n006009205c20e6151ce98a55ff5907664ab4b531d705df1e710b671c0000000000000000f3dd2429ed72032b4b12d7db6f649dd06def2b183d4c43761b33d134f4d3b8e6e439cb6997920019929770b467620e00d3dc16fd4ef5e7531303d055038e022c4844e12f8fab4b7b0000000000000000\n004005209fa4f9b24bf782627999f01fa25fd87fce4f33a17ec35c1d00000000000000005fa06cf66b6b551e05135f80047d4c0244c9f2ecefcd623652ef59279367d84c2239cb69c692001920d51dc766620e005c20e6151ce98a55ff5907664ab4b531d705df1e710b671c0000000000000000\n000000265522724c16ca0000ec0eb2881f11e76a3e0e009f80db896100000000000000002e2a067a5a5a21abe4a161a124345a96810484f81cda80e7cce023f73fdc4db80138cb6991920019bf64681565620e009fa4f9b24bf782627999f01fa25fd87fce4f33a17ec35c1d0000000000000000\n000000204c3afaf2bc196638081f1ca84b5710da5db7eb509f051b5e0000000000000000ea0b5aecd41f7e3bf7f1517ecd2e4d7c665972bb2514718e85bd5b03b3e65f0f4c34cb69a59200198b27486164620e005522724c16ca0000ec0eb2881f11e76a3e0e009f80db89610000000000000000\n00600220cc6fa8971cef8d0f1291706a7934ac0a4bfedf828ccc1765000000000000000031a275cb37b38456bcab0001a3b43dc096c8fdee119dcf1a3f6b51a2fd00052b7c32cb69c292001928d708c463620e004c3afaf2bc196638081f1ca84b5710da5db7eb509f051b5e0000000000000000\n0000c1298bfc2a54d3a1b6905461fa28c4ed08cc9a4b1c3e297a683500000000000000003cc0f07dfd2fda115874c9934a4e8502cdd881c73d16b798209fd7723343bbf7e130cb6902930019875ff29e62620e00cc6fa8971cef8d0f1291706a7934ac0a4bfedf828ccc17650000000000000000\n00e00220604fa2842e8f9f2c7d71a61973d10541ff5a638e7a3e7c050000000000000000e7b822b816937733855f4ef3195f3972bfd628c5c2ade0e9d83eeda7c7910f613030cb69069300194006884b61620e008bfc2a54d3a1b6905461fa28c4ed08cc9a4b1c3e297a68350000000000000000\n000000206db9ab97a953532fe83878b7013d6227de80e07e18f5f01a00000000000000006b93eb8221cb273ede6fcf2f237185d326148f65cf2d5bad59deaaace744c184f12dcb69049300195ade8f3260620e00604fa2842e8f9f2c7d71a61973d10541ff5a638e7a3e7c050000000000000000\n00006e2e5c048c7036d4c56e06183e17951393866cafc5ed9f1304660000000000000000be0ed1ae16444ad899e49e678758ea3205fabcf8c4409c2257de69beeb561ace902bcb691a930019043af6bb5f620e006db9ab97a953532fe83878b7013d6227de80e07e18f5f01a0000000000000000\n00e05a211d0b27cd8e3c3db1c4dea550177a7cf55f40ee0497ae774200000000000000008ba967edbc16598d8a5f95eeb94616f7d901c343f9f14b6bb803c10f390720b3c829cb695893001968d0cc975e620e005c048c7036d4c56e06183e17951393866cafc5ed9f1304660000000000000000\n00200920784d5783f3055e4f8f07aef1f3cdb8f1d6493173ee5c0a920000000000000000b24afb34436613196fc4357ebdbe85ab2e492f25aae624499f14aaff4104c1b30a29cb696d9300195f1f8df35d620e001d0b27cd8e3c3db1c4dea550177a7cf55f40ee0497ae77420000000000000000\n0000002e4d42524af0cc4d81731edbd951d3600de4badf2f1426ed34000000000000000057a27636768662c6950c893fceee6f806b7240150365553be6d693d224c7c0b63c27cb69b4930019c4e8879a5c620e00784d5783f3055e4f8f07aef1f3cdb8f1d6493173ee5c0a920000000000000000\n0000a52b915a6cc2dac4cc587e24933eb73f94c233f8cd283bdfd76b00000000000000008d308115c5a418e7d29f767b23a14d7ad255a064f144e773c1138b04d3861487bc26cb69a7930019372043d25b620e004d42524af0cc4d81731edbd951d3600de4badf2f1426ed340000000000000000\n00601520c0c8ef0a9961c733fcf25cad5e90e13c506c9328a779030b000000000000000076db45d679b6907bdf2d5488c8ebebc33cc3cc8675e633911e13479b16a3cad60e24cb69a9930019daaf06295a620e00915a6cc2dac4cc587e24933eb73f94c233f8cd283bdfd76b0000000000000000\n00806b220e68b87bdd9e7b4331d6ecc76cc24bfc77a5314a994ad50d00000000000000006f724d0711a76e8c2da84ac6acbeaae40653378e5e05556e7a005852bfeea200c021cb69d29300190da9b45259620e00c0c8ef0a9961c733fcf25cad5e90e13c506c9328a779030b0000000000000000\n0060032046b2641e45d5b8ae5c93172ab73b9f89162125d049f77e8200000000000000004253f92f5b79bb684e0e23108aa031ceac8d1a7831fb38ac85862e1360f1f5357920cb691d9300195c6a95f758620e000e68b87bdd9e7b4331d6ecc76cc24bfc77a5314a994ad50d0000000000000000\n0040482580d1d412de04610b3e0576f61c508310db73cb7f72a15e000000000000000000246c9772cc8811f413b0fb398a6532276328ce4667f5a8054a2a0f9811f8fa5c7619cb69e19200194201b3fa57620e0046b2641e45d5b8ae5c93172ab73b9f89162125d049f77e820000000000000000\n00000420f620adee8861e8448cf3e3e73d23baaf6bee000cb84af78f0000000000000000dc8b5b3cbc0a6008a20c3be711f6f70405292960690890736a815085178f60868c15cb693093001951951e6456620e0080d1d412de04610b3e0576f61c508310db73cb7f72a15e000000000000000000\n00800e20532a5a3df8ebaf9ce5ed29c6d28b1c0d0919e67d2963a96f000000000000000094e0de8060b91cbf22eeabbd24ba169be36dd35ac391211b8b5b67abc831c6c84615cb6933930019a3cac67b55620e00f620adee8861e8448cf3e3e73d23baaf6bee000cb84af78f0000000000000000\n00c005300e2d216cf5138d688374400cb0bf57803f90b8067a04807d0000000000000000601dc20b742503f2a86a8ac68403173e95397b6b0ad9e0880fb7d25af857017efb12cb696f9300191db1123554620e00532a5a3df8ebaf9ce5ed29c6d28b1c0d0919e67d2963a96f0000000000000000\n0040e52397b5fa447f1206187716458968a6bd42e4b154909ba34e890000000000000000abf61a105cf0b31838141a19dd2925c7e9595c5d511f275a5a139037c71cfa553412cb699393001908d998ae53620e000e2d216cf5138d688374400cb0bf57803f90b8067a04807d0000000000000000\n0000002a6392cbfd3bba8b326a3fa2b4f0d6358a4311e6f416fc0052000000000000000084ac4b34da11d111aea11f6a9ec2e57153df99a257b0a08d75856c6013fa526aca10cb697093001924ce0ebf52620e0097b5fa447f1206187716458968a6bd42e4b154909ba34e890000000000000000\n00400f202ee145bd34c2b891db7b78bd1bcf39b5ff65ec2526c125200000000000000000eaed096543f595482cea0c8f082bed675d5f560bc9af0bc46ea9c6a643347c728a0dcb699e930019767ea81651620e006392cbfd3bba8b326a3fa2b4f0d6358a4311e6f416fc00520000000000000000\n00004020832f94bde069650c025ffeec72a57d9214d211fab810cb2b00000000000000000a025558045a2c4795edd028800a8c1a1d869d1ecc1efb900456815ee41e8ccb650ccb69dc93001913a346c750620e002ee145bd34c2b891db7b78bd1bcf39b5ff65ec2526c125200000000000000000\n00e003200cac3653bf3711a9e435cbff9d21eb42e90fe911c83ea1010000000000000000f57fb4d57389a9e5cda0bce9274b03d91a0016f5ff7fca7b3b4113f71f3bb2c1a50bcb69fb930019ab45951f4f620e00832f94bde069650c025ffeec72a57d9214d211fab810cb2b0000000000000000\n00600320aada708d2122364fe22223869cb727e6692668c290bd4778000000000000000037113e99131db2fd05e62cd0dc7ff733928777f699a4e79ea0cfef0682b6b7fa140acb6941940019cc11eda84e620e000cac3653bf3711a9e435cbff9d21eb42e90fe911c83ea1010000000000000000\n00a03c35d0d2c90e65594a16541646145a7bbd3c60f934499151ae2200000000000000006e007ba017c2f4ff88ccc18f1f912665f29696d0583662292b0af61a52772a7b8d09cb69f29300197d280bc94d620e00aada708d2122364fe22223869cb727e6692668c290bd47780000000000000000\n0020392397a405b12c9e74f5f342b2f21ee2e406a9d2cccd6c225d1b000000000000000085105c34928448bb0553989c5ffe396470f13f59c67e5b04c2edcc7ecdf2a1e52c05cb69dd9300195471bdff4c620e00d0d2c90e65594a16541646145a7bbd3c60f934499151ae220000000000000000\n0060c33269dfb66cda9beab10300e6286761f970740d2afaab0320910000000000000000e1ca3f9a41249996ca7829f8e95a6ee9f2f5b9cfd019b506783f0098d0164f854902cb6929940019a26cb05e4b620e0097a405b12c9e74f5f342b2f21ee2e406a9d2cccd6c225d1b0000000000000000\n00801d2738e229b23de04fbc11b3308a414ae9c77604858ad7d2cc0800000000000000007b97fc73846a7ee02d3150838c9f78ff877ee35cdf43ef1f19959d963d5267a4e601cb695e9400197c0763384a620e0069dfb66cda9beab10300e6286761f970740d2afaab0320910000000000000000\n00a0ff20749d87f83825d1fe15a5b0569a16059000677f5fc75300690000000000000000705caf5147e444bf5cece61b3feb86444bced03a916a90e93737a969632dd10cee00cb698094001929c0458c49620e0038e229b23de04fbc11b3308a414ae9c77604858ad7d2cc080000000000000000\n00403425dc77602555e52fbbdff585b062a76a460552ff2f4c04942a00000000000000005e6f5a73bbc20f4da36c235c8f47e68ed02f0ecd39b8f5db98721b5adb718b8075ffca69f993001900514bc048620e00749d87f83825d1fe15a5b0569a16059000677f5fc75300690000000000000000\n00e0ff3f182d2447c78f227c8f3a427bcc39db6828b7aa06da4f351800000000000000006637a3a12746106c1c2d65fc8e898953ce4c7222b4d7690e1be6d383858aff7aa6f9ca69e29200198cbc2d2847620e00dc77602555e52fbbdff585b062a76a460552ff2f4c04942a0000000000000000\n000000214cb9701c6293d2d20d6b80d7caa18e5e57c5ac86dc1517390000000000000000fd89bf1d57723b7e1539612dc3812130d630f0ee1acfacd74f2e267309b2ce6a19f0ca692193001991acf8bf46620e00182d2447c78f227c8f3a427bcc39db6828b7aa06da4f35180000000000000000\n00a0042046bfee589fd972e4d17ab79e830cd04e10b9c03f51b6613d00000000000000004877d50f445a1f7bd5c5e5dc47176e7433506d2e64940e28d1b7089a1c1ef03662efca69039300194a17daf345620e004cb9701c6293d2d20d6b80d7caa18e5e57c5ac86dc1517390000000000000000\n00c0ea34146a824505a8448be368bb7b707f69b28dd023bdefc4c95100000000000000004584b688d747b80a52299b52590f26493619103af3a5c505addbdc0a9e3a98df42ecca693593001964ba3ca444620e0046bfee589fd972e4d17ab79e830cd04e10b9c03f51b6613d0000000000000000\n00a00420504660e559d1ce40af9979a2aaa2510d5a689987bb09af010000000000000000659586ca86a48192784cce6be0f0fbda79c28e0764408031d0636773f90526d933ebca69159300192201be5b43620e00146a824505a8448be368bb7b707f69b28dd023bdefc4c9510000000000000000\n00c045267d9939c349987bc82d6c3e92bd67737c8b1f29a54863b9430000000000000000bba787020a4139a26b8deaf0688451bac406ffd3ceb2a67944ed1176f8e093a809e8ca692793001918289c7342620e00504660e559d1ce40af9979a2aaa2510d5a689987bb09af010000000000000000\n00200120b1d0d19ef298ca223d1e7fa35206b21be394243b0b13441800000000000000002b72ffa168bf01df17dc0282292081f0825bec157aa6008b8dc309a8e29dc0332ae6ca695a93001900d8962d41620e007d9939c349987bc82d6c3e92bd67737c8b1f29a54863b9430000000000000000\n00600320ddfa27afbf144a3eed18d5051de1236d8cf26281f460a61d00000000000000000d0aefd80fa21c8b74f06b3039979e9cbcb57bba391e4f145521ca4079dbe47820e5ca69939300199c7ca5ed40620e00b1d0d19ef298ca223d1e7fa35206b21be394243b0b1344180000000000000000\n00600520e3af250f117c244de840dac57f76ff498dad66d1546b2e4b00000000000000000b89bbcca32f61f40bbd701d0d7253e77b65246b7f39ceb7d0756ed41022ffe43fe4ca69bd930019d08e51383f620e00ddfa27afbf144a3eed18d5051de1236d8cf26281f460a61d0000000000000000\n000040208071d46012c63ee54e6f4244bf93e1c077cf5861ba6c744c0000000000000000978a39f0de2bd5519259ff5c8ff4ee77b40c2c371035da1c4b51f6f12c9333f6fde2ca69f893001946967edd3e620e00e3af250f117c244de840dac57f76ff498dad66d1546b2e4b0000000000000000\n00809825950b7b2c7fba186aaf22c1f5ab44aa2b6f7886e317a2fe680000000000000000ab2c5d8b3fed8704edb50fa57d59d0e42181d234c268a8df0e0655401372f77131e2ca69cd9300198948a9903d620e008071d46012c63ee54e6f4244bf93e1c077cf5861ba6c744c0000000000000000\n0080072043afdb7258888caaf7c488034105cabc55b494d39702144b0000000000000000dcd3782f9f89c3446160a28f427d552739e34bcbf38b782aac58d90545c7734fbbdeca69f293001927f694003c620e00950b7b2c7fba186aaf22c1f5ab44aa2b6f7886e317a2fe680000000000000000\n00800520b97172b7df5bd52fd2855a051e2e96284d1942e7c9afdf810000000000000000e7c534aeb9659cf31fcc41cd1aa0f6d732ab1cb381c12f425d3c991438c689f254ddca692f94001939ca7df43b620e0043afdb7258888caaf7c488034105cabc55b494d39702144b0000000000000000\n00000120d6e2f72e994832549ce5d94fe83ad21f850bedcfabacd073000000000000000043af559eb83f8f69e728075582ea560f3dd14260bb3164a3f7b60af71c872a7f91dcca695a9400191e25df8b3a620e00b97172b7df5bd52fd2855a051e2e96284d1942e7c9afdf810000000000000000\n00207c228f2adc2a6b8099613d50fab30f67365064066cca576c6e4c000000000000000066370f398331d18df3cb94476f0ab13599c78719bad20b18c8e0701668b2a30c4edbca699794001981ac019839620e00d6e2f72e994832549ce5d94fe83ad21f850bedcfabacd0730000000000000000\n00808a29a50f072f266a9eeeb55795ba43eac87ec25c6bad72ac650f00000000000000001ca2d0e12c4d0e21574a7d3cc2cf55260f15c2227d2325f1d13a047d9718d44e89daca69099400193274e33238620e008f2adc2a6b8099613d50fab30f67365064066cca576c6e4c0000000000000000\n008001204464899b79cbbdd7ce0dd60e5416d890348b1d1532b11b28000000000000000068d127976723849988260bb5d6ef1cacf6e8e08a0c57f231727b4c3df07fbc228fd4ca69429400194878658737620e00a50f072f266a9eeeb55795ba43eac87ec25c6bad72ac650f0000000000000000\n0000042035444da1b178758a106a45e7682cc29df0c7e3b846f91b5d0000000000000000656f85792a9a14eef4efeb56b24e97158aa1e8b7629b8969d646ef05b1d7175dadd3ca6970940019f847ce9f36620e004464899b79cbbdd7ce0dd60e5416d890348b1d1532b11b280000000000000000\n0040932634de7814c4b76de40ba7dbc999ba95d89c52d1423fd76916000000000000000082370ef9cea068fc7cb6866ba2d92022ffab9d7af6ccc93c0aaab03e0638c36183d2ca699a9400190d32d79435620e0035444da1b178758a106a45e7682cc29df0c7e3b846f91b5d0000000000000000\n00207a26252279f912d4ad5a0d06bfdff517c0e4af2096d1eaa507010000000000000000a952838e0a491378fe42c0b3cdd0f0f8000a19e7d82df97611d100441f2e65c73fd1ca69d49400196ab8eaee34620e0034de7814c4b76de40ba7dbc999ba95d89c52d1423fd769160000000000000000\n00600420c9d085af57fc5e147f747940b159cab24966c0f0be408f5300000000000000004098f623555595a5cbc99df68d35e9c329cd34b20e2ba24b2b92565fb30362ca61d0ca69a194001990acddfe33620e00252279f912d4ad5a0d06bfdff517c0e4af2096d1eaa507010000000000000000\n00a003263af39530422224150bc010a58817c05faafee3c968bba2740000000000000000d77c657ce6cf09f957051761d670be67672f01f7b725a4344f96bfb29cacc4c3bfccca69ed94001910647e2232620e00c9d085af57fc5e147f747940b159cab24966c0f0be408f530000000000000000\n006003209d0ca9b5634d9b5dcc352bdd5d26b6b69925293fae03c702000000000000000015452318d256bca270e5afa047f5e688087707d008d3f8ca05514ce6555c5e7f55ccca69a4940019a031e77731620e003af39530422224150bc010a58817c05faafee3c968bba2740000000000000000\n00600320380848b1842110889c34883badf4b239a88383a52e78368000000000000000008b9200b22b324db6a71b27d8150739275568595d98d3252c658ba981f0dca9fc22c8ca69b09400192450dc6430620e009d0ca9b5634d9b5dcc352bdd5d26b6b69925293fae03c7020000000000000000\n00e0da227e799a3475ae777c905df15a68d705470725cfea86646b0700000000000000002f770a20d60a5443437a8fac00d92bb17de8ef9088d89dd4d0899939f5d6e5d215c6ca69f69400194dc95c2b2f620e00380848b1842110889c34883badf4b239a88383a52e7836800000000000000000\n00c0d129bcf20d9bfc03f2c40cefbe50fc0b484c4b35db31cca7632a0000000000000000596250c65aefeb3667a410a6f775bf2442ffd5f816803548797b8e85e0b4f59f8ac5ca69039500191c656cef2e620e007e799a3475ae777c905df15a68d705470725cfea86646b070000000000000000\n0000ff3f5f192218b7511493e044626c17ef02dcca0bc1f896a5723e00000000000000003f3a5cdb8d617b798b4f3aa4f8f5599bc194051deefd3822a426aa959d3f737a87c3ca69d4940019e7c6d7102d620e00bcf20d9bfc03f2c40cefbe50fc0b484c4b35db31cca7632a0000000000000000\n006004206c673545257436774b7d1273a88c6e5387f07d6c46bcc50800000000000000002adb94370f270258f9ab0dadce52a07d6cc79219732810de4113cb3caa3f25effebfca69179500199da668572c620e005f192218b7511493e044626c17ef02dcca0bc1f896a5723e0000000000000000\n008092339f0b794d8f73bc63a398fb9cc6da195e9ed30408477ccd7b0000000000000000e7e727dc968e231bcdac35f61ef50ac7d43699ec28796cfc90922d373e5b6c6858bfca699d940019bea286d32b620e006c673545257436774b7d1273a88c6e5387f07d6c46bcc5080000000000000000\n00008020952babdb2a1b3020ff70972c5311293f2378079fffcb68070000000000000000a851abf822d7edb26ea87c165d25f4afe079c1e1029fdd5c79eef70247e1a524e1b9ca69b29400197d9fc1e22a620e009f0b794d8f73bc63a398fb9cc6da195e9ed30408477ccd7b0000000000000000\n00a00e2057ea78550a1a7f10836ef7481fb7f0dcb8959780cf121f4300000000000000009c8d5937faabf59bf7f5bfc99252ccc638391c944134ad13df031b920c50ee3d12b8ca69c794001964a4632929620e00952babdb2a1b3020ff70972c5311293f2378079fffcb68070000000000000000\n000021264db14c4e90dbbd9e2c730e214ee3890fdda14f0cd31ea95000000000000000009471c0b7015288fb08c11e1683d7be77ffdaeb991e89f75defe2acf0f2bc525542b6ca69e89400195fa1885328620e0057ea78550a1a7f10836ef7481fb7f0dcb8959780cf121f430000000000000000\n0040ce271ab2d77af9c9b5f55106ada78619912293120fde2b18820400000000000000000471578850e87269b40123d1d4e05322bbaf1df21a414611f28228cc46c00436c0b4ca691e9500192cf66c7627620e004db14c4e90dbbd9e2c730e214ee3890fdda14f0cd31ea9500000000000000000\n00603d265ee765581d250179a4698610405b0157b04b5c1b0eddab1300000000000000006841155af9bda6b438a712b63f868a229869c26001a132a0b9e8bf03d2a7b898d1b3ca693d9500198b70519f26620e001ab2d77af9c9b5f55106ada78619912293120fde2b1882040000000000000000\n00e039260ccc670bed6d48519a199028a93bf8736751b869cbb5150c000000000000000098d779c9fb5ba6e3c00989beec95f029e5d6a84ebc733f6df9872fbcab9e096f3cb2ca697a950019c12a138425620e005ee765581d250179a4698610405b0157b04b5c1b0eddab130000000000000000\n000060203eff5aa74f2d39e2637d7f88b21cfad6abd40ec223e9aa8f0000000000000000fbe8b6a0ab44b567fe78ba7c1cee05481bcfacab15951cf6fda4a90b8aa5fca673b1ca698e9400191099527224620e000ccc670bed6d48519a199028a93bf8736751b869cbb5150c0000000000000000\n00e00020f2c03a14e81ba4373a7f0376db8b0245fd1ac287e846587d0000000000000000200420adc58a9f8d73e5ee598f29221ac1fa1513d4ad8f6bf9b5b25c7655078015a9ca69a1940019b5420f9a23620e003eff5aa74f2d39e2637d7f88b21cfad6abd40ec223e9aa8f0000000000000000\n0060192baba52a69d96a8b7ef7bbe2f4b6cceb8828867f2e8245bd320000000000000000a9c211f0a9fa89cf29cfaeb6676a097d46d0d8853d603540bfc8a9448974ebde38a7ca69a99400195b0060d522620e00f2c03a14e81ba4373a7f0376db8b0245fd1ac287e846587d0000000000000000\n0020022043f11f57b5262238ec6c4c7dad17c21ef3f40b9d7b5b8b8c0000000000000000d379b60d936c0b17ce6bfc0934354d30d829d382d84934ec51cc61efd759b44019a5ca69c394001938833c9821620e00aba52a69d96a8b7ef7bbe2f4b6cceb8828867f2e8245bd320000000000000000\n0000002066573ddfa879027a5fd331f04731ab97b2243efe7b68e36e00000000000000002ef32dd7ffd8f65649009a55e0ce0fd92292853a492226a5ba37722b061a02866ba3ca69b99400198ceabb8220620e0043f11f57b5262238ec6c4c7dad17c21ef3f40b9d7b5b8b8c0000000000000000\n00a09a259c13d8fb1a6602120610f57fb1a7fd5f69acc694c78015070000000000000000e97fb80bc93dfd28285a1a0887350f8afadf958ed114161b51ba0d319bf37c0dd2a0ca69ff9400198bf8340f1f620e0066573ddfa879027a5fd331f04731ab97b2243efe7b68e36e0000000000000000\n00c0d723ff584b5ee59cac5fa388064132d25875533f113fa4337c8c0000000000000000b3a7f205db8182fd76dd2675d65f050ca68e273e13b5e79c68d740fbc1cc501245a0ca69029500199cb28b7d1e620e009c13d8fb1a6602120610f57fb1a7fd5f69acc694c78015070000000000000000\n00200526dc5306e3ca21762ab90da3b98f4640e5140bff25005b5d6e00000000000000007830b906f07ceea97c70bc84b98acf480e4c9ca7fd81630c6ab84ea72ef5352afb9dca690e9500193e48112e1d620e00ff584b5ee59cac5fa388064132d25875533f113fa4337c8c0000000000000000\n0000003a40f122d825af358df624ec9055abdd351c7a1917125b271c00000000000000001ea19f5eac046f7794ce5e8ab60e31c486914d930f76103d0dbd45f5cf9f0850f69bca6950950019f9c0530a1c620e00dc5306e3ca21762ab90da3b98f4640e5140bff25005b5d6e0000000000000000\n0060cc237ba07a57d5632f2f561fb867bcc1516fd0c479fc9160805a00000000000000000a8d4f3716fd10b4e04e3e854cceb13c318591e9b43ed83c501346e72f64b5bd4c9bca69619500192dc97b341b620e0040f122d825af358df624ec9055abdd351c7a1917125b271c0000000000000000\n00801020f3b018b02d0d8978ad0c4fdb0ba273dc56c02a78b8d3430a00000000000000006d07e7d79d4a5484dd6b8c29c44d2e5fa3dc2413a1787dc7dcbef3c3e3192d2b6699ca69a19500197a02eb4d1a620e007ba07a57d5632f2f561fb867bcc1516fd0c479fc9160805a0000000000000000\n00a0002003b5de9d8270dd1cc7d248feae164fcfe2898b8230ffce6c00000000000000009a0cc1ca640204ce9106ef833d94c96c5071b040610fe5e57ba05a77d8b57ec8ae98ca69a9950019676a984a19620e00f3b018b02d0d8978ad0c4fdb0ba273dc56c02a78b8d3430a0000000000000000\n00800020f1735fe6625140224c19b71bdc6b679aaacb2db1b169410b0000000000000000b3644155d097e17b5525f07b4322cf29b1e6b0a45871b6de723847e4c81dabbe8e96ca695093001905e11f1f18620e0003b5de9d8270dd1cc7d248feae164fcfe2898b8230ffce6c0000000000000000\n0080042031f0bfaa12551164dcdb5589d95f5ece2aa23093eb37902b0000000000000000360e95d816850f68466671b825207a6a1c9a3fe27da7134d3b0f6b86215b703bc784ca69789300194a8ba75a17620e00f1735fe6625140224c19b71bdc6b679aaacb2db1b169410b0000000000000000\n00a00420af78cc91bdd7ad6d505076a248c965a40e0360c1d908242b00000000000000007c0dfe072c4160cf6a99a83a8067fc54b9b98025030f74f9e64905e57a3e39587983ca6986930019f443a4f616620e0031f0bfaa12551164dcdb5589d95f5ece2aa23093eb37902b0000000000000000\n00800720b82ef219c7eca38296ea48d766e0a9943e6ef0b447e73226000000000000000015d9be15a5e7cfb6fadb4fbe120437de3275f3e747e446a9483aa60d6a70fb877b81ca69b99300192ba95c8315620e00af78cc91bdd7ad6d505076a248c965a40e0360c1d908242b0000000000000000\n00e005208f95110f3d524bc56b0db72682ce3540debd42e889d3d80500000000000000002442284672bae99e82ff66c5afe43cec71337a7a2360e43b1a507a4146ef24cc7280ca69fe930019d441192f14620e00b82ef219c7eca38296ea48d766e0a9943e6ef0b447e732260000000000000000\n0020132075c1f54ba158172d26865eb88bd30cb5a5d54d23226f604000000000000000005e952082fc535f0fad949288846f56cc3f6d90286b1d03cef1782bb2100bae97e17fca69ee930019d616239a13620e008f95110f3d524bc56b0db72682ce3540debd42e889d3d8050000000000000000\n00a00420732f79870c18903904de27bf93a83f587e87513fa77a0175000000000000000068a2ff1dab44737f33462d87693589fefedddff58edcedebd42217c03d6eae4a277dca69df930019d2f3681f12620e0075c1f54ba158172d26865eb88bd30cb5a5d54d23226f60400000000000000000\n0000003e1030c4be90fac2dbb3eededd44c769e1dc24706d9f3cc03a0000000000000000cddffc3b13972a5ff3d147a6aa301ca43d5db9018144c1c250a38bc10d7824b4687aca690a940019ef49e6bd11620e00732f79870c18903904de27bf93a83f587e87513fa77a01750000000000000000\n00200520e79f687130ae2a187eb38b6ffd8ffff79ef87f54ec9650890000000000000000e93e81d1fdf21779940647219a357c2388aba7553a61a210b89643aa5974d1cb2979ca6938940019af09b1c110620e001030c4be90fac2dbb3eededd44c769e1dc24706d9f3cc03a0000000000000000\n0040022006e196f9f302e1fefa94fa141f0b1eb558eb1590930580530000000000000000cc4962f3b7126290cfe6bcd82d1fdb0c9b6bfa31ddd8ac371ba756a0bb872a760278ca6989940019d12904720f620e00e79f687130ae2a187eb38b6ffd8ffff79ef87f54ec9650890000000000000000\n0020c920f85bbe484d04c67898aee865a29a3b0db314beaab0ca9b690000000000000000aeda6280804b59e714c67be3c30d3af4c7d28efd2605579af770e9bf83d20744ba77ca699d940019693581ef0e620e0006e196f9f302e1fefa94fa141f0b1eb558eb1590930580530000000000000000\n00e00920f67a60aa22820f179ce656b5662ab7e637e7a0ba3cb4ca84000000000000000061fef76593d9f45e977f3eb41ed01b419baac92dbd43ff59fd5677f3d67cb244e875ca69279400197b03488c0d620e00f85bbe484d04c67898aee865a29a3b0db314beaab0ca9b690000000000000000\n00c00520ed205c6d5d558a8f93e9706bbf43ced51f7820bf9f67c727000000000000000071918efa3a2f23c943b672389f51e45f3829cdcdfb7c448464aa984503dba6348b70ca69449400195a873e190c620e00f67a60aa22820f179ce656b5662ab7e637e7a0ba3cb4ca840000000000000000\n00e00820b2f06b15e440367614a175cff5a2ffea6015c10284e47873000000000000000090795c18cb404fb42067bac8213bf40ddf334e2fc155be50f583fdeb2b83d6cdf16eca69b99300193d9442390b620e00ed205c6d5d558a8f93e9706bbf43ced51f7820bf9f67c7270000000000000000\n00c046272d6fe2800f82a80a7d0074072b4b88bd354a73014ff5d4330000000000000000398846736a7aa6aeb5076c84b5bf8807555be524fbd7d72668c3164668b19c540169ca694b9300191406b0050a620e00b2f06b15e440367614a175cff5a2ffea6015c10284e478730000000000000000\n0040a026cc5d4b896af8b3d8ce0d03540e0450e89b864cf585d6ba770000000000000000742e7294726ca4fe984b5e282756589609956da51c5c5a5aeb3890882a01fb98d363ca69829300197635de5009620e002d6fe2800f82a80a7d0074072b4b88bd354a73014ff5d4330000000000000000\n00000030ff8eaa5117100b9c6a38183a1a2ce9230aeceebdbfb0c550000000000000000086e9e3e589de9dd84d222c00a6da4bc95c11d785cc96c2a960b153192499892ce962ca69b2930019bae71ba408620e00cc5d4b896af8b3d8ce0d03540e0450e89b864cf585d6ba770000000000000000\n00000220793b139a15eb0b3a65afbdfb8d817e34c92755f06da2330c000000000000000034a6fcf091f22d27933a3147cf1af0d2926092c11b87f1c51ca70425141de3f1ce61ca69dd930019f09dab7107620e00ff8eaa5117100b9c6a38183a1a2ce9230aeceebdbfb0c5500000000000000000\n00000022680293fb4ccc09e541798ce4e26f68dc58cb145074594f8900000000000000001df899a83b5b6c031983afd982bc23bd4a9be214dc25e6f12c474f9b785e130e9260ca6918940019eb3f18b406620e00793b139a15eb0b3a65afbdfb8d817e34c92755f06da2330c0000000000000000\n00e00520a2090e56667ff2baff7693b666e60bc01f499bbc7548f123000000000000000058aba649cd1badc1b19b147c5bd52895e990d16d65d8d07cca047f18967dc92bbd5fca697893001969a7301705620e00680293fb4ccc09e541798ce4e26f68dc58cb145074594f890000000000000000\n00a0112e8a05d9dbb0e17aa4b1ca6b4fd031ffbfef281160d05b0e480000000000000000cb342abef48f73fa3bc83e007ecff01a51ad0935331b699d86ad79ff36b8186c4659ca69a093001907d096cd04620e00a2090e56667ff2baff7693b666e60bc01f499bbc7548f1230000000000000000\n00000f2051caa0285146470af598f120732d440d0c4676ef8331245e0000000000000000a9d4d4abbec76b90bb132b95b9fa761bef65403cc0a0bc82bcf2bf78833e2068f357ca69b993001958abefe303620e008a05d9dbb0e17aa4b1ca6b4fd031ffbfef281160d05b0e480000000000000000\n00000120b9e16857e29eb224cc57a429005d12dd6f0c394a0ec2a98f00000000000000001f68477549a6a55b96315004a06b3909f0bf405ba9c33a30f2913aedfce51e914156ca69469300199f49cc1202620e0051caa0285146470af598f120732d440d0c4676ef8331245e0000000000000000\n00e0ff3f3881c59068960d5c21f80f3c16cb098c8773e7a629288012000000000000000052a727db7bebf2b4370243ef37a81170ee59624d084ad4e302bf8793073a2fa1f550ca694093001972de873c01620e00b9e16857e29eb224cc57a429005d12dd6f0c394a0ec2a98f0000000000000000\n00200f20cf2fe0566498034756cbb24a948803f9d218bea0d75ea72c000000000000000008b606b76febc0b4d4f97a54daa7a5a320f9289085f8fb0659a122d53e439ff2734eca691e9300193e795a6d00620e003881c59068960d5c21f80f3c16cb098c8773e7a6292880120000000000000000\n00601020add2a54863b762baf2af9b6022ff344e5473bead26636c6c000000000000000095d88a05adb16c9414aee0acb892e9e769913d868afd784c40b73d01bd7d5fa23e4bca695e93001918bd313eff610e00cf2fe0566498034756cbb24a948803f9d218bea0d75ea72c0000000000000000\n00c0fc211759584c6165943911c27340f93b9e9f6540a1c799807b400000000000000000f3938fed024c40a053533a19b39316b29c596cf66100c0d25b8334ed990ad984894aca6947920019b6e6cf26fe610e00add2a54863b762baf2af9b6022ff344e5473bead26636c6c0000000000000000\n00c0072005c9c21a9bd86c2e9e329c2d9c56877625674db4e05e132600000000000000003d262ef880606d6a907f4bd132105c1a7128406caa1d82f3118e43e6ccfba588fa40ca696e92001913a91d1ffd610e001759584c6165943911c27340f93b9e9f6540a1c799807b400000000000000000\n0040282799e6414402e414a32f6738984fabd9de22a3bf450c10240500000000000000000163b87070e79d2cb57e2c915ab0f27e9a9b25af56cf8a4ee1e31686de5cd98da43fca69c49200196ca9474afc610e0005c9c21a9bd86c2e9e329c2d9c56877625674db4e05e13260000000000000000\n00c08c2f1d79c3ee74a5d0436dffd20cb06072428fcf130186781e5d00000000000000000a8317b6eb4999e77fea15c56b719880847555635d5785fe52c6686803058d29893fca69d49200191811d6adfb610e0099e6414402e414a32f6738984fabd9de22a3bf450c1024050000000000000000\n00201e26bb4ba422de571e0cd7f480e1ca1ab971d913d56455ce893f0000000000000000d1615080da57aae043a8ab4ded789710060d1e54d6effc91a38f562af8444bf8993dca69ee9200198ca1fef8fa610e001d79c3ee74a5d0436dffd20cb06072428fcf130186781e5d0000000000000000\n00a045243700ab4e49f52c9873771bc1c5374c1122715d3a752ffa8f00000000000000001d626af56df84196d437b94fcdc5795da13fbbda3b518afd0bacbe95a43c8dc3ee3bca69cd9200196c090f88f9610e00bb4ba422de571e0cd7f480e1ca1ab971d913d56455ce893f0000000000000000\n00009c203f06b06860bb86cb12996bba9d93ebf297049951b56b8241000000000000000066b8943078a61d3030d6f82d826a0d160b6bd70b959a15c53bd202c480b39db2b838ca690d93001991e16b52f8610e003700ab4e49f52c9873771bc1c5374c1122715d3a752ffa8f0000000000000000\n00600e20a8b91bf974ffce0e9963f0c92e92f66a815a4c3d79e6892700000000000000009d46e1efbf759fdad519b71170f91278a48eb20136eee4d4070cd0bc3946c4e30a38ca6916920019941e468af7610e003f06b06860bb86cb12996bba9d93ebf297049951b56b82410000000000000000\n00a00d204ea25d176e6994185e94e252db455bf4731c7886f335e65900000000000000002527990beeb560adc96c798c48b003620e161c61fe5bc370105daa13bff1e0974a2fca69529200191310ea74f6610e00a8b91bf974ffce0e9963f0c92e92f66a815a4c3d79e689270000000000000000\n0000be244ae759ac78506f8bd54f0447b88445eeb6c9b650bad39f5f000000000000000070ee4e96ae994cbabe535d688010239e7e0bef62c4972e28bd13968f7c8ce16d832eca697f920019baee3d14f5610e004ea25d176e6994185e94e252db455bf4731c7886f335e6590000000000000000\n0080802290a7f002def34b8ab55a8dc86204f141a6fe74e88dabde1400000000000000002dd7f5294a32c431f168e27a72f692a22ad06dff29a980390cb7c41a7a9b70e0552dca694e920019351357d7f4610e004ae759ac78506f8bd54f0447b88445eeb6c9b650bad39f5f0000000000000000\n0040ce2a383da64b478264133d1498dce58256eca6ee73a7f99d06400000000000000000e3980d3eeca9d66544888a7c00f250a345ebafdb48be7ed04c3908f944158a7fb629ca699692001948a61ad0f3610e0090a7f002def34b8ab55a8dc86204f141a6fe74e88dabde140000000000000000\n000000209f62e80085da73e794c6995a954e7347c79926758bac9d7d00000000000000006aedc51de21379f72d8afb8ab689174a679b1db6677a4f846929f8b65b2ed7e43c29ca69c2920019946e56cef2610e00383da64b478264133d1498dce58256eca6ee73a7f99d06400000000000000000\n00001520fe38e3669db003295e7f0e4d88e51d193428a8416d90b3430000000000000000a56efd22ad0a51943a3cfc3d87ffe7e4a25181930847786b4fcc6be387dfc88f0b28ca69ee92001907dfd382f1610e009f62e80085da73e794c6995a954e7347c79926758bac9d7d0000000000000000\n00a00220f16d39c644005001e3c0083df8ad367432d6e936bb3b3e2d0000000000000000ad4267462fe2293a1a9654726f358c04b35531424e4ffedbf7ac33964e4b1bdbd326ca6912930019b8935a04f0610e00fe38e3669db003295e7f0e4d88e51d193428a8416d90b3430000000000000000\n0020182a43ab4ff561711b42b79add180912d52a7370852fac93d5690000000000000000986e1a32e4e3b8c75e560675132d7788df168965465463c18428b9d76e397cf76c25ca695e9200191046e29cef610e00f16d39c644005001e3c0083df8ad367432d6e936bb3b3e2d0000000000000000\n00e0163643a956ccc8614fbbb5156ae668bdcefdf0eb5c410114187f000000000000000027d8868f86cbf03838b75b915b7d871be22e62067a63f64f771215d52226ccf86a1eca696c9200194f4b4632ee610e0043ab4ff561711b42b79add180912d52a7370852fac93d5690000000000000000\n0000002c6fdd1161f352137cb104207be548c538df9442e309be5704000000000000000036a1d1236eaaeb31c84e323b03d027372357c353be49ae9d6971134b1a2d9f216d1cca698e920019121c891ded610e0043a956ccc8614fbbb5156ae668bdcefdf0eb5c410114187f0000000000000000\n004024275b078af6ccdf1ba70734f1ee7fcbe91309142c2c01a116450000000000000000e5c9b4115156d5b2a98a3853b6587b01d0f90d39a4638fecda93bc74f7d37d03fc1aca69c892001966befe00ec610e006fdd1161f352137cb104207be548c538df9442e309be57040000000000000000\n0000c0205cbc25e1f60ddd9fb3430ec02c039230a3bdb8b71d7c4b1700000000000000009ae54d2b51c168538eb4aea245e3a5f0312f1766d184377d18debd8cb5b96c4d221aca69809200197e776e73eb610e005b078af6ccdf1ba70734f1ee7fcbe91309142c2c01a116450000000000000000\n0060fc26418d117cc92252f9d58df9f4c62cc4241fc9f45662e04b330000000000000000d0fb16cb5a971661d2f0a700f56b3155087479718a3d55459fd0f4083659eb6bef15ca69c892001920b949b1ea610e005cbc25e1f60ddd9fb3430ec02c039230a3bdb8b71d7c4b170000000000000000\n0020f3221dcba4b64ec585be7836617232a6031aea3b76a285df8d4100000000000000000df3dffd7b856f098eb70de7f5a8123e59114a144a1fb1bc9d05b20d0cd5d4787215ca69db9200194ec9fe36e9610e00418d117cc92252f9d58df9f4c62cc4241fc9f45662e04b330000000000000000\n00404a2e0ddf7ad2e5edbedbe77e578ee8c95eb9dd20c7309e08307e00000000000000006856273a31145ef35f01c6b2045dd63fc98b1e39d77d3dfbf0ae3ad407287f839513ca692d92001937697fc7e8610e001dcba4b64ec585be7836617232a6031aea3b76a285df8d410000000000000000\n000000208a289177456e366dae7865fdd0aacab194e6be407e481a1600000000000000003ee777cb7debce624b6fba1f1b742ca0af564b3a3314d7caa2618ff25d111563bc0cca69449200194f857087e7610e000ddf7ad2e5edbedbe77e578ee8c95eb9dd20c7309e08307e0000000000000000\n00e000209898d5f96a80e0276ec8961383067b9bfbd97e410e8a206600000000000000009904c42793065e3ae5da7ee64853f684929323990c73920b45f10a9eff63efdbff0aca6969920019f5bde171e6610e008a289177456e366dae7865fdd0aacab194e6be407e481a160000000000000000\n00800020416c1955e9cce1544c210477780da23d9338017bfd52f05b0000000000000000191d94a8ad442133203985ea117087af2dcaaee1f8ff117b39fa86328497c01c9b09ca69a5920019d9605f57e5610e009898d5f96a80e0276ec8961383067b9bfbd97e410e8a20660000000000000000\n"
  },
  {
    "path": "ecashkit/src/test/java/io/horizontalsystems/ecash/ExampleUnitTest.kt",
    "content": "package io.horizontalsystems.ecash\n\nimport org.junit.Assert.assertEquals\nimport org.junit.Test\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\nclass ExampleUnitTest {\n    @Test\n    fun addition_isCorrect() {\n        assertEquals(4, 2 + 2)\n    }\n}"
  },
  {
    "path": "gradle/wrapper/gradle-wrapper.properties",
    "content": "#Tue Apr 09 17:20:26 KGT 2024\ndistributionBase=GRADLE_USER_HOME\ndistributionPath=wrapper/dists\ndistributionUrl=https\\://services.gradle.org/distributions/gradle-8.7-bin.zip\nzipStoreBase=GRADLE_USER_HOME\nzipStorePath=wrapper/dists\n"
  },
  {
    "path": "gradle.properties",
    "content": "# Project-wide Gradle settings.\n# IDE (e.g. Android Studio) users:\n# Gradle settings configured through the IDE *will override*\n# any settings specified in this file.\n# For more details on how to configure your build environment visit\n# http://www.gradle.org/docs/current/userguide/build_environment.html\n# Specifies the JVM arguments used for the daemon process.\n# The setting is particularly useful for tweaking memory settings.\nandroid.defaults.buildfeatures.buildconfig=true\nandroid.enableJetifier=true\nandroid.nonFinalResIds=false\nandroid.nonTransitiveRClass=false\nandroid.useAndroidX=true\norg.gradle.jvmargs=-Xmx1536m\n# When configured, Gradle will run in incubating parallel mode.\n# This option should only be used with decoupled projects. More details, visit\n# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects\n# org.gradle.parallel=true\n"
  },
  {
    "path": "gradlew",
    "content": "#!/usr/bin/env sh\n\n#\n# Copyright 2015 the original author or authors.\n#\n# Licensed under the Apache License, Version 2.0 (the \"License\");\n# you may not use this file except in compliance with the License.\n# You may obtain a copy of the License at\n#\n#      https://www.apache.org/licenses/LICENSE-2.0\n#\n# Unless required by applicable law or agreed to in writing, software\n# distributed under the License is distributed on an \"AS IS\" BASIS,\n# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n# See the License for the specific language governing permissions and\n# limitations under the License.\n#\n\n##############################################################################\n##\n##  Gradle start up script for UN*X\n##\n##############################################################################\n\n# Attempt to set APP_HOME\n# Resolve links: $0 may be a link\nPRG=\"$0\"\n# Need this for relative symlinks.\nwhile [ -h \"$PRG\" ] ; do\n    ls=`ls -ld \"$PRG\"`\n    link=`expr \"$ls\" : '.*-> \\(.*\\)$'`\n    if expr \"$link\" : '/.*' > /dev/null; then\n        PRG=\"$link\"\n    else\n        PRG=`dirname \"$PRG\"`\"/$link\"\n    fi\ndone\nSAVED=\"`pwd`\"\ncd \"`dirname \\\"$PRG\\\"`/\" >/dev/null\nAPP_HOME=\"`pwd -P`\"\ncd \"$SAVED\" >/dev/null\n\nAPP_NAME=\"Gradle\"\nAPP_BASE_NAME=`basename \"$0\"`\n\n# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nDEFAULT_JVM_OPTS='\"-Xmx64m\" \"-Xms64m\"'\n\n# Use the maximum available, or set MAX_FD != -1 to use that value.\nMAX_FD=\"maximum\"\n\nwarn () {\n    echo \"$*\"\n}\n\ndie () {\n    echo\n    echo \"$*\"\n    echo\n    exit 1\n}\n\n# OS specific support (must be 'true' or 'false').\ncygwin=false\nmsys=false\ndarwin=false\nnonstop=false\ncase \"`uname`\" in\n  CYGWIN* )\n    cygwin=true\n    ;;\n  Darwin* )\n    darwin=true\n    ;;\n  MINGW* )\n    msys=true\n    ;;\n  NONSTOP* )\n    nonstop=true\n    ;;\nesac\n\nCLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar\n\n# Determine the Java command to use to start the JVM.\nif [ -n \"$JAVA_HOME\" ] ; then\n    if [ -x \"$JAVA_HOME/jre/sh/java\" ] ; then\n        # IBM's JDK on AIX uses strange locations for the executables\n        JAVACMD=\"$JAVA_HOME/jre/sh/java\"\n    else\n        JAVACMD=\"$JAVA_HOME/bin/java\"\n    fi\n    if [ ! -x \"$JAVACMD\" ] ; then\n        die \"ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\n    fi\nelse\n    JAVACMD=\"java\"\n    which java >/dev/null 2>&1 || die \"ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\n\nPlease set the JAVA_HOME variable in your environment to match the\nlocation of your Java installation.\"\nfi\n\n# Increase the maximum file descriptors if we can.\nif [ \"$cygwin\" = \"false\" -a \"$darwin\" = \"false\" -a \"$nonstop\" = \"false\" ] ; then\n    MAX_FD_LIMIT=`ulimit -H -n`\n    if [ $? -eq 0 ] ; then\n        if [ \"$MAX_FD\" = \"maximum\" -o \"$MAX_FD\" = \"max\" ] ; then\n            MAX_FD=\"$MAX_FD_LIMIT\"\n        fi\n        ulimit -n $MAX_FD\n        if [ $? -ne 0 ] ; then\n            warn \"Could not set maximum file descriptor limit: $MAX_FD\"\n        fi\n    else\n        warn \"Could not query maximum file descriptor limit: $MAX_FD_LIMIT\"\n    fi\nfi\n\n# For Darwin, add options to specify how the application appears in the dock\nif $darwin; then\n    GRADLE_OPTS=\"$GRADLE_OPTS \\\"-Xdock:name=$APP_NAME\\\" \\\"-Xdock:icon=$APP_HOME/media/gradle.icns\\\"\"\nfi\n\n# For Cygwin or MSYS, switch paths to Windows format before running java\nif [ \"$cygwin\" = \"true\" -o \"$msys\" = \"true\" ] ; then\n    APP_HOME=`cygpath --path --mixed \"$APP_HOME\"`\n    CLASSPATH=`cygpath --path --mixed \"$CLASSPATH\"`\n    JAVACMD=`cygpath --unix \"$JAVACMD\"`\n\n    # We build the pattern for arguments to be converted via cygpath\n    ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null`\n    SEP=\"\"\n    for dir in $ROOTDIRSRAW ; do\n        ROOTDIRS=\"$ROOTDIRS$SEP$dir\"\n        SEP=\"|\"\n    done\n    OURCYGPATTERN=\"(^($ROOTDIRS))\"\n    # Add a user-defined pattern to the cygpath arguments\n    if [ \"$GRADLE_CYGPATTERN\" != \"\" ] ; then\n        OURCYGPATTERN=\"$OURCYGPATTERN|($GRADLE_CYGPATTERN)\"\n    fi\n    # Now convert the arguments - kludge to limit ourselves to /bin/sh\n    i=0\n    for arg in \"$@\" ; do\n        CHECK=`echo \"$arg\"|egrep -c \"$OURCYGPATTERN\" -`\n        CHECK2=`echo \"$arg\"|egrep -c \"^-\"`                                 ### Determine if an option\n\n        if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then                    ### Added a condition\n            eval `echo args$i`=`cygpath --path --ignore --mixed \"$arg\"`\n        else\n            eval `echo args$i`=\"\\\"$arg\\\"\"\n        fi\n        i=$((i+1))\n    done\n    case $i in\n        (0) set -- ;;\n        (1) set -- \"$args0\" ;;\n        (2) set -- \"$args0\" \"$args1\" ;;\n        (3) set -- \"$args0\" \"$args1\" \"$args2\" ;;\n        (4) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" ;;\n        (5) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" ;;\n        (6) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" ;;\n        (7) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" ;;\n        (8) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" ;;\n        (9) set -- \"$args0\" \"$args1\" \"$args2\" \"$args3\" \"$args4\" \"$args5\" \"$args6\" \"$args7\" \"$args8\" ;;\n    esac\nfi\n\n# Escape application args\nsave () {\n    for i do printf %s\\\\n \"$i\" | sed \"s/'/'\\\\\\\\''/g;1s/^/'/;\\$s/\\$/' \\\\\\\\/\" ; done\n    echo \" \"\n}\nAPP_ARGS=$(save \"$@\")\n\n# Collect all arguments for the java command, following the shell quoting and substitution rules\neval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS \"\\\"-Dorg.gradle.appname=$APP_BASE_NAME\\\"\" -classpath \"\\\"$CLASSPATH\\\"\" org.gradle.wrapper.GradleWrapperMain \"$APP_ARGS\"\n\n# by default we should be in the correct project dir, but when run from Finder on Mac, the cwd is wrong\nif [ \"$(uname)\" = \"Darwin\" ] && [ \"$HOME\" = \"$PWD\" ]; then\n  cd \"$(dirname \"$0\")\"\nfi\n\nexec \"$JAVACMD\" \"$@\"\n"
  },
  {
    "path": "gradlew.bat",
    "content": "@rem\n@rem Copyright 2015 the original author or authors.\n@rem\n@rem Licensed under the Apache License, Version 2.0 (the \"License\");\n@rem you may not use this file except in compliance with the License.\n@rem You may obtain a copy of the License at\n@rem\n@rem      https://www.apache.org/licenses/LICENSE-2.0\n@rem\n@rem Unless required by applicable law or agreed to in writing, software\n@rem distributed under the License is distributed on an \"AS IS\" BASIS,\n@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n@rem See the License for the specific language governing permissions and\n@rem limitations under the License.\n@rem\n\n@if \"%DEBUG%\" == \"\" @echo off\n@rem ##########################################################################\n@rem\n@rem  Gradle startup script for Windows\n@rem\n@rem ##########################################################################\n\n@rem Set local scope for the variables with windows NT shell\nif \"%OS%\"==\"Windows_NT\" setlocal\n\nset DIRNAME=%~dp0\nif \"%DIRNAME%\" == \"\" set DIRNAME=.\nset APP_BASE_NAME=%~n0\nset APP_HOME=%DIRNAME%\n\n@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.\nset DEFAULT_JVM_OPTS=\"-Xmx64m\" \"-Xms64m\"\n\n@rem Find java.exe\nif defined JAVA_HOME goto findJavaFromJavaHome\n\nset JAVA_EXE=java.exe\n%JAVA_EXE% -version >NUL 2>&1\nif \"%ERRORLEVEL%\" == \"0\" goto init\n\necho.\necho ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:findJavaFromJavaHome\nset JAVA_HOME=%JAVA_HOME:\"=%\nset JAVA_EXE=%JAVA_HOME%/bin/java.exe\n\nif exist \"%JAVA_EXE%\" goto init\n\necho.\necho ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME%\necho.\necho Please set the JAVA_HOME variable in your environment to match the\necho location of your Java installation.\n\ngoto fail\n\n:init\n@rem Get command-line arguments, handling Windows variants\n\nif not \"%OS%\" == \"Windows_NT\" goto win9xME_args\n\n:win9xME_args\n@rem Slurp the command line arguments.\nset CMD_LINE_ARGS=\nset _SKIP=2\n\n:win9xME_args_slurp\nif \"x%~1\" == \"x\" goto execute\n\nset CMD_LINE_ARGS=%*\n\n:execute\n@rem Setup the command line\n\nset CLASSPATH=%APP_HOME%\\gradle\\wrapper\\gradle-wrapper.jar\n\n@rem Execute Gradle\n\"%JAVA_EXE%\" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% \"-Dorg.gradle.appname=%APP_BASE_NAME%\" -classpath \"%CLASSPATH%\" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS%\n\n:end\n@rem End local scope for the variables with windows NT shell\nif \"%ERRORLEVEL%\"==\"0\" goto mainEnd\n\n:fail\nrem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of\nrem the _cmd.exe /c_ return code!\nif  not \"\" == \"%GRADLE_EXIT_CONSOLE%\" exit 1\nexit /b 1\n\n:mainEnd\nif \"%OS%\"==\"Windows_NT\" endlocal\n\n:omega\n"
  },
  {
    "path": "hodler/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "hodler/build.gradle",
    "content": "plugins {\n    id 'com.android.library'\n    id 'kotlin-android'\n    id 'kotlin-kapt'\n    id 'maven-publish'\n}\n\nafterEvaluate {\n    publishing {\n        publications {\n            release(MavenPublication) {\n                from components.release\n            }\n        }\n    }\n}\n\nandroid {\n    namespace 'io.horizontalsystems.hodler'\n    compileSdk 34\n\n    defaultConfig {\n        minSdkVersion 23\n        targetSdkVersion 34\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n        consumerProguardFiles 'consumer-rules.pro'\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    sourceSets {\n        main.java.srcDirs += 'src/main/kotlin'\n        test.java.srcDirs += 'src/test/kotlin'\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_17\n        targetCompatibility JavaVersion.VERSION_17\n    }\n    kotlinOptions { jvmTarget = '17' }\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version\"\n\n    implementation project(':bitcoincore')\n\n    androidTestImplementation 'androidx.test.ext:junit:1.1.1'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'\n\n    // Test helpers\n    testImplementation 'junit:junit:4.13.2'\n    testImplementation 'org.junit.jupiter:junit-jupiter:5.6.1'\n    testImplementation \"com.nhaarman.mockitokotlin2:mockito-kotlin:2.2.0\"\n    testImplementation \"org.powermock:powermock-api-mockito2:2.0.7\"\n    testImplementation \"org.powermock:powermock-module-junit4:2.0.7\"\n    testImplementation 'org.mockito:mockito-inline:3.3.3'\n}\n"
  },
  {
    "path": "hodler/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "hodler/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "hodler/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" />\n"
  },
  {
    "path": "hodler/src/main/kotlin/io/horizontalsystems/hodler/HodlerData.kt",
    "content": "package io.horizontalsystems.hodler\n\nimport io.horizontalsystems.bitcoincore.core.IPluginData\n\ndata class HodlerData(val lockTimeInterval: LockTimeInterval) : IPluginData\n"
  },
  {
    "path": "hodler/src/main/kotlin/io/horizontalsystems/hodler/HodlerOutputData.kt",
    "content": "package io.horizontalsystems.hodler\n\nimport io.horizontalsystems.bitcoincore.core.IPluginOutputData\n\nclass HodlerOutputData(val lockTimeInterval: LockTimeInterval,\n                       val addressString: String) : IPluginOutputData {\n\n    var approxUnlockTime: Long? = null\n\n    fun serialize(): String {\n        return listOf(lockTimeInterval.serialize(), addressString).joinToString(\"|\")\n    }\n\n    companion object {\n        fun parse(serialized: String?): HodlerOutputData {\n            val (lockTimeIntervalStr, addressString) = checkNotNull(serialized?.split(\"|\"))\n\n            val lockTimeInterval = checkNotNull(LockTimeInterval.deserialize(lockTimeIntervalStr))\n            return HodlerOutputData(lockTimeInterval, addressString)\n        }\n    }\n}\n"
  },
  {
    "path": "hodler/src/main/kotlin/io/horizontalsystems/hodler/HodlerPlugin.kt",
    "content": "package io.horizontalsystems.hodler\n\nimport io.horizontalsystems.bitcoincore.blocks.BlockMedianTimeHelper\nimport io.horizontalsystems.bitcoincore.core.IPlugin\nimport io.horizontalsystems.bitcoincore.core.IPluginData\nimport io.horizontalsystems.bitcoincore.core.IPluginOutputData\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.models.Address\nimport io.horizontalsystems.bitcoincore.models.PublicKey\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\nimport io.horizontalsystems.bitcoincore.transactions.builder.MutableTransaction\nimport io.horizontalsystems.bitcoincore.transactions.scripts.OP_1\nimport io.horizontalsystems.bitcoincore.transactions.scripts.OP_CHECKSEQUENCEVERIFY\nimport io.horizontalsystems.bitcoincore.transactions.scripts.OP_DROP\nimport io.horizontalsystems.bitcoincore.transactions.scripts.OpCodes\nimport io.horizontalsystems.bitcoincore.transactions.scripts.Script\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport io.horizontalsystems.bitcoincore.utils.IAddressConverter\nimport io.horizontalsystems.bitcoincore.utils.Utils\nimport kotlin.math.min\n\nclass HodlerPlugin(\n    private val addressConverter: IAddressConverter,\n    private val storage: IStorage,\n    private val blockMedianTimeHelper: BlockMedianTimeHelper\n) : IPlugin {\n\n    companion object {\n        const val id = OP_1.toByte()\n    }\n\n    override val id = HodlerPlugin.id\n\n    override fun processOutputs(mutableTransaction: MutableTransaction, pluginData: IPluginData, skipChecking: Boolean) {\n        val lockTimeInterval = checkNotNull((pluginData as? HodlerData)?.lockTimeInterval)\n\n        if (!skipChecking) {\n            check(mutableTransaction.recipientAddress.scriptType == ScriptType.P2PKH) {\n                \"Locking transaction is available only for PKH addresses\"\n            }\n        }\n\n        val pubkeyHash = mutableTransaction.recipientAddress.lockingScriptPayload\n        val redeemScriptHash = Utils.sha256Hash160(redeemScript(lockTimeInterval, pubkeyHash))\n        val newAddress = addressConverter.convert(redeemScriptHash, ScriptType.P2SH)\n\n        mutableTransaction.recipientAddress = newAddress\n        mutableTransaction.addPluginData(id, OpCodes.push(lockTimeInterval.valueAs2BytesLE) + OpCodes.push(pubkeyHash))\n    }\n\n    override fun processTransactionWithNullData(transaction: FullTransaction, nullDataChunks: Iterator<Script.Chunk>) {\n        val lockTimeIntervalData = checkNotNull(nullDataChunks.next().data)\n        val pubkeyHash = checkNotNull(nullDataChunks.next().data)\n\n        val lockTimeInterval = checkNotNull(LockTimeInterval.from2BytesLE(lockTimeIntervalData))\n\n        val redeemScript = redeemScript(lockTimeInterval, pubkeyHash)\n        val redeemScriptHash = Utils.sha256Hash160(redeemScript)\n\n        transaction.outputs.find {\n            it.lockingScriptPayload?.contentEquals(redeemScriptHash) ?: false\n        }?.let { output ->\n            val addressString = addressConverter.convert(pubkeyHash, ScriptType.P2PKH).stringValue\n\n            output.pluginId = id\n            output.pluginData = HodlerOutputData(lockTimeInterval, addressString).serialize()\n\n            storage.getPublicKeyByKeyOrKeyHash(pubkeyHash)?.let { pubkey ->\n                output.redeemScript = redeemScript\n                output.setPublicKey(pubkey)\n            }\n        }\n    }\n\n    override fun isSpendable(unspentOutput: UnspentOutput): Boolean {\n        val lastBlockMedianTimePast = blockMedianTimeHelper.medianTimePast ?: return false\n        return inputLockTime(unspentOutput) < lastBlockMedianTimePast\n    }\n\n    override fun getInputSequence(output: TransactionOutput): Long {\n        return lockTimeIntervalFrom(output).sequenceNumber.toLong()\n    }\n\n    override fun parsePluginData(output: TransactionOutput, txTimestamp: Long): IPluginOutputData {\n        val hodlerData = HodlerOutputData.parse(output.pluginData)\n\n        // When checking if utxo is spendable we use the best block median time.\n        // The median time is 6 blocks earlier which is approximately equal to 1 hour.\n        // Here we add 1 hour to show the time when this UTXO will be spendable\n        hodlerData.approxUnlockTime = hodlerData.lockTimeInterval.valueInSeconds + txTimestamp + 3600\n\n        return hodlerData\n    }\n\n    override fun keysForApiRestore(publicKey: PublicKey): List<String> {\n        return LockTimeInterval.values().map { lockTimeInterval ->\n            val redeemScript = redeemScript(lockTimeInterval, publicKey.publicKeyHash)\n            val redeemScriptHash = Utils.sha256Hash160(redeemScript)\n\n            addressConverter.convert(redeemScriptHash, ScriptType.P2SH).stringValue\n        }\n    }\n\n    override fun bloomFilterElements(publicKey: PublicKey): List<ByteArray> {\n        return listOf()\n    }\n\n    override fun validateAddress(address: Address) {\n        if (address.scriptType != ScriptType.P2PKH) {\n            throw UnsupportedAddressType()\n        }\n    }\n\n    override fun incrementSequence(sequence: Long): Long {\n        val maxInc: Long = 0x7f800000\n        val currentInc = sequence and maxInc\n        val newInc = min(currentInc + (1 shl 23), maxInc)\n        val zeroIncSequence = (0xffffffff - maxInc) and sequence\n        return zeroIncSequence or newInc\n    }\n\n    private fun redeemScript(lockTimeInterval: LockTimeInterval, pubkeyHash: ByteArray): ByteArray {\n        return OpCodes.push(lockTimeInterval.sequenceNumberAs3BytesLE) + byteArrayOf(\n            OP_CHECKSEQUENCEVERIFY.toByte(),\n            OP_DROP.toByte()\n        ) + OpCodes.p2pkhStart + OpCodes.push(pubkeyHash) + OpCodes.p2pkhEnd\n    }\n\n    private fun lockTimeIntervalFrom(output: TransactionOutput): LockTimeInterval {\n        val pluginData = checkNotNull(output.pluginData)\n\n        return HodlerOutputData.parse(pluginData).lockTimeInterval\n    }\n\n    private fun inputLockTime(unspentOutput: UnspentOutput): Long {\n        // Use (an approximate medianTimePast of a block in which given transaction is included) PLUS ~1 hour.\n        // This is not an accurate medianTimePast, it is always a timestamp nearly 7 blocks ahead.\n        // But this is quite enough in our case since we're setting relative time-locks for at least 1 month\n        val previousOutputMedianTime = unspentOutput.transaction.timestamp\n\n        val lockTimeInterval = lockTimeIntervalFrom(unspentOutput.output)\n\n        return previousOutputMedianTime + lockTimeInterval.valueInSeconds\n    }\n\n    class UnsupportedAddressType : Exception()\n}\n"
  },
  {
    "path": "hodler/src/main/kotlin/io/horizontalsystems/hodler/LockTimeInterval.kt",
    "content": "package io.horizontalsystems.hodler\n\nimport io.horizontalsystems.bitcoincore.utils.Utils\n\nenum class LockTimeInterval(private val value: Int) {\n    hour(7),\n    month(5063),     //  30 * 24 * 60 * 60 / 512\n    halfYear(30881), // 183 * 24 * 60 * 60 / 512\n    year(61593);     // 365 * 24 * 60 * 60 / 512\n\n    private val sequenceTimeSecondsGranularity = 512\n    private val relativeLockTimeLockMask = 0x400000 // (1 << 22)\n\n    // need to write to extra data output as 2 bytes\n    val valueAs2BytesLE: ByteArray = Utils.intToByteArray(value).reversedArray().copyOfRange(0, 2)\n    val valueInSeconds: Int = value * sequenceTimeSecondsGranularity\n\n    val sequenceNumber: Int = relativeLockTimeLockMask or value\n    val sequenceNumberAs3BytesLE: ByteArray = Utils.intToByteArray(sequenceNumber).reversedArray().copyOfRange(0, 3)\n\n    fun serialize(): String {\n        return value.toString()\n    }\n\n    companion object {\n        fun deserialize(serialized: String): LockTimeInterval? {\n            return fromValue(serialized.toInt())\n        }\n\n        fun from2BytesLE(bytes: ByteArray): LockTimeInterval? {\n            if (bytes.size != 2) return null\n\n            return fromValue(Utils.byteArrayToUInt16LE(bytes))\n        }\n\n        private fun fromValue(value: Int): LockTimeInterval? {\n            return values().find {\n                it.value == value\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "hodler/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">Hodler</string>\n</resources>\n"
  },
  {
    "path": "hodler/src/test/java/io/horizontalsystems/hodler/HodlerPluginTest.kt",
    "content": "package io.horizontalsystems.hodler\n\nimport com.nhaarman.mockitokotlin2.doReturn\nimport com.nhaarman.mockitokotlin2.mock\nimport com.nhaarman.mockitokotlin2.verify\nimport com.nhaarman.mockitokotlin2.whenever\nimport io.horizontalsystems.bitcoincore.blocks.BlockMedianTimeHelper\nimport io.horizontalsystems.bitcoincore.core.IPluginData\nimport io.horizontalsystems.bitcoincore.core.IStorage\nimport io.horizontalsystems.bitcoincore.extensions.hexToByteArray\nimport io.horizontalsystems.bitcoincore.models.Address\nimport io.horizontalsystems.bitcoincore.models.PublicKey\nimport io.horizontalsystems.bitcoincore.models.Transaction\nimport io.horizontalsystems.bitcoincore.models.TransactionOutput\nimport io.horizontalsystems.bitcoincore.storage.FullTransaction\nimport io.horizontalsystems.bitcoincore.storage.UnspentOutput\nimport io.horizontalsystems.bitcoincore.transactions.builder.MutableTransaction\nimport io.horizontalsystems.bitcoincore.transactions.scripts.Script\nimport io.horizontalsystems.bitcoincore.transactions.scripts.ScriptType\nimport io.horizontalsystems.bitcoincore.utils.IAddressConverter\nimport org.junit.Assert\nimport org.junit.Before\nimport org.junit.Test\nimport org.junit.jupiter.api.assertThrows\n\nclass HodlerPluginTest {\n\n    private lateinit var addressConverter: IAddressConverter\n    private lateinit var storage: IStorage\n    private lateinit var blockMedianTimeHelper: BlockMedianTimeHelper\n    private lateinit var hodlerPlugin: HodlerPlugin\n\n    private lateinit var mutableTransaction: MutableTransaction\n    private lateinit var recipientAddress: Address\n\n    @Before\n    fun setup() {\n        addressConverter = mock()\n        storage = mock()\n        blockMedianTimeHelper = mock()\n        hodlerPlugin = HodlerPlugin(addressConverter, storage, blockMedianTimeHelper)\n\n        mutableTransaction = mock()\n        recipientAddress = mock()\n    }\n\n    @Test\n    fun processOutputs_dataIsNotHodlerData() {\n        val pluginData = mock<IPluginData>()\n\n        assertThrows<IllegalStateException> {\n            hodlerPlugin.processOutputs(mutableTransaction, pluginData, false)\n        }\n    }\n\n    @Test\n    fun processOutputs_scriptTypeNotP2PKH() {\n        val pluginData = HodlerData(LockTimeInterval.hour)\n\n        whenever(mutableTransaction.recipientAddress).thenReturn(recipientAddress)\n        whenever(recipientAddress.scriptType).thenReturn(ScriptType.P2SH)\n\n        assertThrows<java.lang.IllegalStateException> {\n            hodlerPlugin.processOutputs(mutableTransaction, pluginData, false)\n        }\n    }\n\n    val pubkeyHash = \"8c005bb22d520f6a108b108242efcbe5c19315f5\".hexToByteArray()\n    val redeemScriptHash = \"281df2e2e47141575bd98686a4f0452bcc4c7147\".hexToByteArray()\n\n    @Test\n    fun processOutputs_success() {\n        val pluginData = HodlerData(LockTimeInterval.hour)\n\n        val shAddress = mock<Address>()\n\n        whenever(mutableTransaction.recipientAddress).thenReturn(recipientAddress)\n        whenever(recipientAddress.scriptType).thenReturn(ScriptType.P2PKH)\n        whenever(recipientAddress.hash).thenReturn(pubkeyHash)\n        whenever(addressConverter.convert(redeemScriptHash, ScriptType.P2SH)).thenReturn(shAddress)\n\n        hodlerPlugin.processOutputs(mutableTransaction, pluginData, false)\n\n        verify(addressConverter).convert(redeemScriptHash, ScriptType.P2SH)\n        verify(mutableTransaction).recipientAddress = shAddress\n        verify(mutableTransaction).addPluginData(\n            HodlerPlugin.id,\n            (\"02\" + \"0700\" + \"14\" + \"8c005bb22d520f6a108b108242efcbe5c19315f5\").hexToByteArray()\n        )\n    }\n\n    val fullTransaction = mock<FullTransaction>()\n    val chunkLockTimeInterval = mock<Script.Chunk>()\n    val chunkPubkeyHash = mock<Script.Chunk>()\n    val nullDataChunks = listOf(chunkLockTimeInterval, chunkPubkeyHash).iterator()\n\n    @Test\n    fun processTransactionWithNullData_noRequiredData() {\n        assertThrows<IllegalStateException> {\n            hodlerPlugin.processTransactionWithNullData(fullTransaction, nullDataChunks)\n        }\n    }\n\n    @Test\n    fun processTransactionWithNullData_success() {\n        val recipientOutput = mock<TransactionOutput>()\n        val originalAddress = mock<Address>()\n        val originalAddressString = \"originalAddress\"\n        val publicKey = mock<PublicKey>()\n        val redeemScript = \"03070040b27576a9148c005bb22d520f6a108b108242efcbe5c19315f588ac\".hexToByteArray()\n        val transaction = mock<Transaction>()\n\n        whenever(chunkLockTimeInterval.data).thenReturn(\"0700\".hexToByteArray())\n        whenever(chunkPubkeyHash.data).thenReturn(pubkeyHash)\n        whenever(fullTransaction.outputs).thenReturn(listOf(recipientOutput))\n        whenever(recipientOutput.lockingScriptPayload).thenReturn(redeemScriptHash)\n        whenever(addressConverter.convert(pubkeyHash, ScriptType.P2PKH)).thenReturn(originalAddress)\n        whenever(originalAddress.string).thenReturn(originalAddressString)\n        whenever(storage.getPublicKeyByKeyOrKeyHash(pubkeyHash)).thenReturn(publicKey)\n        whenever(publicKey.path).thenReturn(\"publicKey.path\")\n        whenever(fullTransaction.header).thenReturn(transaction)\n\n        hodlerPlugin.processTransactionWithNullData(fullTransaction, nullDataChunks)\n\n        verify(addressConverter).convert(pubkeyHash, ScriptType.P2PKH)\n        verify(recipientOutput).pluginId = HodlerPlugin.id\n        verify(recipientOutput).pluginData = \"7|originalAddress\"\n        verify(storage).getPublicKeyByKeyOrKeyHash(pubkeyHash)\n        verify(recipientOutput).redeemScript = redeemScript\n        verify(recipientOutput).setPublicKey(publicKey)\n        verify(transaction).isMine = true\n    }\n\n    @Test\n    fun isSpendable_nullLastBlockMedianTimePast() {\n        whenever(blockMedianTimeHelper.medianTimePast).thenReturn(null)\n\n        Assert.assertFalse(hodlerPlugin.isSpendable(mock()))\n    }\n\n    @Test\n    fun isSpendable() {\n        assertIsSpendable(100, 3700, true)\n        assertIsSpendable(200, 3100, false)\n    }\n\n    private fun assertIsSpendable(lockedAtTimestamp: Long, lastBlockMedianTimestamp: Long, isSpendable: Boolean) {\n        val unspentOutput = mock<UnspentOutput>()\n        val transactionOutput = mock<TransactionOutput>()\n        val transaction = mock<Transaction>()\n\n        whenever(blockMedianTimeHelper.medianTimePast).thenReturn(lastBlockMedianTimestamp)\n        whenever(unspentOutput.transaction).thenReturn(transaction)\n        whenever(transaction.timestamp).thenReturn(lockedAtTimestamp)\n        whenever(unspentOutput.output).thenReturn(transactionOutput)\n        whenever(transactionOutput.pluginData).thenReturn(\"7|originalAddress\")\n\n        Assert.assertEquals(isSpendable, hodlerPlugin.isSpendable(unspentOutput))\n    }\n\n    @Test\n    fun getInputSequence() {\n        val pluginData = \"7|originalAddress\"\n        val expectedSequence = 0x400007L\n\n        val output = mock<TransactionOutput>()\n        whenever(output.pluginData).thenReturn(pluginData)\n\n        val inputSequence = hodlerPlugin.getInputSequence(output)\n        Assert.assertEquals(expectedSequence, inputSequence)\n    }\n\n    @Test\n    fun parsePluginData() {\n        val pluginData = \"7|originalAddress\"\n\n        val output = mock<TransactionOutput>()\n        whenever(output.pluginData).thenReturn(pluginData)\n\n        val parsePluginData = hodlerPlugin.parsePluginData(output, 1000)\n\n        check(parsePluginData is HodlerOutputData)\n        Assert.assertEquals(LockTimeInterval.hour, parsePluginData.lockTimeInterval)\n        Assert.assertEquals(\"originalAddress\", parsePluginData.addressString)\n        Assert.assertEquals(7 * 512 + 1000 + 3600L, parsePluginData.approxUnlockTime)\n    }\n\n    @Test\n    fun keysForApiRestore() {\n        val publicKey = mock<PublicKey>()\n\n        val addresses = List(4) { index ->\n            mock<Address> {\n                on { string } doReturn \"address${index}\"\n            }\n        }\n\n        whenever(publicKey.publicKeyHash).thenReturn(pubkeyHash)\n        whenever(addressConverter.convert(\"281df2e2e47141575bd98686a4f0452bcc4c7147\".hexToByteArray(), ScriptType.P2SH)).thenReturn(addresses[0])\n        whenever(addressConverter.convert(\"54de17bd15b20e9981e78338c514e73f732a4d48\".hexToByteArray(), ScriptType.P2SH)).thenReturn(addresses[1])\n        whenever(addressConverter.convert(\"932db9d114e7809dfb09104488dce27c50e588fc\".hexToByteArray(), ScriptType.P2SH)).thenReturn(addresses[2])\n        whenever(addressConverter.convert(\"77bcc35ca78676af60334c96879b96d8599a2e9c\".hexToByteArray(), ScriptType.P2SH)).thenReturn(addresses[3])\n\n        val result = hodlerPlugin.keysForApiRestore(publicKey)\n\n        Assert.assertArrayEquals(arrayOf(\"address0\", \"address1\", \"address2\", \"address3\"), result.toTypedArray())\n    }\n}\n"
  },
  {
    "path": "hodler/src/test/resources/mockito-extensions/org.mockito.plugins.MockMaker",
    "content": "mock-maker-inline"
  },
  {
    "path": "litecoinkit/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "litecoinkit/build.gradle",
    "content": "plugins {\n    id 'com.android.library'\n    id 'kotlin-android'\n    id 'kotlin-kapt'\n    id 'maven-publish'\n}\n\nafterEvaluate {\n    publishing {\n        publications {\n            release(MavenPublication) {\n                from components.release\n            }\n        }\n    }\n}\n\nandroid {\n    namespace 'io.horizontalsystems.litecoinkit'\n    compileSdk 34\n\n    defaultConfig {\n        minSdkVersion 23\n        targetSdkVersion 34\n\n        testInstrumentationRunner \"androidx.test.runner.AndroidJUnitRunner\"\n        consumerProguardFiles 'consumer-rules.pro'\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n\n    sourceSets {\n        main.java.srcDirs += 'src/main/kotlin'\n        test.java.srcDirs += 'src/test/kotlin'\n    }\n\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_17\n        targetCompatibility JavaVersion.VERSION_17\n    }\n    kotlinOptions { jvmTarget = '17' }\n\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version\"\n    implementation 'androidx.annotation:annotation:1.1.0'\n\n    // Room\n    implementation 'androidx.room:room-runtime:2.5.0'\n    implementation 'androidx.room:room-rxjava2:2.5.0'\n    kapt 'androidx.room:room-compiler:2.5.0'\n\n    api project(':bitcoincore')\n    api project(':hodler')\n\n    testImplementation 'junit:junit:4.13.2'\n    androidTestImplementation 'androidx.test.ext:junit:1.1.1'\n    androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'\n}\n"
  },
  {
    "path": "litecoinkit/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "litecoinkit/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "litecoinkit/src/androidTest/java/io/horizontalsystems/litecoinkit/ExampleInstrumentedTest.kt",
    "content": "package io.horizontalsystems.litecoinkit\n\nimport androidx.test.platform.app.InstrumentationRegistry\nimport androidx.test.ext.junit.runners.AndroidJUnit4\nimport org.junit.Assert.assertEquals\nimport org.junit.Test\nimport org.junit.runner.RunWith\n\n/**\n * Instrumented test, which will execute on an Android device.\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\n@RunWith(AndroidJUnit4::class)\nclass ExampleInstrumentedTest {\n    @Test\n    fun useAppContext() {\n        // Context of the app under test.\n        val appContext = InstrumentationRegistry.getInstrumentation().targetContext\n        assertEquals(\"io.horizontalsystems.litecoinkit.test\", appContext.packageName)\n    }\n}\n"
  },
  {
    "path": "litecoinkit/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" />\n"
  },
  {
    "path": "litecoinkit/src/main/kotlin/io/horizontalsystems/litecoinkit/LitecoinKit.kt",
    "content": "package io.horizontalsystems.litecoinkit\n\nimport android.content.Context\nimport android.database.sqlite.SQLiteDatabase\nimport io.horizontalsystems.bitcoincore.AbstractKit\nimport io.horizontalsystems.bitcoincore.BitcoinCore\nimport io.horizontalsystems.bitcoincore.BitcoinCore.SyncMode\nimport io.horizontalsystems.bitcoincore.BitcoinCoreBuilder\nimport io.horizontalsystems.bitcoincore.apisync.BCoinApi\nimport io.horizontalsystems.bitcoincore.apisync.blockchair.BlockchairApi\nimport io.horizontalsystems.bitcoincore.apisync.blockchair.BlockchairBlockHashFetcher\nimport io.horizontalsystems.bitcoincore.apisync.blockchair.BlockchairTransactionProvider\nimport io.horizontalsystems.bitcoincore.blocks.validators.BitsValidator\nimport io.horizontalsystems.bitcoincore.blocks.validators.BlockValidatorChain\nimport io.horizontalsystems.bitcoincore.blocks.validators.BlockValidatorSet\nimport io.horizontalsystems.bitcoincore.blocks.validators.LegacyTestNetDifficultyValidator\nimport io.horizontalsystems.bitcoincore.core.purpose\nimport io.horizontalsystems.bitcoincore.managers.ApiSyncStateManager\nimport io.horizontalsystems.bitcoincore.managers.Bip44RestoreKeyConverter\nimport io.horizontalsystems.bitcoincore.managers.Bip49RestoreKeyConverter\nimport io.horizontalsystems.bitcoincore.managers.Bip84RestoreKeyConverter\nimport io.horizontalsystems.bitcoincore.managers.Bip86RestoreKeyConverter\nimport io.horizontalsystems.bitcoincore.managers.BlockValidatorHelper\nimport io.horizontalsystems.bitcoincore.models.Address\nimport io.horizontalsystems.bitcoincore.models.Checkpoint\nimport io.horizontalsystems.bitcoincore.models.WatchAddressPublicKey\nimport io.horizontalsystems.bitcoincore.network.Network\nimport io.horizontalsystems.bitcoincore.storage.CoreDatabase\nimport io.horizontalsystems.bitcoincore.storage.Storage\nimport io.horizontalsystems.bitcoincore.utils.AddressConverterChain\nimport io.horizontalsystems.bitcoincore.utils.Base58AddressConverter\nimport io.horizontalsystems.bitcoincore.utils.PaymentAddressParser\nimport io.horizontalsystems.bitcoincore.utils.SegwitAddressConverter\nimport io.horizontalsystems.hdwalletkit.HDExtendedKey\nimport io.horizontalsystems.hdwalletkit.HDWallet.Purpose\nimport io.horizontalsystems.hdwalletkit.Mnemonic\nimport io.horizontalsystems.litecoinkit.validators.LegacyDifficultyAdjustmentValidator\nimport io.horizontalsystems.litecoinkit.validators.ProofOfWorkValidator\n\nclass LitecoinKit : AbstractKit {\n    enum class NetworkType {\n        MainNet,\n        TestNet\n    }\n\n    interface Listener : BitcoinCore.Listener\n\n    override var bitcoinCore: BitcoinCore\n    override var network: Network\n\n    var listener: Listener? = null\n        set(value) {\n            field = value\n            bitcoinCore.listener = value\n        }\n\n    constructor(\n        context: Context,\n        words: List<String>,\n        passphrase: String,\n        walletId: String,\n        networkType: NetworkType = defaultNetworkType,\n        peerSize: Int = defaultPeerSize,\n        syncMode: SyncMode = defaultSyncMode,\n        confirmationsThreshold: Int = defaultConfirmationsThreshold,\n        purpose: Purpose = Purpose.BIP44\n    ) : this(context, Mnemonic().toSeed(words, passphrase), walletId, networkType, peerSize, syncMode, confirmationsThreshold, purpose)\n\n    constructor(\n        context: Context,\n        seed: ByteArray,\n        walletId: String,\n        networkType: NetworkType = defaultNetworkType,\n        peerSize: Int = defaultPeerSize,\n        syncMode: SyncMode = defaultSyncMode,\n        confirmationsThreshold: Int = defaultConfirmationsThreshold,\n        purpose: Purpose = Purpose.BIP44\n    ) : this(context, HDExtendedKey(seed, purpose), purpose, walletId, networkType, peerSize, syncMode, confirmationsThreshold)\n\n    /**\n     * @constructor Creates and initializes the BitcoinKit\n     * @param context The Android context\n     * @param extendedKey HDExtendedKey that contains HDKey and version\n     * @param walletId an arbitrary ID of type String.\n     * @param networkType The network type. The default is MainNet.\n     * @param peerSize The # of peer-nodes required. The default is 10 peers.\n     * @param syncMode How the kit syncs with the blockchain. The default is SyncMode.Api().\n     * @param confirmationsThreshold How many confirmations required to be considered confirmed. The default is 6 confirmations.\n     */\n    constructor(\n        context: Context,\n        extendedKey: HDExtendedKey,\n        purpose: Purpose,\n        walletId: String,\n        networkType: NetworkType = defaultNetworkType,\n        peerSize: Int = defaultPeerSize,\n        syncMode: SyncMode = defaultSyncMode,\n        confirmationsThreshold: Int = defaultConfirmationsThreshold\n    ) {\n        network = network(networkType)\n\n        bitcoinCore = bitcoinCore(\n            context = context,\n            extendedKey = extendedKey,\n            watchAddressPublicKey = null,\n            networkType = networkType,\n            walletId = walletId,\n            syncMode = syncMode,\n            purpose = purpose,\n            peerSize = peerSize,\n            confirmationsThreshold = confirmationsThreshold\n        )\n    }\n\n    /**\n     * @constructor Creates and initializes the BitcoinKit\n     * @param context The Android context\n     * @param watchAddress address for watching in read-only mode\n     * @param walletId an arbitrary ID of type String.\n     * @param networkType The network type. The default is MainNet.\n     * @param peerSize The # of peer-nodes required. The default is 10 peers.\n     * @param syncMode How the kit syncs with the blockchain. The default is SyncMode.Api().\n     * @param confirmationsThreshold How many confirmations required to be considered confirmed. The default is 6 confirmations.\n     */\n    constructor(\n        context: Context,\n        watchAddress: String,\n        walletId: String,\n        networkType: NetworkType = defaultNetworkType,\n        peerSize: Int = defaultPeerSize,\n        syncMode: SyncMode = defaultSyncMode,\n        confirmationsThreshold: Int = defaultConfirmationsThreshold\n    ) {\n        network = network(networkType)\n\n        val address = parseAddress(watchAddress, network)\n        val watchAddressPublicKey = WatchAddressPublicKey(address.lockingScriptPayload, address.scriptType)\n        val purpose = address.scriptType.purpose ?: throw IllegalStateException(\"Not supported scriptType ${address.scriptType}\")\n\n        bitcoinCore = bitcoinCore(\n            context = context,\n            extendedKey = null,\n            watchAddressPublicKey = watchAddressPublicKey,\n            networkType = networkType,\n            walletId = walletId,\n            syncMode = syncMode,\n            purpose = purpose,\n            peerSize = peerSize,\n            confirmationsThreshold = confirmationsThreshold\n        )\n    }\n\n    private fun bitcoinCore(\n        context: Context,\n        extendedKey: HDExtendedKey?,\n        watchAddressPublicKey: WatchAddressPublicKey?,\n        networkType: NetworkType,\n        walletId: String,\n        syncMode: SyncMode,\n        purpose: Purpose,\n        peerSize: Int,\n        confirmationsThreshold: Int\n    ): BitcoinCore {\n        val database = CoreDatabase.getInstance(context, getDatabaseName(networkType, walletId, syncMode, purpose))\n        val storage = Storage(database)\n        val checkpoint = Checkpoint.resolveCheckpoint(syncMode, network, storage)\n        val apiSyncStateManager = ApiSyncStateManager(storage, network.syncableFromApi && syncMode !is SyncMode.Full)\n        val blockchairApi = BlockchairApi(network.blockchairChainId)\n        val apiTransactionProvider = apiTransactionProvider(networkType, blockchairApi)\n        val paymentAddressParser = PaymentAddressParser(\"litecoin\", removeScheme = true)\n        val blockValidatorSet = blockValidatorSet(storage, networkType)\n\n        val coreBuilder = BitcoinCoreBuilder()\n\n        val bitcoinCore = coreBuilder\n            .setContext(context)\n            .setExtendedKey(extendedKey)\n            .setWatchAddressPublicKey(watchAddressPublicKey)\n            .setPurpose(purpose)\n            .setNetwork(network)\n            .setCheckpoint(checkpoint)\n            .setPaymentAddressParser(paymentAddressParser)\n            .setPeerSize(peerSize)\n            .setSyncMode(syncMode)\n            .setSendType(BitcoinCore.SendType.API(blockchairApi))\n            .setConfirmationThreshold(confirmationsThreshold)\n            .setStorage(storage)\n            .setApiTransactionProvider(apiTransactionProvider)\n            .setApiSyncStateManager(apiSyncStateManager)\n            .setBlockValidator(blockValidatorSet)\n            .build()\n\n        //  extending bitcoinCore\n\n        val bech32AddressConverter = SegwitAddressConverter(network.addressSegwitHrp)\n        val base58AddressConverter = Base58AddressConverter(network.addressVersion, network.addressScriptVersion)\n\n        bitcoinCore.prependAddressConverter(bech32AddressConverter)\n\n        when (purpose) {\n            Purpose.BIP44 -> {\n                bitcoinCore.addRestoreKeyConverter(Bip44RestoreKeyConverter(base58AddressConverter))\n            }\n\n            Purpose.BIP49 -> {\n                bitcoinCore.addRestoreKeyConverter(Bip49RestoreKeyConverter(base58AddressConverter))\n            }\n\n            Purpose.BIP84 -> {\n                bitcoinCore.addRestoreKeyConverter(Bip84RestoreKeyConverter(SegwitAddressConverter(network.addressSegwitHrp)))\n            }\n\n            Purpose.BIP86 -> {\n                bitcoinCore.addRestoreKeyConverter(Bip86RestoreKeyConverter(SegwitAddressConverter(network.addressSegwitHrp)))\n            }\n        }\n\n        return bitcoinCore\n    }\n\n    private fun parseAddress(address: String, network: Network): Address {\n        val addressConverter = AddressConverterChain().apply {\n            prependConverter(SegwitAddressConverter(network.addressSegwitHrp))\n            prependConverter(Base58AddressConverter(network.addressVersion, network.addressScriptVersion))\n        }\n        return addressConverter.convert(address)\n    }\n\n    private fun blockValidatorSet(\n        storage: Storage,\n        networkType: NetworkType\n    ): BlockValidatorSet {\n        val blockValidatorSet = BlockValidatorSet()\n\n        val proofOfWorkValidator = ProofOfWorkValidator(ScryptHasher())\n        blockValidatorSet.addBlockValidator(proofOfWorkValidator)\n\n        val blockValidatorChain = BlockValidatorChain()\n\n        val blockHelper = BlockValidatorHelper(storage)\n\n        if (networkType == NetworkType.MainNet) {\n            blockValidatorChain.add(LegacyDifficultyAdjustmentValidator(blockHelper, heightInterval, targetTimespan, maxTargetBits))\n            blockValidatorChain.add(BitsValidator())\n        } else if (networkType == NetworkType.TestNet) {\n            blockValidatorChain.add(LegacyDifficultyAdjustmentValidator(blockHelper, heightInterval, targetTimespan, maxTargetBits))\n            blockValidatorChain.add(LegacyTestNetDifficultyValidator(storage, heightInterval, targetSpacing, maxTargetBits))\n            blockValidatorChain.add(BitsValidator())\n        }\n\n        blockValidatorSet.addBlockValidator(blockValidatorChain)\n        return blockValidatorSet\n    }\n\n    private fun apiTransactionProvider(\n        networkType: NetworkType,\n        blockchairApi: BlockchairApi\n    ) = when (networkType) {\n        NetworkType.MainNet -> {\n            val blockchairBlockHashFetcher = BlockchairBlockHashFetcher(blockchairApi)\n            BlockchairTransactionProvider(blockchairApi, blockchairBlockHashFetcher)\n        }\n\n        NetworkType.TestNet -> {\n            BCoinApi(\"\")\n        }\n    }\n\n    companion object {\n\n        const val maxTargetBits: Long = 0x1e0fffff      // Maximum difficulty\n        const val targetSpacing = 150                   // 2.5 minutes per block.\n        const val targetTimespan: Long = 302400         // 3.5 days per difficulty cycle, on average.\n        const val heightInterval = targetTimespan / targetSpacing // 2016 blocks\n\n        val defaultNetworkType: NetworkType = NetworkType.MainNet\n        val defaultSyncMode: SyncMode = SyncMode.Api()\n        const val defaultPeerSize: Int = 10\n        const val defaultConfirmationsThreshold: Int = 6\n\n        private fun getDatabaseName(networkType: NetworkType, walletId: String, syncMode: SyncMode, purpose: Purpose): String =\n            \"Litecoin-${networkType.name}-$walletId-${syncMode.javaClass.simpleName}-${purpose.name}\"\n\n        fun clear(context: Context, networkType: NetworkType, walletId: String) {\n            for (syncMode in listOf(SyncMode.Api(), SyncMode.Full(), SyncMode.Blockchair())) {\n                for (purpose in Purpose.values())\n                    try {\n                        SQLiteDatabase.deleteDatabase(context.getDatabasePath(getDatabaseName(networkType, walletId, syncMode, purpose)))\n                    } catch (ex: Exception) {\n                        continue\n                    }\n            }\n        }\n\n        private fun network(networkType: NetworkType) = when (networkType) {\n            NetworkType.MainNet -> MainNetLitecoin()\n            NetworkType.TestNet -> TestNetLitecoin()\n        }\n\n        private fun addressConverter(purpose: Purpose, network: Network): AddressConverterChain {\n            val addressConverter = AddressConverterChain()\n            when (purpose) {\n                Purpose.BIP44,\n                Purpose.BIP49 -> {\n                    addressConverter.prependConverter(\n                        Base58AddressConverter(network.addressVersion, network.addressScriptVersion)\n                    )\n                }\n\n                Purpose.BIP84,\n                Purpose.BIP86 -> {\n                    addressConverter.prependConverter(\n                        SegwitAddressConverter(network.addressSegwitHrp)\n                    )\n                }\n            }\n\n            return addressConverter\n        }\n\n        fun firstAddress(\n            seed: ByteArray,\n            purpose: Purpose,\n            networkType: NetworkType = NetworkType.MainNet,\n        ): Address {\n            return BitcoinCore.firstAddress(\n                seed,\n                purpose,\n                network(networkType),\n                addressConverter(purpose, network(networkType))\n            )\n        }\n\n        fun firstAddress(\n            extendedKey: HDExtendedKey,\n            purpose: Purpose,\n            networkType: NetworkType = NetworkType.MainNet,\n        ): Address {\n            return BitcoinCore.firstAddress(\n                extendedKey,\n                purpose,\n                network(networkType),\n                addressConverter(purpose, network(networkType))\n            )\n        }\n    }\n\n}\n"
  },
  {
    "path": "litecoinkit/src/main/kotlin/io/horizontalsystems/litecoinkit/MainNetLitecoin.kt",
    "content": "package io.horizontalsystems.litecoinkit\n\nimport io.horizontalsystems.bitcoincore.network.Network\n\nclass MainNetLitecoin : Network() {\n    override val protocolVersion: Int = 70015\n    override var port: Int = 9333\n\n    override var magic: Long = 0xdbb6c0fb\n    override var bip32HeaderPub: Int = 0x0488B21E   // The 4 byte header that serializes in base58 to \"xpub\".\n    override var bip32HeaderPriv: Int = 0x0488ADE4  // The 4 byte header that serializes in base58 to \"xprv\"\n    override var addressVersion: Int = 0x30\n    override var addressSegwitHrp: String = \"ltc\"\n    override var addressScriptVersion: Int = 0x32\n    override var coinType: Int = 2\n    override val blockchairChainId: String = \"litecoin\"\n\n    override val maxBlockSize = 1_000_000\n    override val dustRelayTxFee = 3000 // https://github.com/bitcoin/bitcoin/blob/c536dfbcb00fb15963bf5d507b7017c241718bf6/src/policy/policy.h#L50\n\n    override val syncableFromApi = true\n\n    override var dnsSeeds = listOf(\n        \"seed-a.litecoin.loshan.co.uk\",  // Loshan - official, trusted\n        \"dnsseed.thrasher.io\",           // Thrasher - official, trusted\n        \"dnsseed.litecoinpool.org\",      // Litecoin Pool\n    )\n}\n"
  },
  {
    "path": "litecoinkit/src/main/kotlin/io/horizontalsystems/litecoinkit/ScryptHasher.kt",
    "content": "package io.horizontalsystems.litecoinkit\n\nimport io.horizontalsystems.bitcoincore.core.IHasher\nimport io.horizontalsystems.bitcoincore.utils.HashUtils\n\nclass ScryptHasher : IHasher {\n\n    override fun hash(data: ByteArray): ByteArray {\n        return try {\n            HashUtils.scrypt(data, data, 1024, 1, 1, 32).reversedArray()\n        } catch (e: Exception) {\n            byteArrayOf()\n        }\n    }\n\n}\n"
  },
  {
    "path": "litecoinkit/src/main/kotlin/io/horizontalsystems/litecoinkit/TestNetLitecoin.kt",
    "content": "package io.horizontalsystems.litecoinkit\n\nimport io.horizontalsystems.bitcoincore.network.Network\n\nclass TestNetLitecoin : Network() {\n    override val protocolVersion: Int = 70015\n    override var port: Int = 19335\n\n    override var magic: Long = 0xf1c8d2fd\n    override var bip32HeaderPub: Int = 0x043587CF\n    override var bip32HeaderPriv: Int = 0x04358394\n    override var addressVersion: Int = 111\n    override var addressSegwitHrp: String = \"tltc\"\n    override var addressScriptVersion: Int = 0x32\n    override var coinType: Int = 1\n    override val blockchairChainId: String = \"\"\n\n    override val maxBlockSize = 1_000_000\n    override val dustRelayTxFee = 3000 // https://github.com/bitcoin/bitcoin/blob/c536dfbcb00fb15963bf5d507b7017c241718bf6/src/policy/policy.h#L50\n\n    override val syncableFromApi = false\n\n    override var dnsSeeds = listOf(\n            \"testnet-seed.ltc.xurious.com\",\n            \"seed-b.litecoin.loshan.co.uk\",\n            \"dnsseed-testnet.thrasher.io\"\n    )\n}\n"
  },
  {
    "path": "litecoinkit/src/main/kotlin/io/horizontalsystems/litecoinkit/validators/LegacyDifficultyAdjustmentValidator.kt",
    "content": "package io.horizontalsystems.litecoinkit.validators\n\nimport io.horizontalsystems.bitcoincore.blocks.validators.BlockValidatorException\nimport io.horizontalsystems.bitcoincore.blocks.validators.IBlockChainedValidator\nimport io.horizontalsystems.bitcoincore.crypto.CompactBits\nimport io.horizontalsystems.bitcoincore.managers.BlockValidatorHelper\nimport io.horizontalsystems.bitcoincore.models.Block\nimport java.math.BigInteger\nimport kotlin.math.min\n\nclass LegacyDifficultyAdjustmentValidator(\n        private val validatorHelper: BlockValidatorHelper,\n        private val heightInterval: Long,\n        private val targetTimespan: Long,\n        private val maxTargetBits: Long\n) : IBlockChainedValidator {\n\n    override fun isBlockValidatable(block: Block, previousBlock: Block): Boolean {\n        return block.height % heightInterval == 0L\n    }\n\n    override fun validate(block: Block, previousBlock: Block) {\n        val beforeLastCheckPointBlock = checkNotNull(validatorHelper.getPrevious(block, heightInterval.toInt() + 1)) {\n            BlockValidatorException.NoCheckpointBlock()\n        }\n\n        //  Limit the adjustment step\n        var timespan = previousBlock.timestamp - beforeLastCheckPointBlock.timestamp\n        if (timespan < targetTimespan / 4)\n            timespan = targetTimespan / 4\n        if (timespan > targetTimespan * 4)\n            timespan = targetTimespan * 4\n\n        var newTarget = CompactBits.decode(previousBlock.bits)\n        newTarget = newTarget.multiply(BigInteger.valueOf(timespan))\n        newTarget = newTarget.divide(BigInteger.valueOf(targetTimespan))\n\n        //  Difficulty hit proof of work limit: newTarget.toString(16)\n        val newDifficulty = min(CompactBits.encode(newTarget), maxTargetBits)\n\n        if (newDifficulty != block.bits) {\n            throw BlockValidatorException.NotDifficultyTransitionEqualBits()\n        }\n    }\n\n}\n"
  },
  {
    "path": "litecoinkit/src/main/kotlin/io/horizontalsystems/litecoinkit/validators/ProofOfWorkValidator.kt",
    "content": "package io.horizontalsystems.litecoinkit.validators\n\nimport io.horizontalsystems.bitcoincore.blocks.validators.BlockValidatorException\nimport io.horizontalsystems.bitcoincore.blocks.validators.IBlockChainedValidator\nimport io.horizontalsystems.bitcoincore.crypto.CompactBits\nimport io.horizontalsystems.bitcoincore.extensions.toHexString\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.litecoinkit.ScryptHasher\nimport java.math.BigInteger\n\nclass ProofOfWorkValidator(private val scryptHasher: ScryptHasher) : IBlockChainedValidator {\n\n    override fun validate(block: Block, previousBlock: Block) {\n        val blockHeaderData = getSerializedBlockHeader(block)\n\n        val powHash = scryptHasher.hash(blockHeaderData).toHexString()\n\n        check(BigInteger(powHash, 16) < CompactBits.decode(block.bits)) {\n            throw BlockValidatorException.InvalidProofOfWork()\n        }\n    }\n\n    private fun getSerializedBlockHeader(block: Block): ByteArray {\n        return BitcoinOutput()\n                .writeInt(block.version)\n                .write(block.previousBlockHash)\n                .write(block.merkleRoot)\n                .writeUnsignedInt(block.timestamp)\n                .writeUnsignedInt(block.bits)\n                .writeUnsignedInt(block.nonce)\n                .toByteArray()\n    }\n\n    override fun isBlockValidatable(block: Block, previousBlock: Block): Boolean {\n        return true\n    }\n\n}\n"
  },
  {
    "path": "litecoinkit/src/main/resources/MainNetLitecoin-bip44.checkpoint",
    "content": "02000000982f9bc89bf1a828f3472ec5b332b40c3e904336fdce82d39e881108f3106a639fec4363378ef3ace42b11af273bed070d6459f00025b249c1b448cd7e0486997b1f5853037a0c1b39bd90dda07508009f742a26e3a5c6deb6bbb7f73fd88ea21473ba6600bdb1863ead4f15e5056a25\n02000000a75c1531c307b047c759bc15b9988acb9b02d4c8d3071633de78dc98a373c47940eaaf37543281e9d64209d23f997313d218b1607bf2416e4cd42b6337f8ec17cd1e5853d0b10c1b069a92349f750800982f9bc89bf1a828f3472ec5b332b40c3e904336fdce82d39e881108f3106a63\n"
  },
  {
    "path": "litecoinkit/src/main/resources/MainNetLitecoin.checkpoint",
    "content": "00000020c0f8b976bc8164f4a9b3f438d8f0c80f29e4d6a5119f0e35f5f59564ab09b13f4d1da5a27878bc290dbfdec2fe712f1b2b1be9ebc96666e59da699c9427ff451698fc86993972b193dd0565300012f00526822cd95e18a6db0c715fbabefb7e02385bcaed5a2b8f0d5225a83e1e1c22a\n14000020ab11d146680e8c900aefea4ddbd1c4edf3f026abe4db0fea490c576aa4bdb2b3f19b9d0e487a8e6f1637c1ecbec8f8fa35fe3a29217e4e025b22702a4677fd73338fc869fc342e196c12421aff002f00c0f8b976bc8164f4a9b3f438d8f0c80f29e4d6a5119f0e35f5f59564ab09b13f\n"
  },
  {
    "path": "litecoinkit/src/main/resources/TestNetLitecoin-bip44.checkpoint",
    "content": "03000020e5139533e5d19576f69c1ba6c2a3dcdb2f25d38a03b7b181ad9b9e463439080adddcf013d87d13dc5a945b7f3ccc7f35aff69b0164e0313e4b24257d0c90cb8a264ea2580257041e0006e356e007000015c0dcbb2e6b9280f7f93daff368f7b2243a3d04b3c18da574b103d500459a5f\n00000020cf8fdf87e046e6320f77d07f0ee1bfc289d524691af67373e7e5a23a68c5f9c573b4c778d63094be9b8ab63bd958705b4814702deabb97ac409cc71b4bde086d5f4ca258f0ff0f1e000b5ac7df070000e5139533e5d19576f69c1ba6c2a3dcdb2f25d38a03b7b181ad9b9e463439080a\n"
  },
  {
    "path": "litecoinkit/src/main/resources/TestNetLitecoin.checkpoint",
    "content": "000000204f6f64abec2fd95e42c06a8fcfcc4d548e9ef92519ad0e80b0366342958e943dbd8cfbf0e31f74308de69e636ef828f35b323614242a46ef130f86f1be212f09cb393a63c4830d1e95360000c07b24006aff45116256131ad94b2870374284d65e10ea5d6889d9cbc7facd092df9013a\n"
  },
  {
    "path": "litecoinkit/src/test/java/io/horizontalsystems/litecoinkit/ExampleUnitTest.kt",
    "content": "package io.horizontalsystems.litecoinkit\n\nimport org.junit.Assert.assertEquals\nimport org.junit.Test\n\n/**\n * Example local unit test, which will execute on the development machine (host).\n *\n * See [testing documentation](http://d.android.com/tools/testing).\n */\nclass ExampleUnitTest {\n    @Test\n    fun addition_isCorrect() {\n        assertEquals(4, 2 + 2)\n    }\n}\n"
  },
  {
    "path": "litecoinkit/src/test/java/io/horizontalsystems/litecoinkit/ScryptHasherTest.kt",
    "content": "package io.horizontalsystems.litecoinkit\n\nimport io.horizontalsystems.bitcoincore.extensions.hexToByteArray\nimport io.horizontalsystems.bitcoincore.extensions.toHexString\nimport org.junit.Assert.assertEquals\nimport org.junit.Test\n\nclass ScryptHasherTest {\n\n    @Test\n    fun scryptHashing() {\n\n        val inputHex = listOf(\n                \"020000004c1271c211717198227392b029a64a7971931d351b387bb80db027f270411e398a07046f7d4a08dd815412a8712f874a7ebf0507e3878bd24e20a3b73fd750a667d2f451eac7471b00de6659\",\n                \"0200000011503ee6a855e900c00cfdd98f5f55fffeaee9b6bf55bea9b852d9de2ce35828e204eef76acfd36949ae56d1fbe81c1ac9c0209e6331ad56414f9072506a77f8c6faf551eac7471b00389d01\",\n                \"02000000a72c8a177f523946f42f22c3e86b8023221b4105e8007e59e81f6beb013e29aaf635295cb9ac966213fb56e046dc71df5b3f7f67ceaeab24038e743f883aff1aaafaf551eac7471b0166249b\",\n                \"010000007824bc3a8a1b4628485eee3024abd8626721f7f870f8ad4d2f33a27155167f6a4009d1285049603888fe85a84b6c803a53305a8d497965a5e896e1a00568359589faf551eac7471b0065434e\",\n                \"0200000050bfd4e4a307a8cb6ef4aef69abc5c0f2d579648bd80d7733e1ccc3fbc90ed664a7f74006cb11bde87785f229ecd366c2d4e44432832580e0608c579e4cb76f383f7f551eac7471b00c36982\"\n        )\n\n        val expectedHex = listOf(\n                \"00000000002bef4107f882f6115e0b01f348d21195dacd3582aa2dabd7985806\",\n                \"00000000003a0d11bdd5eb634e08b7feddcfbbf228ed35d250daf19f1c88fc94\",\n                \"00000000000b40f895f288e13244728a6c2d9d59d8aff29c65f8dd5114a8ca81\",\n                \"00000000003007005891cd4923031e99d8e8d72f6e8e7edc6a86181897e105fe\",\n                \"000000000018f0b426a4afc7130ccb47fa02af730d345b4fe7c7724d3800ec8c\"\n        )\n\n        val hasher = ScryptHasher()\n\n        inputHex.forEachIndexed { index, hexString ->\n            val hex = hexString.hexToByteArray()\n            val result = hasher.hash(hex).toHexString()\n\n            assertEquals(expectedHex[index], result)\n        }\n\n    }\n\n}\n"
  },
  {
    "path": "settings.gradle",
    "content": "include ':app',             // Demo app\n        ':bitcoincore',     // Bitcoin Core\n        ':bitcoinkit',      // Bitcoin kit\n        ':dashkit',         // Dash kit\n        ':bitcoincashkit',  // Bitcoin Cash kit\n        ':ecashkit',  // Bitcoin Cash kit\n        ':litecoinkit',     // Litecoin kit\n        ':hodler',          // Hodler\n        ':tools'            // Checkpoint syncer\n"
  },
  {
    "path": "tools/.gitignore",
    "content": "/build\n"
  },
  {
    "path": "tools/build.gradle",
    "content": "apply plugin: 'com.android.library'\napply plugin: 'kotlin-android'\napply plugin: 'kotlin-kapt'\n\nandroid {\n    compileSdkVersion 34\n\n    defaultConfig {\n        minSdkVersion 23\n        targetSdkVersion 34\n\n        consumerProguardFiles 'consumer-rules.pro'\n    }\n\n    buildTypes {\n        release {\n            minifyEnabled false\n            proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'\n        }\n    }\n    compileOptions {\n        sourceCompatibility JavaVersion.VERSION_17\n        targetCompatibility JavaVersion.VERSION_17\n    }\n    namespace 'io.horizontalsystems.tools'\n}\n\ndependencies {\n    implementation fileTree(dir: 'libs', include: ['*.jar'])\n    implementation \"org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version\"\n\n    implementation project(':bitcoincore')\n    implementation project(':bitcoinkit')\n    implementation project(':dashkit')\n    implementation project(':bitcoincashkit')\n    implementation project(':litecoinkit')\n    implementation project(':ecashkit')\n}\n"
  },
  {
    "path": "tools/consumer-rules.pro",
    "content": ""
  },
  {
    "path": "tools/proguard-rules.pro",
    "content": "# Add project specific ProGuard rules here.\n# You can control the set of applied configuration files using the\n# proguardFiles setting in build.gradle.\n#\n# For more details, see\n#   http://developer.android.com/guide/developing/tools/proguard.html\n\n# If your project uses WebView with JS, uncomment the following\n# and specify the fully qualified class name to the JavaScript interface\n# class:\n#-keepclassmembers class fqcn.of.javascript.interface.for.webview {\n#   public *;\n#}\n\n# Uncomment this to preserve the line number information for\n# debugging stack traces.\n#-keepattributes SourceFile,LineNumberTable\n\n# If you keep the line number information, uncomment this to\n# hide the original source file name.\n#-renamesourcefileattribute SourceFile\n"
  },
  {
    "path": "tools/src/main/AndroidManifest.xml",
    "content": "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\" />\n"
  },
  {
    "path": "tools/src/main/java/io/horizontalsystems/tools/BuildCheckpoints.kt",
    "content": "package io.horizontalsystems.tools\n\nimport io.horizontalsystems.bitcoincash.MainNetBitcoinCash\nimport io.horizontalsystems.bitcoincash.TestNetBitcoinCash\nimport io.horizontalsystems.bitcoincore.extensions.toHexString\nimport io.horizontalsystems.bitcoincore.io.BitcoinOutput\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.bitcoincore.network.Network\nimport io.horizontalsystems.bitcoinkit.MainNet\nimport io.horizontalsystems.bitcoinkit.TestNet\nimport io.horizontalsystems.dashkit.MainNetDash\nimport io.horizontalsystems.dashkit.TestNetDash\nimport io.horizontalsystems.ecash.MainNetECash\nimport io.horizontalsystems.litecoinkit.MainNetLitecoin\nimport io.horizontalsystems.litecoinkit.TestNetLitecoin\nimport java.io.File\nimport java.io.FileOutputStream\nimport java.io.OutputStream\nimport java.io.OutputStreamWriter\nimport java.io.PrintWriter\nimport java.io.Writer\nimport java.nio.ByteBuffer\nimport java.nio.charset.StandardCharsets\nimport kotlin.system.exitProcess\n\nclass BuildCheckpoints : CheckpointSyncer.Listener {\n\n    private val syncers = mutableListOf<CheckpointSyncer>().also {\n        // Bitcoin\n        it.add(CheckpointSyncer(MainNet(), 2016, 1, this))\n//        it.add(CheckpointSyncer(TestNet(), 2016, 1, this))\n\n        // Bitcoin Cash\n        it.add(CheckpointSyncer(MainNetBitcoinCash(), 147, 147, this))\n//        it.add(CheckpointSyncer(TestNetBitcoinCash(), 147, 147, this))\n\n        // Dash\n        it.add(CheckpointSyncer(MainNetDash(), 24, 24, this))\n//        it.add(CheckpointSyncer(TestNetDash(), 24, 24, this))\n\n        // Litecoin\n        it.add(CheckpointSyncer(MainNetLitecoin(), 2016, 2, this))\n//        it.add(CheckpointSyncer(TestNetLitecoin(), 2016, 2, this))\n\n        // Ecash\n        it.add(CheckpointSyncer(MainNetECash(), 147, 147, this))\n    }\n\n    fun build(checkpoint: Block) {\n        println(\" ================== CHECKPOINT ================== \")\n        println(serialize(checkpoint).toHexString())\n        println(\" ================================================ \")\n    }\n\n    fun sync() {\n        syncers.forEach { it.start() }\n    }\n\n    override fun onSync(network: Network, checkpoints: List<Block>) {\n        val networkName = network.javaClass.simpleName\n        val checkpointFile = \"${packagePath(network)}/src/main/resources/${networkName}.checkpoint\"\n\n        writeCheckpoints(checkpointFile, checkpoints)\n\n        println(\"Synced: ${syncers.count { it.isSynced }}\")\n        println(\"Remaining: ${syncers.count { !it.isSynced }}\")\n\n        if (syncers.none { !it.isSynced }) {\n            exitProcess(0)\n        }\n    }\n\n    // Writing to file\n\n    private fun writeCheckpoints(checkpointFile: String, checkpoints: List<Block>) {\n        val file = File(checkpointFile)\n        val fileOutputStream: OutputStream = FileOutputStream(file)\n        val outputStream: Writer = OutputStreamWriter(fileOutputStream, StandardCharsets.US_ASCII)\n\n        val buffer = ByteBuffer.allocate(80 + 4 + 32) // header + block height + block hash\n        val writer = PrintWriter(outputStream)\n\n        checkpoints.forEach {\n            buffer.put(serialize(it))\n            writer.println(buffer.array().toHexString())\n            buffer.clear()\n        }\n\n        writer.close()\n    }\n\n    private fun serialize(block: Block): ByteArray {\n        val payload = BitcoinOutput().also {\n            it.writeInt(block.version)\n            it.write(block.previousBlockHash)\n            it.write(block.merkleRoot)\n            it.writeUnsignedInt(block.timestamp)\n            it.writeUnsignedInt(block.bits)\n            it.writeUnsignedInt(block.nonce)\n            it.writeInt(block.height)\n            it.write(block.headerHash)\n        }\n\n        return payload.toByteArray()\n    }\n\n    private fun packagePath(network: Network): String {\n        return when (network) {\n            is MainNet,\n            is TestNet -> \"bitcoinkit\"\n            is MainNetBitcoinCash,\n            is TestNetBitcoinCash -> \"bitcoincashkit\"\n            is MainNetDash,\n            is TestNetDash -> \"dashkit\"\n            is MainNetLitecoin,\n            is TestNetLitecoin -> \"litecoinkit\"\n            is MainNetECash -> \"ecashkit\"\n            else -> throw Exception(\"Invalid network: ${network.javaClass.name}\")\n        }\n    }\n}\n"
  },
  {
    "path": "tools/src/main/java/io/horizontalsystems/tools/CheckpointSyncer.kt",
    "content": "package io.horizontalsystems.tools\n\nimport io.horizontalsystems.bitcoincore.core.DoubleSha256Hasher\nimport io.horizontalsystems.bitcoincore.core.IConnectionManager\nimport io.horizontalsystems.bitcoincore.core.IConnectionManagerListener\nimport io.horizontalsystems.bitcoincore.extensions.toReversedHex\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.bitcoincore.network.Network\nimport io.horizontalsystems.bitcoincore.network.messages.GetHeadersMessageSerializer\nimport io.horizontalsystems.bitcoincore.network.messages.HeadersMessageParser\nimport io.horizontalsystems.bitcoincore.network.messages.HeadersMessageSerializer\nimport io.horizontalsystems.bitcoincore.network.messages.InvMessageParser\nimport io.horizontalsystems.bitcoincore.network.messages.InvMessageSerializer\nimport io.horizontalsystems.bitcoincore.network.messages.NetworkMessageParser\nimport io.horizontalsystems.bitcoincore.network.messages.NetworkMessageSerializer\nimport io.horizontalsystems.bitcoincore.network.messages.PingMessageSerializer\nimport io.horizontalsystems.bitcoincore.network.messages.VerAckMessageParser\nimport io.horizontalsystems.bitcoincore.network.messages.VerAckMessageSerializer\nimport io.horizontalsystems.bitcoincore.network.messages.VersionMessageParser\nimport io.horizontalsystems.bitcoincore.network.messages.VersionMessageSerializer\nimport io.horizontalsystems.bitcoincore.network.peer.IPeerTaskHandler\nimport io.horizontalsystems.bitcoincore.network.peer.Peer\nimport io.horizontalsystems.bitcoincore.network.peer.PeerGroup\nimport io.horizontalsystems.bitcoincore.network.peer.PeerManager\nimport io.horizontalsystems.bitcoincore.network.peer.task.GetBlockHeadersTask\nimport io.horizontalsystems.bitcoincore.network.peer.task.PeerTask\nimport io.horizontalsystems.bitcoincore.storage.BlockHeader\nimport io.horizontalsystems.dashkit.MainNetDash\nimport io.horizontalsystems.dashkit.TestNetDash\nimport io.horizontalsystems.dashkit.X11Hasher\nimport java.util.LinkedList\nimport java.util.concurrent.Executors\n\nclass CheckpointSyncer(\n    private val network: Network,\n    private val checkpointInterval: Int,\n    private val blocksToKeep: Int,\n    private val listener: Listener)\n    : PeerGroup.Listener, IPeerTaskHandler {\n\n    interface Listener {\n        fun onSync(network: Network, checkpoints: List<Block>)\n    }\n\n    var isSynced: Boolean = false\n        private set\n\n    @Volatile\n    private var syncPeer: Peer? = null\n    private val peersQueue = Executors.newSingleThreadExecutor()\n    private val peerManager = PeerManager()\n\n    private val peerSize = 2\n    private val peerGroup: PeerGroup\n\n    private val lastCheckpointBlock = network.lastCheckpoint.block\n    private val checkpoints = mutableListOf(lastCheckpointBlock)\n    private val blocks = LinkedList(\n        (listOf(lastCheckpointBlock) + network.lastCheckpoint.additionalBlocks).reversed()\n    )\n\n    init {\n        val blockHeaderHasher = when (network) {\n            is TestNetDash,\n            is MainNetDash -> X11Hasher()\n            else -> DoubleSha256Hasher()\n        }\n\n        val networkMessageParser = NetworkMessageParser(network.magic).apply {\n            add(VersionMessageParser())\n            add(VerAckMessageParser())\n            add(InvMessageParser())\n            add(HeadersMessageParser(blockHeaderHasher))\n        }\n\n        val networkMessageSerializer = NetworkMessageSerializer(network.magic).apply {\n            add(VersionMessageSerializer())\n            add(VerAckMessageSerializer())\n            add(InvMessageSerializer())\n            add(GetHeadersMessageSerializer())\n            add(HeadersMessageSerializer())\n            add(PingMessageSerializer())\n        }\n\n        val connectionManager = object : IConnectionManager {\n            override val listener: IConnectionManagerListener? = null\n            override val isConnected = true\n\n            override fun onEnterForeground() {\n            }\n\n            override fun onEnterBackground() {\n            }\n        }\n\n        val peerHostManager = PeerAddressManager(network)\n        peerGroup = PeerGroup(peerHostManager, network, peerManager, peerSize, networkMessageParser, networkMessageSerializer, connectionManager, 0, false).also {\n            peerHostManager.listener = it\n        }\n\n        peerGroup.addPeerGroupListener(this)\n        peerGroup.peerTaskHandler = this\n    }\n\n    fun start() {\n        isSynced = false\n        peerGroup.start()\n        print(\"Started syncer\")\n    }\n\n    //  PeerGroup Listener\n\n    override fun onPeerConnect(peer: Peer) {\n        print(\"onPeerConnect ${peer.host}\")\n        assignNextSyncPeer()\n    }\n\n    override fun onPeerDisconnect(peer: Peer, e: Exception?) {\n        print(\"onPeerDisconnect, error: ${e?.message} ${e?.javaClass?.simpleName}\")\n        if (peer == syncPeer) {\n            syncPeer = null\n            assignNextSyncPeer()\n        }\n    }\n\n    override fun onPeerReady(peer: Peer) {\n        if (peer == syncPeer) {\n            downloadBlockchain()\n        }\n    }\n\n    //  IPeerTaskHandler\n\n    override fun handleCompletedTask(peer: Peer, task: PeerTask): Boolean {\n        if (task is GetBlockHeadersTask) {\n            validateHeaders(peer, task.blockHeaders)\n            return true\n        }\n\n        return false\n    }\n\n    private fun validateHeaders(peer: Peer, headers: Array<BlockHeader>) {\n        var prevBlock = blocks.last()\n\n        for (header in headers) {\n            if (!prevBlock.headerHash.contentEquals(header.previousBlockHeaderHash)) {\n                syncPeer = null\n                assignNextSyncPeer()\n                break\n            }\n\n            val newBlock = Block(header, prevBlock.height + 1)\n            if (newBlock.height % checkpointInterval == 0) {\n                print(\"Checkpoint block ${header.hash.toReversedHex()} at height ${newBlock.height}, time ${header.timestamp}\")\n                checkpoints.add(newBlock)\n            }\n\n            blocks.add(newBlock)\n            prevBlock = newBlock\n        }\n\n        if (headers.size < 2000) {\n            peer.synced = true\n        }\n\n        downloadBlockchain()\n    }\n\n    private fun assignNextSyncPeer() {\n        peersQueue.execute {\n            print(\"assignNextSyncPeer, connected peers: ${peerManager.connected().size}\")\n            print(\"synced peers: ${peerManager.connected().count { it.synced }}\")\n\n            if (peerManager.connected().all { it.synced }) {\n                isSynced = true\n                peerGroup.stop()\n                print(\"Synced\")\n\n                val lastCheckpoint = checkpoints.last()\n                val blocksPrecedingCheckpoint = blocks.dropLastWhile { it.height > lastCheckpoint.height }\n\n                listener.onSync(network, blocksPrecedingCheckpoint.reversed().take(blocksToKeep))\n            } else if (syncPeer == null) {\n                val notSyncedPeers = peerManager.sorted().filter { !it.synced }\n                notSyncedPeers.firstOrNull { it.ready }?.let { nonSyncedPeer ->\n                    syncPeer = nonSyncedPeer\n\n                    downloadBlockchain()\n                }\n            }\n        }\n    }\n\n    private fun downloadBlockchain() {\n        val peer = syncPeer\n        if (peer == null || !peer.ready) {\n            return\n        }\n\n        if (peer.synced) {\n            syncPeer = null\n            assignNextSyncPeer()\n        } else {\n            peer.addTask(GetBlockHeadersTask(getBlockLocatorHashes()))\n        }\n    }\n\n    private fun getBlockLocatorHashes(): List<ByteArray> {\n        return if (blocks.isEmpty()) {\n            listOf(checkpoints.last().headerHash)\n        } else {\n            listOf(blocks.last().headerHash)\n        }\n    }\n\n    private fun print(message: String) {\n        println(\"${network.javaClass.simpleName}: $message\")\n    }\n}\n"
  },
  {
    "path": "tools/src/main/java/io/horizontalsystems/tools/PeerAddressManager.kt",
    "content": "package io.horizontalsystems.tools\n\nimport io.horizontalsystems.bitcoincore.core.IPeerAddressManager\nimport io.horizontalsystems.bitcoincore.core.IPeerAddressManagerListener\nimport io.horizontalsystems.bitcoincore.models.PeerAddress\nimport io.horizontalsystems.bitcoincore.network.Network\nimport io.horizontalsystems.bitcoincore.network.peer.Peer\nimport io.horizontalsystems.bitcoincore.network.peer.PeerDiscover\n\nclass PeerAddressManager(private val network: Network) : IPeerAddressManager {\n\n    private val state = State()\n    private val peerDiscover = PeerDiscover(this)\n\n    override var listener: IPeerAddressManagerListener? = null\n\n    override val hasFreshIps: Boolean\n        get() {\n            getLeastScoreFastestPeer()?.let { peerAddress ->\n                return peerAddress.connectionTime == null\n            }\n\n            return false\n        }\n\n    override fun getIp(): String? {\n        val peerAddress = getLeastScoreFastestPeer()\n        if (peerAddress == null) {\n            peerDiscover.lookup(network.dnsSeeds)\n            return null\n        }\n\n        state.add(peerAddress.ip)\n\n        return peerAddress.ip\n    }\n\n    override fun addIps(ips: List<String>) {\n        state.setPeerAddresses(ips.map { PeerAddress(it, 0) })\n        listener?.onAddAddress()\n    }\n\n    override fun markFailed(ip: String) {\n        state.remove(ip)\n        state.deletePeerAddress(ip)\n    }\n\n    override fun markSuccess(ip: String) {\n        state.remove(ip)\n        state.increasePeerAddressScore(ip)\n    }\n\n    override fun markConnected(peer: Peer) {\n        state.setPeerConnectionTime(peer.host, peer.connectionTime)\n    }\n\n    private fun getLeastScoreFastestPeer(): PeerAddress? {\n        return state.getLeastScoreFastestPeerAddressExcludingIps(state.getUsedPeers())\n    }\n\n    private class State {\n        private val allPeers = mutableListOf<PeerAddress>()\n        private var usedPeers = mutableListOf<String>()\n\n        @Synchronized\n        fun getUsedPeers(): List<String> {\n            return usedPeers.toList()\n        }\n\n        @Synchronized\n        fun add(ip: String) {\n            usedPeers.add(ip)\n        }\n\n        @Synchronized\n        fun remove(ip: String) {\n            usedPeers.removeAll { it == ip }\n        }\n\n        @Synchronized\n        fun getLeastScoreFastestPeerAddressExcludingIps(ips: List<String>): PeerAddress? {\n            return allPeers.filter { !ips.contains(it.ip) }.sortedBy { it.connectionTime }.minByOrNull { it.score }\n        }\n\n        @Synchronized\n        fun increasePeerAddressScore(ip: String) {\n            val peer = allPeers.find { it.ip == ip }\n            if (peer != null) {\n                peer.score += 1\n            }\n        }\n\n        @Synchronized\n        fun deletePeerAddress(ip: String) {\n            allPeers.removeAll { it.ip == ip }\n        }\n\n        @Synchronized\n        fun setPeerAddresses(list: List<PeerAddress>) {\n            allPeers.addAll(list)\n        }\n\n        @Synchronized\n        fun setPeerConnectionTime(ip: String, time: Long) {\n            val peer = allPeers.find { it.ip == ip }\n            if (peer != null) {\n                peer.connectionTime = time\n            }\n        }\n    }\n}\n"
  },
  {
    "path": "tools/src/main/java/io/horizontalsystems/tools/Tools.kt",
    "content": "package io.horizontalsystems.tools\n\nimport io.horizontalsystems.bitcoincore.models.Block\nimport io.horizontalsystems.bitcoincore.storage.BlockHeader\nimport io.horizontalsystems.bitcoincore.utils.HashUtils\nimport java.util.logging.Level\nimport java.util.logging.Logger\n\n// Go to\n// Edit Configurations... -> ToolsKt -> VM Options\n// And paste the following\n// -classpath $Classpath$:bitcoincashkit/src/main/resources:bitcoinkit/src/main/resources:dashkit/src/main/resources:ecashkit/src/main/resources:litecoinkit/src/main/resources\nfun main() {\n    Logger.getLogger(\"\").level = Level.SEVERE\n    syncCheckpoints()\n}\n\nprivate fun syncCheckpoints() {\n    BuildCheckpoints().sync()\n    Thread.sleep(5000)\n}\n\nprivate fun buildCustomCheckpoint() {\n    val checkpointBlock = Block(BlockHeader(\n            version = 2,\n            previousBlockHeaderHash = HashUtils.toBytesAsLE(\"00000000000000006bcf448b771c8f4db4e2ca653474e3b29504ec08422b3fba\"),\n            merkleRoot = HashUtils.toBytesAsLE(\"4ea18e999a57fc55fb390558dbb88a7b9c55c71c7de4cec160c045802ee587d2\"),\n            timestamp = 1397755646,\n            bits = 419470732,\n            nonce = 2160181286,\n            hash = HashUtils.toBytesAsLE(\"00000000000000003decdbb5f3811eab3148fbc29d3610528eb3b50d9ee5723f\")\n    ), 296352)\n\n    BuildCheckpoints().build(checkpointBlock)\n}\n"
  },
  {
    "path": "tools/src/main/res/values/strings.xml",
    "content": "<resources>\n    <string name=\"app_name\">tools</string>\n</resources>\n"
  }
]