gitignore modefied
https://github.com/moneroexamples/onion-monero-blockchain-explorer/pull/45
This commit is contained in:
parent
40d87bdab4
commit
468f927e8b
|
@ -4,5 +4,6 @@
|
|||
*.user
|
||||
.idea/
|
||||
*.log
|
||||
.orig
|
||||
tests/
|
||||
cmake-build-debug/
|
||||
|
|
348
README.md
348
README.md
|
@ -7,17 +7,16 @@ Currently available Monero blockchain explorer websites have several limitations
|
|||
- track users activates through google analytics,
|
||||
- are closed sourced,
|
||||
- are not available as hidden services,
|
||||
- provide only basic search capabilities,
|
||||
- do not support Monero testnet
|
||||
|
||||
|
||||
In this example, these limitations are addressed by development of
|
||||
an Onion Monero Blockchain Explorer. The example not only shows how to use Monero C++ libraries,
|
||||
but also demonstrates how to use:
|
||||
an Onion Monero Blockchain Explorer. The example not only shows how to use
|
||||
Monero C++ libraries, but also demonstrates how to use:
|
||||
|
||||
- [crow](https://github.com/ipkn/crow) - C++ micro web framework
|
||||
- [mstch](https://github.com/no1msd/mstch) - C++ {{mustache}} templates
|
||||
- [json](https://github.com/nlohmann/json) - JSON for Modern C++
|
||||
- [date](https://github.com/HowardHinnant/date) - C++ date and time library
|
||||
- [fmt](https://github.com/fmtlib/fmt) - Small, safe and fast string formatting library
|
||||
|
||||
## Addresses
|
||||
|
@ -27,32 +26,24 @@ Tor users:
|
|||
- [http://dvwae436pd7nt4bc.onion](http://dvwae436pd7nt4bc.onion)
|
||||
|
||||
Clearnet versions:
|
||||
- [http://139.162.32.245:8081/](http://139.162.32.245:8081/) - bleeding edge, no https.
|
||||
- [https://xmrchain.net/](https://xmrchain.net/) - https enabled.
|
||||
|
||||
- [http://139.162.32.245:8081/](http://139.162.32.245:8081/) - bleeding edge version, no https.
|
||||
- [https://explorer.xmr.my/](https://explorer.xmr.my/) - https enabled, up to date, but sometimes down.
|
||||
- [https://xmrchain.net/](https://xmrchain.net/) - https enabled, most popular and very stable.
|
||||
- [https://monerohash.com/explorer/](https://monerohash.com/explorer/) - nice looking one, https enabled.
|
||||
- [http://blox.supportxmr.com/](http://blox.supportxmr.com/) - no https.
|
||||
- [https://explorer.xmr.my/](https://explorer.xmr.my/) - https enabled.
|
||||
- [http://explore.MoneroWorld.com](http://explore.moneroworld.com) - same as the second one.
|
||||
|
||||
Clearnet testnet Monero version:
|
||||
|
||||
- [http://139.162.32.245:8082/](http://139.162.32.245:8082/) - bleeding edge, no https.
|
||||
- [http://139.162.32.245:8082/](http://139.162.32.245:8082/) - bleeding edge version, no https.
|
||||
- [https://testnet.xmrchain.com/](https://testnet.xmrchain.com/) - https enabled.
|
||||
|
||||
|
||||
i2p users (main Monero network) - down for now:
|
||||
|
||||
- [http://monerotools.i2p](http://monerotools.i2p)
|
||||
|
||||
|
||||
Clearnet testnet priority nodes:
|
||||
|
||||
- 23.228.193.90 - /opt/monero/monerod --testnet --add-priority-node 23.228.193.90
|
||||
- 62.210.104.109 - /opt/monero/monerod --testnet --add-priority-node 62.210.104.109
|
||||
|
||||
Monero tor nodes:
|
||||
|
||||
- 7kome2dwgre4wll6.onion - `/opt/monero/monero-wallet-cli --daemon-host 7kome2dwgre4wll6.onion`
|
||||
- o6nvntbo3qsn36dm.onion - `/opt/monero/monero-wallet-cli --daemon-host o6nvntbo3qsn36dm.onion`
|
||||
|
||||
## Onion Monero Blockchain Explorer features
|
||||
|
||||
The key features of the Onion Monero Blockchain Explorer are:
|
||||
|
@ -71,9 +62,10 @@ The key features of the Onion Monero Blockchain Explorer are:
|
|||
- the only explorer showing number of amount output indices,
|
||||
- the only explorer supporting Monero testnet network,
|
||||
- the only explorer providing tx checker and pusher for online pushing of transactions,
|
||||
- the only explorer allowing to inspect encrypted key images file and output files.
|
||||
- the only explorer able to estimate possible spendings based on address and viewkey.
|
||||
|
||||
## Prerequisite
|
||||
|
||||
## Compilation on Ubuntu 16.04
|
||||
|
||||
##### Compile latest Monero
|
||||
|
@ -100,13 +92,12 @@ make
|
|||
```
|
||||
|
||||
##### Compile and run the explorer
|
||||
Once the Monero is compiled and setup, the explorer can be downloaded and compiled
|
||||
Once the Monero is compiles, the explorer can be downloaded and compiled
|
||||
as follows:
|
||||
|
||||
```bash
|
||||
# go to home folder if still in ~/monero
|
||||
cd ~
|
||||
|
||||
# download the source code
|
||||
git clone https://github.com/moneroexamples/onion-monero-blockchain-explorer.git
|
||||
|
||||
|
@ -135,7 +126,6 @@ To run it:
|
|||
|
||||
By default it will look for blockchain in its default location i.e., `~/.bitmonero/lmdb`.
|
||||
You can use `--bc-path` option if its in different location.
|
||||
|
||||
Example output:
|
||||
|
||||
```bash
|
||||
|
@ -154,14 +144,30 @@ xmrblocks, start Onion Monero Blockchain Explorer:
|
|||
-h [ --help ] [=arg(=1)] (=0) produce help message
|
||||
-t [ --testnet ] [=arg(=1)] (=0) use testnet blockchain
|
||||
--enable-pusher [=arg(=1)] (=0) enable pushing signed tx
|
||||
--enable-mixin-details [=arg(=1)] (=0)
|
||||
enable mixin details for key images,
|
||||
e.g., timescale, mixin of mixins, in tx
|
||||
context
|
||||
--enable-key-image-checker [=arg(=1)] (=0)
|
||||
enable key images file checker
|
||||
--enable-output-key-checker [=arg(=1)] (=0)
|
||||
enable outputs key file checker
|
||||
--enable-mempool-cache arg (=1) enable caching txs in the mempool
|
||||
--enable-json-api arg (=1) enable JSON REST api
|
||||
--enable-tx-cache [=arg(=1)] (=0) enable caching of tx details
|
||||
--show-cache-times [=arg(=1)] (=0) show times of getting data from cache
|
||||
vs no cache
|
||||
--enable-block-cache [=arg(=1)] (=0) enable caching of block details
|
||||
--enable-autorefresh-option [=arg(=1)] (=0)
|
||||
enable users to have the index page on
|
||||
autorefresh
|
||||
-p [ --port ] arg (=8081) default port
|
||||
--testnet-url arg you can specifiy testnet url, if you
|
||||
run it on mainet. link will show on
|
||||
front page to testnet explorer
|
||||
--mainnet-url arg you can specifiy mainnet url, if you
|
||||
run it on testnet. link will show on
|
||||
front page to mainnet explorer
|
||||
--no-blocks-on-index arg (=10) number of last blocks to be shown on
|
||||
index page
|
||||
-b [ --bc-path ] arg path to lmdb blockchain
|
||||
|
@ -196,27 +202,301 @@ Note: Because we generated our own certificate, modern browsers will complain
|
|||
about it as they cant verify the signatures against any third party. So probably
|
||||
for any practical use need to have properly issued ssl certificates.
|
||||
|
||||
## Enable transaction pusher
|
||||
## JSON API
|
||||
|
||||
By default, the tx pusher is disabled. The pushing will not work, but tx checking and inspecting will.
|
||||
The explorer has JSON api. For the API, it uses conventions defined by [JSend](https://labs.omniti.com/labs/jsend).
|
||||
|
||||
To enable pushing the txs, use flag `--enable-pusher`, e.g.:
|
||||
#### api/transaction/<tx_hash>
|
||||
|
||||
```bash
|
||||
./xmrblocks --enable-pusher
|
||||
curl -w "\n" -X GET "http://139.162.32.245:8081/api/transaction/6093260dbe79fd6277694d14789dc8718f1bd54457df8bab338c2efa3bb0f03d"
|
||||
```
|
||||
|
||||
Note: There has been a number of issues with compatibility of tx's binary data between different Monero versions
|
||||
and operating systems. Unless you are using latest development version of Monero and the explorer has been compiled
|
||||
against the lastest version, pushing and checking unsigined and signed tx data
|
||||
might not work due to incompatibilities in binary data.
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"block_height": 1268252,
|
||||
"coinbase": 0,
|
||||
"confirmations": 1,
|
||||
"fee": 12517785574,
|
||||
"inputs": [
|
||||
{
|
||||
"amount": 0,
|
||||
"key_image": "67838fd0ffd79f13e735830d3ec60412aed59e53e1f997feb6f73d088b949611"
|
||||
}
|
||||
],
|
||||
"outputs": [
|
||||
{
|
||||
"amount": 0,
|
||||
"public_key": "525779873776e4a42f517fd79b72e7c31c3ba03e730fc32287f6414fb702c1d7"
|
||||
},
|
||||
{
|
||||
"amount": 0,
|
||||
"public_key": "e25f00fceb77af841d780b68647618812695b4ca6ebe338faba6e077f758ac30"
|
||||
}
|
||||
],
|
||||
"rct_type": 1,
|
||||
"size": 13323000000000000,
|
||||
"timestamp": 1489753456,
|
||||
"timestamp_utc": "2017-03-17 12:24:16",
|
||||
"tx_hash": "6093260dbe79fd6277694d14789dc8718f1bd54457df8bab338c2efa3bb0f03d",
|
||||
"version": 2
|
||||
},
|
||||
"status": "success"
|
||||
}
|
||||
```
|
||||
|
||||
## Example screenshot
|
||||
#### api/transactions
|
||||
|
||||
![Onion Monero Blockchain Explorer](https://raw.githubusercontent.com/moneroexamples/onion-monero-blockchain-explorer/master/screenshot/screenshot_01.jpg)
|
||||
Transactions in last 25 blocks
|
||||
|
||||
|
||||
## Other Monero examples
|
||||
```bash
|
||||
curl -w "\n" -X GET "http://139.162.32.245:8081/api/transactions"
|
||||
```
|
||||
|
||||
Partial results shown:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"blocks": [
|
||||
{
|
||||
"age": "33:16:49:53",
|
||||
"height": 1268252,
|
||||
"size": 105390000000000000,
|
||||
"timestamp": 1489753456,
|
||||
"timestamp_utc": "2017-03-17 12:24:16",
|
||||
"txs": [
|
||||
{
|
||||
"coinbase": true,
|
||||
"mixin": 0,
|
||||
"outputs": 8491554678365,
|
||||
"rct_type": 0,
|
||||
"tx_fee": 0,
|
||||
"tx_hash": "7c4286f64544568265bb5418df84ae69afaa3567749210e46f8340c247f4803f",
|
||||
"tx_size": 151000000000000,
|
||||
"tx_version": 2
|
||||
},
|
||||
{
|
||||
"coinbase": false,
|
||||
"mixin": 5,
|
||||
"outputs": 0,
|
||||
"rct_type": 2,
|
||||
"tx_fee": 17882516700,
|
||||
"tx_hash": "2bfbccb918ee5f050808dd040ce03943b7315b81788e9cdee59cf86b557ba48c",
|
||||
"tx_size": 19586000000000000,
|
||||
"tx_version": 2
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"limit": 25,
|
||||
"page": 0
|
||||
},
|
||||
"status": "success"
|
||||
}
|
||||
```
|
||||
|
||||
#### api/transactions?page=<page_no>&limit=<tx_per_page>
|
||||
|
||||
|
||||
```bash
|
||||
curl -w "\n" -X GET "http://139.162.32.245:8081/api/transactions?page=2&limit=10"
|
||||
```
|
||||
|
||||
Result analogical to the one above.
|
||||
|
||||
#### api/block/<block_number|block_hash>
|
||||
|
||||
|
||||
```bash
|
||||
curl -w "\n" -X GET "http://139.162.32.245:8081/api/block/1293257"
|
||||
```
|
||||
|
||||
Partial results shown:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"block_height": 1293257,
|
||||
"block_reward": 0,
|
||||
"current_height": 1293264,
|
||||
"hash": "9ef6bb8f9b8bd253fc6390e5c2cdc45c8ee99fad16447437108bf301fe6bd6e1",
|
||||
"size": 141244,
|
||||
"timestamp": 1492761974,
|
||||
"timestamp_utc": "2017-04-21 08:06:14",
|
||||
"txs": [
|
||||
{
|
||||
"coinbase": true,
|
||||
"extra": "018ae9560eb85d5ebd22d3beaed55c21d469eab430c5e3cac61b3fe2f5ad156770020800000001a9030800",
|
||||
"mixin": 0,
|
||||
"payment_id": "",
|
||||
"payment_id8": "",
|
||||
"rct_type": 0,
|
||||
"tx_fee": 0,
|
||||
"tx_hash": "3ff71b65bec34c9261e01a856e6a03594cf0472acf6b77db3f17ebd18eaa30bf",
|
||||
"tx_size": 95,
|
||||
"tx_version": 2,
|
||||
"xmr_inputs": 0,
|
||||
"xmr_outputs": 8025365394426
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": "success"
|
||||
}
|
||||
```
|
||||
|
||||
|
||||
#### api/mempool
|
||||
|
||||
|
||||
```bash
|
||||
curl -w "\n" -X GET "http://139.162.32.245:8081/api/mempool"
|
||||
```
|
||||
|
||||
Partial results shown:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"coinbase": false,
|
||||
"extra": "02210001c32d313b74a859b904079c69dbc04ea6e37eddcf4aeb34e9400cc12831da5401b34082a9ff7476fe29a19fa6a1735a9c59db226b9ddcf715928aa71625b13062",
|
||||
"mixin": 7,
|
||||
"payment_id": "01c32d313b74a859b904079c69dbc04ea6e37eddcf4aeb34e9400cc12831da54",
|
||||
"payment_id8": "",
|
||||
"rct_type": 1,
|
||||
"timestamp": 1492763220,
|
||||
"timestamp_utc": "2017-04-21 08:27:00",
|
||||
"tx_fee": 4083040000,
|
||||
"tx_hash": "6751e0029558fdc4ab4528896529e32b2864c6ad43c5d8838c8ebe156ada0514",
|
||||
"tx_size": 13224,
|
||||
"tx_version": 2,
|
||||
"xmr_inputs": 0,
|
||||
"xmr_outputs": 0
|
||||
}
|
||||
],
|
||||
"status": "success"
|
||||
}
|
||||
```
|
||||
|
||||
#### api/search/<block_number|tx_hash|block_hash>
|
||||
|
||||
```bash
|
||||
curl -w "\n" -X GET "http://139.162.32.245:8081/api/search/1293669"
|
||||
```
|
||||
|
||||
Partial results shown:
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"block_height": 1293669,
|
||||
"current_height": 1293670,
|
||||
"hash": "5d55b8fabf85b0b4c959d66ad509eb92ddfe5c2b0e84e1760abcb090195c1913",
|
||||
"size": 118026,
|
||||
"timestamp": 1492815321,
|
||||
"timestamp_utc": "2017-04-21 22:55:21",
|
||||
"title": "block",
|
||||
"txs": [
|
||||
{
|
||||
"coinbase": true,
|
||||
"extra": "01cb7fda09033a5fa06dc601b9295ef3790397cf3c645e958e34cf7ab699d2f5230208000000027f030200",
|
||||
"mixin": 0,
|
||||
"payment_id": "",
|
||||
"payment_id8": "",
|
||||
"rct_type": 0,
|
||||
"tx_fee": 0,
|
||||
"tx_hash": "479ba432f5c88736b438dd4446a11a13046a752d469f7828151f5c5b86be4e9a",
|
||||
"tx_size": 95,
|
||||
"tx_version": 2,
|
||||
"xmr_inputs": 0,
|
||||
"xmr_outputs": 7992697599717
|
||||
}
|
||||
]
|
||||
},
|
||||
"status": "success"
|
||||
}
|
||||
```
|
||||
|
||||
#### api/outputs?txhash=<tx_hash>&address=<address>&viewkey=<viewkey>&txprove=<0|1>
|
||||
|
||||
For `txprove=0` we check which outputs belong to given address and corresponding viewkey.
|
||||
For `txprove=1` we use to prove to the recipient that we sent them founds.
|
||||
For this, we use recipient's address and our tx private key as a viewkey value,
|
||||
i.e., `viewkey=<tx_private_key>`
|
||||
|
||||
Checking outputs:
|
||||
|
||||
```bash
|
||||
# we use here official Monero project's donation address as an example
|
||||
curl -w "\n" -X GET "http://139.162.32.245:8081/api/outputs?txhash=17049bc5f2d9fbca1ce8dae443bbbbed2fc02f1ee003ffdd0571996905faa831&address=44AFFq5kSiGBoZ4NMDwYtN18obc8AemS33DBLWs3H7otXft3XjrpDtQGv7SqSsaBYBb98uNbr2VBBEt7f2wfn3RVGQBEP3A&viewkey=f359631075708155cc3d92a32b75a7d02a5dcf27756707b47a2b31b21c389501&txprove=0"
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"address": "42f18fc61586554095b0799b5c4b6f00cdeb26a93b20540d366932c6001617b75db35109fbba7d5f275fef4b9c49e0cc1c84b219ec6ff652fda54f89f7f63c88",
|
||||
"outputs": [
|
||||
{
|
||||
"amount": 34980000000000,
|
||||
"match": true,
|
||||
"output_idx": 0,
|
||||
"output_pubkey": "35d7200229e725c2bce0da3a2f20ef0720d242ecf88bfcb71eff2025c2501fdb"
|
||||
},
|
||||
{
|
||||
"amount": 0,
|
||||
"match": false,
|
||||
"output_idx": 1,
|
||||
"output_pubkey": "44efccab9f9b42e83c12da7988785d6c4eb3ec6e7aa2ae1234e2f0f7cb9ed6dd"
|
||||
}
|
||||
],
|
||||
"tx_hash": "17049bc5f2d9fbca1ce8dae443bbbbed2fc02f1ee003ffdd0571996905faa831",
|
||||
"tx_prove": false,
|
||||
"viewkey": "f359631075708155cc3d92a32b75a7d02a5dcf27756707b47a2b31b21c389501"
|
||||
},
|
||||
"status": "success"
|
||||
}
|
||||
```
|
||||
|
||||
Proving transfer:
|
||||
|
||||
We use recipient's address (i.e. not our address from which we sent xmr to recipient).
|
||||
For the viewkey, we use `tx_private_key` (although the GET variable is still called `viewkey`) that we obtained by sending this txs.
|
||||
|
||||
```bash
|
||||
# this is for testnet transaction
|
||||
curl -w "\n" -X GET "http://139.162.32.245:8082/api/outputs?txhash=94782a8c0aa8d8768afa0c040ef0544b63eb5148ca971a024ac402cad313d3b3&address=9wUf8UcPUtb2huK7RphBw5PFCyKosKxqtGxbcKBDnzTCPrdNfJjLjtuht87zhTgsffCB21qmjxjj18Pw7cBnRctcKHrUB7N&viewkey=e94b5bfc599d2f741d6f07e3ab2a83f915e96fb374dfb2cd3dbe730e34ecb40b&txprove=1"
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"data": {
|
||||
"address": "71bef5945b70bc0a31dbbe6cd0bd5884fe694bbfd18fff5f68f709438554fb88a51b1291e378e2f46a0155108782c242cc1be78af229242c36d4f4d1c4f72da2",
|
||||
"outputs": [
|
||||
{
|
||||
"amount": 1000000000000,
|
||||
"match": true,
|
||||
"output_idx": 0,
|
||||
"output_pubkey": "c1bf4dd020b5f0ab70bd672d2f9e800ea7b8ab108b080825c1d6cfc0b7f7ee00"
|
||||
},
|
||||
{
|
||||
"amount": 0,
|
||||
"match": false,
|
||||
"output_idx": 1,
|
||||
"output_pubkey": "8c61fae6ada2a103565dfdd307c7145b2479ddb1dab1eaadfa6c34db65d189d5"
|
||||
}
|
||||
],
|
||||
"tx_hash": "94782a8c0aa8d8768afa0c040ef0544b63eb5148ca971a024ac402cad313d3b3",
|
||||
"tx_prove": true,
|
||||
"viewkey": "e94b5bfc599d2f741d6f07e3ab2a83f915e96fb374dfb2cd3dbe730e34ecb40b"
|
||||
},
|
||||
"status": "success"
|
||||
}
|
||||
```
|
||||
|
||||
## Other monero examples
|
||||
|
||||
Other examples can be found on [github](https://github.com/moneroexamples?tab=repositories).
|
||||
Please know that some of the examples/repositories are not
|
||||
|
|
|
@ -7,13 +7,11 @@ project(myext)
|
|||
|
||||
set(SOURCE_HEADERS
|
||||
minicsv.h
|
||||
format.h
|
||||
)
|
||||
fmt/format.h)
|
||||
|
||||
set(SOURCE_FILES
|
||||
fmt/format.cc
|
||||
fmt/ostream.cc
|
||||
date/tz.cpp)
|
||||
fmt/ostream.cc)
|
||||
|
||||
# make static library called libmyxrm
|
||||
# that we are going to link to
|
||||
|
|
|
@ -1,668 +0,0 @@
|
|||
#ifndef CHRONO_IO_H
|
||||
#define CHRONO_IO_H
|
||||
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2016 Howard Hinnant
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
// Our apologies. When the previous paragraph was written, lowercase had not yet
|
||||
// been invented (that woud involve another several millennia of evolution).
|
||||
// We did not mean to shout.
|
||||
|
||||
#include <chrono>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <iosfwd>
|
||||
#include <ratio>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
|
||||
namespace date
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
#if __cplusplus >= 201402
|
||||
|
||||
template <class CharT, std::size_t N>
|
||||
class string_literal
|
||||
{
|
||||
CharT p_[N];
|
||||
|
||||
public:
|
||||
using const_iterator = const CharT*;
|
||||
|
||||
string_literal(string_literal const&) = default;
|
||||
string_literal& operator=(string_literal const&) = delete;
|
||||
|
||||
template <std::size_t N1 = 2,
|
||||
class = std::enable_if_t<N1 == N>>
|
||||
constexpr string_literal(CharT c) noexcept
|
||||
: p_{c}
|
||||
{
|
||||
}
|
||||
|
||||
constexpr string_literal(const CharT(&a)[N]) noexcept
|
||||
: p_{}
|
||||
{
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
p_[i] = a[i];
|
||||
}
|
||||
|
||||
template <class U = CharT, class = std::enable_if_t<1 < sizeof(U)>>
|
||||
constexpr string_literal(const char(&a)[N]) noexcept
|
||||
: p_{}
|
||||
{
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
p_[i] = a[i];
|
||||
}
|
||||
|
||||
template <class CharT2, class = std::enable_if_t<!std::is_same<CharT2, CharT>{}>>
|
||||
constexpr string_literal(string_literal<CharT2, N> const& a) noexcept
|
||||
: p_{}
|
||||
{
|
||||
for (std::size_t i = 0; i < N; ++i)
|
||||
p_[i] = a[i];
|
||||
}
|
||||
|
||||
template <std::size_t N1, std::size_t N2,
|
||||
class = std::enable_if_t<N1 + N2 - 1 == N>>
|
||||
constexpr string_literal(const string_literal<CharT, N1>& x,
|
||||
const string_literal<CharT, N2>& y) noexcept
|
||||
: p_{}
|
||||
{
|
||||
std::size_t i = 0;
|
||||
for (; i < N1-1; ++i)
|
||||
p_[i] = x[i];
|
||||
for (std::size_t j = 0; j < N2; ++j, ++i)
|
||||
p_[i] = y[j];
|
||||
}
|
||||
|
||||
constexpr const CharT* data() const noexcept {return p_;}
|
||||
constexpr std::size_t size() const noexcept {return N-1;}
|
||||
|
||||
constexpr const_iterator begin() const noexcept {return p_;}
|
||||
constexpr const_iterator end() const noexcept {return p_ + N-1;}
|
||||
|
||||
constexpr CharT const& operator[](std::size_t n) const noexcept
|
||||
{
|
||||
return p_[n];
|
||||
}
|
||||
|
||||
template <class Traits>
|
||||
friend
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
operator<<(std::basic_ostream<CharT, Traits>& os, const string_literal& s)
|
||||
{
|
||||
return os << s.p_;
|
||||
}
|
||||
};
|
||||
|
||||
template <class CharT1, class CharT2, std::size_t N1, std::size_t N2>
|
||||
constexpr
|
||||
inline
|
||||
string_literal<std::conditional_t<sizeof(CharT2) <= sizeof(CharT1), CharT1, CharT2>,
|
||||
N1 + N2 - 1>
|
||||
operator+(const string_literal<CharT1, N1>& x, const string_literal<CharT2, N2>& y) noexcept
|
||||
{
|
||||
using CharT = std::conditional_t<sizeof(CharT2) <= sizeof(CharT1), CharT1, CharT2>;
|
||||
return string_literal<CharT, N1 + N2 - 1>{string_literal<CharT, N1>{x},
|
||||
string_literal<CharT, N2>{y}};
|
||||
}
|
||||
|
||||
template <class CharT, std::size_t N>
|
||||
constexpr
|
||||
inline
|
||||
string_literal<CharT, N>
|
||||
msl(const CharT(&a)[N]) noexcept
|
||||
{
|
||||
return string_literal<CharT, N>{a};
|
||||
}
|
||||
|
||||
template <class CharT,
|
||||
class = std::enable_if_t<std::is_same<CharT, char>{} ||
|
||||
std::is_same<CharT, wchar_t>{} ||
|
||||
std::is_same<CharT, char16_t>{} ||
|
||||
std::is_same<CharT, char32_t>{}>>
|
||||
constexpr
|
||||
inline
|
||||
string_literal<CharT, 2>
|
||||
msl(CharT c) noexcept
|
||||
{
|
||||
return string_literal<CharT, 2>{c};
|
||||
}
|
||||
|
||||
constexpr
|
||||
std::size_t
|
||||
to_string_len(std::intmax_t i)
|
||||
{
|
||||
std::size_t r = 0;
|
||||
do
|
||||
{
|
||||
i /= 10;
|
||||
++r;
|
||||
} while (i > 0);
|
||||
return r;
|
||||
}
|
||||
|
||||
template <std::intmax_t N>
|
||||
constexpr
|
||||
inline
|
||||
std::enable_if_t
|
||||
<
|
||||
N < 10,
|
||||
string_literal<char, to_string_len(N)+1>
|
||||
>
|
||||
msl() noexcept
|
||||
{
|
||||
return msl(char(N % 10 + '0'));
|
||||
}
|
||||
|
||||
template <std::intmax_t N>
|
||||
constexpr
|
||||
inline
|
||||
std::enable_if_t
|
||||
<
|
||||
10 <= N,
|
||||
string_literal<char, to_string_len(N)+1>
|
||||
>
|
||||
msl() noexcept
|
||||
{
|
||||
return msl<N/10>() + msl(char(N % 10 + '0'));
|
||||
}
|
||||
|
||||
template <class CharT, std::intmax_t N, std::intmax_t D>
|
||||
constexpr
|
||||
inline
|
||||
std::enable_if_t
|
||||
<
|
||||
std::ratio<N, D>::type::den != 1,
|
||||
string_literal<CharT, to_string_len(std::ratio<N, D>::type::num) +
|
||||
to_string_len(std::ratio<N, D>::type::den) + 4>
|
||||
>
|
||||
msl(std::ratio<N, D>) noexcept
|
||||
{
|
||||
using R = typename std::ratio<N, D>::type;
|
||||
return msl(CharT{'['}) + msl<R::num>() + msl(CharT{'/'}) +
|
||||
msl<R::den>() + msl(CharT{']'});
|
||||
}
|
||||
|
||||
template <class CharT, std::intmax_t N, std::intmax_t D>
|
||||
constexpr
|
||||
inline
|
||||
std::enable_if_t
|
||||
<
|
||||
std::ratio<N, D>::type::den == 1,
|
||||
string_literal<CharT, to_string_len(std::ratio<N, D>::type::num) + 3>
|
||||
>
|
||||
msl(std::ratio<N, D>) noexcept
|
||||
{
|
||||
using R = typename std::ratio<N, D>::type;
|
||||
return msl(CharT{'['}) + msl<R::num>() + msl(CharT{']'});
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
auto
|
||||
msl(std::atto) noexcept
|
||||
{
|
||||
return msl(CharT{'a'});
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
auto
|
||||
msl(std::femto) noexcept
|
||||
{
|
||||
return msl(CharT{'f'});
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
auto
|
||||
msl(std::pico) noexcept
|
||||
{
|
||||
return msl(CharT{'p'});
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
auto
|
||||
msl(std::nano) noexcept
|
||||
{
|
||||
return msl(CharT{'n'});
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
std::enable_if_t
|
||||
<
|
||||
std::is_same<CharT, char>{},
|
||||
string_literal<char, 3>
|
||||
>
|
||||
msl(std::micro) noexcept
|
||||
{
|
||||
return string_literal<char, 3>{"\xC2\xB5"};
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
std::enable_if_t
|
||||
<
|
||||
!std::is_same<CharT, char>{},
|
||||
string_literal<CharT, 2>
|
||||
>
|
||||
msl(std::micro) noexcept
|
||||
{
|
||||
return string_literal<CharT, 2>{CharT{static_cast<unsigned char>('\xB5')}};
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
auto
|
||||
msl(std::milli) noexcept
|
||||
{
|
||||
return msl(CharT{'m'});
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
auto
|
||||
msl(std::centi) noexcept
|
||||
{
|
||||
return msl(CharT{'c'});
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
auto
|
||||
msl(std::deci) noexcept
|
||||
{
|
||||
return msl(CharT{'d'});
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
auto
|
||||
msl(std::deca) noexcept
|
||||
{
|
||||
return string_literal<CharT, 3>{"da"};
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
auto
|
||||
msl(std::hecto) noexcept
|
||||
{
|
||||
return msl(CharT{'h'});
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
auto
|
||||
msl(std::kilo) noexcept
|
||||
{
|
||||
return msl(CharT{'k'});
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
auto
|
||||
msl(std::mega) noexcept
|
||||
{
|
||||
return msl(CharT{'M'});
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
auto
|
||||
msl(std::giga) noexcept
|
||||
{
|
||||
return msl(CharT{'G'});
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
auto
|
||||
msl(std::tera) noexcept
|
||||
{
|
||||
return msl(CharT{'T'});
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
auto
|
||||
msl(std::peta) noexcept
|
||||
{
|
||||
return msl(CharT{'P'});
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
auto
|
||||
msl(std::exa) noexcept
|
||||
{
|
||||
return msl(CharT{'E'});
|
||||
}
|
||||
|
||||
template <class CharT, class Period>
|
||||
constexpr
|
||||
auto
|
||||
get_units(Period p)
|
||||
{
|
||||
return msl<CharT>(p) + string_literal<CharT, 2>{"s"};
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
auto
|
||||
get_units(std::ratio<1>)
|
||||
{
|
||||
return string_literal<CharT, 2>{"s"};
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
auto
|
||||
get_units(std::ratio<60>)
|
||||
{
|
||||
return string_literal<CharT, 4>{"min"};
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
auto
|
||||
get_units(std::ratio<3600>)
|
||||
{
|
||||
return string_literal<CharT, 2>{"h"};
|
||||
}
|
||||
|
||||
#else // __cplusplus < 201402
|
||||
|
||||
inline
|
||||
std::string
|
||||
to_string(std::uint64_t x)
|
||||
{
|
||||
return std::to_string(x);
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
std::basic_string<CharT>
|
||||
to_string(std::uint64_t x)
|
||||
{
|
||||
auto y = std::to_string(x);
|
||||
return std::basic_string<CharT>(y.begin(), y.end());
|
||||
}
|
||||
|
||||
template <class CharT, std::intmax_t N, std::intmax_t D>
|
||||
constexpr
|
||||
inline
|
||||
typename std::enable_if
|
||||
<
|
||||
std::ratio<N, D>::type::den != 1,
|
||||
std::basic_string<CharT>
|
||||
>::type
|
||||
msl(std::ratio<N, D>) noexcept
|
||||
{
|
||||
using R = typename std::ratio<N, D>::type;
|
||||
return std::basic_string<CharT>(1, '[') + to_string<CharT>(R::num) + CharT{'/'} +
|
||||
to_string<CharT>(R::den) + CharT{']'};
|
||||
}
|
||||
|
||||
template <class CharT, std::intmax_t N, std::intmax_t D>
|
||||
constexpr
|
||||
inline
|
||||
typename std::enable_if
|
||||
<
|
||||
std::ratio<N, D>::type::den == 1,
|
||||
std::basic_string<CharT>
|
||||
>::type
|
||||
msl(std::ratio<N, D>) noexcept
|
||||
{
|
||||
using R = typename std::ratio<N, D>::type;
|
||||
return std::basic_string<CharT>(1, '[') + to_string<CharT>(R::num) + CharT{']'};
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
std::basic_string<CharT>
|
||||
msl(std::atto) noexcept
|
||||
{
|
||||
return {'a'};
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
std::basic_string<CharT>
|
||||
msl(std::femto) noexcept
|
||||
{
|
||||
return {'f'};
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
std::basic_string<CharT>
|
||||
msl(std::pico) noexcept
|
||||
{
|
||||
return {'p'};
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
std::basic_string<CharT>
|
||||
msl(std::nano) noexcept
|
||||
{
|
||||
return {'n'};
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
typename std::enable_if
|
||||
<
|
||||
std::is_same<CharT, char>::value,
|
||||
std::string
|
||||
>::type
|
||||
msl(std::micro) noexcept
|
||||
{
|
||||
return "\xC2\xB5";
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
typename std::enable_if
|
||||
<
|
||||
!std::is_same<CharT, char>::value,
|
||||
std::basic_string<CharT>
|
||||
>::type
|
||||
msl(std::micro) noexcept
|
||||
{
|
||||
return {CharT(static_cast<unsigned char>('\xB5'))};
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
std::basic_string<CharT>
|
||||
msl(std::milli) noexcept
|
||||
{
|
||||
return {'m'};
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
std::basic_string<CharT>
|
||||
msl(std::centi) noexcept
|
||||
{
|
||||
return {'c'};
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
std::basic_string<CharT>
|
||||
msl(std::deci) noexcept
|
||||
{
|
||||
return {'d'};
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
std::basic_string<CharT>
|
||||
msl(std::deca) noexcept
|
||||
{
|
||||
return {'d', 'a'};
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
std::basic_string<CharT>
|
||||
msl(std::hecto) noexcept
|
||||
{
|
||||
return {'h'};
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
std::basic_string<CharT>
|
||||
msl(std::kilo) noexcept
|
||||
{
|
||||
return {'k'};
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
std::basic_string<CharT>
|
||||
msl(std::mega) noexcept
|
||||
{
|
||||
return {'M'};
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
std::basic_string<CharT>
|
||||
msl(std::giga) noexcept
|
||||
{
|
||||
return {'G'};
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
std::basic_string<CharT>
|
||||
msl(std::tera) noexcept
|
||||
{
|
||||
return {'T'};
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
std::basic_string<CharT>
|
||||
msl(std::peta) noexcept
|
||||
{
|
||||
return {'P'};
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
constexpr
|
||||
inline
|
||||
std::basic_string<CharT>
|
||||
msl(std::exa) noexcept
|
||||
{
|
||||
return {'E'};
|
||||
}
|
||||
|
||||
template <class CharT, class Period>
|
||||
std::basic_string<CharT>
|
||||
get_units(Period p)
|
||||
{
|
||||
return msl<CharT>(p) + CharT{'s'};
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
std::basic_string<CharT>
|
||||
get_units(std::ratio<1>)
|
||||
{
|
||||
return {'s'};
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
std::basic_string<CharT>
|
||||
get_units(std::ratio<60>)
|
||||
{
|
||||
return {'m', 'i', 'n'};
|
||||
}
|
||||
|
||||
template <class CharT>
|
||||
std::basic_string<CharT>
|
||||
get_units(std::ratio<3600>)
|
||||
{
|
||||
return {'h'};
|
||||
}
|
||||
|
||||
#endif // __cplusplus >= 201402
|
||||
|
||||
} // namespace detail
|
||||
|
||||
template <class CharT, class Traits, class Rep, class Period>
|
||||
inline
|
||||
std::basic_ostream<CharT, Traits>&
|
||||
operator<<(std::basic_ostream<CharT, Traits>& os,
|
||||
const std::chrono::duration<Rep, Period>& d)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
return os << d.count()
|
||||
<< detail::get_units<CharT>(typename Period::type{});
|
||||
}
|
||||
|
||||
} // namespace date
|
||||
|
||||
#endif // CHRONO_IO_H
|
4823
ext/date/date.h
4823
ext/date/date.h
File diff suppressed because it is too large
Load Diff
|
@ -1,49 +0,0 @@
|
|||
//
|
||||
// ios.h
|
||||
// DateTimeLib
|
||||
//
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2016 Alexander Kormanovsky
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
|
||||
#ifndef ios_hpp
|
||||
#define ios_hpp
|
||||
|
||||
#if __APPLE__
|
||||
# include <TargetConditionals.h>
|
||||
# if TARGET_OS_IPHONE
|
||||
# include <string>
|
||||
|
||||
namespace date
|
||||
{
|
||||
namespace iOSUtils
|
||||
{
|
||||
|
||||
std::string get_tzdata_path();
|
||||
|
||||
} // namespace iOSUtils
|
||||
} // namespace date
|
||||
|
||||
# endif // TARGET_OS_IPHONE
|
||||
#else // !__APPLE__
|
||||
# define TARGET_OS_IPHONE 0
|
||||
#endif // !__APPLE__
|
||||
#endif // ios_hpp
|
405
ext/date/ios.mm
405
ext/date/ios.mm
|
@ -1,405 +0,0 @@
|
|||
//
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2016 Alexander Kormanovsky
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
#include "ios.h"
|
||||
|
||||
#if TARGET_OS_IPHONE
|
||||
|
||||
#include <Foundation/Foundation.h>
|
||||
|
||||
#include <iostream>
|
||||
#include <zlib.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#ifndef TAR_DEBUG
|
||||
# define TAR_DEBUG 0
|
||||
#endif
|
||||
|
||||
#define INTERNAL_DIR "Library/tzdata"
|
||||
#define TARGZ_EXTENSION "tar.gz"
|
||||
|
||||
#define TAR_BLOCK_SIZE 512
|
||||
#define TAR_TYPE_POSITION 156
|
||||
#define TAR_NAME_POSITION 0
|
||||
#define TAR_NAME_SIZE 100
|
||||
#define TAR_SIZE_POSITION 124
|
||||
#define TAR_SIZE_SIZE 12
|
||||
|
||||
namespace date
|
||||
{
|
||||
namespace iOSUtils
|
||||
{
|
||||
|
||||
struct TarInfo
|
||||
{
|
||||
char objType;
|
||||
std::string objName;
|
||||
int64_t realContentSize; // writable size without padding zeroes
|
||||
int64_t blocksContentSize; // adjusted size to 512 bytes blocks
|
||||
bool success;
|
||||
};
|
||||
|
||||
char* convertCFStringRefPathToCStringPath(CFStringRef ref);
|
||||
bool extractTzdata(CFURLRef homeUrl, CFURLRef archiveUrl, std::string destPath);
|
||||
TarInfo getTarObjectInfo(CFReadStreamRef readStream, int64_t location);
|
||||
std::string getTarObject(CFReadStreamRef readStream, int64_t size);
|
||||
bool writeFile(CFURLRef tzdataUrl, std::string fileName, std::string data,
|
||||
int64_t realContentSize);
|
||||
|
||||
std::string
|
||||
date::iOSUtils::get_tzdata_path()
|
||||
{
|
||||
CFURLRef ref = CFCopyHomeDirectoryURL();
|
||||
CFStringRef homePath = CFURLCopyPath(CFCopyHomeDirectoryURL());
|
||||
std::string tzdata_path(std::string(convertCFStringRefPathToCStringPath(homePath)) +
|
||||
INTERNAL_DIR);
|
||||
|
||||
if (access(tzdata_path.c_str(), F_OK) == 0)
|
||||
{
|
||||
#if TAR_DEBUG
|
||||
printf("tzdata exists\n");
|
||||
#endif
|
||||
return tzdata_path;
|
||||
}
|
||||
|
||||
CFBundleRef mainBundle = CFBundleGetMainBundle();
|
||||
CFArrayRef paths = CFBundleCopyResourceURLsOfType(mainBundle, CFSTR(TARGZ_EXTENSION),
|
||||
NULL);
|
||||
|
||||
if (CFArrayGetCount(paths) != 0)
|
||||
{
|
||||
// get archive path, assume there is no other tar.gz in bundle
|
||||
CFURLRef archiveUrl = static_cast<CFURLRef>(CFArrayGetValueAtIndex(paths, 0));
|
||||
CFStringRef archiveName= CFURLCopyPath(archiveUrl);
|
||||
archiveUrl = CFBundleCopyResourceURL(mainBundle, archiveName, NULL, NULL);
|
||||
|
||||
extractTzdata(CFCopyHomeDirectoryURL(), archiveUrl, tzdata_path);
|
||||
}
|
||||
|
||||
return tzdata_path;
|
||||
}
|
||||
|
||||
char*
|
||||
convertCFStringRefPathToCStringPath(CFStringRef ref)
|
||||
{
|
||||
CFIndex bufferSize = CFStringGetMaximumSizeOfFileSystemRepresentation(ref);
|
||||
char *buffer = new char[bufferSize];
|
||||
CFStringGetFileSystemRepresentation(ref, buffer, bufferSize);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
bool extractTzdata(CFURLRef homeUrl, CFURLRef archiveUrl, std::string destPath)
|
||||
{
|
||||
const char *TAR_TMP_PATH = "/tmp.tar";
|
||||
|
||||
// create Library path
|
||||
CFStringRef libraryStr = CFStringCreateWithCString(NULL, "Library",
|
||||
CFStringGetSystemEncoding());
|
||||
CFURLRef libraryUrl = CFURLCreateCopyAppendingPathComponent(kCFAllocatorDefault,
|
||||
homeUrl, libraryStr,
|
||||
false);
|
||||
|
||||
// create tzdata path
|
||||
CFStringRef tzdataPathRef = CFStringCreateWithCString(NULL, INTERNAL_DIR,
|
||||
CFStringGetSystemEncoding());
|
||||
CFURLRef tzdataPathUrl = CFURLCreateCopyAppendingPathComponent(NULL, homeUrl,
|
||||
tzdataPathRef, false);
|
||||
|
||||
// create src archive path
|
||||
CFStringRef archivePath = CFURLCopyPath(archiveUrl);
|
||||
gzFile tarFile = gzopen(convertCFStringRefPathToCStringPath(archivePath), "rb");
|
||||
|
||||
// create tar unpacking path
|
||||
CFStringRef tarName = CFStringCreateWithCString(NULL, TAR_TMP_PATH,
|
||||
CFStringGetSystemEncoding());
|
||||
CFURLRef tarUrl = CFURLCreateCopyAppendingPathComponent(NULL, libraryUrl, tarName,
|
||||
false);
|
||||
const char *tarPath = convertCFStringRefPathToCStringPath(CFURLCopyPath(tarUrl));
|
||||
|
||||
// create tzdata directory
|
||||
mkdir(destPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
||||
|
||||
// create stream
|
||||
CFWriteStreamRef writeStream = CFWriteStreamCreateWithFile(NULL, tarUrl);
|
||||
bool success = true;
|
||||
|
||||
if (!CFWriteStreamOpen(writeStream))
|
||||
{
|
||||
CFStreamError err = CFWriteStreamGetError(writeStream);
|
||||
|
||||
if (err.domain == kCFStreamErrorDomainPOSIX)
|
||||
{
|
||||
printf("kCFStreamErrorDomainPOSIX %i\n", err.error);
|
||||
}
|
||||
else if(err.domain == kCFStreamErrorDomainMacOSStatus)
|
||||
{
|
||||
printf("kCFStreamErrorDomainMacOSStatus %i\n", err.error);
|
||||
}
|
||||
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
remove(tarPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// ======= extract tar ========
|
||||
|
||||
unsigned int bufferLength = 1024 * 256; // 256Kb
|
||||
void *buffer = malloc(bufferLength);
|
||||
|
||||
while (true)
|
||||
{
|
||||
int readBytes = gzread(tarFile, buffer, bufferLength);
|
||||
|
||||
if (readBytes > 0)
|
||||
{
|
||||
CFIndex writtenBytes = CFWriteStreamWrite(writeStream, (unsigned char*)buffer,
|
||||
readBytes);
|
||||
|
||||
if (writtenBytes < 0)
|
||||
{
|
||||
CFStreamError err = CFWriteStreamGetError(writeStream);
|
||||
printf("write stream error %i\n", err.error);
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (readBytes == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
else if (readBytes == -1)
|
||||
{
|
||||
printf("decompression failed\n");
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("unexpected zlib state\n");
|
||||
success = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
CFWriteStreamClose(writeStream);
|
||||
CFRelease(writeStream);
|
||||
free(buffer);
|
||||
gzclose(tarFile);
|
||||
|
||||
if (!success)
|
||||
{
|
||||
remove(tarPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
// ======== extract files =========
|
||||
|
||||
uint64_t location = 0; // Position in the file
|
||||
|
||||
// get file size
|
||||
struct stat stat_buf;
|
||||
int res = stat(tarPath, &stat_buf);
|
||||
if (res != 0)
|
||||
{
|
||||
printf("error file size\n");
|
||||
remove(tarPath);
|
||||
return false;
|
||||
}
|
||||
int64_t tarSize = stat_buf.st_size;
|
||||
|
||||
// create read stream
|
||||
CFReadStreamRef readStream = CFReadStreamCreateWithFile(kCFAllocatorDefault, tarUrl);
|
||||
|
||||
if (!CFReadStreamOpen(readStream))
|
||||
{
|
||||
CFStreamError err = CFReadStreamGetError(readStream);
|
||||
|
||||
if (err.domain == kCFStreamErrorDomainPOSIX)
|
||||
{
|
||||
printf("kCFStreamErrorDomainPOSIX %i", err.error);
|
||||
}
|
||||
else if(err.domain == kCFStreamErrorDomainMacOSStatus)
|
||||
{
|
||||
printf("kCFStreamErrorDomainMacOSStatus %i", err.error);
|
||||
}
|
||||
|
||||
success = false;
|
||||
}
|
||||
|
||||
if (!success)
|
||||
{
|
||||
CFRelease(readStream);
|
||||
remove(tarPath);
|
||||
return false;
|
||||
}
|
||||
|
||||
int count = 0;
|
||||
long size = 0;
|
||||
|
||||
// process files
|
||||
while (location < tarSize)
|
||||
{
|
||||
TarInfo info = getTarObjectInfo(readStream, location);
|
||||
|
||||
if (!info.success || info.realContentSize == 0)
|
||||
{
|
||||
break; // something wrong or all files are read
|
||||
}
|
||||
|
||||
switch (info.objType)
|
||||
{
|
||||
case '0': // file
|
||||
case '\0': //
|
||||
{
|
||||
std::string obj = getTarObject(readStream, info.blocksContentSize);
|
||||
#if TAR_DEBUG
|
||||
size += info.realContentSize;
|
||||
printf("#%i %s file size %lld written total %ld from %lld\n", ++count,
|
||||
info.objName.c_str(), info.realContentSize, size, tarSize);
|
||||
#endif
|
||||
writeFile(tzdataPathUrl, info.objName, obj, info.realContentSize);
|
||||
location += info.blocksContentSize;
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
CFReadStreamClose(readStream);
|
||||
CFRelease(readStream);
|
||||
|
||||
remove(tarPath);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
TarInfo
|
||||
getTarObjectInfo(CFReadStreamRef readStream, int64_t location)
|
||||
{
|
||||
int64_t length = TAR_BLOCK_SIZE;
|
||||
uint8_t buffer[length];
|
||||
|
||||
char type;
|
||||
char name[TAR_NAME_SIZE + 1];
|
||||
char sizeBuf[TAR_SIZE_SIZE + 1];
|
||||
CFIndex bytesRead;
|
||||
|
||||
bool avail = CFReadStreamHasBytesAvailable(readStream);
|
||||
|
||||
bytesRead = CFReadStreamRead(readStream, buffer, length);
|
||||
|
||||
if (bytesRead < 0)
|
||||
{
|
||||
CFStreamError err = CFReadStreamGetError(readStream);
|
||||
printf("error reading tar object info %i", err.error);
|
||||
return {false};
|
||||
}
|
||||
|
||||
memcpy(&type, &buffer[TAR_TYPE_POSITION], 1);
|
||||
|
||||
memset(&name, '\0', TAR_NAME_SIZE + 1);
|
||||
memcpy(&name, &buffer[TAR_NAME_POSITION], TAR_NAME_SIZE);
|
||||
|
||||
memset(&sizeBuf, '\0', TAR_SIZE_SIZE + 1);
|
||||
memcpy(&sizeBuf, &buffer[TAR_SIZE_POSITION], TAR_SIZE_SIZE);
|
||||
int64_t realSize = strtol(sizeBuf, NULL, 8);
|
||||
int64_t blocksSize = realSize + (TAR_BLOCK_SIZE - (realSize % TAR_BLOCK_SIZE));
|
||||
|
||||
return {type, std::string(name), realSize, blocksSize, true};
|
||||
}
|
||||
|
||||
std::string
|
||||
getTarObject(CFReadStreamRef readStream, int64_t size)
|
||||
{
|
||||
uint8_t buffer[size];
|
||||
|
||||
CFIndex bytesRead = CFReadStreamRead(readStream, buffer, size);
|
||||
|
||||
if (bytesRead < 0)
|
||||
{
|
||||
CFStreamError err = CFReadStreamGetError(readStream);
|
||||
printf("error reading tar object info %i", err.error);
|
||||
}
|
||||
|
||||
return std::string((char *)buffer);
|
||||
}
|
||||
|
||||
bool
|
||||
writeFile(CFURLRef tzdataUrl, std::string fileName, std::string data,
|
||||
int64_t realContentSize)
|
||||
{
|
||||
// create stream
|
||||
CFStringRef fileNameRef = CFStringCreateWithCString(NULL, fileName.c_str(),
|
||||
CFStringGetSystemEncoding());
|
||||
CFURLRef url = CFURLCreateCopyAppendingPathComponent(NULL, tzdataUrl, fileNameRef,
|
||||
false);
|
||||
CFWriteStreamRef writeStream = CFWriteStreamCreateWithFile(NULL, url);
|
||||
|
||||
// open stream
|
||||
if (!CFWriteStreamOpen(writeStream))
|
||||
{
|
||||
CFStreamError err = CFWriteStreamGetError(writeStream);
|
||||
|
||||
if (err.domain == kCFStreamErrorDomainPOSIX)
|
||||
{
|
||||
printf("kCFStreamErrorDomainPOSIX %i\n", err.error);
|
||||
}
|
||||
else if(err.domain == kCFStreamErrorDomainMacOSStatus)
|
||||
{
|
||||
printf("kCFStreamErrorDomainMacOSStatus %i\n", err.error);
|
||||
}
|
||||
|
||||
CFRelease(writeStream);
|
||||
return false;
|
||||
}
|
||||
|
||||
// trim empty space
|
||||
uint8_t trimmedData[realContentSize + 1];
|
||||
memset(&trimmedData, '\0', realContentSize);
|
||||
memcpy(&trimmedData, data.c_str(), realContentSize);
|
||||
|
||||
// write
|
||||
CFIndex writtenBytes = CFWriteStreamWrite(writeStream, trimmedData, realContentSize);
|
||||
|
||||
if (writtenBytes < 0)
|
||||
{
|
||||
CFStreamError err = CFWriteStreamGetError(writeStream);
|
||||
printf("write stream error %i\n", err.error);
|
||||
}
|
||||
|
||||
CFWriteStreamClose(writeStream);
|
||||
CFRelease(writeStream);
|
||||
writeStream = NULL;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
} // namespace iOSUtils
|
||||
} // namespace date
|
||||
|
||||
#endif // TARGET_OS_IPHONE
|
3046
ext/date/julian.h
3046
ext/date/julian.h
File diff suppressed because it is too large
Load Diff
3120
ext/date/tz.cpp
3120
ext/date/tz.cpp
File diff suppressed because it is too large
Load Diff
1345
ext/date/tz.h
1345
ext/date/tz.h
File diff suppressed because it is too large
Load Diff
|
@ -1,265 +0,0 @@
|
|||
#ifndef TZ_PRIVATE_H
|
||||
#define TZ_PRIVATE_H
|
||||
|
||||
// The MIT License (MIT)
|
||||
//
|
||||
// Copyright (c) 2015, 2016 Howard Hinnant
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
// Our apologies. When the previous paragraph was written, lowercase had not yet
|
||||
// been invented (that woud involve another several millennia of evolution).
|
||||
// We did not mean to shout.
|
||||
|
||||
#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
|
||||
#include "tz.h"
|
||||
#else
|
||||
#include "date.h"
|
||||
#include <vector>
|
||||
#endif
|
||||
|
||||
namespace date
|
||||
{
|
||||
|
||||
namespace detail
|
||||
{
|
||||
|
||||
enum class tz {utc, local, standard};
|
||||
|
||||
//forward declare to avoid warnings in gcc 6.2
|
||||
class MonthDayTime;
|
||||
std::istream& operator>>(std::istream& is, MonthDayTime& x);
|
||||
std::ostream& operator<<(std::ostream& os, const MonthDayTime& x);
|
||||
|
||||
|
||||
class MonthDayTime
|
||||
{
|
||||
private:
|
||||
struct pair
|
||||
{
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||
pair() : month_day_(date::jan / 1), weekday_(0U) {}
|
||||
|
||||
pair(const date::month_day& month_day, const date::weekday& weekday)
|
||||
: month_day_(month_day), weekday_(weekday) {}
|
||||
#endif
|
||||
|
||||
date::month_day month_day_;
|
||||
date::weekday weekday_;
|
||||
};
|
||||
|
||||
enum Type {month_day, month_last_dow, lteq, gteq};
|
||||
|
||||
Type type_{month_day};
|
||||
|
||||
#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
|
||||
union U
|
||||
#else
|
||||
struct U
|
||||
#endif
|
||||
{
|
||||
date::month_day month_day_;
|
||||
date::month_weekday_last month_weekday_last_;
|
||||
pair month_day_weekday_;
|
||||
|
||||
#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
|
||||
U() : month_day_{date::jan/1} {}
|
||||
#else
|
||||
U() :
|
||||
month_day_(date::jan/1),
|
||||
month_weekday_last_(date::month(0U), date::weekday_last(date::weekday(0U)))
|
||||
{}
|
||||
|
||||
#endif // !defined(_MSC_VER) || (_MSC_VER >= 1900)
|
||||
|
||||
U& operator=(const date::month_day& x);
|
||||
U& operator=(const date::month_weekday_last& x);
|
||||
U& operator=(const pair& x);
|
||||
} u;
|
||||
|
||||
std::chrono::hours h_{0};
|
||||
std::chrono::minutes m_{0};
|
||||
std::chrono::seconds s_{0};
|
||||
tz zone_{tz::local};
|
||||
|
||||
public:
|
||||
MonthDayTime() = default;
|
||||
MonthDayTime(local_seconds tp, tz timezone);
|
||||
MonthDayTime(const date::month_day& md, tz timezone);
|
||||
|
||||
date::day day() const;
|
||||
date::month month() const;
|
||||
tz zone() const {return zone_;}
|
||||
|
||||
void canonicalize(date::year y);
|
||||
|
||||
sys_seconds
|
||||
to_sys(date::year y, std::chrono::seconds offset, std::chrono::seconds save) const;
|
||||
sys_days to_sys_days(date::year y) const;
|
||||
|
||||
sys_seconds to_time_point(date::year y) const;
|
||||
int compare(date::year y, const MonthDayTime& x, date::year yx,
|
||||
std::chrono::seconds offset, std::chrono::minutes prev_save) const;
|
||||
|
||||
friend std::istream& operator>>(std::istream& is, MonthDayTime& x);
|
||||
friend std::ostream& operator<<(std::ostream& os, const MonthDayTime& x);
|
||||
};
|
||||
|
||||
// A Rule specifies one or more set of datetimes without using an offset.
|
||||
// Multiple dates are specified with multiple years. The years in effect
|
||||
// go from starting_year_ to ending_year_, inclusive. starting_year_ <=
|
||||
// ending_year_. save_ is ineffect for times from the specified time
|
||||
// onward, including the specified time. When the specified time is
|
||||
// local, it uses the save_ from the chronologically previous Rule, or if
|
||||
// there is none, 0.
|
||||
|
||||
//forward declare to avoid warnings in gcc 6.2
|
||||
class Rule;
|
||||
bool operator==(const Rule& x, const Rule& y);
|
||||
bool operator<(const Rule& x, const Rule& y);
|
||||
bool operator==(const Rule& x, const date::year& y);
|
||||
bool operator<(const Rule& x, const date::year& y);
|
||||
bool operator==(const date::year& x, const Rule& y);
|
||||
bool operator<(const date::year& x, const Rule& y);
|
||||
bool operator==(const Rule& x, const std::string& y);
|
||||
bool operator<(const Rule& x, const std::string& y);
|
||||
bool operator==(const std::string& x, const Rule& y);
|
||||
bool operator<(const std::string& x, const Rule& y);
|
||||
std::ostream& operator<<(std::ostream& os, const Rule& r);
|
||||
|
||||
class Rule
|
||||
{
|
||||
private:
|
||||
std::string name_;
|
||||
date::year starting_year_{0};
|
||||
date::year ending_year_{0};
|
||||
MonthDayTime starting_at_;
|
||||
std::chrono::minutes save_{0};
|
||||
std::string abbrev_;
|
||||
|
||||
public:
|
||||
Rule() = default;
|
||||
explicit Rule(const std::string& s);
|
||||
Rule(const Rule& r, date::year starting_year, date::year ending_year);
|
||||
|
||||
const std::string& name() const {return name_;}
|
||||
const std::string& abbrev() const {return abbrev_;}
|
||||
|
||||
const MonthDayTime& mdt() const {return starting_at_;}
|
||||
const date::year& starting_year() const {return starting_year_;}
|
||||
const date::year& ending_year() const {return ending_year_;}
|
||||
const std::chrono::minutes& save() const {return save_;}
|
||||
|
||||
static void split_overlaps(std::vector<Rule>& rules);
|
||||
|
||||
friend bool operator==(const Rule& x, const Rule& y);
|
||||
friend bool operator<(const Rule& x, const Rule& y);
|
||||
friend bool operator==(const Rule& x, const date::year& y);
|
||||
friend bool operator<(const Rule& x, const date::year& y);
|
||||
friend bool operator==(const date::year& x, const Rule& y);
|
||||
friend bool operator<(const date::year& x, const Rule& y);
|
||||
friend bool operator==(const Rule& x, const std::string& y);
|
||||
friend bool operator<(const Rule& x, const std::string& y);
|
||||
friend bool operator==(const std::string& x, const Rule& y);
|
||||
friend bool operator<(const std::string& x, const Rule& y);
|
||||
|
||||
friend std::ostream& operator<<(std::ostream& os, const Rule& r);
|
||||
|
||||
private:
|
||||
date::day day() const;
|
||||
date::month month() const;
|
||||
static void split_overlaps(std::vector<Rule>& rules, std::size_t i, std::size_t& e);
|
||||
static bool overlaps(const Rule& x, const Rule& y);
|
||||
static void split(std::vector<Rule>& rules, std::size_t i, std::size_t k,
|
||||
std::size_t& e);
|
||||
};
|
||||
|
||||
inline bool operator!=(const Rule& x, const Rule& y) {return !(x == y);}
|
||||
inline bool operator> (const Rule& x, const Rule& y) {return y < x;}
|
||||
inline bool operator<=(const Rule& x, const Rule& y) {return !(y < x);}
|
||||
inline bool operator>=(const Rule& x, const Rule& y) {return !(x < y);}
|
||||
|
||||
inline bool operator!=(const Rule& x, const date::year& y) {return !(x == y);}
|
||||
inline bool operator> (const Rule& x, const date::year& y) {return y < x;}
|
||||
inline bool operator<=(const Rule& x, const date::year& y) {return !(y < x);}
|
||||
inline bool operator>=(const Rule& x, const date::year& y) {return !(x < y);}
|
||||
|
||||
inline bool operator!=(const date::year& x, const Rule& y) {return !(x == y);}
|
||||
inline bool operator> (const date::year& x, const Rule& y) {return y < x;}
|
||||
inline bool operator<=(const date::year& x, const Rule& y) {return !(y < x);}
|
||||
inline bool operator>=(const date::year& x, const Rule& y) {return !(x < y);}
|
||||
|
||||
inline bool operator!=(const Rule& x, const std::string& y) {return !(x == y);}
|
||||
inline bool operator> (const Rule& x, const std::string& y) {return y < x;}
|
||||
inline bool operator<=(const Rule& x, const std::string& y) {return !(y < x);}
|
||||
inline bool operator>=(const Rule& x, const std::string& y) {return !(x < y);}
|
||||
|
||||
inline bool operator!=(const std::string& x, const Rule& y) {return !(x == y);}
|
||||
inline bool operator> (const std::string& x, const Rule& y) {return y < x;}
|
||||
inline bool operator<=(const std::string& x, const Rule& y) {return !(y < x);}
|
||||
inline bool operator>=(const std::string& x, const Rule& y) {return !(x < y);}
|
||||
|
||||
struct zonelet
|
||||
{
|
||||
enum tag {has_rule, has_save, is_empty};
|
||||
|
||||
std::chrono::seconds gmtoff_;
|
||||
tag tag_ = has_rule;
|
||||
|
||||
#if !defined(_MSC_VER) || (_MSC_VER >= 1900)
|
||||
union U
|
||||
#else
|
||||
struct U
|
||||
#endif
|
||||
{
|
||||
std::string rule_;
|
||||
std::chrono::minutes save_;
|
||||
|
||||
~U() {}
|
||||
U() {}
|
||||
U(const U&) {}
|
||||
U& operator=(const U&) = delete;
|
||||
} u;
|
||||
|
||||
std::string format_;
|
||||
date::year until_year_{0};
|
||||
MonthDayTime until_date_;
|
||||
sys_seconds until_utc_;
|
||||
local_seconds until_std_;
|
||||
local_seconds until_loc_;
|
||||
std::chrono::minutes initial_save_{};
|
||||
std::string initial_abbrev_;
|
||||
std::pair<const Rule*, date::year> first_rule_{nullptr, date::year::min()};
|
||||
std::pair<const Rule*, date::year> last_rule_{nullptr, date::year::max()};
|
||||
|
||||
~zonelet();
|
||||
zonelet();
|
||||
zonelet(const zonelet& i);
|
||||
zonelet& operator=(const zonelet&) = delete;
|
||||
};
|
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace date
|
||||
|
||||
#if defined(_MSC_VER) && (_MSC_VER < 1900)
|
||||
#include "tz.h"
|
||||
#endif
|
||||
|
||||
#endif // TZ_PRIVATE_H
|
|
@ -94,7 +94,7 @@ class lambda_t {
|
|||
}
|
||||
|
||||
using node = boost::make_recursive_variant<
|
||||
std::nullptr_t, std::string, int, double, bool, uint64_t, uint32_t,
|
||||
std::nullptr_t, std::string, int, double, bool, uint64_t, int64_t, uint32_t,
|
||||
internal::lambda_t<boost::recursive_variant_>,
|
||||
std::shared_ptr<internal::object_t<boost::recursive_variant_>>,
|
||||
std::map<const std::string, boost::recursive_variant_>,
|
||||
|
|
|
@ -38,6 +38,12 @@ class render_node: public boost::static_visitor<std::string> {
|
|||
return ss.str();
|
||||
}
|
||||
|
||||
std::string operator()(const int64_t& value) const {
|
||||
std::stringstream ss;
|
||||
ss << value;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
std::string operator()(const uint32_t& value) const {
|
||||
std::stringstream ss;
|
||||
ss << value;
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
#ifndef CACHE_HPP
|
||||
#define CACHE_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <unordered_map>
|
||||
#include "cache_policy.hpp"
|
||||
|
||||
namespace caches
|
||||
{
|
||||
|
||||
// Base class for caching algorithms
|
||||
template <typename Key, typename Value, typename Policy = NoCachePolicy<Key>>
|
||||
class fixed_sized_cache
|
||||
{
|
||||
public:
|
||||
|
||||
using iterator = typename std::unordered_map<Key, Value>::iterator;
|
||||
|
||||
using const_iterator =
|
||||
typename std::unordered_map<Key, Value>::const_iterator;
|
||||
|
||||
using operation_guard = typename std::lock_guard<std::mutex>;
|
||||
|
||||
fixed_sized_cache(
|
||||
size_t max_size,
|
||||
const Policy& policy = Policy())
|
||||
: max_cache_size{max_size},
|
||||
cache_policy(policy)
|
||||
{
|
||||
if (max_cache_size == 0)
|
||||
{
|
||||
max_cache_size = std::numeric_limits<size_t>::max();
|
||||
}
|
||||
}
|
||||
|
||||
void Put(const Key& key, const Value& value)
|
||||
{
|
||||
operation_guard{safe_op};
|
||||
auto elem_it = FindElem(key);
|
||||
|
||||
if (elem_it == cache_items_map.end())
|
||||
{
|
||||
// add new element to the cache
|
||||
if (Size() + 1 > max_cache_size)
|
||||
{
|
||||
auto disp_candidate_key = cache_policy.ReplCandidate();
|
||||
|
||||
Erase(disp_candidate_key);
|
||||
}
|
||||
|
||||
Insert(key, value);
|
||||
}
|
||||
else
|
||||
{
|
||||
// update previous value
|
||||
Update(key, value);
|
||||
}
|
||||
}
|
||||
|
||||
bool Contains(const Key& key)
|
||||
{
|
||||
operation_guard{safe_op};
|
||||
auto elem_it = FindElem(key);
|
||||
return elem_it != cache_items_map.end();
|
||||
}
|
||||
|
||||
const Value& Get(const Key& key) const
|
||||
{
|
||||
operation_guard{safe_op};
|
||||
auto elem_it = FindElem(key);
|
||||
|
||||
if (elem_it == cache_items_map.end())
|
||||
{
|
||||
throw std::range_error{"No such element in the cache"};
|
||||
}
|
||||
cache_policy.Touch(key);
|
||||
|
||||
return elem_it->second;
|
||||
}
|
||||
|
||||
const size_t Size() const
|
||||
{
|
||||
operation_guard{safe_op};
|
||||
|
||||
return cache_items_map.size();
|
||||
}
|
||||
|
||||
// return a key of a displacement candidate
|
||||
void Clear()
|
||||
{
|
||||
operation_guard{safe_op};
|
||||
|
||||
cache_policy.Clear();
|
||||
cache_items_map.clear();
|
||||
}
|
||||
|
||||
protected:
|
||||
|
||||
void Insert(const Key& key, const Value& value)
|
||||
{
|
||||
cache_policy.Insert(key);
|
||||
cache_items_map.emplace(std::make_pair(key, value));
|
||||
}
|
||||
|
||||
void Erase(const Key& key)
|
||||
{
|
||||
cache_policy.Erase(key);
|
||||
cache_items_map.erase(key);
|
||||
}
|
||||
|
||||
void Update(const Key& key, const Value& value)
|
||||
{
|
||||
cache_policy.Touch(key);
|
||||
cache_items_map[key] = value;
|
||||
}
|
||||
|
||||
const_iterator FindElem(const Key& key) const
|
||||
{
|
||||
return cache_items_map.find(key);
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
std::unordered_map<Key, Value> cache_items_map;
|
||||
|
||||
mutable Policy cache_policy;
|
||||
mutable std::mutex safe_op;
|
||||
|
||||
size_t max_cache_size;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif // CACHE_HPP
|
|
@ -0,0 +1,77 @@
|
|||
#ifndef CACHE_POLICY_HPP
|
||||
#define CACHE_POLICY_HPP
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
namespace caches
|
||||
{
|
||||
|
||||
template <typename Key>
|
||||
|
||||
class ICachePolicy
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~ICachePolicy() {}
|
||||
|
||||
// handle element insertion in a cache
|
||||
virtual void Insert(const Key& key) = 0;
|
||||
|
||||
// handle request to the key-element in a cache
|
||||
virtual void Touch(const Key& key) = 0;
|
||||
|
||||
// handle element deletion from a cache
|
||||
virtual void Erase(const Key& key) = 0;
|
||||
|
||||
// return a key of a replacement candidate
|
||||
virtual const Key& ReplCandidate() const = 0;
|
||||
|
||||
// clear the cache
|
||||
virtual void Clear() = 0;
|
||||
|
||||
};
|
||||
|
||||
template <typename Key>
|
||||
class NoCachePolicy : public ICachePolicy<Key>
|
||||
{
|
||||
public:
|
||||
|
||||
NoCachePolicy() = default;
|
||||
|
||||
~NoCachePolicy() override = default;
|
||||
|
||||
void Insert(const Key& key) override
|
||||
{
|
||||
key_storage.emplace(key);
|
||||
}
|
||||
|
||||
void Touch(const Key& key) override
|
||||
{
|
||||
// do not do anything
|
||||
}
|
||||
|
||||
void Erase(const Key& key) override
|
||||
{
|
||||
key_storage.erase(key);
|
||||
}
|
||||
|
||||
// return a key of a displacement candidate
|
||||
const Key& ReplCandidate() const override
|
||||
{
|
||||
return *key_storage.crbegin();
|
||||
}
|
||||
|
||||
// return a key of a displacement candidate
|
||||
void Clear() override
|
||||
{
|
||||
key_storage.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::unordered_set<Key> key_storage;
|
||||
};
|
||||
|
||||
} // namespace caches
|
||||
|
||||
#endif // CACHE_POLICY_HPP
|
|
@ -0,0 +1,53 @@
|
|||
#ifndef FIFO_CACHE_POLICY_HPP
|
||||
#define FIFO_CACHE_POLICY_HPP
|
||||
|
||||
#include <list>
|
||||
#include "cache_policy.hpp"
|
||||
|
||||
namespace caches
|
||||
{
|
||||
|
||||
template <typename Key>
|
||||
class FIFOCachePolicy : public ICachePolicy<Key>
|
||||
{
|
||||
public:
|
||||
|
||||
FIFOCachePolicy() = default;
|
||||
~FIFOCachePolicy() = default;
|
||||
|
||||
void Insert(const Key& key) override
|
||||
{
|
||||
fifo_queue.emplace_front(key);
|
||||
}
|
||||
|
||||
// handle request to the key-element in a cache
|
||||
void Touch(const Key& key) override
|
||||
{
|
||||
// nothing to do here in the FIFO strategy
|
||||
}
|
||||
|
||||
// handle element deletion from a cache
|
||||
void Erase(const Key& key) override
|
||||
{
|
||||
fifo_queue.pop_back();
|
||||
}
|
||||
|
||||
// return a key of a replacement candidate
|
||||
const Key& ReplCandidate() const override
|
||||
{
|
||||
return fifo_queue.back();
|
||||
}
|
||||
|
||||
// return a key of a displacement candidate
|
||||
void Clear() override
|
||||
{
|
||||
fifo_queue.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::list<Key> fifo_queue;
|
||||
};
|
||||
} // namespace caches
|
||||
|
||||
#endif // FIFO_CACHE_POLICY_HPP
|
|
@ -0,0 +1,76 @@
|
|||
#ifndef LFU_CACHE_POLICY_HPP
|
||||
#define LFU_CACHE_POLICY_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <unordered_map>
|
||||
#include <map>
|
||||
#include <iostream>
|
||||
#include "cache_policy.hpp"
|
||||
|
||||
namespace caches
|
||||
{
|
||||
template <typename Key>
|
||||
class LFUCachePolicy : public ICachePolicy<Key>
|
||||
{
|
||||
public:
|
||||
|
||||
using lfu_iterator = typename std::multimap<std::size_t, Key>::iterator;
|
||||
|
||||
LFUCachePolicy() = default;
|
||||
|
||||
~LFUCachePolicy() override = default;
|
||||
|
||||
void Insert(const Key& key) override
|
||||
{
|
||||
constexpr std::size_t INIT_VAL = 1;
|
||||
|
||||
// all new value initialized with the frequency 1
|
||||
lfu_storage[key] = frequency_storage.emplace_hint(
|
||||
frequency_storage.cbegin(), INIT_VAL, key);
|
||||
}
|
||||
|
||||
void Touch(const Key& key) override
|
||||
{
|
||||
// get the previous frequency value of a key
|
||||
auto elem_for_update = lfu_storage[key];
|
||||
|
||||
auto updated_elem = std::make_pair(
|
||||
elem_for_update->first + 1, elem_for_update->second);
|
||||
|
||||
// update the previous value
|
||||
frequency_storage.erase(elem_for_update);
|
||||
|
||||
lfu_storage[key] = frequency_storage.emplace_hint(
|
||||
frequency_storage.cend(), std::move(updated_elem));
|
||||
}
|
||||
|
||||
void Erase(const Key& key) override
|
||||
{
|
||||
frequency_storage.erase(lfu_storage[key]);
|
||||
lfu_storage.erase(key);
|
||||
}
|
||||
|
||||
const Key& ReplCandidate() const override
|
||||
{
|
||||
// at the beginning of the frequency_storage we have the
|
||||
// least frequency used value
|
||||
return frequency_storage.cbegin()->second;
|
||||
}
|
||||
|
||||
// return a key of a displacement candidate
|
||||
void Clear() override
|
||||
{
|
||||
frequency_storage.clear();
|
||||
lfu_storage.clear();
|
||||
}
|
||||
|
||||
|
||||
private:
|
||||
|
||||
std::multimap<std::size_t, Key> frequency_storage;
|
||||
|
||||
std::unordered_map<Key, lfu_iterator> lfu_storage;
|
||||
};
|
||||
} // namespace caches
|
||||
|
||||
#endif // LFU_CACHE_POLICY_HPP
|
|
@ -0,0 +1,63 @@
|
|||
#ifndef LRU_CACHE_POLICY_HPP
|
||||
#define LRU_CACHE_POLICY_HPP
|
||||
|
||||
#include <list>
|
||||
#include <unordered_map>
|
||||
#include "cache_policy.hpp"
|
||||
|
||||
namespace caches
|
||||
{
|
||||
template <typename Key>
|
||||
class LRUCachePolicy : public ICachePolicy<Key>
|
||||
{
|
||||
|
||||
public:
|
||||
|
||||
using lru_iterator = typename std::list<Key>::const_iterator;
|
||||
|
||||
LRUCachePolicy() = default;
|
||||
~LRUCachePolicy() = default;
|
||||
|
||||
void Insert(const Key& key) override
|
||||
{
|
||||
lru_queue.emplace_front(key);
|
||||
key_finder[key] = lru_queue.cbegin();
|
||||
}
|
||||
|
||||
void Touch(const Key& key) override
|
||||
{
|
||||
// move the touched element at the beginning of the lru_queue
|
||||
lru_queue.splice(lru_queue.cbegin(), lru_queue, key_finder[key]);
|
||||
}
|
||||
|
||||
void Erase(const Key& key) override
|
||||
{
|
||||
// remove the least recently used element
|
||||
key_finder.erase(lru_queue.back());
|
||||
|
||||
lru_queue.pop_back();
|
||||
}
|
||||
|
||||
// return a key of a displacement candidate
|
||||
const Key& ReplCandidate() const override
|
||||
{
|
||||
return lru_queue.back();
|
||||
}
|
||||
|
||||
// return a key of a displacement candidate
|
||||
void Clear() override
|
||||
{
|
||||
lru_queue.clear();
|
||||
key_finder.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
std::list<Key> lru_queue;
|
||||
|
||||
std::unordered_map<Key, lru_iterator> key_finder;
|
||||
};
|
||||
|
||||
} // namespace caches
|
||||
|
||||
#endif // LRU_CACHE_POLICY_HPP
|
184
main.cpp
184
main.cpp
|
@ -6,10 +6,8 @@
|
|||
#include "src/MicroCore.h"
|
||||
#include "src/page.h"
|
||||
|
||||
|
||||
#include "ext/member_checker.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
|
||||
using boost::filesystem::path;
|
||||
|
||||
|
@ -21,11 +19,6 @@ int main(int ac, const char* av[]) {
|
|||
xmreg::CmdLineOptions opts {ac, av};
|
||||
|
||||
auto help_opt = opts.get_option<bool>("help");
|
||||
auto testnet_opt = opts.get_option<bool>("testnet");
|
||||
auto enable_key_image_checker_opt = opts.get_option<bool>("enable-key-image-checker");
|
||||
auto enable_output_key_checker_opt = opts.get_option<bool>("enable-output-key-checker");
|
||||
auto enable_autorefresh_option_opt = opts.get_option<bool>("enable-autorefresh-option");
|
||||
auto enable_pusher_opt = opts.get_option<bool>("enable-pusher");
|
||||
|
||||
// if help was chosen, display help text and finish
|
||||
if (*help_opt)
|
||||
|
@ -33,19 +26,38 @@ int main(int ac, const char* av[]) {
|
|||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
auto port_opt = opts.get_option<string>("port");
|
||||
auto bc_path_opt = opts.get_option<string>("bc-path");
|
||||
auto deamon_url_opt = opts.get_option<string>("deamon-url");
|
||||
auto ssl_crt_file_opt = opts.get_option<string>("ssl-crt-file");
|
||||
auto ssl_key_file_opt = opts.get_option<string>("ssl-key-file");
|
||||
auto no_blocks_on_index_opt = opts.get_option<string>("no-blocks-on-index");
|
||||
auto testnet_url = opts.get_option<string>("testnet-url");
|
||||
auto mainnet_url = opts.get_option<string>("mainnet-url");
|
||||
auto testnet_opt = opts.get_option<bool>("testnet");
|
||||
auto enable_key_image_checker_opt = opts.get_option<bool>("enable-key-image-checker");
|
||||
auto enable_output_key_checker_opt = opts.get_option<bool>("enable-output-key-checker");
|
||||
auto enable_autorefresh_option_opt = opts.get_option<bool>("enable-autorefresh-option");
|
||||
auto enable_pusher_opt = opts.get_option<bool>("enable-pusher");
|
||||
auto enable_mixin_details_opt = opts.get_option<bool>("enable-mixin-details");
|
||||
auto enable_mempool_cache_opt = opts.get_option<bool>("enable-mempool-cache");
|
||||
auto enable_json_api_opt = opts.get_option<bool>("enable-json-api");
|
||||
auto enable_tx_cache_opt = opts.get_option<bool>("enable-tx-cache");
|
||||
auto enable_block_cache_opt = opts.get_option<bool>("enable-block-cache");
|
||||
auto show_cache_times_opt = opts.get_option<bool>("show-cache-times");
|
||||
|
||||
bool testnet {*testnet_opt};
|
||||
bool enable_pusher {*enable_pusher_opt};
|
||||
bool enable_key_image_checker {*enable_key_image_checker_opt};
|
||||
bool enable_autorefresh_option {*enable_autorefresh_option_opt};
|
||||
bool enable_output_key_checker {*enable_output_key_checker_opt};
|
||||
bool enable_mixin_details {*enable_mixin_details_opt};
|
||||
bool enable_mempool_cache {*enable_mempool_cache_opt};
|
||||
bool enable_json_api {*enable_json_api_opt};
|
||||
bool enable_tx_cache {*enable_tx_cache_opt};
|
||||
bool enable_block_cache {*enable_block_cache_opt};
|
||||
bool show_cache_times {*show_cache_times_opt};
|
||||
|
||||
auto port_opt = opts.get_option<string>("port");
|
||||
auto bc_path_opt = opts.get_option<string>("bc-path");
|
||||
auto custom_db_path_opt = opts.get_option<string>("custom-db-path");
|
||||
auto deamon_url_opt = opts.get_option<string>("deamon-url");
|
||||
auto ssl_crt_file_opt = opts.get_option<string>("ssl-crt-file");
|
||||
auto ssl_key_file_opt = opts.get_option<string>("ssl-key-file");
|
||||
auto no_blocks_on_index_opt = opts.get_option<string>("no-blocks-on-index");
|
||||
|
||||
// set monero log output level
|
||||
uint32_t log_level = 0;
|
||||
|
@ -114,36 +126,6 @@ int main(int ac, const char* av[]) {
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// check if we have path to lmdb2 (i.e., custom db)
|
||||
// and if it exists
|
||||
|
||||
string custom_db_path_str;
|
||||
|
||||
if (custom_db_path_opt)
|
||||
{
|
||||
if (boost::filesystem::exists(boost::filesystem::path(*custom_db_path_opt)))
|
||||
{
|
||||
custom_db_path_str = *custom_db_path_opt;
|
||||
}
|
||||
else
|
||||
{
|
||||
cerr << "Custom db path: " << *custom_db_path_opt
|
||||
<< "does not exist" << endl;
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// if not given assume it is located in ~./bitmonero/lmdb2 folder
|
||||
// or ~./bitmonero/testnet/lmdb2 for testnet network
|
||||
custom_db_path_str = blockchain_path.parent_path().string()
|
||||
+ string("/lmdb2");
|
||||
}
|
||||
|
||||
custom_db_path_str = xmreg::remove_trailing_path_separator(custom_db_path_str);
|
||||
|
||||
|
||||
string deamon_url {*deamon_url_opt};
|
||||
|
||||
if (testnet && deamon_url == "http:://127.0.0.1:18081")
|
||||
|
@ -154,13 +136,19 @@ int main(int ac, const char* av[]) {
|
|||
xmreg::page xmrblocks(&mcore,
|
||||
core_storage,
|
||||
deamon_url,
|
||||
custom_db_path_str,
|
||||
testnet,
|
||||
enable_pusher,
|
||||
enable_key_image_checker,
|
||||
enable_output_key_checker,
|
||||
enable_autorefresh_option,
|
||||
no_blocks_on_index);
|
||||
enable_mixin_details,
|
||||
enable_mempool_cache,
|
||||
enable_tx_cache,
|
||||
enable_block_cache,
|
||||
show_cache_times,
|
||||
no_blocks_on_index,
|
||||
*testnet_url,
|
||||
*mainnet_url);
|
||||
|
||||
// crow instance
|
||||
crow::SimpleApp app;
|
||||
|
@ -350,6 +338,108 @@ int main(int ac, const char* av[]) {
|
|||
return text;
|
||||
});
|
||||
|
||||
if (enable_json_api)
|
||||
{
|
||||
CROW_ROUTE(app, "/api/transaction/<string>")
|
||||
([&](const crow::request &req, string tx_hash) {
|
||||
|
||||
crow::response r{xmrblocks.json_transaction(tx_hash).dump()};
|
||||
|
||||
r.add_header("Access-Control-Allow-Origin", "*");
|
||||
r.add_header("Access-Control-Allow-Headers", "Content-Type");
|
||||
r.add_header("Content-Type", "application/json");
|
||||
|
||||
return r;
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/api/block/<string>")
|
||||
([&](const crow::request &req, string block_no_or_hash) {
|
||||
|
||||
crow::response r{xmrblocks.json_block(block_no_or_hash).dump()};
|
||||
|
||||
r.add_header("Access-Control-Allow-Origin", "*");
|
||||
r.add_header("Access-Control-Allow-Headers", "Content-Type");
|
||||
r.add_header("Content-Type", "application/json");
|
||||
|
||||
return r;
|
||||
});
|
||||
|
||||
|
||||
CROW_ROUTE(app, "/api/transactions").methods("GET"_method)
|
||||
([&](const crow::request &req) {
|
||||
|
||||
string page = regex_search(req.raw_url, regex {"page=\\d+"}) ?
|
||||
req.url_params.get("page") : "0";
|
||||
|
||||
string limit = regex_search(req.raw_url, regex {"limit=\\d+"}) ?
|
||||
req.url_params.get("limit") : "25";
|
||||
|
||||
crow::response r{xmrblocks.json_transactions(page, limit).dump()};
|
||||
|
||||
r.add_header("Access-Control-Allow-Origin", "*");
|
||||
r.add_header("Access-Control-Allow-Headers", "Content-Type");
|
||||
r.add_header("Content-Type", "application/json");
|
||||
|
||||
return r;
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/api/mempool")
|
||||
([&](const crow::request &req) {
|
||||
|
||||
crow::response r{xmrblocks.json_mempool().dump()};
|
||||
|
||||
r.add_header("Access-Control-Allow-Origin", "*");
|
||||
r.add_header("Access-Control-Allow-Headers", "Content-Type");
|
||||
r.add_header("Content-Type", "application/json");
|
||||
|
||||
return r;
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/api/search/<string>")
|
||||
([&](const crow::request &req, string search_value) {
|
||||
|
||||
crow::response r{xmrblocks.json_search(search_value).dump()};
|
||||
|
||||
r.add_header("Access-Control-Allow-Origin", "*");
|
||||
r.add_header("Access-Control-Allow-Headers", "Content-Type");
|
||||
r.add_header("Content-Type", "application/json");
|
||||
|
||||
return r;
|
||||
});
|
||||
|
||||
CROW_ROUTE(app, "/api/outputs").methods("GET"_method)
|
||||
([&](const crow::request &req) {
|
||||
|
||||
string tx_hash = regex_search(req.raw_url, regex {"txhash=\\w+"}) ?
|
||||
req.url_params.get("txhash") : "";
|
||||
|
||||
string address = regex_search(req.raw_url, regex {"address=\\w+"}) ?
|
||||
req.url_params.get("address") : "";
|
||||
|
||||
string viewkey = regex_search(req.raw_url, regex {"viewkey=\\w+"}) ?
|
||||
req.url_params.get("viewkey") : "";
|
||||
|
||||
bool tx_prove{false};
|
||||
|
||||
try {
|
||||
tx_prove = regex_search(req.raw_url, regex {"txprove=[01]"}) ?
|
||||
boost::lexical_cast<bool>(req.url_params.get("txprove")) :
|
||||
false;
|
||||
}
|
||||
catch (const boost::bad_lexical_cast &e) {
|
||||
cerr << "Cant parse tx_prove as bool. Using default value" << endl;
|
||||
}
|
||||
|
||||
crow::response r{xmrblocks.json_outputs(
|
||||
tx_hash, address, viewkey, tx_prove).dump()};
|
||||
|
||||
r.add_header("Access-Control-Allow-Origin", "*");
|
||||
r.add_header("Access-Control-Allow-Headers", "Content-Type");
|
||||
r.add_header("Content-Type", "application/json");
|
||||
|
||||
return r;
|
||||
});
|
||||
}
|
||||
|
||||
if (enable_autorefresh_option)
|
||||
{
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 575 KiB |
|
@ -13,7 +13,8 @@ set(SOURCE_FILES
|
|||
tools.cpp
|
||||
CmdLineOptions.cpp
|
||||
page.h
|
||||
rpccalls.cpp rpccalls.h version.h.in)
|
||||
rpccalls.cpp rpccalls.h
|
||||
version.h.in)
|
||||
|
||||
# make static library called libmyxrm
|
||||
# that we are going to link to
|
||||
|
|
|
@ -27,14 +27,30 @@ namespace xmreg
|
|||
"use testnet blockchain")
|
||||
("enable-pusher", value<bool>()->default_value(false)->implicit_value(true),
|
||||
"enable pushing signed tx")
|
||||
("enable-mixin-details", value<bool>()->default_value(false)->implicit_value(true),
|
||||
"enable mixin details for key images, e.g., timescale, mixin of mixins, in tx context")
|
||||
("enable-key-image-checker", value<bool>()->default_value(false)->implicit_value(true),
|
||||
"enable key images file checker")
|
||||
("enable-output-key-checker", value<bool>()->default_value(false)->implicit_value(true),
|
||||
"enable outputs key file checker")
|
||||
("enable-mempool-cache", value<bool>()->default_value(true),
|
||||
"enable caching txs in the mempool")
|
||||
("enable-json-api", value<bool>()->default_value(true),
|
||||
"enable JSON REST api")
|
||||
("enable-tx-cache", value<bool>()->default_value(false)->implicit_value(true),
|
||||
"enable caching of tx details")
|
||||
("show-cache-times", value<bool>()->default_value(false)->implicit_value(true),
|
||||
"show times of getting data from cache vs no cache")
|
||||
("enable-block-cache", value<bool>()->default_value(false)->implicit_value(true),
|
||||
"enable caching of block details")
|
||||
("enable-autorefresh-option", value<bool>()->default_value(false)->implicit_value(true),
|
||||
"enable users to have the index page on autorefresh")
|
||||
("port,p", value<string>()->default_value("8081"),
|
||||
"default port")
|
||||
("testnet-url", value<string>()->default_value(""),
|
||||
"you can specifiy testnet url, if you run it on mainet. link will show on front page to testnet explorer")
|
||||
("mainnet-url", value<string>()->default_value(""),
|
||||
"you can specifiy mainnet url, if you run it on testnet. link will show on front page to mainnet explorer")
|
||||
("no-blocks-on-index", value<string>()->default_value("10"),
|
||||
"number of last blocks to be shown on index page")
|
||||
("bc-path,b", value<string>(),
|
||||
|
@ -43,8 +59,6 @@ namespace xmreg
|
|||
"A path to crt file for ssl (https) functionality")
|
||||
("ssl-key-file", value<string>(),
|
||||
"A path to key file for ssl (https) functionality")
|
||||
("custom-db-path,c", value<string>(),
|
||||
"path to the custom lmdb database used for searching things")
|
||||
("deamon-url,d", value<string>()->default_value("http:://127.0.0.1:18081"),
|
||||
"monero address string");
|
||||
|
||||
|
|
755
src/mylmdb.h
755
src/mylmdb.h
|
@ -1,755 +0,0 @@
|
|||
//
|
||||
// Created by mwo on 27/04/16.
|
||||
//
|
||||
#ifndef XMRLMDBCPP_MYLMDB_H
|
||||
#define XMRLMDBCPP_MYLMDB_H
|
||||
|
||||
#include "../ext/lmdb++.h"
|
||||
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
namespace xmreg
|
||||
{
|
||||
|
||||
using epee::string_tools::pod_to_hex;
|
||||
using epee::string_tools::hex_to_pod;
|
||||
|
||||
using namespace std;
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* Stores info about outputs useful
|
||||
* for checking which ouputs belong to a
|
||||
* given address and viewkey
|
||||
*/
|
||||
struct output_info
|
||||
{
|
||||
crypto::public_key out_pub_key;
|
||||
crypto::hash tx_hash;
|
||||
crypto::public_key tx_pub_key;
|
||||
uint64_t amount;
|
||||
uint64_t index_in_tx;
|
||||
};
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const output_info& out_info)
|
||||
{
|
||||
os << ", out_pub_key: " << out_info.out_pub_key
|
||||
<< ", tx_hash: " << out_info.tx_hash
|
||||
<< ", tx_pub_key: " << out_info.tx_pub_key
|
||||
<< ", amount: " << XMR_AMOUNT(out_info.amount)
|
||||
<< ", index_in_tx: " << out_info.index_in_tx;
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
class MyLMDB
|
||||
{
|
||||
|
||||
|
||||
static const uint64_t DEFAULT_MAPSIZE = 30UL * 1024UL * 1024UL * 1024UL; /* 30 GiB */
|
||||
static const uint64_t DEFAULT_NO_DBs = 10;
|
||||
|
||||
|
||||
string m_db_path;
|
||||
|
||||
uint64_t m_mapsize;
|
||||
uint64_t m_no_dbs;
|
||||
|
||||
lmdb::env m_env;
|
||||
|
||||
|
||||
public:
|
||||
MyLMDB(string _path,
|
||||
uint64_t _mapsize = DEFAULT_MAPSIZE,
|
||||
uint64_t _no_dbs = DEFAULT_NO_DBs)
|
||||
: m_db_path {_path},
|
||||
m_mapsize {_mapsize},
|
||||
m_no_dbs {_no_dbs},
|
||||
m_env {nullptr}
|
||||
{
|
||||
create_and_open_env();
|
||||
}
|
||||
|
||||
bool
|
||||
create_and_open_env()
|
||||
{
|
||||
try
|
||||
{ m_env = lmdb::env::create();
|
||||
m_env.set_mapsize(m_mapsize);
|
||||
m_env.set_max_dbs(m_no_dbs);
|
||||
m_env.open(m_db_path.c_str(), MDB_CREATE, 0664);
|
||||
}
|
||||
catch (lmdb::error& e )
|
||||
{
|
||||
cerr << e.what() << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
write_key_images(const transaction& tx)
|
||||
{
|
||||
crypto::hash tx_hash = get_transaction_hash(tx);
|
||||
|
||||
string tx_hash_str = pod_to_hex(tx_hash);
|
||||
|
||||
vector<cryptonote::txin_to_key> key_images
|
||||
= xmreg::get_key_images(tx);
|
||||
|
||||
lmdb::txn wtxn {nullptr};
|
||||
lmdb::dbi wdbi {0};
|
||||
|
||||
unsigned int flags = MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED;
|
||||
|
||||
try
|
||||
{
|
||||
wtxn = lmdb::txn::begin(m_env);
|
||||
wdbi = lmdb::dbi::open(wtxn, "key_images", flags);
|
||||
}
|
||||
catch (lmdb::error& e )
|
||||
{
|
||||
cerr << e.what() << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (const cryptonote::txin_to_key& key_image: key_images)
|
||||
{
|
||||
string key_img_str = pod_to_hex(key_image.k_image);
|
||||
|
||||
lmdb::val key_img_val {key_img_str};
|
||||
lmdb::val tx_hash_val {tx_hash_str};
|
||||
|
||||
wdbi.put(wtxn, key_img_val, tx_hash_val);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
wtxn.commit();
|
||||
}
|
||||
catch (lmdb::error& e )
|
||||
{
|
||||
cerr << e.what() << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
write_output_public_keys(const transaction& tx, const block& blk)
|
||||
{
|
||||
crypto::hash tx_hash = get_transaction_hash(tx);
|
||||
|
||||
crypto::public_key tx_pub_key = get_tx_pub_key_from_extra(tx);
|
||||
|
||||
string tx_hash_str = pod_to_hex(tx_hash);
|
||||
|
||||
vector<tuple<txout_to_key, uint64_t, uint64_t>> outputs =
|
||||
xmreg::get_ouputs_tuple(tx);
|
||||
|
||||
lmdb::txn wtxn {nullptr};
|
||||
lmdb::dbi wdbi1 {0};
|
||||
lmdb::dbi wdbi2 {0};
|
||||
lmdb::dbi wdbi3 {0};
|
||||
|
||||
unsigned int flags = MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED;
|
||||
|
||||
try
|
||||
{
|
||||
wtxn = lmdb::txn::begin(m_env);
|
||||
wdbi1 = lmdb::dbi::open(wtxn, "output_public_keys", flags);
|
||||
wdbi2 = lmdb::dbi::open(wtxn, "output_amounts", flags);
|
||||
wdbi3 = lmdb::dbi::open(wtxn, "output_info",
|
||||
flags | MDB_INTEGERKEY | MDB_INTEGERDUP);
|
||||
}
|
||||
catch (lmdb::error& e )
|
||||
{
|
||||
cerr << e.what() << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
for (auto& output: outputs)
|
||||
{
|
||||
|
||||
public_key out_pub_key = std::get<0>(output).key;
|
||||
|
||||
string public_key_str = pod_to_hex(out_pub_key);
|
||||
|
||||
lmdb::val public_key_val {public_key_str};
|
||||
lmdb::val tx_hash_val {tx_hash_str};
|
||||
|
||||
uint64_t amount = std::get<1>(output);
|
||||
|
||||
lmdb::val amount_val {static_cast<void*>(&amount), sizeof(amount)};
|
||||
|
||||
uint64_t index_in_tx = std::get<2>(output);
|
||||
|
||||
output_info out_info {out_pub_key, tx_hash,
|
||||
tx_pub_key, amount,
|
||||
index_in_tx};
|
||||
|
||||
uint64_t out_timestamp = blk.timestamp;
|
||||
|
||||
lmdb::val out_timestamp_val {static_cast<void*>(&out_timestamp),
|
||||
sizeof(out_timestamp)};
|
||||
lmdb::val out_info_val {static_cast<void*>(&out_info),
|
||||
sizeof(out_info)};
|
||||
|
||||
wdbi1.put(wtxn, public_key_val, tx_hash_val);
|
||||
wdbi2.put(wtxn, public_key_val, amount_val);
|
||||
wdbi3.put(wtxn, out_timestamp_val, out_info_val);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
wtxn.commit();
|
||||
}
|
||||
catch (lmdb::error& e )
|
||||
{
|
||||
cerr << e.what() << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
write_tx_public_key(const transaction& tx)
|
||||
{
|
||||
crypto::hash tx_hash = get_transaction_hash(tx);
|
||||
|
||||
string tx_hash_str = pod_to_hex(tx_hash);
|
||||
|
||||
unsigned int flags = MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED;
|
||||
|
||||
public_key pk = get_tx_pub_key_from_extra(tx);
|
||||
|
||||
string pk_str = pod_to_hex(pk);
|
||||
|
||||
try
|
||||
{
|
||||
lmdb::txn wtxn = lmdb::txn::begin(m_env);
|
||||
lmdb::dbi wdbi = lmdb::dbi::open(wtxn, "tx_public_keys", flags);
|
||||
|
||||
//cout << "Saving public_key: " << pk_str << endl;
|
||||
|
||||
lmdb::val public_key_val {pk_str};
|
||||
lmdb::val tx_hash_val {tx_hash_str};
|
||||
|
||||
wdbi.put(wtxn, public_key_val, tx_hash_val);
|
||||
|
||||
wtxn.commit();
|
||||
}
|
||||
catch (lmdb::error& e)
|
||||
{
|
||||
cerr << e.what() << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
write_payment_id(const transaction& tx)
|
||||
{
|
||||
crypto::hash tx_hash = get_transaction_hash(tx);
|
||||
|
||||
string tx_hash_str = pod_to_hex(tx_hash);
|
||||
|
||||
crypto::hash payment_id;
|
||||
crypto::hash8 payment_id8;
|
||||
|
||||
get_payment_id(tx, payment_id, payment_id8);
|
||||
|
||||
if (payment_id == null_hash)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int flags = MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED;
|
||||
|
||||
string payment_id_str = pod_to_hex(payment_id);
|
||||
|
||||
try
|
||||
{
|
||||
lmdb::txn wtxn = lmdb::txn::begin(m_env);
|
||||
lmdb::dbi wdbi = lmdb::dbi::open(wtxn, "payments_id", flags);
|
||||
|
||||
//cout << "Saving payiment_id: " << payment_id_str << endl;
|
||||
|
||||
lmdb::val payment_id_val {payment_id_str};
|
||||
lmdb::val tx_hash_val {tx_hash_str};
|
||||
|
||||
wdbi.put(wtxn, payment_id_val, tx_hash_val);
|
||||
|
||||
wtxn.commit();
|
||||
}
|
||||
catch (lmdb::error& e)
|
||||
{
|
||||
cerr << e.what() << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
write_encrypted_payment_id(const transaction& tx)
|
||||
{
|
||||
crypto::hash tx_hash = get_transaction_hash(tx);
|
||||
|
||||
string tx_hash_str = pod_to_hex(tx_hash);
|
||||
|
||||
crypto::hash payment_id;
|
||||
crypto::hash8 payment_id8;
|
||||
|
||||
get_payment_id(tx, payment_id, payment_id8);
|
||||
|
||||
if (payment_id8 == null_hash8)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned int flags = MDB_CREATE | MDB_DUPSORT | MDB_DUPFIXED;
|
||||
|
||||
string payment_id_str = pod_to_hex(payment_id8);
|
||||
|
||||
try
|
||||
{
|
||||
lmdb::txn wtxn = lmdb::txn::begin(m_env);
|
||||
lmdb::dbi wdbi = lmdb::dbi::open(wtxn, "encrypted_payments_id", flags);
|
||||
|
||||
//cout << "Saving encrypted payiment_id: " << payment_id_str << endl;
|
||||
//string wait_for_enter;
|
||||
//cin >> wait_for_enter;
|
||||
|
||||
lmdb::val payment_id_val {payment_id_str};
|
||||
lmdb::val tx_hash_val {tx_hash_str};
|
||||
|
||||
wdbi.put(wtxn, payment_id_val, tx_hash_val);
|
||||
|
||||
wtxn.commit();
|
||||
}
|
||||
catch (lmdb::error& e)
|
||||
{
|
||||
cerr << e.what() << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// // this seems to be not needed as outputs are written based on timestamps
|
||||
//
|
||||
// bool
|
||||
// write_block_timestamp(uint64_t& blk_timestamp, uint64_t& blk_height)
|
||||
// {
|
||||
//
|
||||
// unsigned int flags = MDB_CREATE | MDB_INTEGERKEY;
|
||||
//
|
||||
// try
|
||||
// {
|
||||
// lmdb::txn wtxn = lmdb::txn::begin(m_env);
|
||||
// lmdb::dbi wdbi = lmdb::dbi::open(wtxn, "block_timestamps", flags);
|
||||
//
|
||||
// lmdb::val blk_timestamp_val {static_cast<void*>(&blk_timestamp),
|
||||
// sizeof(blk_timestamp)};
|
||||
// lmdb::val blk_height_val {static_cast<void*>(&blk_height),
|
||||
// sizeof(blk_height)};
|
||||
//
|
||||
// wdbi.put(wtxn, blk_timestamp_val, blk_height_val);
|
||||
//
|
||||
// wtxn.commit();
|
||||
// }
|
||||
// catch (lmdb::error& e)
|
||||
// {
|
||||
// cerr << e.what() << endl;
|
||||
// return false;
|
||||
// }
|
||||
//
|
||||
// return true;
|
||||
// }
|
||||
|
||||
bool
|
||||
search(const string& key,
|
||||
vector<string>& found_tx_hashes,
|
||||
const string& db_name = "key_images")
|
||||
{
|
||||
unsigned int flags = MDB_DUPSORT | MDB_DUPFIXED;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
lmdb::txn rtxn = lmdb::txn::begin(m_env, nullptr, MDB_RDONLY);
|
||||
lmdb::dbi rdbi = lmdb::dbi::open(rtxn, db_name.c_str(), flags);
|
||||
lmdb::cursor cr = lmdb::cursor::open(rtxn, rdbi);
|
||||
|
||||
lmdb::val key_to_find{key};
|
||||
lmdb::val tx_hash_val;
|
||||
|
||||
// set cursor the the first item
|
||||
if (cr.get(key_to_find, tx_hash_val, MDB_SET))
|
||||
{
|
||||
//cout << key_val_to_str(key_to_find, tx_hash_val) << endl;
|
||||
found_tx_hashes.push_back(string(tx_hash_val.data(), tx_hash_val.size()));
|
||||
|
||||
// process other values for the same key
|
||||
while (cr.get(key_to_find, tx_hash_val, MDB_NEXT_DUP))
|
||||
{
|
||||
//cout << key_val_to_str(key_to_find, tx_hash_val) << endl;
|
||||
found_tx_hashes.push_back(string(tx_hash_val.data(), tx_hash_val.size()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
cr.close();
|
||||
rtxn.abort();
|
||||
|
||||
}
|
||||
catch (lmdb::error& e)
|
||||
{
|
||||
cerr << e.what() << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
get_output_amount(const string& key,
|
||||
uint64_t& amount,
|
||||
const string& db_name = "output_amounts")
|
||||
{
|
||||
|
||||
unsigned int flags = 0;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
lmdb::txn rtxn = lmdb::txn::begin(m_env, nullptr, MDB_RDONLY);
|
||||
lmdb::dbi rdbi = lmdb::dbi::open(rtxn, db_name.c_str(), flags);
|
||||
|
||||
lmdb::val key_to_find{key};
|
||||
lmdb::val amount_val;
|
||||
|
||||
if(!rdbi.get(rtxn, key_to_find, amount_val))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
amount = *(amount_val.data<uint64_t>());
|
||||
|
||||
rtxn.abort();
|
||||
|
||||
}
|
||||
catch (lmdb::error& e)
|
||||
{
|
||||
cerr << e.what() << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
get_output_info(uint64_t key_timestamp,
|
||||
vector<output_info>& out_infos,
|
||||
const string& db_name = "output_info")
|
||||
{
|
||||
|
||||
unsigned int flags = 0;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
lmdb::txn rtxn = lmdb::txn::begin(m_env, nullptr, MDB_RDONLY);
|
||||
lmdb::dbi rdbi = lmdb::dbi::open(rtxn, db_name.c_str(), flags);
|
||||
|
||||
lmdb::val key_to_find{static_cast<void*>(&key_timestamp),
|
||||
sizeof(key_timestamp)};
|
||||
lmdb::val info_val;
|
||||
|
||||
lmdb::cursor cr = lmdb::cursor::open(rtxn, rdbi);
|
||||
|
||||
// set cursor the the first item
|
||||
if (cr.get(key_to_find, info_val, MDB_SET_RANGE))
|
||||
{
|
||||
out_infos.push_back(*(info_val.data<output_info>()));
|
||||
|
||||
// process other values for the same key
|
||||
while (cr.get(key_to_find, info_val, MDB_NEXT_DUP))
|
||||
{
|
||||
//cout << key_val_to_str(key_to_find, tx_hash_val) << endl;
|
||||
out_infos.push_back(*(info_val.data<output_info>()));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
rtxn.abort();
|
||||
|
||||
}
|
||||
catch (lmdb::error& e)
|
||||
{
|
||||
cerr << e.what() << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
get_output_info_range(uint64_t key_timestamp_start,
|
||||
uint64_t key_timestamp_end,
|
||||
vector<pair<uint64_t, output_info>>& out_infos,
|
||||
const string& db_name = "output_info")
|
||||
{
|
||||
|
||||
unsigned int flags = 0;
|
||||
|
||||
try
|
||||
{
|
||||
lmdb::txn rtxn = lmdb::txn::begin(m_env, nullptr, MDB_RDONLY);
|
||||
lmdb::dbi rdbi = lmdb::dbi::open(rtxn, db_name.c_str(), flags);
|
||||
|
||||
lmdb::val key_to_find{static_cast<void*>(&key_timestamp_start),
|
||||
sizeof(key_timestamp_start)};
|
||||
lmdb::val info_val;
|
||||
|
||||
lmdb::cursor cr = lmdb::cursor::open(rtxn, rdbi);
|
||||
|
||||
uint64_t current_timestamp = key_timestamp_start;
|
||||
|
||||
// set cursor the the first item
|
||||
if (cr.get(key_to_find, info_val, MDB_SET_RANGE))
|
||||
{
|
||||
|
||||
current_timestamp = *key_to_find.data<uint64_t>();
|
||||
|
||||
if (current_timestamp > key_timestamp_end)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
out_infos.push_back(make_pair(
|
||||
current_timestamp,
|
||||
*(info_val.data<output_info>())));
|
||||
|
||||
// process other values for the same key
|
||||
while (cr.get(key_to_find, info_val, MDB_NEXT))
|
||||
{
|
||||
current_timestamp = *key_to_find.data<uint64_t>();
|
||||
|
||||
if (current_timestamp > key_timestamp_end)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
out_infos.push_back(make_pair(
|
||||
current_timestamp,
|
||||
*(info_val.data<output_info>())));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
rtxn.abort();
|
||||
|
||||
}
|
||||
catch (lmdb::error& e)
|
||||
{
|
||||
cerr << e.what() << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns sorted and unique tx hashes withing a
|
||||
* given timestamp range
|
||||
*
|
||||
* @param key_timestamp_start
|
||||
* @param key_timestamp_end
|
||||
* @param out_txs
|
||||
* @return bool
|
||||
*/
|
||||
bool
|
||||
get_txs_from_timestamp_range(uint64_t key_timestamp_start,
|
||||
uint64_t key_timestamp_end,
|
||||
vector<crypto::hash>& out_txs)
|
||||
{
|
||||
using output_pair = pair<uint64_t, output_info>;
|
||||
|
||||
auto sort_by_timestamp = [](const output_pair& l,
|
||||
const output_pair& r)
|
||||
{
|
||||
return l.first < r.first;
|
||||
};
|
||||
|
||||
vector<output_pair> out_infos;
|
||||
|
||||
if (get_output_info_range(key_timestamp_start,
|
||||
key_timestamp_end,
|
||||
out_infos))
|
||||
{
|
||||
|
||||
set<output_pair, decltype(sort_by_timestamp)> unique_txs(sort_by_timestamp);
|
||||
|
||||
for (auto oi: out_infos)
|
||||
unique_txs.insert(oi);
|
||||
|
||||
for (auto ut: unique_txs)
|
||||
out_txs.push_back(ut.second.tx_hash);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void
|
||||
for_all_outputs(
|
||||
std::function<bool(public_key& out_pubkey,
|
||||
output_info& out_info)> f)
|
||||
{
|
||||
unsigned int flags = MDB_DUPSORT | MDB_DUPFIXED;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
lmdb::txn rtxn = lmdb::txn::begin(m_env, nullptr, MDB_RDONLY);
|
||||
lmdb::dbi rdbi = lmdb::dbi::open(rtxn, "output_info", flags);
|
||||
lmdb::cursor cr = lmdb::cursor::open(rtxn, rdbi);
|
||||
|
||||
lmdb::val key_to_find;
|
||||
lmdb::val amount_val;
|
||||
|
||||
|
||||
// process all values for the same key
|
||||
while (cr.get(key_to_find, amount_val, MDB_NEXT))
|
||||
{
|
||||
public_key pub_key;
|
||||
|
||||
hex_to_pod(string(key_to_find.data(), key_to_find.size()),
|
||||
pub_key);
|
||||
|
||||
output_info out_info = *(amount_val.data<output_info>());
|
||||
|
||||
if (f(pub_key, out_info) == false)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
cr.close();
|
||||
rtxn.abort();
|
||||
|
||||
}
|
||||
catch (lmdb::error& e)
|
||||
{
|
||||
cerr << e.what() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
print_all(const string& db_name)
|
||||
{
|
||||
unsigned int flags = MDB_DUPSORT | MDB_DUPFIXED;
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
lmdb::txn rtxn = lmdb::txn::begin(m_env, nullptr, MDB_RDONLY);
|
||||
lmdb::dbi rdbi = lmdb::dbi::open(rtxn, db_name.c_str(), flags);
|
||||
lmdb::cursor cr = lmdb::cursor::open(rtxn, rdbi);
|
||||
|
||||
lmdb::val key_to_find;
|
||||
lmdb::val tx_hash_val;
|
||||
|
||||
|
||||
// process other values for the same key
|
||||
while (cr.get(key_to_find, tx_hash_val, MDB_NEXT))
|
||||
{
|
||||
cout << key_val_to_str(key_to_find, tx_hash_val) << endl;
|
||||
}
|
||||
|
||||
cr.close();
|
||||
rtxn.abort();
|
||||
|
||||
}
|
||||
catch (lmdb::error& e)
|
||||
{
|
||||
cerr << e.what() << endl;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
get_blockchain_height(string blk_path = "/home/mwo/.blockchain/lmdb")
|
||||
{
|
||||
uint64_t height {0};
|
||||
|
||||
try
|
||||
{
|
||||
auto env = lmdb::env::create();
|
||||
env.set_mapsize(DEFAULT_MAPSIZE * 3);
|
||||
env.set_max_dbs(20);
|
||||
env.open(blk_path.c_str(), MDB_CREATE, 0664);
|
||||
|
||||
//auto rtxn = lmdb::txn::begin(env, nullptr, MDB_RDONLY);
|
||||
auto rtxn = lmdb::txn::begin(env, nullptr);
|
||||
auto rdbi = lmdb::dbi::open(rtxn, "blocks");
|
||||
|
||||
MDB_stat stats = rdbi.stat(rtxn);
|
||||
|
||||
height = static_cast<uint64_t>(stats.ms_entries);
|
||||
|
||||
rtxn.abort();
|
||||
}
|
||||
catch (lmdb::error& e)
|
||||
{
|
||||
cerr << e.what() << endl;
|
||||
return height;
|
||||
}
|
||||
catch (exception& e)
|
||||
{
|
||||
cerr << e.what() << endl;
|
||||
return height;
|
||||
}
|
||||
|
||||
//cout << height << endl;
|
||||
|
||||
return height;
|
||||
|
||||
}
|
||||
|
||||
string
|
||||
key_val_to_str(const lmdb::val& key, const lmdb::val& val)
|
||||
{
|
||||
return "key: " + string(key.data(), key.size())
|
||||
+ ", val: " + string(val.data(), val.size());
|
||||
}
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif //XMRLMDBCPP_MYLMDB_H
|
2443
src/page.h
2443
src/page.h
File diff suppressed because it is too large
Load Diff
147
src/rpccalls.cpp
147
src/rpccalls.cpp
|
@ -3,3 +3,150 @@
|
|||
//
|
||||
|
||||
#include "rpccalls.h"
|
||||
|
||||
namespace xmreg
|
||||
{
|
||||
|
||||
|
||||
rpccalls::rpccalls(string _deamon_url,
|
||||
uint64_t _timeout)
|
||||
: deamon_url {_deamon_url},
|
||||
timeout_time {_timeout}
|
||||
{
|
||||
epee::net_utils::parse_url(deamon_url, url);
|
||||
|
||||
port = std::to_string(url.port);
|
||||
|
||||
timeout_time_ms = std::chrono::milliseconds {timeout_time};
|
||||
|
||||
m_http_client.set_server(
|
||||
deamon_url,
|
||||
boost::optional<epee::net_utils::http::login>{});
|
||||
}
|
||||
|
||||
bool
|
||||
rpccalls::connect_to_monero_deamon()
|
||||
{
|
||||
//std::lock_guard<std::mutex> guard(m_daemon_rpc_mutex);
|
||||
|
||||
if(m_http_client.is_connected())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return m_http_client.connect(timeout_time_ms);
|
||||
}
|
||||
|
||||
uint64_t
|
||||
rpccalls::get_current_height()
|
||||
{
|
||||
COMMAND_RPC_GET_HEIGHT::request req;
|
||||
COMMAND_RPC_GET_HEIGHT::response res;
|
||||
|
||||
std::lock_guard<std::mutex> guard(m_daemon_rpc_mutex);
|
||||
|
||||
if (!connect_to_monero_deamon())
|
||||
{
|
||||
cerr << "get_current_height: not connected to deamon" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool r = epee::net_utils::invoke_http_json(
|
||||
"/getheight",
|
||||
req, res, m_http_client, timeout_time_ms);
|
||||
|
||||
if (!r)
|
||||
{
|
||||
cerr << "Error connecting to Monero deamon at "
|
||||
<< deamon_url << endl;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "rpc call /getheight OK: " << endl;
|
||||
}
|
||||
|
||||
return res.height;
|
||||
}
|
||||
|
||||
bool
|
||||
rpccalls::get_mempool(vector<tx_info>& mempool_txs)
|
||||
{
|
||||
|
||||
COMMAND_RPC_GET_TRANSACTION_POOL::request req;
|
||||
COMMAND_RPC_GET_TRANSACTION_POOL::response res;
|
||||
|
||||
std::lock_guard<std::mutex> guard(m_daemon_rpc_mutex);
|
||||
|
||||
if (!connect_to_monero_deamon())
|
||||
{
|
||||
cerr << "get_mempool: not connected to deamon" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool r = epee::net_utils::invoke_http_json(
|
||||
"/get_transaction_pool",
|
||||
req, res, m_http_client, timeout_time_ms);
|
||||
|
||||
if (!r)
|
||||
{
|
||||
cerr << "Error connecting to Monero deamon at "
|
||||
<< deamon_url << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
mempool_txs = res.transactions;
|
||||
|
||||
// mempool txs are not sorted base on their arival time,
|
||||
// so we sort it here.
|
||||
|
||||
std::sort(mempool_txs.begin(), mempool_txs.end(),
|
||||
[](tx_info& t1, tx_info& t2)
|
||||
{
|
||||
return t1.receive_time > t2.receive_time;
|
||||
});
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
rpccalls::commit_tx(tools::wallet2::pending_tx& ptx, string& error_msg)
|
||||
{
|
||||
COMMAND_RPC_SEND_RAW_TX::request req;
|
||||
COMMAND_RPC_SEND_RAW_TX::response res;
|
||||
|
||||
req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(
|
||||
tx_to_blob(ptx.tx)
|
||||
);
|
||||
|
||||
req.do_not_relay = false;
|
||||
|
||||
std::lock_guard<std::mutex> guard(m_daemon_rpc_mutex);
|
||||
|
||||
if (!connect_to_monero_deamon())
|
||||
{
|
||||
cerr << "commit_tx: not connected to deamon" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool r = epee::net_utils::invoke_http_json(
|
||||
"/sendrawtransaction",
|
||||
req, res, m_http_client, timeout_time_ms);
|
||||
|
||||
if (!r || res.status == "Failed")
|
||||
{
|
||||
error_msg = res.reason;
|
||||
|
||||
cerr << "Error sending tx: " << res.reason << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
|
123
src/rpccalls.h
123
src/rpccalls.h
|
@ -35,133 +35,20 @@ namespace xmreg
|
|||
public:
|
||||
|
||||
rpccalls(string _deamon_url = "http:://127.0.0.1:18081",
|
||||
uint64_t _timeout = 200000)
|
||||
: deamon_url {_deamon_url},
|
||||
timeout_time {_timeout}
|
||||
{
|
||||
epee::net_utils::parse_url(deamon_url, url);
|
||||
|
||||
port = std::to_string(url.port);
|
||||
|
||||
timeout_time_ms = std::chrono::milliseconds {timeout_time};
|
||||
|
||||
m_http_client.set_server(
|
||||
deamon_url,
|
||||
boost::optional<epee::net_utils::http::login>{});
|
||||
}
|
||||
uint64_t _timeout = 200000);
|
||||
|
||||
bool
|
||||
connect_to_monero_deamon()
|
||||
{
|
||||
//std::lock_guard<std::mutex> guard(m_daemon_rpc_mutex);
|
||||
|
||||
if(m_http_client.is_connected())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return m_http_client.connect(timeout_time_ms);
|
||||
}
|
||||
connect_to_monero_deamon();
|
||||
|
||||
uint64_t
|
||||
get_current_height()
|
||||
{
|
||||
COMMAND_RPC_GET_HEIGHT::request req;
|
||||
COMMAND_RPC_GET_HEIGHT::response res;
|
||||
|
||||
std::lock_guard<std::mutex> guard(m_daemon_rpc_mutex);
|
||||
|
||||
if (!connect_to_monero_deamon())
|
||||
{
|
||||
cerr << "get_current_height: not connected to deamon" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool r = epee::net_utils::invoke_http_json(
|
||||
"/getheight",
|
||||
req, res, m_http_client, timeout_time_ms);
|
||||
|
||||
if (!r)
|
||||
{
|
||||
cerr << "Error connecting to Monero deamon at "
|
||||
<< deamon_url << endl;
|
||||
return 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
cout << "rpc call /getheight OK: " << endl;
|
||||
}
|
||||
|
||||
return res.height;
|
||||
}
|
||||
get_current_height();
|
||||
|
||||
bool
|
||||
get_mempool(vector<tx_info>& mempool_txs)
|
||||
{
|
||||
|
||||
COMMAND_RPC_GET_TRANSACTION_POOL::request req;
|
||||
COMMAND_RPC_GET_TRANSACTION_POOL::response res;
|
||||
|
||||
std::lock_guard<std::mutex> guard(m_daemon_rpc_mutex);
|
||||
|
||||
if (!connect_to_monero_deamon())
|
||||
{
|
||||
cerr << "get_mempool: not connected to deamon" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool r = epee::net_utils::invoke_http_json(
|
||||
"/get_transaction_pool",
|
||||
req, res, m_http_client, timeout_time_ms);
|
||||
|
||||
if (!r)
|
||||
{
|
||||
cerr << "Error connecting to Monero deamon at "
|
||||
<< deamon_url << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
mempool_txs = res.transactions;
|
||||
|
||||
return true;
|
||||
}
|
||||
get_mempool(vector<tx_info>& mempool_txs);
|
||||
|
||||
|
||||
bool
|
||||
commit_tx(tools::wallet2::pending_tx& ptx, string& error_msg)
|
||||
{
|
||||
COMMAND_RPC_SEND_RAW_TX::request req;
|
||||
COMMAND_RPC_SEND_RAW_TX::response res;
|
||||
|
||||
req.tx_as_hex = epee::string_tools::buff_to_hex_nodelimer(
|
||||
tx_to_blob(ptx.tx)
|
||||
);
|
||||
|
||||
req.do_not_relay = false;
|
||||
|
||||
std::lock_guard<std::mutex> guard(m_daemon_rpc_mutex);
|
||||
|
||||
if (!connect_to_monero_deamon())
|
||||
{
|
||||
cerr << "commit_tx: not connected to deamon" << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool r = epee::net_utils::invoke_http_json(
|
||||
"/sendrawtransaction",
|
||||
req, res, m_http_client, timeout_time_ms);
|
||||
|
||||
if (!r || res.status == "Failed")
|
||||
{
|
||||
error_msg = res.reason;
|
||||
|
||||
cerr << "Error sending tx: " << res.reason << endl;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
commit_tx(tools::wallet2::pending_tx& ptx, string& error_msg);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -7,7 +7,13 @@
|
|||
|
||||
<div class="center">
|
||||
|
||||
{{#has_error}}
|
||||
<h4 style="color:red">Checking tx failed</h4>
|
||||
<h4>{{error_msg}}</h4>
|
||||
{{/has_error}}
|
||||
{{^has_error}}
|
||||
<h3>Data file prefix: {{data_prefix}}</h3>
|
||||
{{/has_error}}
|
||||
|
||||
{{#unsigned_tx_given}}
|
||||
<h3>Details of unsigned raw tx data given</h3>
|
||||
|
|
|
@ -115,7 +115,7 @@ form {
|
|||
|
||||
input#toggle-1[type=checkbox] {
|
||||
position: absolute;
|
||||
top: -9999px;
|
||||
/*top: -9999px;*/
|
||||
left: -9999px;
|
||||
|
||||
}
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
| explorer version: {{last_git_commit_date}}-{{last_git_commit_hash}}
|
||||
| monero version: {{monero_version_full}}
|
||||
</h6>
|
||||
</h6>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -22,23 +22,12 @@
|
|||
</div>
|
||||
|
||||
|
||||
|
||||
{{#have_custom_lmdb}}
|
||||
<div class="center">
|
||||
<form action="/search" method="get" style="width:100%; margin-top:15px" class="style-1">
|
||||
<input type="text" name="value" size="120"
|
||||
placeholder="blk height, blk hash, tx hash, tx payment id, encrypted payment id, tx public key, input key image, stealth address, and datetime">
|
||||
placeholder="block height, block hash, transaction hash">
|
||||
<input type="submit" value="Search">
|
||||
</form>
|
||||
</div>
|
||||
{{/have_custom_lmdb}}
|
||||
{{^have_custom_lmdb}}
|
||||
<div class="center">
|
||||
<form action="/search" method="get" style="width:100%; margin-top:15px" class="style-1">
|
||||
<input type="text" name="value" size="120"
|
||||
placeholder="blk height, blk hash, tx hash">
|
||||
<input type="submit" value="Search">
|
||||
</form>
|
||||
</div>
|
||||
{{/have_custom_lmdb}}
|
||||
|
||||
</div>
|
||||
|
|
|
@ -21,6 +21,12 @@
|
|||
<a href="/autorefresh">Autorefresh is OFF</a>
|
||||
{{/refresh}}
|
||||
{{/enable_autorefresh_option}}
|
||||
{{#testnet_url}}
|
||||
| <a href="{{testnet_url}}">Go to testnet explorer</a>
|
||||
{{/testnet_url}}
|
||||
{{#mainnet_url}}
|
||||
| <a href="{{mainnet_url}}">Go to mainnet explorer</a>
|
||||
{{/mainnet_url}}
|
||||
{{#testnet}}
|
||||
| This is <span style="color:#ff6b62">testnet</span> blockchian
|
||||
{{/testnet}}
|
||||
|
@ -52,7 +58,6 @@
|
|||
<td>fees</td>
|
||||
<td>outputs</td>
|
||||
<td>in(nonrct)/out</td>
|
||||
<td>rct/type</td>
|
||||
<td>mixin</td>
|
||||
<td>tx size [kB]</td>
|
||||
</tr>
|
||||
|
@ -65,10 +70,6 @@
|
|||
<td>{{tx_fee_short}}</td>
|
||||
<td>{{sum_outputs_short}}</td>
|
||||
<td>{{no_inputs}}({{no_nonrct_inputs}})/{{no_outputs}}</td>
|
||||
<td>
|
||||
{{#is_ringct}}yes/{{rct_type}}{{/is_ringct}}
|
||||
{{^is_ringct}}no{{/is_ringct}}
|
||||
</td>
|
||||
<td>{{mixin}}</td>
|
||||
<td>{{tx_size_short}}</td>
|
||||
</tr>
|
||||
|
@ -85,3 +86,14 @@
|
|||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{{#show_cache_times}}
|
||||
<div class="center">
|
||||
<h6 style="margin-top: 1px;color:#949490">
|
||||
Tx details construction time: {{construction_time_total}} s
|
||||
<br/>
|
||||
includes {{construction_time_cached}} s from block cache ({{cache_hits}} hits)
|
||||
and {{construction_time_non_cached}} s from non cache ({{cache_misses}} misses)
|
||||
</h6>
|
||||
</div>
|
||||
{{/show_cache_times}}
|
|
@ -11,7 +11,6 @@
|
|||
<td>fee</td>
|
||||
<td>outputs</td>
|
||||
<td>in(nonrct)/out</td>
|
||||
<td>rct/type</td>
|
||||
<td>mixin</td>
|
||||
<td>tx size [kB]</td>
|
||||
</tr>
|
||||
|
@ -22,7 +21,6 @@
|
|||
<td>{{fee}}</td>
|
||||
<td>{{xmr_outputs}}</td>
|
||||
<td>{{no_inputs}}({{no_nonrct_inputs}})/{{no_outputs}}</td>
|
||||
<td>{{is_ringct}}{{rct_type}}</td>
|
||||
<td>{{mixin}}</td>
|
||||
<td>{{txsize}}</td>
|
||||
</tr>
|
||||
|
@ -31,7 +29,7 @@
|
|||
|
||||
{{^mempool_fits_on_front_page}}
|
||||
{{#partial_mempool_shown}}
|
||||
<div class="center" style="text-align: center">
|
||||
<div class="center" style="text-align: center; margin-bottom: 10px">
|
||||
<a href="/mempool">Only {{no_of_mempool_tx_of_frontpage}} txs shown. Click here to see all of them</a>
|
||||
</div>
|
||||
{{/partial_mempool_shown}}
|
||||
|
@ -39,4 +37,17 @@
|
|||
{{/mempool_fits_on_front_page}}
|
||||
|
||||
|
||||
|
||||
{{#show_cache_times}}
|
||||
<div class="center">
|
||||
<h6 style="margin-top: 1px;color:#949490">
|
||||
Mempoool tx details construction time: {{construction_time_total}} s
|
||||
<br/>
|
||||
includes {{construction_time_cached}} s from mempool cache ({{cache_hits}} hits)
|
||||
and {{construction_time_non_cached}} s from non cache ({{cache_misses}} misses)
|
||||
</h6>
|
||||
</div>
|
||||
{{/show_cache_times}}
|
||||
|
||||
|
||||
</div>
|
||||
|
|
|
@ -2,12 +2,10 @@
|
|||
<div>
|
||||
|
||||
<H4 style="margin:5px">Tx hash: {{tx_hash}}</H4>
|
||||
{{#enable_mixins_details}}
|
||||
<H5 style="margin:5px">Tx prefix hash: {{tx_prefix_hash}}</H5>
|
||||
{{/enable_mixins_details}}
|
||||
<H5 style="margin:5px">Tx public key: {{tx_pub_key}}</H5>
|
||||
{{#have_raw_tx}}
|
||||
<!--<H5 style="margin:5px">Tx private key: {{tx_prv_key}}</H5>-->
|
||||
{{/have_raw_tx}}
|
||||
|
||||
|
||||
{{#has_payment_id}}
|
||||
<H5 style="margin:5px">Payment id: {{payment_id}}</H5>
|
||||
|
@ -126,8 +124,10 @@
|
|||
|
||||
|
||||
{{#has_inputs}}
|
||||
{{#enable_mixins_details}}
|
||||
<h3>Inputs' mixins time scale (from {{min_mix_time}} till {{max_mix_time}};
|
||||
resolution: {{timescales_scale}} days{{#have_raw_tx}}; R - real mixin {{/have_raw_tx}})</h3>
|
||||
resolution: {{timescales_scale}} days{{#have_raw_tx}}; R - real mixin {{/have_raw_tx}})
|
||||
</h3>
|
||||
<div class="center">
|
||||
<ul class="center">
|
||||
{{#timescales}}
|
||||
|
@ -135,10 +135,10 @@
|
|||
{{/timescales}}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{{/enable_mixins_details}}
|
||||
|
||||
{{^inputs_xmr_sum_not_zero}}
|
||||
<h3>{{inputs_no}} inputs(s) for total of {{inputs_xmr_sum}} xmr</h3>
|
||||
<h3>{{inputs_no}} input(s) for total of {{inputs_xmr_sum}} xmr</h3>
|
||||
{{/inputs_xmr_sum_not_zero}}
|
||||
{{#inputs_xmr_sum_not_zero}}
|
||||
{{^have_any_unknown_amount}}
|
||||
|
@ -149,6 +149,12 @@
|
|||
{{/have_any_unknown_amount}}
|
||||
{{/inputs_xmr_sum_not_zero}}
|
||||
|
||||
{{#show_part_of_inputs}}
|
||||
<h5 style="margin-top: 2px">
|
||||
Only {{max_no_of_inputs_to_show}} are inputs shown. To see all,
|
||||
click "<a href="/tx/{{tx_hash}}/1">more details</a>"
|
||||
</h5>
|
||||
{{/show_part_of_inputs}}
|
||||
|
||||
<div class="center">
|
||||
<table class="center">
|
||||
|
@ -171,6 +177,7 @@
|
|||
</tr>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
{{#enable_mixins_details}}
|
||||
<table style="width:100%; margin-bottom:20px">
|
||||
<tr>
|
||||
<td>Mixin stealth address</td>
|
||||
|
@ -200,10 +207,35 @@
|
|||
<td>{{mix_inputs_no}}/{{mix_outputs_no}}</td>
|
||||
<td>{{mix_timestamp}}</td>
|
||||
<td>{{mix_age}}</td>
|
||||
|
||||
</tr>
|
||||
{{/mixins}}
|
||||
</table>
|
||||
{{/enable_mixins_details}}
|
||||
{{^enable_mixins_details}}
|
||||
<table style="width:100%; margin-bottom:20px">
|
||||
<tr>
|
||||
<td>Mixin stealth address</td>
|
||||
{{#have_raw_tx}}
|
||||
<td>Is it real?</td>
|
||||
{{/have_raw_tx}}
|
||||
<td>blk</td>
|
||||
</tr>
|
||||
{{#mixins}}
|
||||
<tr>
|
||||
<td> - {{mix_idx}}: {{mix_pub_key}}</td>
|
||||
{{#have_raw_tx}}
|
||||
{{#mix_is_it_real}}
|
||||
<td><span style="color: #008009;font-weight: bold">{{mix_is_it_real}}</span></td>
|
||||
{{/mix_is_it_real}}
|
||||
{{^mix_is_it_real}}
|
||||
<td>{{mix_is_it_real}}</td>
|
||||
{{/mix_is_it_real}}
|
||||
{{/have_raw_tx}}
|
||||
<td>{{mix_blk}}</td>
|
||||
</tr>
|
||||
{{/mixins}}
|
||||
</table>
|
||||
{{/enable_mixins_details}}
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
|
@ -234,6 +266,7 @@
|
|||
|
||||
</div>
|
||||
|
||||
|
||||
{{/has_inputs}}
|
||||
|
||||
{{^have_raw_tx}}
|
||||
|
@ -243,14 +276,32 @@
|
|||
{{/show_more_details_link}}
|
||||
{{/with_ring_signatures}}
|
||||
{{#with_ring_signatures}}
|
||||
<h3>JSON representation of tx</h3>
|
||||
<label id="show-decoded-inputs" for="toggle-1">Show JSON representation of tx</label>
|
||||
<input type="checkbox" id="toggle-1">
|
||||
<div id="decoded-inputs">
|
||||
<div class="center">
|
||||
<code style="white-space: pre-wrap; font-size: 10px">
|
||||
{{tx_json}}
|
||||
</code>
|
||||
</div>
|
||||
</div>
|
||||
<br/><br/>
|
||||
<h5 style="margin-top:1px"><a href="/tx/{{tx_hash}}">Less details</a></h5>
|
||||
{{/with_ring_signatures}}
|
||||
{{/have_raw_tx}}
|
||||
|
||||
|
||||
{{#show_cache_times}}
|
||||
<div class="center">
|
||||
{{#construction_time}}
|
||||
<h6 style="margin-top: 1px;color:#949490">
|
||||
Tx details construction time: {{construction_time}} s
|
||||
{{#from_cache}}
|
||||
<br/>Tx read from the tx cache
|
||||
{{/from_cache}}
|
||||
</h6>
|
||||
{{/construction_time}}
|
||||
</div>
|
||||
{{/show_cache_times}}
|
||||
|
||||
</div>
|
||||
|
|
151
src/tools.cpp
151
src/tools.cpp
|
@ -148,53 +148,32 @@ remove_trailing_path_separator(const bf::path& in_path)
|
|||
return bf::path(remove_trailing_path_separator(path_str));
|
||||
}
|
||||
|
||||
string
|
||||
timestamp_to_str(time_t timestamp, const char* format)
|
||||
{
|
||||
auto a_time_point = chrono::system_clock::from_time_t(timestamp);
|
||||
|
||||
try
|
||||
{
|
||||
auto utc = date::to_utc_time(chrono::system_clock::from_time_t(timestamp));
|
||||
auto sys_time = date::to_sys_time(utc);
|
||||
|
||||
return date::format(format, date::floor<chrono::seconds>(sys_time));
|
||||
}
|
||||
catch (std::runtime_error& e)
|
||||
{
|
||||
cerr << "xmreg::timestamp_to_str: " << e.what() << endl;
|
||||
cerr << "Seems cant convert to UTC timezone using date library. "
|
||||
"So just use local timezone." <<endl;
|
||||
|
||||
return timestamp_to_str_local(timestamp, format);
|
||||
}
|
||||
}
|
||||
|
||||
//string
|
||||
//timestamp_to_str(time_t timestamp, const char* format)
|
||||
//{
|
||||
// return get_human_readable_timestamp(timestamp);
|
||||
//}
|
||||
|
||||
|
||||
string
|
||||
timestamp_to_str_local(time_t timestamp, const char* format)
|
||||
timestamp_to_str_gm(time_t timestamp, const char* format)
|
||||
{
|
||||
const time_t* t = ×tamp;
|
||||
|
||||
const int TIME_LENGTH = 60;
|
||||
|
||||
char str_buff[TIME_LENGTH];
|
||||
|
||||
tm *tm_ptr;
|
||||
tm_ptr = localtime(×tamp);
|
||||
std::tm tmp;
|
||||
gmtime_r(t, &tmp);
|
||||
|
||||
size_t len;
|
||||
|
||||
len = std::strftime(str_buff, TIME_LENGTH, format, tm_ptr);
|
||||
len = std::strftime(str_buff, TIME_LENGTH, format, &tmp);
|
||||
|
||||
return string(str_buff, len);
|
||||
}
|
||||
|
||||
|
||||
ostream&
|
||||
operator<< (ostream& os, const account_public_address& addr)
|
||||
{
|
||||
|
@ -357,6 +336,109 @@ sum_money_in_outputs(const json& _json)
|
|||
return sum_xmr;
|
||||
};
|
||||
|
||||
|
||||
array<uint64_t, 4>
|
||||
summary_of_in_out_rct(
|
||||
const transaction& tx,
|
||||
vector<pair<txout_to_key, uint64_t>>& output_pub_keys,
|
||||
vector<txin_to_key>& input_key_imgs)
|
||||
{
|
||||
|
||||
uint64_t xmr_outputs {0};
|
||||
uint64_t xmr_inputs {0};
|
||||
uint64_t mixin_no {0};
|
||||
uint64_t num_nonrct_inputs {0};
|
||||
|
||||
|
||||
for (const tx_out& txout: tx.vout)
|
||||
{
|
||||
if (txout.target.type() != typeid(txout_to_key))
|
||||
{
|
||||
// push empty pair.
|
||||
output_pub_keys.push_back(pair<txout_to_key, uint64_t>{});
|
||||
continue;
|
||||
}
|
||||
|
||||
// get tx input key
|
||||
const txout_to_key& txout_key
|
||||
= boost::get<cryptonote::txout_to_key>(txout.target);
|
||||
|
||||
output_pub_keys.push_back(make_pair(txout_key, txout.amount));
|
||||
|
||||
xmr_outputs += txout.amount;
|
||||
}
|
||||
|
||||
size_t input_no = tx.vin.size();
|
||||
|
||||
for (size_t i = 0; i < input_no; ++i)
|
||||
{
|
||||
|
||||
if(tx.vin[i].type() != typeid(cryptonote::txin_to_key))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// get tx input key
|
||||
const cryptonote::txin_to_key& tx_in_to_key
|
||||
= boost::get<cryptonote::txin_to_key>(tx.vin[i]);
|
||||
|
||||
xmr_inputs += tx_in_to_key.amount;
|
||||
|
||||
if (tx_in_to_key.amount != 0)
|
||||
{
|
||||
++num_nonrct_inputs;
|
||||
}
|
||||
|
||||
if (mixin_no == 0)
|
||||
{
|
||||
mixin_no = tx_in_to_key.key_offsets.size();
|
||||
}
|
||||
|
||||
input_key_imgs.push_back(tx_in_to_key);
|
||||
|
||||
} // for (size_t i = 0; i < input_no; ++i)
|
||||
|
||||
|
||||
return {xmr_outputs, xmr_inputs, mixin_no, num_nonrct_inputs};
|
||||
};
|
||||
|
||||
|
||||
// this version for mempool txs from json
|
||||
array<uint64_t, 6>
|
||||
summary_of_in_out_rct(const json& _json)
|
||||
{
|
||||
uint64_t xmr_outputs {0};
|
||||
uint64_t xmr_inputs {0};
|
||||
uint64_t no_outputs {0};
|
||||
uint64_t no_inputs {0};
|
||||
uint64_t mixin_no {0};
|
||||
uint64_t num_nonrct_inputs {0};
|
||||
|
||||
for (const json& vout: _json["vout"])
|
||||
{
|
||||
xmr_outputs += vout["amount"].get<uint64_t>();
|
||||
}
|
||||
|
||||
no_outputs = _json["vout"].size();
|
||||
|
||||
for (const json& vin: _json["vin"])
|
||||
{
|
||||
uint64_t amount = vin["key"]["amount"].get<uint64_t>();
|
||||
|
||||
xmr_inputs += amount;
|
||||
|
||||
if (amount != 0)
|
||||
++num_nonrct_inputs;
|
||||
}
|
||||
|
||||
no_inputs = _json["vin"].size();
|
||||
|
||||
mixin_no = _json["vin"].at(0)["key"]["key_offsets"].size() - 1;
|
||||
|
||||
return {xmr_outputs, xmr_inputs, no_outputs, no_inputs, mixin_no, num_nonrct_inputs};
|
||||
};
|
||||
|
||||
|
||||
uint64_t
|
||||
sum_money_in_inputs(const transaction& tx)
|
||||
{
|
||||
|
@ -1035,21 +1117,6 @@ get_tx_pub_key_from_received_outs(const transaction &tx)
|
|||
return null_pkey;
|
||||
}
|
||||
|
||||
date::sys_seconds
|
||||
parse(const std::string& str, string format)
|
||||
{
|
||||
std::istringstream in(str);
|
||||
date::sys_seconds tp;
|
||||
in >> date::parse(format, tp);
|
||||
if (in.fail())
|
||||
{
|
||||
in.clear();
|
||||
in.str(str);
|
||||
in >> date::parse(format, tp);
|
||||
}
|
||||
return tp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if given output (specified by output_index)
|
||||
* belongs is ours based
|
||||
|
|
15
src/tools.h
15
src/tools.h
|
@ -16,7 +16,6 @@
|
|||
#include "monero_headers.h"
|
||||
|
||||
#include "../ext/infix_iterator.h"
|
||||
#include "../ext/date/tz.h"
|
||||
#include "../ext/fmt/ostream.h"
|
||||
#include "../ext/fmt/format.h"
|
||||
#include "../ext/json.hpp"
|
||||
|
@ -104,10 +103,7 @@ bf::path
|
|||
remove_trailing_path_separator(const bf::path& in_path);
|
||||
|
||||
string
|
||||
timestamp_to_str(time_t timestamp, const char* format = "%F %T");
|
||||
|
||||
string
|
||||
timestamp_to_str_local(time_t timestamp, const char* format = "%F %T");
|
||||
timestamp_to_str_gm(time_t timestamp, const char* format = "%F %T");
|
||||
|
||||
ostream&
|
||||
operator<< (ostream& os, const account_public_address& addr);
|
||||
|
@ -136,6 +132,15 @@ sum_money_in_outputs(const string& json_str);
|
|||
|
||||
pair<uint64_t, uint64_t>
|
||||
sum_money_in_outputs(const json& _json);
|
||||
array<uint64_t, 4>
|
||||
summary_of_in_out_rct(
|
||||
const transaction& tx,
|
||||
vector<pair<txout_to_key, uint64_t>>& output_pub_keys,
|
||||
vector<txin_to_key>& input_key_imgs);
|
||||
|
||||
// this version for mempool txs from json
|
||||
array<uint64_t, 6>
|
||||
summary_of_in_out_rct(const json& _json);
|
||||
|
||||
uint64_t
|
||||
sum_money_in_inputs(const transaction& tx);
|
||||
|
|
Loading…
Reference in New Issue