moneroexamples 8 years ago
parent 40d87bdab4
commit 468f927e8b

1
.gitignore vendored

@ -4,5 +4,6 @@
*.user *.user
.idea/ .idea/
*.log *.log
.orig
tests/ tests/
cmake-build-debug/ cmake-build-debug/

@ -55,8 +55,8 @@ find_package(Boost COMPONENTS
if(APPLE) if(APPLE)
include_directories(/usr/local/opt/openssl/include) include_directories(/usr/local/opt/openssl/include)
link_directories(/usr/local/opt/openssl/lib) link_directories(/usr/local/opt/openssl/lib)
endif() endif()
@ -116,16 +116,16 @@ set(LIBRARIES
ssl) ssl)
if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT WIN32) if (NOT ${CMAKE_SYSTEM_NAME} MATCHES "Darwin" AND NOT WIN32)
set(LIBRARIES ${LIBRARIES} unwind) set(LIBRARIES ${LIBRARIES} unwind)
endif() endif()
if (WIN32) if (WIN32)
set(LIBRARIES ${LIBRARIES} set(LIBRARIES ${LIBRARIES}
wsock32 wsock32
ntdll ntdll
ws2_32 ws2_32
Iphlpapi Iphlpapi
) )
else() else()
set(LIBRARIES ${LIBRARIES} dl) set(LIBRARIES ${LIBRARIES} dl)
endif() endif()

@ -7,17 +7,16 @@ Currently available Monero blockchain explorer websites have several limitations
- track users activates through google analytics, - track users activates through google analytics,
- are closed sourced, - are closed sourced,
- are not available as hidden services, - are not available as hidden services,
- provide only basic search capabilities,
- do not support Monero testnet - do not support Monero testnet
In this example, these limitations are addressed by development of 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, an Onion Monero Blockchain Explorer. The example not only shows how to use
but also demonstrates how to use: Monero C++ libraries, but also demonstrates how to use:
- [crow](https://github.com/ipkn/crow) - C++ micro web framework - [crow](https://github.com/ipkn/crow) - C++ micro web framework
- [mstch](https://github.com/no1msd/mstch) - C++ {{mustache}} templates - [mstch](https://github.com/no1msd/mstch) - C++ {{mustache}} templates
- [json](https://github.com/nlohmann/json) - JSON for Modern C++ - [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 - [fmt](https://github.com/fmtlib/fmt) - Small, safe and fast string formatting library
## Addresses ## Addresses
@ -25,34 +24,26 @@ an Onion Monero Blockchain Explorer. The example not only shows how to use Moner
Tor users: Tor users:
- [http://dvwae436pd7nt4bc.onion](http://dvwae436pd7nt4bc.onion) - [http://dvwae436pd7nt4bc.onion](http://dvwae436pd7nt4bc.onion)
Clearnet versions: 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://monerohash.com/explorer/](https://monerohash.com/explorer/) - nice looking one, https enabled. - [https://explorer.xmr.my/](https://explorer.xmr.my/) - https enabled, up to date, but sometimes down.
- [http://blox.supportxmr.com/](http://blox.supportxmr.com/) - no https. - [https://xmrchain.net/](https://xmrchain.net/) - https enabled, most popular and very stable.
- [https://explorer.xmr.my/](https://explorer.xmr.my/) - https enabled. - [https://monerohash.com/explorer/](https://monerohash.com/explorer/) - nice looking one, https enabled.
- [http://explore.MoneroWorld.com](http://explore.moneroworld.com) - same as the second one.
Clearnet testnet Monero version: 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. - [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 i2p users (main Monero network) - down for now:
- 62.210.104.109 - /opt/monero/monerod --testnet --add-priority-node 62.210.104.109
Monero tor nodes: - [http://monerotools.i2p](http://monerotools.i2p)
- 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 ## Onion Monero Blockchain Explorer features
The key features of the Onion Monero Blockchain Explorer are: 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 showing number of amount output indices,
- the only explorer supporting Monero testnet network, - the only explorer supporting Monero testnet network,
- the only explorer providing tx checker and pusher for online pushing of transactions, - 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. - the only explorer able to estimate possible spendings based on address and viewkey.
## Prerequisite
## Compilation on Ubuntu 16.04 ## Compilation on Ubuntu 16.04
##### Compile latest Monero ##### Compile latest Monero
@ -100,13 +92,12 @@ make
``` ```
##### Compile and run the explorer ##### 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: as follows:
```bash ```bash
# go to home folder if still in ~/monero # go to home folder if still in ~/monero
cd ~ cd ~
# download the source code # download the source code
git clone https://github.com/moneroexamples/onion-monero-blockchain-explorer.git 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`. 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. You can use `--bc-path` option if its in different location.
Example output: Example output:
```bash ```bash
@ -154,14 +144,30 @@ xmrblocks, start Onion Monero Blockchain Explorer:
-h [ --help ] [=arg(=1)] (=0) produce help message -h [ --help ] [=arg(=1)] (=0) produce help message
-t [ --testnet ] [=arg(=1)] (=0) use testnet blockchain -t [ --testnet ] [=arg(=1)] (=0) use testnet blockchain
--enable-pusher [=arg(=1)] (=0) enable pushing signed tx --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-image-checker [=arg(=1)] (=0)
enable key images file checker enable key images file checker
--enable-output-key-checker [=arg(=1)] (=0) --enable-output-key-checker [=arg(=1)] (=0)
enable outputs key file checker 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-autorefresh-option [=arg(=1)] (=0)
enable users to have the index page on enable users to have the index page on
autorefresh autorefresh
-p [ --port ] arg (=8081) default port -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 --no-blocks-on-index arg (=10) number of last blocks to be shown on
index page index page
-b [ --bc-path ] arg path to lmdb blockchain -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 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. 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 ```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.
## Example screenshot ```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"
}
```
#### api/transactions
Transactions in last 25 blocks
```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>
![Onion Monero Blockchain Explorer](https://raw.githubusercontent.com/moneroexamples/onion-monero-blockchain-explorer/master/screenshot/screenshot_01.jpg) ```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 monero examples
Other examples can be found on [github](https://github.com/moneroexamples?tab=repositories). Other examples can be found on [github](https://github.com/moneroexamples?tab=repositories).
Please know that some of the examples/repositories are not Please know that some of the examples/repositories are not

@ -7,13 +7,11 @@ project(myext)
set(SOURCE_HEADERS set(SOURCE_HEADERS
minicsv.h minicsv.h
format.h fmt/format.h)
)
set(SOURCE_FILES set(SOURCE_FILES
fmt/format.cc fmt/format.cc
fmt/ostream.cc fmt/ostream.cc)
date/tz.cpp)
# make static library called libmyxrm # make static library called libmyxrm
# that we are going to link to # 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

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

@ -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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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< 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_>, internal::lambda_t<boost::recursive_variant_>,
std::shared_ptr<internal::object_t<boost::recursive_variant_>>, std::shared_ptr<internal::object_t<boost::recursive_variant_>>,
std::map<const std::string, 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(); 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::string operator()(const uint32_t& value) const {
std::stringstream ss; std::stringstream ss;
ss << value; 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

@ -6,10 +6,8 @@
#include "src/MicroCore.h" #include "src/MicroCore.h"
#include "src/page.h" #include "src/page.h"
#include "ext/member_checker.h"
#include <fstream> #include <fstream>
#include <regex>
using boost::filesystem::path; using boost::filesystem::path;
@ -21,11 +19,6 @@ int main(int ac, const char* av[]) {
xmreg::CmdLineOptions opts {ac, av}; xmreg::CmdLineOptions opts {ac, av};
auto help_opt = opts.get_option<bool>("help"); 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 was chosen, display help text and finish
if (*help_opt) if (*help_opt)
@ -33,19 +26,38 @@ int main(int ac, const char* av[]) {
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
bool testnet {*testnet_opt}; auto port_opt = opts.get_option<string>("port");
bool enable_pusher {*enable_pusher_opt}; auto bc_path_opt = opts.get_option<string>("bc-path");
bool enable_key_image_checker {*enable_key_image_checker_opt}; auto deamon_url_opt = opts.get_option<string>("deamon-url");
bool enable_autorefresh_option {*enable_autorefresh_option_opt}; auto ssl_crt_file_opt = opts.get_option<string>("ssl-crt-file");
bool enable_output_key_checker {*enable_output_key_checker_opt}; 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 // set monero log output level
uint32_t log_level = 0; uint32_t log_level = 0;
@ -114,36 +126,6 @@ int main(int ac, const char* av[]) {
return EXIT_FAILURE; 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}; string deamon_url {*deamon_url_opt};
if (testnet && deamon_url == "http:://127.0.0.1:18081") 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, xmreg::page xmrblocks(&mcore,
core_storage, core_storage,
deamon_url, deamon_url,
custom_db_path_str,
testnet, testnet,
enable_pusher, enable_pusher,
enable_key_image_checker, enable_key_image_checker,
enable_output_key_checker, enable_output_key_checker,
enable_autorefresh_option, 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 instance
crow::SimpleApp app; crow::SimpleApp app;
@ -350,6 +338,108 @@ int main(int ac, const char* av[]) {
return text; 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) if (enable_autorefresh_option)
{ {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 575 KiB

@ -13,7 +13,8 @@ set(SOURCE_FILES
tools.cpp tools.cpp
CmdLineOptions.cpp CmdLineOptions.cpp
page.h page.h
rpccalls.cpp rpccalls.h version.h.in) rpccalls.cpp rpccalls.h
version.h.in)
# make static library called libmyxrm # make static library called libmyxrm
# that we are going to link to # that we are going to link to

@ -27,14 +27,30 @@ namespace xmreg
"use testnet blockchain") "use testnet blockchain")
("enable-pusher", value<bool>()->default_value(false)->implicit_value(true), ("enable-pusher", value<bool>()->default_value(false)->implicit_value(true),
"enable pushing signed tx") "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-image-checker", value<bool>()->default_value(false)->implicit_value(true),
"enable key images file checker") "enable key images file checker")
("enable-output-key-checker", value<bool>()->default_value(false)->implicit_value(true), ("enable-output-key-checker", value<bool>()->default_value(false)->implicit_value(true),
"enable outputs key file checker") "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-autorefresh-option", value<bool>()->default_value(false)->implicit_value(true),
"enable users to have the index page on autorefresh") "enable users to have the index page on autorefresh")
("port,p", value<string>()->default_value("8081"), ("port,p", value<string>()->default_value("8081"),
"default port") "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"), ("no-blocks-on-index", value<string>()->default_value("10"),
"number of last blocks to be shown on index page") "number of last blocks to be shown on index page")
("bc-path,b", value<string>(), ("bc-path,b", value<string>(),
@ -43,8 +59,6 @@ namespace xmreg
"A path to crt file for ssl (https) functionality") "A path to crt file for ssl (https) functionality")
("ssl-key-file", value<string>(), ("ssl-key-file", value<string>(),
"A path to key file for ssl (https) functionality") "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"), ("deamon-url,d", value<string>()->default_value("http:://127.0.0.1:18081"),
"monero address string"); "monero address string");

@ -17,344 +17,344 @@ namespace
namespace xmreg namespace xmreg
{ {
/** /**
* The constructor is interesting, as * The constructor is interesting, as
* m_mempool and m_blockchain_storage depend * m_mempool and m_blockchain_storage depend
* on each other. * on each other.
* *
* So basically m_mempool is initialized with * So basically m_mempool is initialized with
* reference to Blockchain (i.e., Blockchain&) * reference to Blockchain (i.e., Blockchain&)
* and m_blockchain_storage is initialized with * and m_blockchain_storage is initialized with
* reference to m_mempool (i.e., tx_memory_pool&) * reference to m_mempool (i.e., tx_memory_pool&)
* *
* The same is done in cryptonode::core. * The same is done in cryptonode::core.
*/ */
MicroCore::MicroCore(): MicroCore::MicroCore():
m_mempool(m_blockchain_storage), m_mempool(m_blockchain_storage),
m_blockchain_storage(m_mempool) m_blockchain_storage(m_mempool)
{} {}
/** /**
* Initialized the MicroCore object. * Initialized the MicroCore object.
* *
* Create BlockchainLMDB on the heap. * Create BlockchainLMDB on the heap.
* Open database files located in blockchain_path. * Open database files located in blockchain_path.
* Initialize m_blockchain_storage with the BlockchainLMDB object. * Initialize m_blockchain_storage with the BlockchainLMDB object.
*/ */
bool bool
MicroCore::init(const string& _blockchain_path) MicroCore::init(const string& _blockchain_path)
{ {
int db_flags = 0; int db_flags = 0;
blockchain_path = _blockchain_path;
//db_flags |= MDB_RDONLY; blockchain_path = _blockchain_path;
db_flags |= MDB_NOLOCK;
//db_flags |= MDB_SYNC;
// uint64_t DEFAULT_FLAGS = MDB_NOMETASYNC | MDB_NORDAHEAD; //db_flags |= MDB_RDONLY;
db_flags |= MDB_NOLOCK;
//db_flags |= MDB_SYNC;
//db_flags = DEFAULT_FLAGS; // uint64_t DEFAULT_FLAGS = MDB_NOMETASYNC | MDB_NORDAHEAD;
HardFork* m_hardfork = nullptr; //db_flags = DEFAULT_FLAGS;
BlockchainDB* db = nullptr; HardFork* m_hardfork = nullptr;
db = new BlockchainLMDB();
bool use_testnet {false}; BlockchainDB* db = nullptr;
db = new BlockchainLMDB();
uint64_t hard_fork_version_1_till = use_testnet ? testnet_hard_fork_version_1_till : mainnet_hard_fork_version_1_till; bool use_testnet {false};
m_hardfork = new HardFork(*db, 1, hard_fork_version_1_till); uint64_t hard_fork_version_1_till = use_testnet ? testnet_hard_fork_version_1_till : mainnet_hard_fork_version_1_till;
try m_hardfork = new HardFork(*db, 1, hard_fork_version_1_till);
{
// try opening lmdb database files
db->open(blockchain_path, db_flags);
}
catch (const std::exception& e)
{
cerr << "Error opening database: " << e.what();
return false;
}
// check if the blockchain database try
// is successful opened {
if(!db->is_open()) // try opening lmdb database files
{ db->open(blockchain_path, db_flags);
return false;
}
// initialize Blockchain object to manage
// the database.
return m_blockchain_storage.init(db, m_hardfork, false);
} }
catch (const std::exception& e)
/**
* Get m_blockchain_storage.
* Initialize m_blockchain_storage with the BlockchainLMDB object.
*/
Blockchain&
MicroCore::get_core()
{ {
return m_blockchain_storage; cerr << "Error opening database: " << e.what();
return false;
} }
/** // check if the blockchain database
* Get block by its height // is successful opened
* if(!db->is_open())
* returns true if success
*/
bool
MicroCore::get_block_by_height(const uint64_t& height, block& blk)
{ {
try return false;
{
blk = m_blockchain_storage.get_db().get_block_from_height(height);
}
catch (const BLOCK_DNE& e)
{
cerr << "Block of height " << height
<< " not found in the blockchain!"
<< e.what()
<< endl;
return false;
}
catch (const DB_ERROR& e)
{
cerr << "Blockchain access error when getting block " << height
<< e.what()
<< endl;
return false;
}
catch (...)
{
cerr << "Something went terribly wrong when getting block " << height
<< endl;
return false;
}
return true;
} }
// initialize Blockchain object to manage
// the database.
return m_blockchain_storage.init(db, m_hardfork, false);
}
/**
* Get m_blockchain_storage.
* Initialize m_blockchain_storage with the BlockchainLMDB object.
*/
Blockchain&
MicroCore::get_core()
{
return m_blockchain_storage;
}
/** /**
* Get transaction tx from the blockchain using it hash * Get block by its height
*/ *
bool * returns true if success
MicroCore::get_tx(const crypto::hash& tx_hash, transaction& tx) */
bool
MicroCore::get_block_by_height(const uint64_t& height, block& blk)
{
try
{ {
if (m_blockchain_storage.have_tx(tx_hash)) blk = m_blockchain_storage.get_db().get_block_from_height(height);
{
// get transaction with given hash
tx = m_blockchain_storage.get_db().get_tx(tx_hash);
}
else
{
cerr << "MicroCore::get_tx tx does not exist in blockchain: " << tx_hash << endl;
return false;
}
return true;
} }
catch (const BLOCK_DNE& e)
{
cerr << "Block of height " << height
<< " not found in the blockchain!"
<< e.what()
<< endl;
bool return false;
MicroCore::get_tx(const string& tx_hash_str, transaction& tx) }
catch (const DB_ERROR& e)
{ {
cerr << "Blockchain access error when getting block " << height
<< e.what()
<< endl;
// parse tx hash string to hash object return false;
crypto::hash tx_hash; }
catch (...)
{
cerr << "Something went terribly wrong when getting block " << height
<< endl;
if (!xmreg::parse_str_secret_key(tx_hash_str, tx_hash)) return false;
{ }
cerr << "Cant parse tx hash: " << tx_hash_str << endl;
return false;
}
return true;
}
if (!get_tx(tx_hash, tx))
{
return false;
}
return true; /**
* Get transaction tx from the blockchain using it hash
*/
bool
MicroCore::get_tx(const crypto::hash& tx_hash, transaction& tx)
{
if (m_blockchain_storage.have_tx(tx_hash))
{
// get transaction with given hash
tx = m_blockchain_storage.get_db().get_tx(tx_hash);
}
else
{
cerr << "MicroCore::get_tx tx does not exist in blockchain: " << tx_hash << endl;
return false;
} }
return true;
}
bool
MicroCore::get_tx(const string& tx_hash_str, transaction& tx)
{
// parse tx hash string to hash object
crypto::hash tx_hash;
/** if (!xmreg::parse_str_secret_key(tx_hash_str, tx_hash))
* Find output with given public key in a given transaction
*/
bool
MicroCore::find_output_in_tx(const transaction& tx,
const public_key& output_pubkey,
tx_out& out,
size_t& output_index)
{ {
cerr << "Cant parse tx hash: " << tx_hash_str << endl;
return false;
}
size_t idx {0};
if (!get_tx(tx_hash, tx))
{
return false;
}
// search in the ouputs for an output which
// public key matches to what we want
auto it = std::find_if(tx.vout.begin(), tx.vout.end(),
[&](const tx_out& o)
{
const txout_to_key& tx_in_to_key
= boost::get<txout_to_key>(o.target);
++idx; return true;
}
return tx_in_to_key.key == output_pubkey;
});
if (it != tx.vout.end())
{
// we found the desired public key
out = *it;
output_index = idx > 0 ? idx - 1 : idx;
//cout << idx << " " << output_index << endl;
return true; /**
} * Find output with given public key in a given transaction
*/
bool
MicroCore::find_output_in_tx(const transaction& tx,
const public_key& output_pubkey,
tx_out& out,
size_t& output_index)
{
return false; size_t idx {0};
}
/** // search in the ouputs for an output which
* Returns tx hash in a given block which // public key matches to what we want
* contains given output's public key auto it = std::find_if(tx.vout.begin(), tx.vout.end(),
*/ [&](const tx_out& o)
bool {
MicroCore::get_tx_hash_from_output_pubkey(const public_key& output_pubkey, const txout_to_key& tx_in_to_key
const uint64_t& block_height, = boost::get<txout_to_key>(o.target);
crypto::hash& tx_hash,
cryptonote::transaction& tx_found)
{
tx_hash = null_hash; ++idx;
// get block of given height return tx_in_to_key.key == output_pubkey;
block blk; });
if (!get_block_by_height(block_height, blk))
{
cerr << "Cant get block of height: " << block_height << endl;
return false;
}
if (it != tx.vout.end())
{
// we found the desired public key
out = *it;
output_index = idx > 0 ? idx - 1 : idx;
// get all transactions in the block found //cout << idx << " " << output_index << endl;
// initialize the first list with transaction for solving
// the block i.e. coinbase.
list<transaction> txs {blk.miner_tx};
list<crypto::hash> missed_txs;
if (!m_blockchain_storage.get_transactions(blk.tx_hashes, txs, missed_txs)) return true;
{ }
cerr << "Cant find transcations in block: " << block_height << endl;
return false;
}
if (!missed_txs.empty()) return false;
{ }
cerr << "Transactions not found in blk: " << block_height << endl;
for (const crypto::hash& h : missed_txs)
{
cerr << " - tx hash: " << h << endl;
}
return false; /**
} * Returns tx hash in a given block which
* contains given output's public key
*/
bool
MicroCore::get_tx_hash_from_output_pubkey(const public_key& output_pubkey,
const uint64_t& block_height,
crypto::hash& tx_hash,
cryptonote::transaction& tx_found)
{
tx_hash = null_hash;
// search outputs in each transactions // get block of given height
// until output with pubkey of interest is found block blk;
for (const transaction& tx : txs) if (!get_block_by_height(block_height, blk))
{ {
cerr << "Cant get block of height: " << block_height << endl;
return false;
}
tx_out found_out;
// we dont need here output_index // get all transactions in the block found
size_t output_index; // initialize the first list with transaction for solving
// the block i.e. coinbase.
list<transaction> txs {blk.miner_tx};
list<crypto::hash> missed_txs;
if (find_output_in_tx(tx, output_pubkey, found_out, output_index)) if (!m_blockchain_storage.get_transactions(blk.tx_hashes, txs, missed_txs))
{ {
// we found the desired public key cerr << "Cant find transcations in block: " << block_height << endl;
tx_hash = get_transaction_hash(tx); return false;
tx_found = tx; }
return true; if (!missed_txs.empty())
} {
cerr << "Transactions not found in blk: " << block_height << endl;
for (const crypto::hash& h : missed_txs)
{
cerr << " - tx hash: " << h << endl;
} }
return false; return false;
} }
uint64_t // search outputs in each transactions
MicroCore::get_blk_timestamp(uint64_t blk_height) // until output with pubkey of interest is found
for (const transaction& tx : txs)
{ {
cryptonote::block blk;
if (!get_block_by_height(blk_height, blk)) tx_out found_out;
// we dont need here output_index
size_t output_index;
if (find_output_in_tx(tx, output_pubkey, found_out, output_index))
{ {
cerr << "Cant get block by height: " << blk_height << endl; // we found the desired public key
return 0; tx_hash = get_transaction_hash(tx);
tx_found = tx;
return true;
} }
return blk.timestamp;
} }
return false;
}
/** uint64_t
* De-initialized Blockchain. MicroCore::get_blk_timestamp(uint64_t blk_height)
* {
* since blockchain is opened as MDB_RDONLY cryptonote::block blk;
* need to manually free memory taken on heap
* by BlockchainLMDB if (!get_block_by_height(blk_height, blk))
*/
MicroCore::~MicroCore()
{ {
delete &m_blockchain_storage.get_db(); cerr << "Cant get block by height: " << blk_height << endl;
return 0;
} }
return blk.timestamp;
}
bool
init_blockchain(const string& path,
MicroCore& mcore,
Blockchain*& core_storage)
{
// initialize the core using the blockchain path /**
if (!mcore.init(path)) * De-initialized Blockchain.
{ *
cerr << "Error accessing blockchain." << endl; * since blockchain is opened as MDB_RDONLY
return false; * need to manually free memory taken on heap
} * by BlockchainLMDB
*/
MicroCore::~MicroCore()
{
delete &m_blockchain_storage.get_db();
}
// get the high level Blockchain object to interact
// with the blockchain lmdb database
core_storage = &(mcore.get_core());
return true; bool
} init_blockchain(const string& path,
MicroCore& mcore,
Blockchain*& core_storage)
{
string // initialize the core using the blockchain path
MicroCore::get_blkchain_path() if (!mcore.init(path))
{ {
return blockchain_path; cerr << "Error accessing blockchain." << endl;
return false;
} }
// get the high level Blockchain object to interact
// with the blockchain lmdb database
core_storage = &(mcore.get_core());
return true;
}
string
MicroCore::get_blkchain_path()
{
return blockchain_path;
}
} }

@ -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

File diff suppressed because it is too large Load Diff

@ -3,3 +3,150 @@
// //
#include "rpccalls.h" #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;
}
}

@ -13,157 +13,44 @@
namespace xmreg namespace xmreg
{ {
using namespace cryptonote; using namespace cryptonote;
using namespace crypto; using namespace crypto;
using namespace std; using namespace std;
class rpccalls class rpccalls
{ {
string deamon_url ; string deamon_url ;
uint64_t timeout_time; uint64_t timeout_time;
std::chrono::milliseconds timeout_time_ms;
epee::net_utils::http::url_content url;
epee::net_utils::http::http_simple_client m_http_client;
std::mutex m_daemon_rpc_mutex;
string port;
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>{});
}
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);
}
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;
}
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; std::chrono::milliseconds timeout_time_ms;
}
epee::net_utils::http::url_content url;
bool epee::net_utils::http::http_simple_client m_http_client;
commit_tx(tools::wallet2::pending_tx& ptx, string& error_msg) std::mutex m_daemon_rpc_mutex;
{
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( string port;
tx_to_blob(ptx.tx)
);
req.do_not_relay = false; public:
std::lock_guard<std::mutex> guard(m_daemon_rpc_mutex); rpccalls(string _deamon_url = "http:://127.0.0.1:18081",
uint64_t _timeout = 200000);
if (!connect_to_monero_deamon()) bool
{ connect_to_monero_deamon();
cerr << "commit_tx: not connected to deamon" << endl;
return false;
}
bool r = epee::net_utils::invoke_http_json( uint64_t
"/sendrawtransaction", get_current_height();
req, res, m_http_client, timeout_time_ms);
if (!r || res.status == "Failed") bool
{ get_mempool(vector<tx_info>& mempool_txs);
error_msg = res.reason;
cerr << "Error sending tx: " << res.reason << endl;
return false;
}
return true; bool
} commit_tx(tools::wallet2::pending_tx& ptx, string& error_msg);
}; };
} }

@ -7,7 +7,13 @@
<div class="center"> <div class="center">
<h3>Data file prefix: {{data_prefix}}</h3> {{#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}} {{#unsigned_tx_given}}
<h3>Details of unsigned raw tx data given</h3> <h3>Details of unsigned raw tx data given</h3>
@ -16,7 +22,7 @@
<div class="center" style="width: 100%; margin-top:10px"> <div class="center" style="width: 100%; margin-top:10px">
<h4>Basic information</h4> <h4>Basic information</h4>
<h5> <h5>
{{#dest_infos}} {{#dest_infos}}
Send {{dest_amount}} to {{dest_address}}<br/> Send {{dest_amount}} to {{dest_address}}<br/>
{{/dest_infos}} {{/dest_infos}}

@ -115,7 +115,7 @@ form {
input#toggle-1[type=checkbox] { input#toggle-1[type=checkbox] {
position: absolute; position: absolute;
top: -9999px; /*top: -9999px;*/
left: -9999px; left: -9999px;
} }

@ -4,7 +4,6 @@
| explorer version: {{last_git_commit_date}}-{{last_git_commit_hash}} | explorer version: {{last_git_commit_date}}-{{last_git_commit_hash}}
| monero version: {{monero_version_full}} | monero version: {{monero_version_full}}
</h6> </h6>
</h6>
</div> </div>
</body> </body>
</html> </html>

@ -22,23 +22,12 @@
</div> </div>
<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="block height, block hash, transaction hash">
<input type="submit" value="Search">
</form>
</div>
{{#have_custom_lmdb}} </div>
<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">
<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}}

@ -17,12 +17,18 @@
{{#refresh}} {{#refresh}}
<a href="/">Autorefresh is ON (10 s)</a> <a href="/">Autorefresh is ON (10 s)</a>
{{/refresh}} {{/refresh}}
{{^refresh}} {{^refresh}}
<a href="/autorefresh">Autorefresh is OFF</a> <a href="/autorefresh">Autorefresh is OFF</a>
{{/refresh}} {{/refresh}}
{{/enable_autorefresh_option}} {{/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}} {{#testnet}}
| This is <span style="color:#ff6b62">testnet</span> blockchian | This is <span style="color:#ff6b62">testnet</span> blockchian
{{/testnet}} {{/testnet}}
</h3> </h3>
@ -52,7 +58,6 @@
<td>fees</td> <td>fees</td>
<td>outputs</td> <td>outputs</td>
<td>in(nonrct)/out</td> <td>in(nonrct)/out</td>
<td>rct/type</td>
<td>mixin</td> <td>mixin</td>
<td>tx size [kB]</td> <td>tx size [kB]</td>
</tr> </tr>
@ -65,10 +70,6 @@
<td>{{tx_fee_short}}</td> <td>{{tx_fee_short}}</td>
<td>{{sum_outputs_short}}</td> <td>{{sum_outputs_short}}</td>
<td>{{no_inputs}}({{no_nonrct_inputs}})/{{no_outputs}}</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>{{mixin}}</td>
<td>{{tx_size_short}}</td> <td>{{tx_size_short}}</td>
</tr> </tr>
@ -85,3 +86,14 @@
</div> </div>
</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>fee</td>
<td>outputs</td> <td>outputs</td>
<td>in(nonrct)/out</td> <td>in(nonrct)/out</td>
<td>rct/type</td>
<td>mixin</td> <td>mixin</td>
<td>tx size [kB]</td> <td>tx size [kB]</td>
</tr> </tr>
@ -22,7 +21,6 @@
<td>{{fee}}</td> <td>{{fee}}</td>
<td>{{xmr_outputs}}</td> <td>{{xmr_outputs}}</td>
<td>{{no_inputs}}({{no_nonrct_inputs}})/{{no_outputs}}</td> <td>{{no_inputs}}({{no_nonrct_inputs}})/{{no_outputs}}</td>
<td>{{is_ringct}}{{rct_type}}</td>
<td>{{mixin}}</td> <td>{{mixin}}</td>
<td>{{txsize}}</td> <td>{{txsize}}</td>
</tr> </tr>
@ -31,7 +29,7 @@
{{^mempool_fits_on_front_page}} {{^mempool_fits_on_front_page}}
{{#partial_mempool_shown}} {{#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> <a href="/mempool">Only {{no_of_mempool_tx_of_frontpage}} txs shown. Click here to see all of them</a>
</div> </div>
{{/partial_mempool_shown}} {{/partial_mempool_shown}}
@ -39,4 +37,17 @@
{{/mempool_fits_on_front_page}} {{/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> </div>

@ -2,12 +2,10 @@
<div> <div>
<H4 style="margin:5px">Tx hash: {{tx_hash}}</H4> <H4 style="margin:5px">Tx hash: {{tx_hash}}</H4>
<H5 style="margin:5px">Tx prefix hash: {{tx_prefix_hash}}</H5> {{#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> <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}} {{#has_payment_id}}
<H5 style="margin:5px">Payment id: {{payment_id}}</H5> <H5 style="margin:5px">Payment id: {{payment_id}}</H5>
@ -19,11 +17,11 @@
{{#have_prev_hash}} {{#have_prev_hash}}
<H5>Previous tx: <a href="/tx/{{prev_hash}}">{{prev_hash}}</a></H5> <H5>Previous tx: <a href="/tx/{{prev_hash}}">{{prev_hash}}</a></H5>
{{/have_prev_hash}} {{/have_prev_hash}}
{{#have_next_hash}} {{#have_next_hash}}
<H5>Next tx: <a href="/tx/{{next_hash}}">{{next_hash}}</a></H5> <H5>Next tx: <a href="/tx/{{next_hash}}">{{next_hash}}</a></H5>
{{/have_next_hash}} {{/have_next_hash}}
@ -126,19 +124,21 @@
{{#has_inputs}} {{#has_inputs}}
<h3>Inputs' mixins time scale (from {{min_mix_time}} till {{max_mix_time}}; {{#enable_mixins_details}}
resolution: {{timescales_scale}} days{{#have_raw_tx}}; R - real mixin {{/have_raw_tx}})</h3> <h3>Inputs' mixins time scale (from {{min_mix_time}} till {{max_mix_time}};
<div class="center"> resolution: {{timescales_scale}} days{{#have_raw_tx}}; R - real mixin {{/have_raw_tx}})
<ul class="center"> </h3>
{{#timescales}} <div class="center">
<li style="list-style-type: none; text-align: center; font-size: 8px">|{{timescale}}|</li> <ul class="center">
{{/timescales}} {{#timescales}}
</ul> <li style="list-style-type: none; text-align: center; font-size: 8px">|{{timescale}}|</li>
</div> {{/timescales}}
</ul>
</div>
{{/enable_mixins_details}}
{{^inputs_xmr_sum_not_zero}} {{^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}}
{{#inputs_xmr_sum_not_zero}} {{#inputs_xmr_sum_not_zero}}
{{^have_any_unknown_amount}} {{^have_any_unknown_amount}}
@ -149,6 +149,12 @@
{{/have_any_unknown_amount}} {{/have_any_unknown_amount}}
{{/inputs_xmr_sum_not_zero}} {{/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"> <div class="center">
<table class="center"> <table class="center">
@ -171,39 +177,65 @@
</tr> </tr>
<tr> <tr>
<td colspan="2"> <td colspan="2">
{{#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>
<td>mixin</td>
<td>in/out</td>
<td>timestamp</td>
<td>age [y:d:h:m:s]</td>
</tr>
{{#mixins}}
<tr>
<td> - {{mix_idx}}: <a href="/tx/{{mix_tx_hash}}">{{mix_pub_key}}</a></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>
<td>{{mix_mixin_no}}</td>
<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"> <table style="width:100%; margin-bottom:20px">
<tr> <tr>
<td>Mixin stealth address</td> <td>Mixin stealth address</td>
{{#have_raw_tx}} {{#have_raw_tx}}
<td>Is it real?</td> <td>Is it real?</td>
{{/have_raw_tx}} {{/have_raw_tx}}
<td>blk</td> <td>blk</td>
<td>mixin</td> </tr>
<td>in/out</td> {{#mixins}}
<td>timestamp</td> <tr>
<td>age [y:d:h:m:s]</td> <td> - {{mix_idx}}: {{mix_pub_key}}</td>
{{#have_raw_tx}}
</tr> {{#mix_is_it_real}}
{{#mixins}} <td><span style="color: #008009;font-weight: bold">{{mix_is_it_real}}</span></td>
<tr> {{/mix_is_it_real}}
<td> - {{mix_idx}}: <a href="/tx/{{mix_tx_hash}}">{{mix_pub_key}}</a></td> {{^mix_is_it_real}}
{{#have_raw_tx}} <td>{{mix_is_it_real}}</td>
{{#mix_is_it_real}} {{/mix_is_it_real}}
<td><span style="color: #008009;font-weight: bold">{{mix_is_it_real}}</span></td> {{/have_raw_tx}}
{{/mix_is_it_real}} <td>{{mix_blk}}</td>
{{^mix_is_it_real}} </tr>
<td>{{mix_is_it_real}}</td> {{/mixins}}
{{/mix_is_it_real}} </table>
{{/have_raw_tx}} {{/enable_mixins_details}}
<td>{{mix_blk}}</td>
<td>{{mix_mixin_no}}</td>
<td>{{mix_inputs_no}}/{{mix_outputs_no}}</td>
<td>{{mix_timestamp}}</td>
<td>{{mix_age}}</td>
</tr>
{{/mixins}}
</table>
</td> </td>
</tr> </tr>
@ -234,6 +266,7 @@
</div> </div>
{{/has_inputs}} {{/has_inputs}}
{{^have_raw_tx}} {{^have_raw_tx}}
@ -243,14 +276,32 @@
{{/show_more_details_link}} {{/show_more_details_link}}
{{/with_ring_signatures}} {{/with_ring_signatures}}
{{#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>
<div class="center"> <input type="checkbox" id="toggle-1">
<code style="white-space: pre-wrap; font-size: 10px"> <div id="decoded-inputs">
{{tx_json}} <div class="center">
</code> <code style="white-space: pre-wrap; font-size: 10px">
{{tx_json}}
</code>
</div>
</div> </div>
<br/><br/>
<h5 style="margin-top:1px"><a href="/tx/{{tx_hash}}">Less details</a></h5> <h5 style="margin-top:1px"><a href="/tx/{{tx_hash}}">Less details</a></h5>
{{/with_ring_signatures}} {{/with_ring_signatures}}
{{/have_raw_tx}} {{/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> </div>

@ -148,53 +148,32 @@ remove_trailing_path_separator(const bf::path& in_path)
return bf::path(remove_trailing_path_separator(path_str)); 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 //string
//timestamp_to_str(time_t timestamp, const char* format) //timestamp_to_str(time_t timestamp, const char* format)
//{ //{
// return get_human_readable_timestamp(timestamp); // return get_human_readable_timestamp(timestamp);
//} //}
string 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 = &timestamp;
const int TIME_LENGTH = 60; const int TIME_LENGTH = 60;
char str_buff[TIME_LENGTH]; char str_buff[TIME_LENGTH];
tm *tm_ptr; std::tm tmp;
tm_ptr = localtime(&timestamp); gmtime_r(t, &tmp);
size_t len; 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); return string(str_buff, len);
} }
ostream& ostream&
operator<< (ostream& os, const account_public_address& addr) operator<< (ostream& os, const account_public_address& addr)
{ {
@ -357,6 +336,109 @@ sum_money_in_outputs(const json& _json)
return sum_xmr; 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 uint64_t
sum_money_in_inputs(const transaction& tx) sum_money_in_inputs(const transaction& tx)
{ {
@ -1035,21 +1117,6 @@ get_tx_pub_key_from_received_outs(const transaction &tx)
return null_pkey; 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) * Check if given output (specified by output_index)
* belongs is ours based * belongs is ours based

@ -16,7 +16,6 @@
#include "monero_headers.h" #include "monero_headers.h"
#include "../ext/infix_iterator.h" #include "../ext/infix_iterator.h"
#include "../ext/date/tz.h"
#include "../ext/fmt/ostream.h" #include "../ext/fmt/ostream.h"
#include "../ext/fmt/format.h" #include "../ext/fmt/format.h"
#include "../ext/json.hpp" #include "../ext/json.hpp"
@ -104,10 +103,7 @@ bf::path
remove_trailing_path_separator(const bf::path& in_path); remove_trailing_path_separator(const bf::path& in_path);
string string
timestamp_to_str(time_t timestamp, const char* format = "%F %T"); timestamp_to_str_gm(time_t timestamp, const char* format = "%F %T");
string
timestamp_to_str_local(time_t timestamp, const char* format = "%F %T");
ostream& ostream&
operator<< (ostream& os, const account_public_address& addr); 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> pair<uint64_t, uint64_t>
sum_money_in_outputs(const json& _json); 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 uint64_t
sum_money_in_inputs(const transaction& tx); sum_money_in_inputs(const transaction& tx);

Loading…
Cancel
Save