├── .dockerignore ├── .gitignore ├── .gitmodules ├── CODE_OF_CONDUCT.md ├── Dockerfile ├── LICENSE ├── Makefile ├── README.md ├── bitcoin-mainnet.yml ├── bitcoin-testnet.yml ├── build.sh ├── build └── base │ └── Dockerfile ├── coins ├── bitcoin-mainnet.json ├── bitcoin-testnet.json ├── litecoin-mainnet.json ├── litecoin-testnet.json ├── vertcoin-mainnet.json └── vertcoin-testnet.json ├── docker-compose.yml ├── litecoin-mainnet.yml ├── litecoin-testnet.yml ├── src ├── blockchaintypes.h ├── blockfilewatcher.cpp ├── blockfilewatcher.h ├── blockindexer.cpp ├── blockindexer.h ├── blockreader.cpp ├── blockreader.h ├── blockscanner.cpp ├── blockscanner.h ├── byte_array_buffer.cpp ├── byte_array_buffer.h ├── coinparams.cpp ├── coinparams.h ├── crypto │ ├── bech32.cpp │ ├── bech32.h │ ├── common.h │ ├── ripemd160.cpp │ └── ripemd160.h ├── cxxopts.hpp ├── filereader.cpp ├── filereader.h ├── httpserver.cpp ├── httpserver.h ├── json.hpp ├── main.cpp ├── membuf.h ├── mempoolmonitor.cpp ├── mempoolmonitor.h ├── scriptsolver.cpp ├── scriptsolver.h ├── utility.cpp ├── utility.h └── vertcoinrpc.h ├── vertcoin-mainnet.yml └── vertcoin-testnet.yml /.dockerignore: -------------------------------------------------------------------------------- 1 | data/ -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | vtc_indexer 3 | .vscode/ 4 | data/ 5 | build_and_restart.sh -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/vertcoin-project/blockchain-indexer/f2ae12d3afe68ea599198cfa24334fe71293f97c/.gitmodules -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | ## Our Standards 8 | 9 | Examples of behavior that contributes to creating a positive environment include: 10 | 11 | * Using welcoming and inclusive language 12 | * Being respectful of differing viewpoints and experiences 13 | * Gracefully accepting constructive criticism 14 | * Focusing on what is best for the community 15 | * Showing empathy towards other community members 16 | 17 | Examples of unacceptable behavior by participants include: 18 | 19 | * The use of sexualized language or imagery and unwelcome sexual attention or advances 20 | * Trolling, insulting/derogatory comments, and personal or political attacks 21 | * Public or private harassment 22 | * Publishing others' private information, such as a physical or electronic address, without explicit permission 23 | * Other conduct which could reasonably be considered inappropriate in a professional setting 24 | 25 | ## Our Responsibilities 26 | 27 | Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. 28 | 29 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. 30 | 31 | ## Scope 32 | 33 | This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. 34 | 35 | ## Enforcement 36 | 37 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at contact@vertcoin.org. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. 38 | 39 | Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. 40 | 41 | ## Attribution 42 | 43 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [https://contributor-covenant.org/version/1/4][version] 44 | 45 | [homepage]: https://contributor-covenant.org 46 | [version]: https://contributor-covenant.org/version/1/4/ 47 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM vtc-wallet-middleware-base 2 | 3 | ADD src /root/sources/vtc-wallet-middleware-cpp/src 4 | ADD Makefile /root/sources/vtc-wallet-middleware-cpp/Makefile 5 | 6 | RUN make clean -C /root/sources/vtc-wallet-middleware-cpp 7 | RUN make -C /root/sources/vtc-wallet-middleware-cpp 8 | 9 | ENTRYPOINT ["/root/sources/vtc-wallet-middleware-cpp/vtc_indexer"] 10 | 11 | 12 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017-present The Vertcoin developers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | UNAME := $(shell uname) 2 | 3 | 4 | ifeq ($(UNAME), Linux) 5 | BINFLAGS ?= -I. -L. -L/usr/local/lib -I/usr/local/include 6 | PLATFORMCXXFLAGS ?= -fPIC 7 | INDEXERBIN ?= vtc_indexer 8 | CC = g++ 9 | C = gcc 10 | endif 11 | ifeq ($(UNAME), MINGW32_NT-6.2) 12 | BINFLAGS ?= -I. -L. 13 | INDEXERBIN ?= vtc_indexer.exe 14 | CC = g++ 15 | C = gcc 16 | endif 17 | ifeq ($(UNAME), Darwin) 18 | BINFLAGS ?= -I. -L. 19 | INDEXERBIN ?= vtc_indexer 20 | CC = clang++ 21 | C = clang 22 | endif 23 | ifeq ($(UNAME), Darwin-Cross) 24 | BINFLAGS ?= -I. -L. 25 | INDEXERBIN ?= vtc_indexer 26 | CC = o64-clang++ 27 | C = o64-clang 28 | endif 29 | 30 | PLATFORMCXXFLAGS += -g -Wall -std=c++14 -O3 -Wl,-E 31 | 32 | INDEXERSRC = src/main.cpp src/blockfilewatcher.cpp src/coinparams.cpp src/byte_array_buffer.cpp src/blockscanner.cpp src/scriptsolver.cpp src/httpserver.cpp src/utility.cpp src/blockreader.cpp src/filereader.cpp src/mempoolmonitor.cpp src/blockindexer.cpp src/crypto/ripemd160.cpp src/crypto/bech32.cpp 33 | INDEXEROBJS = $(INDEXERSRC:.cpp=.cpp.o) 34 | 35 | INDEXERLDFLAGS = $(BINFLAGS) -lrestbed -lcrypto -ldl -pthread -lleveldb -lssl -lsecp256k1 -ljsonrpccpp-client -ljsonrpccpp-common -ljsoncpp 36 | 37 | CXXFLAGS = $(PLATFORMCXXFLAGS) 38 | 39 | all: indexer 40 | 41 | indexer: $(INDEXERSRC) $(INDEXERBIN) 42 | 43 | clean: 44 | $(RM) -r $(INDEXEROBJS) 45 | 46 | $(INDEXERBIN): $(INDEXEROBJS) 47 | $(CC) $(INDEXEROBJS) -o $@ $(INDEXERLDFLAGS) 48 | 49 | %.c.o: %.c 50 | $(C) $(PLATFORMCXXFLAGS) -O3 -c $< -o $@ 51 | 52 | %.cpp.o: %.cpp 53 | $(CC) $(CXXFLAGS) -c $< -o $@ 54 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # C++ Blockchain Indexer 2 | 3 | https://blkidx.org/ 4 | 5 | Pre-release warning 6 | ---------------- 7 | IMPORTANT: This software is still under active development. Do not depend on this for production situations. 8 | 9 | What is this? 10 | ---------------- 11 | This project aims to be a independant blockchain indexer for Bitcoin-based blockchains. It can read the blockfiles directly and indexes these blocks to allow a number of queries via its built-in HTTP service: 12 | 13 | * Fetch TXOs for an address 14 | * Optionally only return new TXOs since a particular height 15 | * Return only unspent TXOs 16 | * Fetch the balance for an address 17 | * Check if one or more outpoints are spent 18 | * Get a transaction 19 | * Send a transaction 20 | * Return the most recent blocks (hash, height, time) 21 | * Return basic sync status (highest block on coind, highest block in index) 22 | 23 | Supported elements 24 | ---------------- 25 | The indexer currently supports: 26 | * Standard P2PK and P2SH scripts 27 | * Segwit P2WPK and P2WSH scripts (using bech32 address formats) 28 | * Multi-sig scripts 29 | 30 | Compatible coins 31 | ---------------- 32 | The indexer should work for any Bitcoin derivative. The indexer is working for Vertcoin and Litecoin, whereas Bitcoin support is currently under development. 33 | 34 | Docker 35 | ---------------- 36 | The indexer is built around Docker. It is possible to compile and run it on bare Linux, but to get running quickly it's easier to use Docker. There's docker-compose files available for all the supported coins. 37 | 38 | Get started 39 | ---------------- 40 | * Install [Docker](https://www.docker.com/) 41 | 42 | * Since the containers for the indexer will run in an isolated network, we first have to create it: 43 | ``` 44 | docker network create blockchain-indexer 45 | ``` 46 | 47 | * Clone the repository and build the images 48 | ``` 49 | git clone https://github.com/gertjaap/blockchain-indexer 50 | cd blockchain-indexer 51 | ./build.sh 52 | ``` 53 | 54 | * Once the images are built, you can start the indexer -for example- for Vertcoin: 55 | ``` 56 | docker-compose -f vertcoin-mainnet.yml up -d 57 | ``` 58 | 59 | * You can now check you running containers and look up the IP address for the indexer: 60 | ``` 61 | docker ps 62 | ``` 63 | 64 | ``` 65 | CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 66 | 034492bce34b vtc-wallet-middleware "/root/sources/vtc..." 5 seconds ago Up 1 second 8888/tcp blockchainindexer_vtc-middleware-cpp-main_1 67 | 48c3446ed3df lukechilds/vertcoind "init -rpcuser=mid..." 5 seconds ago Up 3 seconds 0.0.0.0:5889->5889/tcp, 8332-8333/tcp blockchainindexer_vertcoind-main_1 68 | ``` 69 | 70 | * Using the ID of the container you can find out its IP address 71 | 72 | ``` 73 | docker inspect 034492bce34b 74 | ``` 75 | 76 | ``` 77 | { 78 | {...} 79 | "Networks": { 80 | "blockchain-indexer": { 81 | {...} 82 | "IPAddress": "172.19.0.3", 83 | {...} 84 | } 85 | } 86 | } 87 | ``` 88 | 89 | * You can then look at the status by opening the URL in the browser port 8888: 90 | 91 | ``` 92 | http://172.19.0.3:8888/blocks 93 | ``` 94 | 95 | -------------------------------------------------------------------------------- /bitcoin-mainnet.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | services: 3 | 4 | bitcoind-main: 5 | image: kylemanna/bitcoind 6 | restart: always 7 | expose: 8 | - "8332" 9 | ports: 10 | - "8333:8333" 11 | volumes: 12 | - ./data/btc/main/coind:/bitcoin/.bitcoin 13 | environment: 14 | - RPCUSER=middleware 15 | - RPCPASSWORD=middleware 16 | command: -rpcport=8332 -server -rpcallowip='0.0.0.0/0' -txindex 17 | 18 | btc-middleware-cpp-main: 19 | image: vtc-wallet-middleware 20 | environment: 21 | - COIND_HOST=bitcoind-main 22 | expose: 23 | - "8888" 24 | depends_on: 25 | - bitcoind-main 26 | volumes: 27 | - ./data/btc/main/coind/blocks:/blocks 28 | - ./data/btc/main/index:/index 29 | - ./coins:/coins 30 | command: --coinParams=/coins/bitcoin-mainnet.json 31 | 32 | networks: 33 | default: 34 | external: 35 | name: blockchain-indexer -------------------------------------------------------------------------------- /bitcoin-testnet.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | services: 3 | 4 | bitcoind-test: 5 | image: kylemanna/bitcoind 6 | restart: always 7 | expose: 8 | - "8332" 9 | ports: 10 | - "8333:8333" 11 | volumes: 12 | - ./data/btc/test/coind:/bitcoin/.bitcoin 13 | environment: 14 | - RPCUSER=middleware 15 | - RPCPASSWORD=middleware 16 | command: -rpcport=8332 -server -rpcallowip='0.0.0.0/0' -testnet -txindex 17 | 18 | btc-middleware-cpp-test: 19 | image: vtc-wallet-middleware 20 | environment: 21 | - COIND_HOST=bitcoind-test 22 | expose: 23 | - "8888" 24 | depends_on: 25 | - bitcoind-test 26 | volumes: 27 | - ./data/btc/test/coind/testnet3/blocks:/blocks 28 | - ./data/btc/test/index:/index 29 | - ./coins:/coins 30 | command: --coinParams=/coins/bitcoin-testnet.json 31 | 32 | networks: 33 | default: 34 | external: 35 | name: blockchain-indexer -------------------------------------------------------------------------------- /build.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | docker build -t vtc-wallet-middleware-base build/base/ 3 | docker build -t vtc-wallet-middleware . 4 | -------------------------------------------------------------------------------- /build/base/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ubuntu:16.04 2 | 3 | RUN apt-get update && apt install -y git wget build-essential libleveldb-dev cmake automake libssl-dev libtool autoconf libjsonrpccpp-dev libjsoncpp-dev libcurl4-openssl-dev 4 | 5 | RUN git clone --recursive https://github.com/Corvusoft/restbed 6 | RUN mkdir restbed/build 7 | WORKDIR /restbed/build 8 | RUN cmake .. 9 | RUN make install 10 | RUN cp -r ../distribution/include/* /usr/local/include 11 | RUN cp -r ../distribution/library/* /usr/lib 12 | 13 | WORKDIR / 14 | RUN git clone https://github.com/vertcoin/vertcoin 15 | WORKDIR /vertcoin/src/secp256k1 16 | RUN ./autogen.sh 17 | RUN ./configure 18 | RUN make 19 | RUN make install 20 | 21 | RUN ldconfig 22 | -------------------------------------------------------------------------------- /coins/bitcoin-mainnet.json: -------------------------------------------------------------------------------- 1 | { 2 | "magic" : "f9beb4d9", 3 | "version_p2pkh" : "00", 4 | "version_p2sh" : "05", 5 | "prefix_bech32": "btc" 6 | } -------------------------------------------------------------------------------- /coins/bitcoin-testnet.json: -------------------------------------------------------------------------------- 1 | { 2 | "magic" : "0b110907", 3 | "version_p2pkh" : "6f", 4 | "version_p2sh" : "c4", 5 | "prefix_bech32": "tbtc" 6 | } -------------------------------------------------------------------------------- /coins/litecoin-mainnet.json: -------------------------------------------------------------------------------- 1 | { 2 | "magic" : "fbc0b6db", 3 | "version_p2pkh" : "30", 4 | "version_p2sh" : "32", 5 | "prefix_bech32": "ltc" 6 | } -------------------------------------------------------------------------------- /coins/litecoin-testnet.json: -------------------------------------------------------------------------------- 1 | { 2 | "magic" : "fdd2c8f1", 3 | "version_p2pkh" : "6f", 4 | "version_p2sh" : "c4", 5 | "prefix_bech32": "tltc" 6 | } -------------------------------------------------------------------------------- /coins/vertcoin-mainnet.json: -------------------------------------------------------------------------------- 1 | { 2 | "magic" : "fabfb5da", 3 | "version_p2pkh" : "47", 4 | "version_p2sh" : "05", 5 | "prefix_bech32": "vtc" 6 | } -------------------------------------------------------------------------------- /coins/vertcoin-testnet.json: -------------------------------------------------------------------------------- 1 | { 2 | "magic" : "76657274", 3 | "version_p2pkh" : "4a", 4 | "version_p2sh" : "c4", 5 | "prefix_bech32": "tvtc" 6 | } -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | vertcoin-mainnet.yml -------------------------------------------------------------------------------- /litecoin-mainnet.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | services: 3 | 4 | litecoind-main: 5 | image: uphold/litecoind 6 | restart: always 7 | expose: 8 | - "8332" 9 | ports: 10 | - "9333:9333" 11 | volumes: 12 | - ./data/ltc/main/coind:/home/litecoin/.litecoin 13 | command: -rpcuser=middleware -rpcpassword=middleware -rpcallowip='0.0.0.0/0' -rpcport=8332 -server -txindex 14 | 15 | ltc-middleware-cpp-main: 16 | image: vtc-wallet-middleware 17 | restart: always 18 | environment: 19 | - COIND_HOST=litecoind-main 20 | expose: 21 | - "8888" 22 | volumes: 23 | - ./data/ltc/main/coind/blocks:/blocks 24 | - ./data/ltc/main/index:/index 25 | - ./coins:/coins 26 | command: --coinParams=/coins/litecoin-mainnet.json 27 | 28 | networks: 29 | default: 30 | external: 31 | name: blockchain-indexer -------------------------------------------------------------------------------- /litecoin-testnet.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | services: 3 | 4 | litecoind-test: 5 | image: uphold/litecoind 6 | restart: always 7 | expose: 8 | - "8332" 9 | ports: 10 | - "9333:9333" 11 | volumes: 12 | - ./data/ltc/test/coind:/home/litecoin/.litecoin 13 | command: -rpcuser=middleware -rpcpassword=middleware -rpcport=8332 -rpcallowip='0.0.0.0/0' -testnet -server -txindex 14 | 15 | ltc-middleware-cpp-test: 16 | image: vtc-wallet-middleware 17 | restart: always 18 | environment: 19 | - COIND_HOST=litecoind-test 20 | expose: 21 | - "8888" 22 | volumes: 23 | - ./data/ltc/test/coind/testnet4/blocks:/blocks 24 | - ./data/ltc/test/index:/index 25 | - ./coins:/coins 26 | command: --coinParams=/coins/litecoin-testnet.json 27 | 28 | networks: 29 | default: 30 | external: 31 | name: blockchain-indexer -------------------------------------------------------------------------------- /src/blockchaintypes.h: -------------------------------------------------------------------------------- 1 | /* VTC Blockindexer - A utility to build additional indexes to the 2 | Vertcoin blockchain by scanning and indexing the blockfiles 3 | downloaded by Vertcoin Core. 4 | 5 | Copyright (C) 2017 Gert-Jaap Glasbergen 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #ifndef BLOCKCHAINTYPES_H_INCLUDED 22 | #define BLOCKCHAINTYPES_H_INCLUDED 23 | 24 | #include 25 | #include 26 | using namespace std; 27 | 28 | namespace VtcBlockIndexer { 29 | // ScannedBlock is used to store information about block headers obtained while initially scanning through the block files 30 | struct ScannedBlock { 31 | // The filename (without path) where the block is located in 32 | string fileName; 33 | 34 | // The position inside the block file where the block header starts 35 | uint64_t filePosition; 36 | 37 | // The total size of the block 38 | uint32_t blockSize; 39 | 40 | // The hash of the block. This string is the the "reverse hash" used on block explorers 41 | string blockHash; 42 | 43 | // The hash of the previous block used to form the chain. This string is the the "reverse hash" used on block explorers 44 | string previousBlockHash; 45 | }; 46 | 47 | // Describes a transaction output inside a blockchain transaction 48 | struct TransactionOutput { 49 | // The value of the output in Satoshis (0.00000001 VTC) 50 | uint64_t value; 51 | 52 | // The output script in Bitcoinscript 53 | vector script; 54 | 55 | // The index of the output in the list of outputs 56 | uint32_t index; 57 | 58 | // Convenience method for keeping TXOs in memory (mempool) 59 | string txHash; 60 | }; 61 | 62 | // Describes a transaction input inside a blockchain transaction 63 | struct TransactionInput { 64 | // The index of the input int he list of indexes 65 | uint32_t index; 66 | 67 | // The hash of the transaction whose output is being spent 68 | string txHash; 69 | 70 | // The index of the output inside the transaction being spent 71 | uint32_t txoIndex; 72 | 73 | // Part of all transactions. A number intended to allow unconfirmed time-locked transactions to be updated before being finalized; not currently used except to disable locktime in a transaction 74 | uint32_t sequence; 75 | 76 | // Indicating if this is a coinbase (Generated coins) input 77 | bool coinbase; 78 | 79 | // The script of the input in Bitcoinscript 80 | vector script; 81 | 82 | // The witness data for the input 83 | vector> witnessData; 84 | }; 85 | 86 | // Describes a transaction inside a block 87 | struct Transaction { 88 | // The list of inputs for this transaction 89 | vector inputs; 90 | 91 | // The list of outputs for this transaction 92 | vector outputs; 93 | 94 | // The hash for the transaction. This is the reverse hash as used on block explorers. 95 | string txHash; 96 | 97 | // The hash for the witness transaction. Contains a different hash in case the transaction uses SegWit. Will be equal to TXHash otherwise. 98 | string txWitHash; 99 | 100 | // Position inside the blockfile where this transaction starts 101 | uint64_t filePosition; 102 | 103 | // Version bit for the transaction 104 | uint32_t version; 105 | 106 | // Locktime. Transaction cannot be spent until this number of blocks have been confirmed after its initial inclusion in the blockchain 107 | uint32_t lockTime; 108 | }; 109 | 110 | // Describes a block 111 | struct Block { 112 | // The blk????.dat file this block is located in. 113 | string fileName; 114 | 115 | // The position where the block starts inside the file 116 | int filePosition; 117 | 118 | // The hash of the block. This string is the the "reverse hash" used on block explorers 119 | string blockHash; 120 | 121 | string previousBlockHash; 122 | 123 | // The merkle root of the transactions inside this block 124 | string merkleRoot; 125 | 126 | // The height of the block in the chain 127 | uint64_t height; 128 | 129 | // Size of the block in bytes 130 | uint64_t byteSize; 131 | 132 | // Timestamp of the block 133 | uint32_t time; 134 | 135 | // encoded target threshold 136 | uint32_t bits; 137 | 138 | // Unique value to make the header hash match the target 139 | uint32_t nonce; 140 | 141 | // Version of the block 142 | uint32_t version; 143 | 144 | // The list of transactions inside this block 145 | vector transactions; 146 | }; 147 | 148 | 149 | } 150 | #endif // BLOCKCHAINTYPES_H_INCLUDED -------------------------------------------------------------------------------- /src/blockfilewatcher.cpp: -------------------------------------------------------------------------------- 1 | /* VTC Blockindexer - A utility to build additional indexes to the 2 | Vertcoin blockchain by scanning and indexing the blockfiles 3 | downloaded by Vertcoin Core. 4 | 5 | Copyright (C) 2017 Gert-Jaap Glasbergen 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | #include "blockfilewatcher.h" 21 | #include "scriptsolver.h" 22 | #include "blockchaintypes.h" 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "blockscanner.h" 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | using namespace std; 37 | 38 | // Constructor 39 | VtcBlockIndexer::BlockFileWatcher::BlockFileWatcher(string blocksDir, const shared_ptr db, const shared_ptr mempoolMonitor) { 40 | this->db = db; 41 | this->mempoolMonitor = mempoolMonitor; 42 | blockIndexer.reset(new VtcBlockIndexer::BlockIndexer(this->db, this->mempoolMonitor)); 43 | blockReader.reset(new VtcBlockIndexer::BlockReader(blocksDir)); 44 | this->blocksDir = blocksDir; 45 | this->maxLastModified.tv_sec = 0; 46 | this->maxLastModified.tv_nsec = 0; 47 | } 48 | 49 | void VtcBlockIndexer::BlockFileWatcher::startWatcher() { 50 | DIR *dir; 51 | dirent *ent; 52 | string blockFilePrefix = "blk"; 53 | 54 | while(true) { 55 | bool shouldUpdate = false; 56 | dir = opendir(&*this->blocksDir.begin()); 57 | while ((ent = readdir(dir)) != NULL) { 58 | const string file_name = ent->d_name; 59 | struct stat result; 60 | 61 | // Check if the filename starts with "blk" 62 | if(strncmp(file_name.c_str(), blockFilePrefix.c_str(), blockFilePrefix.size()) == 0) 63 | { 64 | stringstream fullPath; 65 | fullPath << this->blocksDir << "/" << file_name; 66 | if(stat(fullPath.str().c_str(), &result)==0) 67 | { 68 | if(result.st_mtim.tv_sec > this->maxLastModified.tv_sec) { 69 | this->maxLastModified = result.st_mtim; 70 | if(!shouldUpdate) 71 | cout << "Change(s) detected, starting index update." << endl; 72 | shouldUpdate = true; 73 | } 74 | } 75 | } 76 | } 77 | 78 | closedir(dir); 79 | 80 | if(shouldUpdate) { 81 | updateIndex(); 82 | } 83 | 84 | std::this_thread::sleep_for(std::chrono::seconds(1)); 85 | } 86 | } 87 | 88 | void VtcBlockIndexer::BlockFileWatcher::scanBlocks(string fileName) { 89 | unique_ptr blockScanner(new VtcBlockIndexer::BlockScanner(blocksDir, fileName)); 90 | if(blockScanner->open()) 91 | { 92 | while(blockScanner->moveNext()) { 93 | this->totalBlocks++; 94 | VtcBlockIndexer::ScannedBlock block = blockScanner->scanNextBlock(); 95 | // Create an empty vector inside the unordered map if this previousBlockHash 96 | // was not found before. 97 | if(this->blocks.find(block.previousBlockHash) == this->blocks.end()) { 98 | this->blocks[block.previousBlockHash] = {}; 99 | } 100 | 101 | // Check if a block with the same hash already exists. Unfortunately, I found 102 | // instances where a block is included in the block files more than once. 103 | vector matchingBlocks = this->blocks[block.previousBlockHash]; 104 | bool blockFound = false; 105 | for(VtcBlockIndexer::ScannedBlock matchingBlock : matchingBlocks) { 106 | if(matchingBlock.blockHash == block.blockHash) { 107 | blockFound = true; 108 | } 109 | } 110 | 111 | // If the block is not present, add it to the vector. 112 | if(!blockFound) { 113 | this->blocks[block.previousBlockHash].push_back(block); 114 | } 115 | } 116 | blockScanner->close(); 117 | } 118 | } 119 | 120 | 121 | void VtcBlockIndexer::BlockFileWatcher::scanBlockFiles(string dirPath) { 122 | DIR *dir; 123 | dirent *ent; 124 | 125 | dir = opendir(&*dirPath.begin()); 126 | while ((ent = readdir(dir)) != NULL) { 127 | const string file_name = ent->d_name; 128 | 129 | // Check if the filename starts with "blk" 130 | string prefix = "blk"; 131 | if(strncmp(file_name.c_str(), prefix.c_str(), prefix.size()) == 0) 132 | { 133 | scanBlocks(file_name); 134 | } 135 | } 136 | closedir(dir); 137 | } 138 | 139 | 140 | VtcBlockIndexer::ScannedBlock VtcBlockIndexer::BlockFileWatcher::findLongestChain(vector matchingBlocks) { 141 | vector nextBlockHashes; 142 | for(uint i = 0; i < matchingBlocks.size(); i++) { 143 | nextBlockHashes.push_back(matchingBlocks.at(i).blockHash); 144 | } 145 | 146 | while(true) { 147 | 148 | for(uint i = 0; i < nextBlockHashes.size(); i++) { 149 | int countChains = 0; 150 | for(uint i = 0; i < nextBlockHashes.size(); i++) { 151 | if(nextBlockHashes.at(i) != "") { 152 | countChains++; 153 | } 154 | } 155 | 156 | if(countChains == 1) { 157 | for(uint i = 0; i < nextBlockHashes.size(); i++) { 158 | if(nextBlockHashes.at(i) != "") { 159 | return matchingBlocks.at(i); 160 | } 161 | } 162 | } 163 | 164 | if(this->blocks.find(nextBlockHashes.at(i)) == this->blocks.end()) { 165 | nextBlockHashes.at(i).assign(""); 166 | } else { 167 | vector matchingBlocks = this->blocks[nextBlockHashes.at(i)]; 168 | VtcBlockIndexer::ScannedBlock bestBlock = matchingBlocks.at(0); 169 | if(matchingBlocks.size() > 1) { 170 | bestBlock = findLongestChain(matchingBlocks); 171 | } 172 | nextBlockHashes.at(i).assign(bestBlock.blockHash); 173 | } 174 | } 175 | } 176 | } 177 | 178 | 179 | string VtcBlockIndexer::BlockFileWatcher::processNextBlock(string prevBlockHash) { 180 | 181 | 182 | // If there is no block present with this hash as previousBlockHash, return an empty 183 | // string signaling we're at the end of the chain. 184 | if(this->blocks.find(prevBlockHash) == this->blocks.end()) { 185 | return ""; 186 | } 187 | 188 | // Find the blocks that match 189 | vector matchingBlocks = this->blocks[prevBlockHash]; 190 | 191 | if(matchingBlocks.size() > 0) { 192 | VtcBlockIndexer::ScannedBlock bestBlock = matchingBlocks.at(0); 193 | 194 | if(matchingBlocks.size() > 1) { 195 | bestBlock = findLongestChain(matchingBlocks); 196 | } 197 | 198 | if(!blockIndexer->hasIndexedBlock(bestBlock.blockHash, this->blockHeight)) { 199 | VtcBlockIndexer::Block fullBlock = blockReader->readBlock(bestBlock.fileName, bestBlock.filePosition, this->blockHeight, false); 200 | 201 | blockIndexer->indexBlock(fullBlock); 202 | } 203 | return bestBlock.blockHash; 204 | 205 | } else { 206 | // Somehow found an empty vector in the unordered_map. This should not happen. 207 | // But just in case, returning an empty value here. 208 | return ""; 209 | } 210 | } 211 | 212 | void VtcBlockIndexer::BlockFileWatcher::updateIndex() { 213 | 214 | time_t start; 215 | time(&start); 216 | 217 | this->blockHeight = 0; 218 | this->totalBlocks = 0; 219 | cout << "Scanning blocks..." << endl; 220 | 221 | scanBlockFiles(blocksDir); 222 | 223 | cout << "Found " << this->totalBlocks << " blocks. Constructing longest chain..." << endl; 224 | 225 | // The blockchain starts with the genesis block that has a zero hash as Previous Block Hash 226 | string nextBlock = "0000000000000000000000000000000000000000000000000000000000000000"; 227 | string processedBlock = processNextBlock(nextBlock); 228 | double nextUpdate = 10; 229 | while(processedBlock != "") { 230 | 231 | // Show progress every 10 seconds 232 | double seconds = difftime(time(NULL), start); 233 | if(seconds >= nextUpdate) { 234 | nextUpdate += 10; 235 | cout << "Construction is at height " << this->blockHeight << endl; 236 | } 237 | this->blockHeight++; 238 | nextBlock = processedBlock; 239 | processedBlock = processNextBlock(nextBlock); 240 | } 241 | 242 | cout << "Done. Processed " << this->blockHeight << " blocks. Have a nice day." << endl; 243 | 244 | this->blocks.clear(); 245 | } 246 | -------------------------------------------------------------------------------- /src/blockfilewatcher.h: -------------------------------------------------------------------------------- 1 | /* VTC Blockindexer - A utility to build additional indexes to the 2 | Vertcoin blockchain by scanning and indexing the blockfiles 3 | downloaded by Vertcoin Core. 4 | 5 | Copyright (C) 2017 Gert-Jaap Glasbergen 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | 22 | #ifndef BLOCKFILEWATCHER_H_INCLUDED 23 | #define BLOCKFILEWATCHER_H_INCLUDED 24 | 25 | #include 26 | #include 27 | #include 28 | #include "leveldb/db.h" 29 | #include "leveldb/write_batch.h" 30 | #include "blockchaintypes.h" 31 | #include "mempoolmonitor.h" 32 | #include "blockindexer.h" 33 | #include "blockreader.h" 34 | 35 | using namespace std; 36 | 37 | namespace VtcBlockIndexer { 38 | 39 | /** 40 | * The BlockFileWatcher class provides methods to watch and scan a blocks directory 41 | * and process the blockfiles when changes occur. 42 | */ 43 | 44 | class BlockFileWatcher { 45 | public: 46 | /** Constructs a BlockIndexer instance using the given block data directory 47 | */ 48 | BlockFileWatcher(string blocksDir, const shared_ptr db, const shared_ptr mempoolMonitor); 49 | 50 | /** Starts watching the blocksdir for changes and will execute an incremental 51 | * indexing when files have changed */ 52 | void startWatcher(); 53 | 54 | /** Updates the blockchain index incrementally */ 55 | void updateIndex(); 56 | 57 | private: 58 | /** Uses the blockscanner to scan blocks within a file and add them to the 59 | * unordered map. 60 | * 61 | * @param fileName The file name of the BLK????.DAT to scan for blocks. 62 | */ 63 | void scanBlocks(string fileName); 64 | 65 | /** Scans a folder for block files present and passes them to the scanBlocks 66 | * method 67 | * 68 | * @param dirPath The directory to scan for blockfiles. 69 | */ 70 | void scanBlockFiles(string dirName); 71 | 72 | /** Orphaned blocks stay in the blockfiles. So this method is created to find out which of the canditate follow-up blocks 73 | * chain of work behind it. 74 | * @param matchingBlocks The blocks that should be investigated. 75 | */ 76 | VtcBlockIndexer::ScannedBlock findLongestChain(vector matchingBlocks); 77 | 78 | /** Finds the next block in line (by matching the prevBlockHash which is the 79 | * key in the unordered_map). Then uses the block processor to do the indexing. 80 | * Returns the hash of the block that was processed. 81 | * 82 | * @param prevBlockHash the hex hash of the block that was last processed that we should 83 | * extend the chain onto. 84 | */ 85 | string processNextBlock(string prevBlockHash); 86 | string blocksDir; 87 | shared_ptr db; 88 | shared_ptr mempoolMonitor; 89 | unique_ptr blockReader; 90 | unique_ptr blockIndexer; 91 | int totalBlocks; 92 | int blockHeight; 93 | unordered_map> blocks; 94 | struct timespec maxLastModified; 95 | }; 96 | 97 | } 98 | 99 | #endif // BLOCKINDEXER_H_INCLUDED -------------------------------------------------------------------------------- /src/blockindexer.cpp: -------------------------------------------------------------------------------- 1 | /* VTC Blockindexer - A utility to build additional indexes to the 2 | Vertcoin blockchain by scanning and indexing the blockfiles 3 | downloaded by Vertcoin Core. 4 | 5 | Copyright (C) 2017 Gert-Jaap Glasbergen 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | #include "blockindexer.h" 21 | #include "scriptsolver.h" 22 | #include "blockchaintypes.h" 23 | #include 24 | #include 25 | 26 | //#include "hashing.h" 27 | #include 28 | #include 29 | #include 30 | 31 | 32 | using namespace std; 33 | 34 | // This map keeps the nextTxoIndex in memory for speed - no database fetching on every TX 35 | unordered_map nextTxoIndex; 36 | 37 | 38 | 39 | VtcBlockIndexer::BlockIndexer::BlockIndexer(const shared_ptr db, const shared_ptr mempoolMonitor) { 40 | this->db = db; 41 | this->mempoolMonitor = mempoolMonitor; 42 | this->scriptSolver = make_unique(); 43 | } 44 | 45 | 46 | int VtcBlockIndexer::BlockIndexer::getNextTxoIndex(string prefix) { 47 | if(nextTxoIndex.find(prefix) == nextTxoIndex.end()) { 48 | leveldb::Iterator* it = this->db->NewIterator(leveldb::ReadOptions()); 49 | nextTxoIndex[prefix] = 1; 50 | string start(prefix + "-00000001"); 51 | string limit(prefix + "-99999999"); 52 | 53 | for (it->Seek(start); 54 | it->Valid() && it->key().ToString() < limit; 55 | it->Next()) { 56 | nextTxoIndex[prefix]++; 57 | } 58 | assert(it->status().ok()); // Check for any errors found during the scan 59 | delete it; 60 | } else { 61 | nextTxoIndex[prefix]++; 62 | } 63 | 64 | return nextTxoIndex[prefix]; 65 | } 66 | 67 | bool VtcBlockIndexer::BlockIndexer::clearBlockTxos(string blockHash) { 68 | leveldb::WriteBatch batch; 69 | 70 | string start(blockHash + "-txo-00000001"); 71 | string limit(blockHash + "-txo-99999999"); 72 | leveldb::Iterator* it = this->db->NewIterator(leveldb::ReadOptions()); 73 | for (it->Seek(start); 74 | it->Valid() && it->key().ToString() < limit; 75 | it->Next()) { 76 | batch.Delete(it->value().ToString()); 77 | } 78 | assert(it->status().ok()); // Check for any errors found during the scan 79 | delete it; 80 | 81 | string spentStart(blockHash + "-txospent-00000001"); 82 | string spentLimit(blockHash + "-txospent-99999999"); 83 | it = this->db->NewIterator(leveldb::ReadOptions()); 84 | for (it->Seek(spentStart); 85 | it->Valid() && it->key().ToString() < spentLimit; 86 | it->Next()) { 87 | batch.Delete(it->value().ToString()); 88 | } 89 | assert(it->status().ok()); // Check for any errors found during the scan 90 | delete it; 91 | 92 | leveldb::Status s = this->db->Write(leveldb::WriteOptions(), &batch); 93 | return s.ok(); 94 | } 95 | 96 | bool VtcBlockIndexer::BlockIndexer::hasIndexedBlock(string blockHash, int blockHeight) 97 | { 98 | stringstream ss; 99 | ss << "block-" << setw(8) << setfill('0') << blockHeight; 100 | 101 | string existingBlockHash; 102 | leveldb::Status s = this->db->Get(leveldb::ReadOptions(), ss.str(), &existingBlockHash); 103 | if(s.ok() && existingBlockHash == blockHash) { 104 | return true; 105 | } 106 | 107 | return false; 108 | } 109 | 110 | bool VtcBlockIndexer::BlockIndexer::indexBlock(Block block) { 111 | //cout << "Indexing block " << block.blockHash << " (Height " << block.height << ")" << endl; 112 | 113 | stringstream ss; 114 | ss << "block-" << setw(8) << setfill('0') << block.height; 115 | 116 | string existingBlockHash; 117 | leveldb::Status s = this->db->Get(leveldb::ReadOptions(), ss.str(), &existingBlockHash); 118 | 119 | if(s.ok() && existingBlockHash == block.blockHash) { 120 | // Block found in database and matches. This block is indexed already, so skip. 121 | return true; 122 | } else if (s.ok()) { 123 | // There was a different block at this height. Ditch the TXOs from the old block. 124 | clearBlockTxos(existingBlockHash); 125 | } 126 | 127 | stringstream blockHeight; 128 | blockHeight << setw(8) << setfill('0') << block.height; 129 | 130 | string highestBlock; 131 | s = this->db->Get(leveldb::ReadOptions(), "highestblock", &highestBlock); 132 | if(!s.ok()) { 133 | this->db->Put(leveldb::WriteOptions(), "highestblock", blockHeight.str()); 134 | } else { 135 | if(stoull(highestBlock) < block.height) { 136 | this->db->Put(leveldb::WriteOptions(), "highestblock", blockHeight.str()); 137 | } 138 | } 139 | 140 | this->db->Put(leveldb::WriteOptions(), ss.str(), block.blockHash); 141 | 142 | stringstream ssBlockFilePositionKey; 143 | ssBlockFilePositionKey << "block-filePosition-" << setw(8) << setfill('0') << block.height; 144 | stringstream ssBlockFilePositionValue; 145 | ssBlockFilePositionValue << block.fileName << setw(12) << setfill('0') << block.filePosition; 146 | 147 | this->db->Put(leveldb::WriteOptions(), ssBlockFilePositionKey.str(), ssBlockFilePositionValue.str()); 148 | 149 | stringstream ssBlockHashHeightKey; 150 | ssBlockHashHeightKey << "block-hash-" << block.blockHash; 151 | stringstream ssBlockHashHeightValue; 152 | ssBlockHashHeightValue << setw(8) << setfill('0') << block.height; 153 | 154 | this->db->Put(leveldb::WriteOptions(), ssBlockHashHeightKey.str(), ssBlockHashHeightValue.str()); 155 | 156 | stringstream ssBlockTimeHeightKey; 157 | ssBlockTimeHeightKey << "block-time-" << setw(8) << setfill('0') << block.height; 158 | this->db->Put(leveldb::WriteOptions(), ssBlockTimeHeightKey.str(), std::to_string(block.time)); 159 | 160 | stringstream ssBlockSizeHeightKey; 161 | ssBlockSizeHeightKey << "block-size-" << setw(8) << setfill('0') << block.height; 162 | this->db->Put(leveldb::WriteOptions(), ssBlockSizeHeightKey.str(), std::to_string(block.byteSize)); 163 | 164 | stringstream ssBlockTxCountHeightKey; 165 | ssBlockTxCountHeightKey << "block-txcount-" << setw(8) << setfill('0') << block.height; 166 | this->db->Put(leveldb::WriteOptions(), ssBlockTxCountHeightKey.str(), std::to_string(block.transactions.size())); 167 | 168 | int txIndex = -1; 169 | // TODO: Verify block integrity 170 | for(VtcBlockIndexer::Transaction tx : block.transactions) { 171 | txIndex++; 172 | stringstream blockTxKey; 173 | blockTxKey << "block-" << block.blockHash << "-tx-" << setw(8) << setfill('0') << txIndex; 174 | this->db->Put(leveldb::WriteOptions(), blockTxKey.str(), tx.txHash); 175 | 176 | stringstream ssTxFilePositionKey; 177 | ssTxFilePositionKey << "tx-filePosition-" << tx.txHash; 178 | stringstream ssTxFilePositionValue; 179 | ssTxFilePositionValue << block.fileName << setw(12) << setfill('0') << tx.filePosition; 180 | 181 | this->db->Put(leveldb::WriteOptions(), ssTxFilePositionKey.str(), ssTxFilePositionValue.str()); 182 | 183 | stringstream txBlockKey; 184 | txBlockKey << "tx-" << tx.txHash << "-block"; 185 | this->db->Put(leveldb::WriteOptions(), txBlockKey.str(), block.blockHash); 186 | 187 | 188 | for(VtcBlockIndexer::TransactionOutput out : tx.outputs) { 189 | vector addresses = this->scriptSolver->getAddressesFromScript(out.script); 190 | if(addresses.size() > 1) { 191 | if(scriptSolver->isMultiSig(out.script)) { 192 | stringstream txoMultiSigKey; 193 | txoMultiSigKey << "multisigtx-" << tx.txHash << "-" << setw(8) << setfill('0') << out.index; 194 | this->db->Put(leveldb::WriteOptions(), txoMultiSigKey.str(), std::to_string(scriptSolver->requiredSignatures(out.script))); 195 | } 196 | } 197 | for(string address : addresses) { 198 | int nextIndex = getNextTxoIndex(address + "-txo"); 199 | stringstream txoKey; 200 | txoKey << address << "-txo-" << setw(8) << setfill('0') << nextIndex; 201 | stringstream txoValue; 202 | txoValue << tx.txHash << setw(8) << setfill('0') << out.index << setw(8) << setfill('0') << block.height << out.value; 203 | this->db->Put(leveldb::WriteOptions(), txoKey.str(), txoValue.str()); 204 | 205 | nextIndex = getNextTxoIndex(block.blockHash + "-txo"); 206 | stringstream blockTxoKey; 207 | blockTxoKey << block.blockHash << "-txo-" << setw(8) << setfill('0') << nextIndex; 208 | this->db->Put(leveldb::WriteOptions(), blockTxoKey.str(), txoKey.str()); 209 | } 210 | } 211 | 212 | for(VtcBlockIndexer::TransactionInput txi : tx.inputs) { 213 | if(!txi.coinbase) 214 | { 215 | stringstream txSpentKey; 216 | txSpentKey << "txo-" << txi.txHash << "-" << setw(8) << setfill('0') << txi.txoIndex << "-spent"; 217 | 218 | stringstream spendingTx; 219 | spendingTx << block.blockHash << "-" << tx.txHash; 220 | 221 | this->db->Put(leveldb::WriteOptions(), txSpentKey.str(), spendingTx.str()); 222 | 223 | int nextIndex = getNextTxoIndex(block.blockHash + "-txospent"); 224 | stringstream blockTxoSpentKey; 225 | blockTxoSpentKey << block.blockHash << "-txospent-" << setw(8) << setfill('0') << nextIndex; 226 | this->db->Put(leveldb::WriteOptions(), blockTxoSpentKey.str(), txSpentKey.str()); 227 | } 228 | } 229 | this->mempoolMonitor->transactionIndexed(tx.txHash); 230 | } 231 | 232 | 233 | 234 | return true; 235 | } 236 | 237 | -------------------------------------------------------------------------------- /src/blockindexer.h: -------------------------------------------------------------------------------- 1 | /* VTC Blockindexer - A utility to build additional indexes to the 2 | Vertcoin blockchain by scanning and indexing the blockfiles 3 | downloaded by Vertcoin Core. 4 | 5 | Copyright (C) 2017 Gert-Jaap Glasbergen 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | 22 | #ifndef BLOCKINDEXER_H_INCLUDED 23 | #define BLOCKINDEXER_H_INCLUDED 24 | 25 | #include 26 | #include 27 | #include "leveldb/db.h" 28 | #include "leveldb/write_batch.h" 29 | #include "blockchaintypes.h" 30 | #include "scriptsolver.h" 31 | #include "mempoolmonitor.h" 32 | 33 | using namespace std; 34 | 35 | namespace VtcBlockIndexer { 36 | 37 | /** 38 | * The BlockIndexer class provides methods to index a block that was fully 39 | * read (so including its transactions). It will index the necessary elements 40 | * to be able to query balances per address, blocks by hash and index, and 41 | * data to handle reorgs. 42 | */ 43 | 44 | class BlockIndexer { 45 | public: 46 | /** Constructs a BlockIndexer instance using the given block data directory 47 | */ 48 | BlockIndexer(const shared_ptr db, const shared_ptr mempoolMonitor); 49 | 50 | /** Indexes the contents of the block 51 | */ 52 | bool indexBlock(Block block); 53 | 54 | /** Returns true when there's already a block with the passed hash 55 | * in the index at the passed blockheight. No need to reindex 56 | * in that case. 57 | */ 58 | bool hasIndexedBlock(string blockHash, int blockHeight); 59 | 60 | private: 61 | /** Removes TXOs and spends from a particular blockhash 62 | * in case of a reorg */ 63 | 64 | bool clearBlockTxos(string blockHash); 65 | /** Returns the next index to use for storing the TXO 66 | */ 67 | int getNextTxoIndex(string prefix); 68 | 69 | shared_ptr db; 70 | shared_ptr mempoolMonitor; 71 | 72 | // Reference to the scriptsolver class 73 | unique_ptr scriptSolver; 74 | }; 75 | 76 | } 77 | 78 | #endif // BLOCKINDEXER_H_INCLUDED -------------------------------------------------------------------------------- /src/blockreader.cpp: -------------------------------------------------------------------------------- 1 | /* VTC Blockindexer - A utility to build additional indexes to the 2 | Vertcoin blockchain by scanning and indexing the blockfiles 3 | downloaded by Vertcoin Core. 4 | 5 | Copyright (C) 2017 Gert-Jaap Glasbergen 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | #include "blockreader.h" 21 | #include "filereader.h" 22 | #include "blockchaintypes.h" 23 | #include "utility.h" 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | #include 32 | 33 | using namespace std; 34 | 35 | VtcBlockIndexer::BlockReader::BlockReader(const string blocksDir) { 36 | 37 | this->blocksDir = blocksDir; 38 | } 39 | 40 | std::vector VtcBlockIndexer::BlockReader::readRawBlockHeader(string fileName, uint64_t filePosition) { 41 | stringstream ss; 42 | ss << blocksDir << "/" << fileName; 43 | ifstream blockFile(ss.str(), ios_base::in | ios_base::binary); 44 | vector blockHeader(80); 45 | blockFile.read(reinterpret_cast(&blockHeader[0]) , 80); 46 | blockFile.close(); 47 | return blockHeader; 48 | } 49 | 50 | 51 | VtcBlockIndexer::Block VtcBlockIndexer::BlockReader::readBlock(string fileName, uint64_t filePosition, uint64_t blockHeight, bool headerOnly) { 52 | VtcBlockIndexer::Block fullBlock; 53 | 54 | fullBlock.fileName = fileName; 55 | fullBlock.filePosition = filePosition; 56 | fullBlock.height = blockHeight; 57 | 58 | stringstream ss; 59 | ss << blocksDir << "/" << fileName; 60 | ifstream blockFile(ss.str(), ios_base::in | ios_base::binary); 61 | 62 | if(!blockFile.is_open()) { 63 | cerr << "Block file could not be opened" << endl; 64 | exit(0); 65 | } 66 | 67 | blockFile.seekg(filePosition, ios_base::beg); 68 | vector blockHeader(80); 69 | blockFile.read(reinterpret_cast(&blockHeader[0]) , 80); 70 | fullBlock.blockHash = VtcBlockIndexer::Utility::hashToReverseHex(VtcBlockIndexer::Utility::sha256(VtcBlockIndexer::Utility::sha256(blockHeader))); 71 | 72 | blockFile.seekg(filePosition, ios_base::beg); 73 | 74 | blockFile.read(reinterpret_cast(&fullBlock.version), sizeof(fullBlock.version)); 75 | fullBlock.previousBlockHash = VtcBlockIndexer::Utility::hashToReverseHex(VtcBlockIndexer::FileReader::readHash(blockFile)); 76 | fullBlock.merkleRoot = VtcBlockIndexer::Utility::hashToReverseHex(VtcBlockIndexer::FileReader::readHash(blockFile)); 77 | blockFile.read(reinterpret_cast(&fullBlock.time), sizeof(fullBlock.time)); 78 | blockFile.read(reinterpret_cast(&fullBlock.bits), sizeof(fullBlock.bits)); 79 | blockFile.read(reinterpret_cast(&fullBlock.nonce), sizeof(fullBlock.nonce)); 80 | 81 | if(!headerOnly) { 82 | // Find number of transactions 83 | blockFile.seekg(filePosition+80, ios_base::beg); 84 | uint64_t txCount = VtcBlockIndexer::FileReader::readVarInt(blockFile); 85 | fullBlock.transactions = {}; 86 | for(uint64_t tx = 0; tx < txCount; tx++) { 87 | VtcBlockIndexer::Transaction transaction = readTransaction(blockFile); 88 | fullBlock.transactions.push_back(transaction); 89 | } 90 | } 91 | uint64_t endPosBlock = blockFile.tellg(); 92 | fullBlock.byteSize = endPosBlock - filePosition; 93 | blockFile.close(); 94 | return fullBlock; 95 | } 96 | 97 | VtcBlockIndexer::Transaction VtcBlockIndexer::BlockReader::readTransaction(istream& blockFile) { 98 | bool segwit = false; 99 | 100 | VtcBlockIndexer::Transaction transaction; 101 | uint64_t startPosTx = blockFile.tellg(); 102 | 103 | transaction.filePosition = startPosTx; 104 | blockFile.read(reinterpret_cast(&transaction.version), sizeof(transaction.version)); 105 | 106 | // determine if this is a segwit tx 107 | // https://bitcoincore.org/en/segwit_wallet_dev/ 108 | unique_ptr segwitMarker(new unsigned char[2]); 109 | blockFile.read(reinterpret_cast(&segwitMarker.get()[0]) , 2); 110 | segwit = (segwitMarker.get()[0] == 0x00 && segwitMarker.get()[1] != 0x00); 111 | 112 | // If the segwit marker is not found, the number of inputs is located in its place 113 | // so rewind the stream to continue. 114 | if(!segwit) blockFile.seekg(-2, ios_base::cur); 115 | 116 | uint64_t startPosInputs = blockFile.tellg(); 117 | 118 | transaction.inputs = {}; 119 | 120 | uint64_t inputCount = VtcBlockIndexer::FileReader::readVarInt(blockFile); 121 | 122 | for(uint64_t input = 0; input < inputCount; input++) { 123 | VtcBlockIndexer::TransactionInput txInput; 124 | txInput.txHash = VtcBlockIndexer::Utility::hashToReverseHex(VtcBlockIndexer::FileReader::readHash(blockFile)); 125 | blockFile.read(reinterpret_cast(&txInput.txoIndex), sizeof(txInput.txoIndex)); 126 | txInput.script = VtcBlockIndexer::FileReader::readString(blockFile); 127 | blockFile.read(reinterpret_cast(&txInput.sequence), sizeof(txInput.sequence)); 128 | txInput.index = input; 129 | txInput.coinbase = (input == 0 && txInput.txHash == "0000000000000000000000000000000000000000000000000000000000000000" && txInput.txoIndex == 4294967295); 130 | transaction.inputs.push_back(txInput); 131 | } 132 | 133 | uint64_t outputCount = VtcBlockIndexer::FileReader::readVarInt(blockFile); 134 | transaction.outputs = {}; 135 | for(uint64_t output = 0; output < outputCount; output++) { 136 | VtcBlockIndexer::TransactionOutput txOutput; 137 | blockFile.read(reinterpret_cast(&txOutput.value), sizeof(txOutput.value)); 138 | txOutput.script = VtcBlockIndexer::FileReader::readString(blockFile); 139 | txOutput.index = output; 140 | transaction.outputs.push_back(txOutput); 141 | } 142 | 143 | uint64_t endPosOutputs = blockFile.tellg(); 144 | 145 | 146 | if(segwit) { 147 | for(uint64_t input = 0; input < inputCount; input++) { 148 | uint64_t witnessItems = VtcBlockIndexer::FileReader::readVarInt(blockFile); 149 | if(witnessItems > 0) { 150 | transaction.inputs.at(input).witnessData = {}; 151 | for(uint64_t witnessItem = 0; witnessItem < witnessItems; witnessItem++) { 152 | vector witnessData = VtcBlockIndexer::FileReader::readString(blockFile); 153 | transaction.inputs.at(input).witnessData.push_back(witnessData); 154 | } 155 | } 156 | } 157 | } 158 | 159 | blockFile.read(reinterpret_cast(&transaction.lockTime), sizeof(transaction.lockTime)); 160 | 161 | uint64_t endPosTx = blockFile.tellg(); 162 | 163 | blockFile.seekg(startPosTx, ios_base::beg); 164 | 165 | // The tx hash must still be calculated over the original serialization format. 166 | // That's why this seems a bit overcomplex 167 | uint64_t txitxoLength = endPosOutputs-startPosInputs; 168 | std::vector txHashBytes( 169 | 4 + // version 170 | txitxoLength + 171 | 4 // locktime 172 | ); 173 | 174 | blockFile.read(reinterpret_cast(&txHashBytes[0]), sizeof(transaction.version)); 175 | 176 | blockFile.seekg(startPosInputs, ios_base::beg); 177 | blockFile.read(reinterpret_cast(&txHashBytes[0] + 4), txitxoLength); 178 | 179 | blockFile.seekg(endPosTx-4, ios_base::beg); 180 | blockFile.read(reinterpret_cast(&txHashBytes[0] + 4 + txitxoLength), sizeof(transaction.lockTime)); 181 | transaction.txHash = VtcBlockIndexer::Utility::hashToReverseHex(VtcBlockIndexer::Utility::sha256(VtcBlockIndexer::Utility::sha256(txHashBytes))); 182 | 183 | if(segwit) { 184 | blockFile.seekg(startPosTx, ios_base::beg); 185 | uint64_t length = endPosTx-startPosTx; 186 | std::vector transactionBytes(length); 187 | blockFile.read(reinterpret_cast(&transactionBytes[0]) , length); 188 | transaction.txWitHash = VtcBlockIndexer::Utility::hashToReverseHex(VtcBlockIndexer::Utility::sha256(VtcBlockIndexer::Utility::sha256(transactionBytes))); 189 | } else { 190 | transaction.txWitHash = string(transaction.txHash); 191 | } 192 | blockFile.seekg(endPosTx, ios_base::beg); 193 | 194 | return transaction; 195 | } 196 | -------------------------------------------------------------------------------- /src/blockreader.h: -------------------------------------------------------------------------------- 1 | /* VTC Blockindexer - A utility to build additional indexes to the 2 | Vertcoin blockchain by scanning and indexing the blockfiles 3 | downloaded by Vertcoin Core. 4 | 5 | Copyright (C) 2017 Gert-Jaap Glasbergen 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | 22 | #ifndef BLOCKREADER_H_INCLUDED 23 | #define BLOCKREADER_H_INCLUDED 24 | 25 | #include 26 | #include 27 | 28 | #include "blockchaintypes.h" 29 | 30 | namespace VtcBlockIndexer { 31 | 32 | /** 33 | * The BlockReader class provides methods to read in the full details of 34 | * a block and its transactions from the block file based on a ScannedBlock 35 | */ 36 | 37 | class BlockReader { 38 | public: 39 | /** Constructs a BlockReader instance using the given block data directory 40 | * 41 | * @param blocksDir required Directory where the blockfiles are located. 42 | */ 43 | BlockReader(const std::string blocksDir); 44 | 45 | /** Reads the contents of the block that was scanned 46 | */ 47 | Block readBlock(std::string fileName, uint64_t filePosition, uint64_t blockHeight, bool headerOnly); 48 | 49 | /** Reads a transaction from an open stream 50 | */ 51 | Transaction readTransaction(std::istream& blockFile); 52 | 53 | /** Reads a transaction from an open file stream 54 | */ 55 | std::vector readRawBlockHeader(std::string fileName, uint64_t filePosition); 56 | 57 | private: 58 | 59 | /** Directory containing the blocks 60 | */ 61 | std::string blocksDir; 62 | }; 63 | 64 | } 65 | 66 | #endif // BLOCKREADER_H_INCLUDED -------------------------------------------------------------------------------- /src/blockscanner.cpp: -------------------------------------------------------------------------------- 1 | /* VTC Blockindexer - A utility to build additional indexes to the 2 | Vertcoin blockchain by scanning and indexing the blockfiles 3 | downloaded by Vertcoin Core. 4 | 5 | Copyright (C) 2017 Gert-Jaap Glasbergen 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | #include "blockscanner.h" 21 | #include "utility.h" 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | 29 | VtcBlockIndexer::BlockScanner::BlockScanner(const std::string blocksDir, const std::string blockFileName) { 30 | std::stringstream ss; 31 | ss << blocksDir << "/" << blockFileName; 32 | this->blockFilePath = ss.str(); 33 | this->blockFileName = blockFileName; 34 | } 35 | 36 | bool VtcBlockIndexer::BlockScanner::open() { 37 | this->blockFileStream.open(this->blockFilePath, std::ios_base::in | std::ios_base::binary); 38 | return this->blockFileStream.is_open(); 39 | } 40 | 41 | bool VtcBlockIndexer::BlockScanner::close() { 42 | if(!this->blockFileStream.is_open()) return false; 43 | this->blockFileStream.close(); 44 | return !this->blockFileStream.is_open(); 45 | } 46 | 47 | bool VtcBlockIndexer::BlockScanner::moveNext() { 48 | std::vector buffer(4); 49 | this->blockFileStream.read(reinterpret_cast(&buffer[0]), 4); 50 | 51 | if(this->blockFileStream.eof()) { 52 | return false; 53 | } 54 | 55 | if(this->blockFileStream.fail()) { 56 | return false; 57 | } 58 | 59 | return std::equal(buffer.begin(), buffer.end(), VtcBlockIndexer::CoinParams::magic.begin()); 60 | } 61 | 62 | VtcBlockIndexer::ScannedBlock VtcBlockIndexer::BlockScanner::scanNextBlock() { 63 | VtcBlockIndexer::ScannedBlock block; 64 | 65 | uint32_t blockSize; 66 | this->blockFileStream.read(reinterpret_cast(&blockSize), sizeof(blockSize)); 67 | 68 | // Store the file name and position of the block inside the struct so we can 69 | // use that to read the actual block later after sorting the blockchain. 70 | block.fileName = this->blockFileName; 71 | block.filePosition = this->blockFileStream.tellg(); 72 | 73 | vector blockHeader(80); 74 | this->blockFileStream.read(reinterpret_cast(&blockHeader[0]) , 80); 75 | 76 | block.blockHash = VtcBlockIndexer::Utility::hashToReverseHex(VtcBlockIndexer::Utility::sha256(VtcBlockIndexer::Utility::sha256(blockHeader))); 77 | vector previousBlockHash(32); 78 | memcpy(&previousBlockHash[0], &blockHeader[4], 32); 79 | block.previousBlockHash = VtcBlockIndexer::Utility::hashToReverseHex(previousBlockHash); 80 | 81 | this->blockFileStream.seekg(blockSize - 80, std::ios_base::cur); 82 | 83 | return block; 84 | } -------------------------------------------------------------------------------- /src/blockscanner.h: -------------------------------------------------------------------------------- 1 | /* VTC Blockindexer - A utility to build additional indexes to the 2 | Vertcoin blockchain by scanning and indexing the blockfiles 3 | downloaded by Vertcoin Core. 4 | 5 | Copyright (C) 2017 Gert-Jaap Glasbergen 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | 22 | #ifndef BLOCKSCANNER_H_INCLUDED 23 | #define BLOCKSCANNER_H_INCLUDED 24 | 25 | #include 26 | #include 27 | 28 | #include "blockchaintypes.h" 29 | #include "coinparams.h" 30 | namespace VtcBlockIndexer { 31 | 32 | /** 33 | * The BlockScanner class provides methods to scan blk????.dat files 34 | * for block data. It only scans blocks, and reads its header. No 35 | * block data like transactions are read. 36 | */ 37 | 38 | class BlockScanner { 39 | public: 40 | /** Constructs a BlockScanner instance using the given block data file 41 | * 42 | * @param blocksDir required Directory where the blockfile is located. 43 | * @param blockFileName required Filename of the block file to read. 44 | */ 45 | BlockScanner(const std::string blocksDir, const std::string blockFileName); 46 | 47 | /** Opens the file for reading and allows scanning for blocks 48 | */ 49 | bool open(); 50 | 51 | /** Tries reading the magic string from the file stream and move the 52 | * file pointer to the start of the block following it. If the magic 53 | * string was not found, either because of the EOF or the wrong 54 | * sequence was found, there is no block and this function will return 55 | * false. 56 | */ 57 | bool moveNext(); 58 | 59 | /** Scans the next block. Scanning only reads the header and returns a 60 | * ScannedBlock struct that contains the file the block was found in, 61 | * the start position and length inside that file, its hash and 62 | * previousBlockHash. A collection of ScannedBlock objects should be 63 | * sufficient to construct the blockchain. 64 | */ 65 | ScannedBlock scanNextBlock(); 66 | 67 | /** Closes the file 68 | */ 69 | bool close(); 70 | 71 | private: 72 | 73 | /** Reference to the stream when the blockfile was opened 74 | */ 75 | std::ifstream blockFileStream; 76 | 77 | /** Full path to the blockfile 78 | */ 79 | std::string blockFilePath; 80 | 81 | /** File name only of the blockfile 82 | */ 83 | std::string blockFileName; 84 | 85 | }; 86 | 87 | } 88 | 89 | #endif // BLOCKSCANNER_H_INCLUDED -------------------------------------------------------------------------------- /src/byte_array_buffer.cpp: -------------------------------------------------------------------------------- 1 | #include "byte_array_buffer.h" 2 | 3 | #include 4 | 5 | 6 | byte_array_buffer::byte_array_buffer(const uint8_t *begin, const size_t size) : 7 | begin_(begin), 8 | end_(begin + size), 9 | current_(begin_) 10 | { 11 | assert(std::less_equal()(begin_, end_)); 12 | } 13 | 14 | byte_array_buffer::int_type byte_array_buffer::underflow() 15 | { 16 | if (current_ == end_) 17 | return traits_type::eof(); 18 | 19 | return traits_type::to_int_type(*current_); 20 | } 21 | 22 | byte_array_buffer::int_type byte_array_buffer::uflow() 23 | { 24 | if (current_ == end_) 25 | return traits_type::eof(); 26 | 27 | return traits_type::to_int_type(*current_++); 28 | } 29 | 30 | byte_array_buffer::int_type byte_array_buffer::pbackfail(int_type ch) 31 | { 32 | if (current_ == begin_ || (ch != traits_type::eof() && ch != current_[-1])) 33 | return traits_type::eof(); 34 | 35 | return traits_type::to_int_type(*--current_); 36 | } 37 | 38 | std::streamsize byte_array_buffer::showmanyc() 39 | { 40 | assert(std::less_equal()(current_, end_)); 41 | return end_ - current_; 42 | } 43 | 44 | 45 | std::streampos byte_array_buffer::seekoff ( std::streamoff off, std::ios_base::seekdir way, 46 | std::ios_base::openmode which ) 47 | { 48 | if (way == std::ios_base::beg) 49 | { 50 | current_ = begin_ + off; 51 | } 52 | else if (way == std::ios_base::cur) 53 | { 54 | current_ += off; 55 | } 56 | else if (way == std::ios_base::end) 57 | { 58 | current_ = end_; 59 | } 60 | 61 | if (current_ < begin_ || current_ > end_) 62 | return -1; 63 | 64 | 65 | return current_ - begin_; 66 | } 67 | 68 | std::streampos byte_array_buffer::seekpos ( std::streampos sp, 69 | std::ios_base::openmode which ) 70 | { 71 | current_ = begin_ + sp; 72 | 73 | if (current_ < begin_ || current_ > end_) 74 | return -1; 75 | 76 | return current_ - begin_; 77 | } -------------------------------------------------------------------------------- /src/byte_array_buffer.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | // 8 | // http://www.mr-edd.co.uk/blog/beginners_guide_streambuf 9 | // 10 | 11 | class byte_array_buffer : public std::streambuf 12 | { 13 | public: 14 | byte_array_buffer(const uint8_t *begin, const size_t size); 15 | 16 | private: 17 | int_type underflow(); 18 | int_type uflow(); 19 | int_type pbackfail(int_type ch); 20 | std::streamsize showmanyc(); 21 | std::streampos seekoff ( std::streamoff off, std::ios_base::seekdir way, 22 | std::ios_base::openmode which = std::ios_base::in | std::ios_base::out ); 23 | std::streampos seekpos ( std::streampos sp, 24 | std::ios_base::openmode which = std::ios_base::in | std::ios_base::out); 25 | 26 | // copy ctor and assignment not implemented; 27 | // copying not allowed 28 | byte_array_buffer(const byte_array_buffer &); 29 | byte_array_buffer &operator= (const byte_array_buffer &); 30 | 31 | private: 32 | const uint8_t * const begin_; 33 | const uint8_t * const end_; 34 | const uint8_t * current_; 35 | }; -------------------------------------------------------------------------------- /src/coinparams.cpp: -------------------------------------------------------------------------------- 1 | #include "coinparams.h" 2 | /* VTC Blockindexer - A utility to build additional indexes to the 3 | Vertcoin blockchain by scanning and indexing the blockfiles 4 | downloaded by Vertcoin Core. 5 | 6 | Copyright (C) 2017 Gert-Jaap Glasbergen 7 | 8 | This program is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program. If not, see . 20 | */ 21 | 22 | 23 | #include 24 | #include 25 | #include "json.hpp" 26 | #include "utility.h" 27 | using json = nlohmann::json; 28 | using namespace std; 29 | 30 | vector VtcBlockIndexer::CoinParams::magic; 31 | string VtcBlockIndexer::CoinParams::bech32Prefix; 32 | unsigned char VtcBlockIndexer::CoinParams::p2pkhVersion; 33 | unsigned char VtcBlockIndexer::CoinParams::p2shVersion; 34 | 35 | void VtcBlockIndexer::CoinParams::readFromFile(string fileName) 36 | { 37 | cout << "Reading coin params from [" << fileName << "]" << endl; 38 | ifstream i(fileName); 39 | json j; 40 | i >> j; 41 | 42 | assert(j["magic"].is_string()); 43 | assert(j["prefix_bech32"].is_string()); 44 | assert(j["version_p2sh"].is_string()); 45 | assert(j["version_p2pkh"].is_string()); 46 | 47 | magic = VtcBlockIndexer::Utility::hexToBytes(j["magic"].get()); 48 | bech32Prefix = j["prefix_bech32"].get(); 49 | p2shVersion = (unsigned char) strtol(j["version_p2sh"].get().c_str(), NULL, 16); 50 | p2pkhVersion = (unsigned char) strtol(j["version_p2pkh"].get().c_str(), NULL, 16); 51 | 52 | } 53 | -------------------------------------------------------------------------------- /src/coinparams.h: -------------------------------------------------------------------------------- 1 | /* VTC Blockindexer - A utility to build additional indexes to the 2 | Vertcoin blockchain by scanning and indexing the blockfiles 3 | downloaded by Vertcoin Core. 4 | 5 | Copyright (C) 2017 Gert-Jaap Glasbergen 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | using namespace std; 25 | 26 | namespace VtcBlockIndexer { 27 | 28 | /** 29 | * The CoinParms class provides static access to the coin parameters and a method 30 | * to read them from a JSON file 31 | */ 32 | 33 | class CoinParams { 34 | public: 35 | static void readFromFile(string fileName); 36 | static vector magic; 37 | static string bech32Prefix; 38 | static unsigned char p2pkhVersion; 39 | static unsigned char p2shVersion; 40 | ~CoinParams(); 41 | 42 | private: 43 | CoinParams() {} 44 | }; 45 | } 46 | -------------------------------------------------------------------------------- /src/crypto/bech32.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pieter Wuille 2 | // Distributed under the MIT software license, see the accompanying 3 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 | 5 | #include "bech32.h" 6 | #include 7 | 8 | namespace 9 | { 10 | 11 | typedef std::vector data; 12 | 13 | /** The Bech32 character set for encoding. */ 14 | const char* CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l"; 15 | 16 | /** The Bech32 character set for decoding. */ 17 | const int8_t CHARSET_REV[128] = { 18 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 19 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 20 | -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 21 | 15, -1, 10, 17, 21, 20, 26, 30, 7, 5, -1, -1, -1, -1, -1, -1, 22 | -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, 23 | 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1, 24 | -1, 29, -1, 24, 13, 25, 9, 8, 23, -1, 18, 22, 31, 27, 19, -1, 25 | 1, 0, 3, 16, 11, 28, 12, 14, 6, 4, 2, -1, -1, -1, -1, -1 26 | }; 27 | 28 | /** Concatenate two byte arrays. */ 29 | data Cat(data x, const data& y) 30 | { 31 | x.insert(x.end(), y.begin(), y.end()); 32 | return x; 33 | } 34 | 35 | 36 | 37 | 38 | /** This function will compute what 6 5-bit values to XOR into the last 6 input values, in order to 39 | * make the checksum 0. These 6 values are packed together in a single 30-bit integer. The higher 40 | * bits correspond to earlier values. */ 41 | uint32_t PolyMod(const data& v) 42 | { 43 | // The input is interpreted as a list of coefficients of a polynomial over F = GF(32), with an 44 | // implicit 1 in front. If the input is [v0,v1,v2,v3,v4], that polynomial is v(x) = 45 | // 1*x^5 + v0*x^4 + v1*x^3 + v2*x^2 + v3*x + v4. The implicit 1 guarantees that 46 | // [v0,v1,v2,...] has a distinct checksum from [0,v0,v1,v2,...]. 47 | 48 | // The output is a 30-bit integer whose 5-bit groups are the coefficients of the remainder of 49 | // v(x) mod g(x), where g(x) is the Bech32 generator, 50 | // x^6 + {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18}. g(x) is chosen in such a way 51 | // that the resulting code is a BCH code, guaranteeing detection of up to 3 errors within a 52 | // window of 1023 characters. Among the various possible BCH codes, one was selected to in 53 | // fact guarantee detection of up to 4 errors within a window of 89 characters. 54 | 55 | // Note that the coefficients are elements of GF(32), here represented as decimal numbers 56 | // between {}. In this finite field, addition is just XOR of the corresponding numbers. For 57 | // example, {27} + {13} = {27 ^ 13} = {22}. Multiplication is more complicated, and requires 58 | // treating the bits of values themselves as coefficients of a polynomial over a smaller field, 59 | // GF(2), and multiplying those polynomials mod a^5 + a^3 + 1. For example, {5} * {26} = 60 | // (a^2 + 1) * (a^4 + a^3 + a) = (a^4 + a^3 + a) * a^2 + (a^4 + a^3 + a) = a^6 + a^5 + a^4 + a 61 | // = a^3 + 1 (mod a^5 + a^3 + 1) = {9}. 62 | 63 | // During the course of the loop below, `c` contains the bitpacked coefficients of the 64 | // polynomial constructed from just the values of v that were processed so far, mod g(x). In 65 | // the above example, `c` initially corresponds to 1 mod (x), and after processing 2 inputs of 66 | // v, it corresponds to x^2 + v0*x + v1 mod g(x). As 1 mod g(x) = 1, that is the starting value 67 | // for `c`. 68 | uint32_t c = 1; 69 | for (auto v_i : v) { 70 | // We want to update `c` to correspond to a polynomial with one extra term. If the initial 71 | // value of `c` consists of the coefficients of c(x) = f(x) mod g(x), we modify it to 72 | // correspond to c'(x) = (f(x) * x + v_i) mod g(x), where v_i is the next input to 73 | // process. Simplifying: 74 | // c'(x) = (f(x) * x + v_i) mod g(x) 75 | // ((f(x) mod g(x)) * x + v_i) mod g(x) 76 | // (c(x) * x + v_i) mod g(x) 77 | // If c(x) = c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5, we want to compute 78 | // c'(x) = (c0*x^5 + c1*x^4 + c2*x^3 + c3*x^2 + c4*x + c5) * x + v_i mod g(x) 79 | // = c0*x^6 + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i mod g(x) 80 | // = c0*(x^6 mod g(x)) + c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i 81 | // If we call (x^6 mod g(x)) = k(x), this can be written as 82 | // c'(x) = (c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i) + c0*k(x) 83 | 84 | // First, determine the value of c0: 85 | uint8_t c0 = c >> 25; 86 | 87 | // Then compute c1*x^5 + c2*x^4 + c3*x^3 + c4*x^2 + c5*x + v_i: 88 | c = ((c & 0x1ffffff) << 5) ^ v_i; 89 | 90 | // Finally, for each set bit n in c0, conditionally add {2^n}k(x): 91 | if (c0 & 1) c ^= 0x3b6a57b2; // k(x) = {29}x^5 + {22}x^4 + {20}x^3 + {21}x^2 + {29}x + {18} 92 | if (c0 & 2) c ^= 0x26508e6d; // {2}k(x) = {19}x^5 + {5}x^4 + x^3 + {3}x^2 + {19}x + {13} 93 | if (c0 & 4) c ^= 0x1ea119fa; // {4}k(x) = {15}x^5 + {10}x^4 + {2}x^3 + {6}x^2 + {15}x + {26} 94 | if (c0 & 8) c ^= 0x3d4233dd; // {8}k(x) = {30}x^5 + {20}x^4 + {4}x^3 + {12}x^2 + {30}x + {29} 95 | if (c0 & 16) c ^= 0x2a1462b3; // {16}k(x) = {21}x^5 + x^4 + {8}x^3 + {24}x^2 + {21}x + {19} 96 | } 97 | return c; 98 | } 99 | 100 | /** Convert to lower case. */ 101 | inline unsigned char LowerCase(unsigned char c) 102 | { 103 | return (c >= 'A' && c <= 'Z') ? (c - 'A') + 'a' : c; 104 | } 105 | 106 | /** Expand a HRP for use in checksum computation. */ 107 | data ExpandHRP(const std::string& hrp) 108 | { 109 | data ret; 110 | ret.reserve(hrp.size() + 90); 111 | ret.resize(hrp.size() * 2 + 1); 112 | for (size_t i = 0; i < hrp.size(); ++i) { 113 | unsigned char c = hrp[i]; 114 | ret[i] = c >> 5; 115 | ret[i + hrp.size() + 1] = c & 0x1f; 116 | } 117 | ret[hrp.size()] = 0; 118 | return ret; 119 | } 120 | 121 | /** Verify a checksum. */ 122 | bool VerifyChecksum(const std::string& hrp, const data& values) 123 | { 124 | // PolyMod computes what value to xor into the final values to make the checksum 0. However, 125 | // if we required that the checksum was 0, it would be the case that appending a 0 to a valid 126 | // list of values would result in a new valid list. For that reason, Bech32 requires the 127 | // resulting checksum to be 1 instead. 128 | return PolyMod(Cat(ExpandHRP(hrp), values)) == 1; 129 | } 130 | 131 | /** Create a checksum. */ 132 | data CreateChecksum(const std::string& hrp, const data& values) 133 | { 134 | data enc = Cat(ExpandHRP(hrp), values); 135 | enc.resize(enc.size() + 6); // Append 6 zeroes 136 | uint32_t mod = PolyMod(enc) ^ 1; // Determine what to XOR into those 6 zeroes. 137 | data ret(6); 138 | for (size_t i = 0; i < 6; ++i) { 139 | // Convert the 5-bit groups in mod to checksum values. 140 | ret[i] = (mod >> (5 * (5 - i))) & 31; 141 | } 142 | return ret; 143 | } 144 | 145 | } // namespace 146 | 147 | namespace bech32 148 | { 149 | 150 | /** Encode a Bech32 string. */ 151 | std::string Encode(const std::string& hrp, const data& values) { 152 | data checksum = CreateChecksum(hrp, values); 153 | data combined = Cat(values, checksum); 154 | 155 | std::string ret = hrp + '1'; 156 | ret.reserve(ret.size() + combined.size()); 157 | for (auto c : combined) { 158 | ret += CHARSET[c]; 159 | } 160 | return ret; 161 | } 162 | 163 | /** Decode a Bech32 string. */ 164 | std::pair Decode(const std::string& str) { 165 | bool lower = false, upper = false; 166 | for (size_t i = 0; i < str.size(); ++i) { 167 | unsigned char c = str[i]; 168 | if (c < 33 || c > 126) return {}; 169 | if (c >= 'a' && c <= 'z') lower = true; 170 | if (c >= 'A' && c <= 'Z') upper = true; 171 | } 172 | if (lower && upper) return {}; 173 | size_t pos = str.rfind('1'); 174 | if (str.size() > 90 || pos == str.npos || pos == 0 || pos + 7 > str.size()) { 175 | return {}; 176 | } 177 | data values(str.size() - 1 - pos); 178 | for (size_t i = 0; i < str.size() - 1 - pos; ++i) { 179 | unsigned char c = str[i + pos + 1]; 180 | int8_t rev = (c < 33 || c > 126) ? -1 : CHARSET_REV[c]; 181 | if (rev == -1) { 182 | return {}; 183 | } 184 | values[i] = rev; 185 | } 186 | std::string hrp; 187 | for (size_t i = 0; i < pos; ++i) { 188 | hrp += LowerCase(str[i]); 189 | } 190 | if (!VerifyChecksum(hrp, values)) { 191 | return {}; 192 | } 193 | return {hrp, data(values.begin(), values.end() - 6)}; 194 | } 195 | 196 | } // namespace bech32 -------------------------------------------------------------------------------- /src/crypto/bech32.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2017 Pieter Wuille 2 | // Distributed under the MIT software license, see the accompanying 3 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 | 5 | // Bech32 is a string encoding format used in newer address types. 6 | // The output consists of a human-readable part (alphanumeric), a 7 | // separator character (1), and a base32 data section, the last 8 | // 6 characters of which are a checksum. 9 | // 10 | // For more information, see BIP 173. 11 | 12 | #include 13 | #include 14 | #include 15 | 16 | namespace bech32 17 | { 18 | 19 | /** Encode a Bech32 string. Returns the empty string in case of failure. */ 20 | std::string Encode(const std::string& hrp, const std::vector& values); 21 | 22 | /** Decode a Bech32 string. Returns (hrp, data). Empty hrp means failure. */ 23 | std::pair> Decode(const std::string& str); 24 | 25 | } // namespace bech32 -------------------------------------------------------------------------------- /src/crypto/common.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 The Bitcoin Core developers 2 | // Distributed under the MIT software license, see the accompanying 3 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 | 5 | #ifndef BITCOIN_CRYPTO_COMMON_H 6 | #define BITCOIN_CRYPTO_COMMON_H 7 | 8 | #include 9 | 10 | uint16_t static inline ReadLE16(const unsigned char* ptr) 11 | { 12 | return le16toh(*((uint16_t*)ptr)); 13 | } 14 | 15 | uint32_t static inline ReadLE32(const unsigned char* ptr) 16 | { 17 | return le32toh(*((uint32_t*)ptr)); 18 | } 19 | 20 | uint64_t static inline ReadLE64(const unsigned char* ptr) 21 | { 22 | return le64toh(*((uint64_t*)ptr)); 23 | } 24 | 25 | void static inline WriteLE16(unsigned char* ptr, uint16_t x) 26 | { 27 | *((uint16_t*)ptr) = htole16(x); 28 | } 29 | 30 | void static inline WriteLE32(unsigned char* ptr, uint32_t x) 31 | { 32 | *((uint32_t*)ptr) = htole32(x); 33 | } 34 | 35 | void static inline WriteLE64(unsigned char* ptr, uint64_t x) 36 | { 37 | *((uint64_t*)ptr) = htole64(x); 38 | } 39 | 40 | uint32_t static inline ReadBE32(const unsigned char* ptr) 41 | { 42 | return be32toh(*((uint32_t*)ptr)); 43 | } 44 | 45 | uint64_t static inline ReadBE64(const unsigned char* ptr) 46 | { 47 | return be64toh(*((uint64_t*)ptr)); 48 | } 49 | 50 | void static inline WriteBE32(unsigned char* ptr, uint32_t x) 51 | { 52 | *((uint32_t*)ptr) = htobe32(x); 53 | } 54 | 55 | void static inline WriteBE64(unsigned char* ptr, uint64_t x) 56 | { 57 | *((uint64_t*)ptr) = htobe64(x); 58 | } 59 | 60 | #endif // BITCOIN_CRYPTO_COMMON_H 61 | -------------------------------------------------------------------------------- /src/crypto/ripemd160.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 The Bitcoin Core developers 2 | // Distributed under the MIT software license, see the accompanying 3 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 | 5 | #include "ripemd160.h" 6 | 7 | #include "common.h" 8 | 9 | #include 10 | 11 | // Internal implementation code. 12 | namespace 13 | { 14 | /// Internal RIPEMD-160 implementation. 15 | namespace ripemd160 16 | { 17 | uint32_t inline f1(uint32_t x, uint32_t y, uint32_t z) { return x ^ y ^ z; } 18 | uint32_t inline f2(uint32_t x, uint32_t y, uint32_t z) { return (x & y) | (~x & z); } 19 | uint32_t inline f3(uint32_t x, uint32_t y, uint32_t z) { return (x | ~y) ^ z; } 20 | uint32_t inline f4(uint32_t x, uint32_t y, uint32_t z) { return (x & z) | (y & ~z); } 21 | uint32_t inline f5(uint32_t x, uint32_t y, uint32_t z) { return x ^ (y | ~z); } 22 | 23 | /** Initialize RIPEMD-160 state. */ 24 | void inline Initialize(uint32_t* s) 25 | { 26 | s[0] = 0x67452301ul; 27 | s[1] = 0xEFCDAB89ul; 28 | s[2] = 0x98BADCFEul; 29 | s[3] = 0x10325476ul; 30 | s[4] = 0xC3D2E1F0ul; 31 | } 32 | 33 | uint32_t inline rol(uint32_t x, int i) { return (x << i) | (x >> (32 - i)); } 34 | 35 | void inline Round(uint32_t& a, uint32_t b, uint32_t& c, uint32_t d, uint32_t e, uint32_t f, uint32_t x, uint32_t k, int r) 36 | { 37 | a = rol(a + f + x + k, r) + e; 38 | c = rol(c, 10); 39 | } 40 | 41 | void inline R11(uint32_t& a, uint32_t b, uint32_t& c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f1(b, c, d), x, 0, r); } 42 | void inline R21(uint32_t& a, uint32_t b, uint32_t& c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f2(b, c, d), x, 0x5A827999ul, r); } 43 | void inline R31(uint32_t& a, uint32_t b, uint32_t& c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f3(b, c, d), x, 0x6ED9EBA1ul, r); } 44 | void inline R41(uint32_t& a, uint32_t b, uint32_t& c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f4(b, c, d), x, 0x8F1BBCDCul, r); } 45 | void inline R51(uint32_t& a, uint32_t b, uint32_t& c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f5(b, c, d), x, 0xA953FD4Eul, r); } 46 | 47 | void inline R12(uint32_t& a, uint32_t b, uint32_t& c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f5(b, c, d), x, 0x50A28BE6ul, r); } 48 | void inline R22(uint32_t& a, uint32_t b, uint32_t& c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f4(b, c, d), x, 0x5C4DD124ul, r); } 49 | void inline R32(uint32_t& a, uint32_t b, uint32_t& c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f3(b, c, d), x, 0x6D703EF3ul, r); } 50 | void inline R42(uint32_t& a, uint32_t b, uint32_t& c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f2(b, c, d), x, 0x7A6D76E9ul, r); } 51 | void inline R52(uint32_t& a, uint32_t b, uint32_t& c, uint32_t d, uint32_t e, uint32_t x, int r) { Round(a, b, c, d, e, f1(b, c, d), x, 0, r); } 52 | 53 | /** Perform a RIPEMD-160 transformation, processing a 64-byte chunk. */ 54 | void Transform(uint32_t* s, const unsigned char* chunk) 55 | { 56 | uint32_t a1 = s[0], b1 = s[1], c1 = s[2], d1 = s[3], e1 = s[4]; 57 | uint32_t a2 = a1, b2 = b1, c2 = c1, d2 = d1, e2 = e1; 58 | uint32_t w0 = ReadLE32(chunk + 0), w1 = ReadLE32(chunk + 4), w2 = ReadLE32(chunk + 8), w3 = ReadLE32(chunk + 12); 59 | uint32_t w4 = ReadLE32(chunk + 16), w5 = ReadLE32(chunk + 20), w6 = ReadLE32(chunk + 24), w7 = ReadLE32(chunk + 28); 60 | uint32_t w8 = ReadLE32(chunk + 32), w9 = ReadLE32(chunk + 36), w10 = ReadLE32(chunk + 40), w11 = ReadLE32(chunk + 44); 61 | uint32_t w12 = ReadLE32(chunk + 48), w13 = ReadLE32(chunk + 52), w14 = ReadLE32(chunk + 56), w15 = ReadLE32(chunk + 60); 62 | 63 | R11(a1, b1, c1, d1, e1, w0, 11); 64 | R12(a2, b2, c2, d2, e2, w5, 8); 65 | R11(e1, a1, b1, c1, d1, w1, 14); 66 | R12(e2, a2, b2, c2, d2, w14, 9); 67 | R11(d1, e1, a1, b1, c1, w2, 15); 68 | R12(d2, e2, a2, b2, c2, w7, 9); 69 | R11(c1, d1, e1, a1, b1, w3, 12); 70 | R12(c2, d2, e2, a2, b2, w0, 11); 71 | R11(b1, c1, d1, e1, a1, w4, 5); 72 | R12(b2, c2, d2, e2, a2, w9, 13); 73 | R11(a1, b1, c1, d1, e1, w5, 8); 74 | R12(a2, b2, c2, d2, e2, w2, 15); 75 | R11(e1, a1, b1, c1, d1, w6, 7); 76 | R12(e2, a2, b2, c2, d2, w11, 15); 77 | R11(d1, e1, a1, b1, c1, w7, 9); 78 | R12(d2, e2, a2, b2, c2, w4, 5); 79 | R11(c1, d1, e1, a1, b1, w8, 11); 80 | R12(c2, d2, e2, a2, b2, w13, 7); 81 | R11(b1, c1, d1, e1, a1, w9, 13); 82 | R12(b2, c2, d2, e2, a2, w6, 7); 83 | R11(a1, b1, c1, d1, e1, w10, 14); 84 | R12(a2, b2, c2, d2, e2, w15, 8); 85 | R11(e1, a1, b1, c1, d1, w11, 15); 86 | R12(e2, a2, b2, c2, d2, w8, 11); 87 | R11(d1, e1, a1, b1, c1, w12, 6); 88 | R12(d2, e2, a2, b2, c2, w1, 14); 89 | R11(c1, d1, e1, a1, b1, w13, 7); 90 | R12(c2, d2, e2, a2, b2, w10, 14); 91 | R11(b1, c1, d1, e1, a1, w14, 9); 92 | R12(b2, c2, d2, e2, a2, w3, 12); 93 | R11(a1, b1, c1, d1, e1, w15, 8); 94 | R12(a2, b2, c2, d2, e2, w12, 6); 95 | 96 | R21(e1, a1, b1, c1, d1, w7, 7); 97 | R22(e2, a2, b2, c2, d2, w6, 9); 98 | R21(d1, e1, a1, b1, c1, w4, 6); 99 | R22(d2, e2, a2, b2, c2, w11, 13); 100 | R21(c1, d1, e1, a1, b1, w13, 8); 101 | R22(c2, d2, e2, a2, b2, w3, 15); 102 | R21(b1, c1, d1, e1, a1, w1, 13); 103 | R22(b2, c2, d2, e2, a2, w7, 7); 104 | R21(a1, b1, c1, d1, e1, w10, 11); 105 | R22(a2, b2, c2, d2, e2, w0, 12); 106 | R21(e1, a1, b1, c1, d1, w6, 9); 107 | R22(e2, a2, b2, c2, d2, w13, 8); 108 | R21(d1, e1, a1, b1, c1, w15, 7); 109 | R22(d2, e2, a2, b2, c2, w5, 9); 110 | R21(c1, d1, e1, a1, b1, w3, 15); 111 | R22(c2, d2, e2, a2, b2, w10, 11); 112 | R21(b1, c1, d1, e1, a1, w12, 7); 113 | R22(b2, c2, d2, e2, a2, w14, 7); 114 | R21(a1, b1, c1, d1, e1, w0, 12); 115 | R22(a2, b2, c2, d2, e2, w15, 7); 116 | R21(e1, a1, b1, c1, d1, w9, 15); 117 | R22(e2, a2, b2, c2, d2, w8, 12); 118 | R21(d1, e1, a1, b1, c1, w5, 9); 119 | R22(d2, e2, a2, b2, c2, w12, 7); 120 | R21(c1, d1, e1, a1, b1, w2, 11); 121 | R22(c2, d2, e2, a2, b2, w4, 6); 122 | R21(b1, c1, d1, e1, a1, w14, 7); 123 | R22(b2, c2, d2, e2, a2, w9, 15); 124 | R21(a1, b1, c1, d1, e1, w11, 13); 125 | R22(a2, b2, c2, d2, e2, w1, 13); 126 | R21(e1, a1, b1, c1, d1, w8, 12); 127 | R22(e2, a2, b2, c2, d2, w2, 11); 128 | 129 | R31(d1, e1, a1, b1, c1, w3, 11); 130 | R32(d2, e2, a2, b2, c2, w15, 9); 131 | R31(c1, d1, e1, a1, b1, w10, 13); 132 | R32(c2, d2, e2, a2, b2, w5, 7); 133 | R31(b1, c1, d1, e1, a1, w14, 6); 134 | R32(b2, c2, d2, e2, a2, w1, 15); 135 | R31(a1, b1, c1, d1, e1, w4, 7); 136 | R32(a2, b2, c2, d2, e2, w3, 11); 137 | R31(e1, a1, b1, c1, d1, w9, 14); 138 | R32(e2, a2, b2, c2, d2, w7, 8); 139 | R31(d1, e1, a1, b1, c1, w15, 9); 140 | R32(d2, e2, a2, b2, c2, w14, 6); 141 | R31(c1, d1, e1, a1, b1, w8, 13); 142 | R32(c2, d2, e2, a2, b2, w6, 6); 143 | R31(b1, c1, d1, e1, a1, w1, 15); 144 | R32(b2, c2, d2, e2, a2, w9, 14); 145 | R31(a1, b1, c1, d1, e1, w2, 14); 146 | R32(a2, b2, c2, d2, e2, w11, 12); 147 | R31(e1, a1, b1, c1, d1, w7, 8); 148 | R32(e2, a2, b2, c2, d2, w8, 13); 149 | R31(d1, e1, a1, b1, c1, w0, 13); 150 | R32(d2, e2, a2, b2, c2, w12, 5); 151 | R31(c1, d1, e1, a1, b1, w6, 6); 152 | R32(c2, d2, e2, a2, b2, w2, 14); 153 | R31(b1, c1, d1, e1, a1, w13, 5); 154 | R32(b2, c2, d2, e2, a2, w10, 13); 155 | R31(a1, b1, c1, d1, e1, w11, 12); 156 | R32(a2, b2, c2, d2, e2, w0, 13); 157 | R31(e1, a1, b1, c1, d1, w5, 7); 158 | R32(e2, a2, b2, c2, d2, w4, 7); 159 | R31(d1, e1, a1, b1, c1, w12, 5); 160 | R32(d2, e2, a2, b2, c2, w13, 5); 161 | 162 | R41(c1, d1, e1, a1, b1, w1, 11); 163 | R42(c2, d2, e2, a2, b2, w8, 15); 164 | R41(b1, c1, d1, e1, a1, w9, 12); 165 | R42(b2, c2, d2, e2, a2, w6, 5); 166 | R41(a1, b1, c1, d1, e1, w11, 14); 167 | R42(a2, b2, c2, d2, e2, w4, 8); 168 | R41(e1, a1, b1, c1, d1, w10, 15); 169 | R42(e2, a2, b2, c2, d2, w1, 11); 170 | R41(d1, e1, a1, b1, c1, w0, 14); 171 | R42(d2, e2, a2, b2, c2, w3, 14); 172 | R41(c1, d1, e1, a1, b1, w8, 15); 173 | R42(c2, d2, e2, a2, b2, w11, 14); 174 | R41(b1, c1, d1, e1, a1, w12, 9); 175 | R42(b2, c2, d2, e2, a2, w15, 6); 176 | R41(a1, b1, c1, d1, e1, w4, 8); 177 | R42(a2, b2, c2, d2, e2, w0, 14); 178 | R41(e1, a1, b1, c1, d1, w13, 9); 179 | R42(e2, a2, b2, c2, d2, w5, 6); 180 | R41(d1, e1, a1, b1, c1, w3, 14); 181 | R42(d2, e2, a2, b2, c2, w12, 9); 182 | R41(c1, d1, e1, a1, b1, w7, 5); 183 | R42(c2, d2, e2, a2, b2, w2, 12); 184 | R41(b1, c1, d1, e1, a1, w15, 6); 185 | R42(b2, c2, d2, e2, a2, w13, 9); 186 | R41(a1, b1, c1, d1, e1, w14, 8); 187 | R42(a2, b2, c2, d2, e2, w9, 12); 188 | R41(e1, a1, b1, c1, d1, w5, 6); 189 | R42(e2, a2, b2, c2, d2, w7, 5); 190 | R41(d1, e1, a1, b1, c1, w6, 5); 191 | R42(d2, e2, a2, b2, c2, w10, 15); 192 | R41(c1, d1, e1, a1, b1, w2, 12); 193 | R42(c2, d2, e2, a2, b2, w14, 8); 194 | 195 | R51(b1, c1, d1, e1, a1, w4, 9); 196 | R52(b2, c2, d2, e2, a2, w12, 8); 197 | R51(a1, b1, c1, d1, e1, w0, 15); 198 | R52(a2, b2, c2, d2, e2, w15, 5); 199 | R51(e1, a1, b1, c1, d1, w5, 5); 200 | R52(e2, a2, b2, c2, d2, w10, 12); 201 | R51(d1, e1, a1, b1, c1, w9, 11); 202 | R52(d2, e2, a2, b2, c2, w4, 9); 203 | R51(c1, d1, e1, a1, b1, w7, 6); 204 | R52(c2, d2, e2, a2, b2, w1, 12); 205 | R51(b1, c1, d1, e1, a1, w12, 8); 206 | R52(b2, c2, d2, e2, a2, w5, 5); 207 | R51(a1, b1, c1, d1, e1, w2, 13); 208 | R52(a2, b2, c2, d2, e2, w8, 14); 209 | R51(e1, a1, b1, c1, d1, w10, 12); 210 | R52(e2, a2, b2, c2, d2, w7, 6); 211 | R51(d1, e1, a1, b1, c1, w14, 5); 212 | R52(d2, e2, a2, b2, c2, w6, 8); 213 | R51(c1, d1, e1, a1, b1, w1, 12); 214 | R52(c2, d2, e2, a2, b2, w2, 13); 215 | R51(b1, c1, d1, e1, a1, w3, 13); 216 | R52(b2, c2, d2, e2, a2, w13, 6); 217 | R51(a1, b1, c1, d1, e1, w8, 14); 218 | R52(a2, b2, c2, d2, e2, w14, 5); 219 | R51(e1, a1, b1, c1, d1, w11, 11); 220 | R52(e2, a2, b2, c2, d2, w0, 15); 221 | R51(d1, e1, a1, b1, c1, w6, 8); 222 | R52(d2, e2, a2, b2, c2, w3, 13); 223 | R51(c1, d1, e1, a1, b1, w15, 5); 224 | R52(c2, d2, e2, a2, b2, w9, 11); 225 | R51(b1, c1, d1, e1, a1, w13, 6); 226 | R52(b2, c2, d2, e2, a2, w11, 11); 227 | 228 | uint32_t t = s[0]; 229 | s[0] = s[1] + c1 + d2; 230 | s[1] = s[2] + d1 + e2; 231 | s[2] = s[3] + e1 + a2; 232 | s[3] = s[4] + a1 + b2; 233 | s[4] = t + b1 + c2; 234 | } 235 | 236 | } // namespace ripemd160 237 | 238 | } // namespace 239 | 240 | ////// RIPEMD160 241 | 242 | CRIPEMD160::CRIPEMD160() : bytes(0) 243 | { 244 | ripemd160::Initialize(s); 245 | } 246 | 247 | CRIPEMD160& CRIPEMD160::Write(const unsigned char* data, size_t len) 248 | { 249 | const unsigned char* end = data + len; 250 | size_t bufsize = bytes % 64; 251 | if (bufsize && bufsize + len >= 64) { 252 | // Fill the buffer, and process it. 253 | memcpy(buf + bufsize, data, 64 - bufsize); 254 | bytes += 64 - bufsize; 255 | data += 64 - bufsize; 256 | ripemd160::Transform(s, buf); 257 | bufsize = 0; 258 | } 259 | while (end >= data + 64) { 260 | // Process full chunks directly from the source. 261 | ripemd160::Transform(s, data); 262 | bytes += 64; 263 | data += 64; 264 | } 265 | if (end > data) { 266 | // Fill the buffer with what remains. 267 | memcpy(buf + bufsize, data, end - data); 268 | bytes += end - data; 269 | } 270 | return *this; 271 | } 272 | 273 | void CRIPEMD160::Finalize(unsigned char hash[OUTPUT_SIZE]) 274 | { 275 | static const unsigned char pad[64] = {0x80}; 276 | unsigned char sizedesc[8]; 277 | WriteLE64(sizedesc, bytes << 3); 278 | Write(pad, 1 + ((119 - (bytes % 64)) % 64)); 279 | Write(sizedesc, 8); 280 | WriteLE32(hash, s[0]); 281 | WriteLE32(hash + 4, s[1]); 282 | WriteLE32(hash + 8, s[2]); 283 | WriteLE32(hash + 12, s[3]); 284 | WriteLE32(hash + 16, s[4]); 285 | } 286 | 287 | CRIPEMD160& CRIPEMD160::Reset() 288 | { 289 | bytes = 0; 290 | ripemd160::Initialize(s); 291 | return *this; 292 | } 293 | -------------------------------------------------------------------------------- /src/crypto/ripemd160.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2014 The Bitcoin Core developers 2 | // Distributed under the MIT software license, see the accompanying 3 | // file COPYING or http://www.opensource.org/licenses/mit-license.php. 4 | 5 | #ifndef BITCOIN_CRYPTO_RIPEMD160_H 6 | #define BITCOIN_CRYPTO_RIPEMD160_H 7 | 8 | #include 9 | #include 10 | 11 | /** A hasher class for RIPEMD-160. */ 12 | class CRIPEMD160 13 | { 14 | private: 15 | uint32_t s[5]; 16 | unsigned char buf[64]; 17 | uint64_t bytes; 18 | 19 | public: 20 | static const size_t OUTPUT_SIZE = 20; 21 | 22 | CRIPEMD160(); 23 | CRIPEMD160& Write(const unsigned char* data, size_t len); 24 | void Finalize(unsigned char hash[OUTPUT_SIZE]); 25 | CRIPEMD160& Reset(); 26 | }; 27 | 28 | #endif // BITCOIN_CRYPTO_RIPEMD160_H 29 | -------------------------------------------------------------------------------- /src/cxxopts.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Copyright (c) 2014, 2015, 2016, 2017 Jarryd Beck 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | 23 | */ 24 | 25 | #ifndef CXX_OPTS_HPP 26 | #define CXX_OPTS_HPP 27 | 28 | #if defined(__GNUC__) 29 | #pragma GCC diagnostic push 30 | #pragma GCC diagnostic ignored "-Wnon-virtual-dtor" 31 | #endif 32 | 33 | #include 34 | #include 35 | #include 36 | #include 37 | #include 38 | #include 39 | #include 40 | #include 41 | #include 42 | #include 43 | #include 44 | 45 | //when we ask cxxopts to use Unicode, help strings are processed using ICU, 46 | //which results in the correct lengths being computed for strings when they 47 | //are formatted for the help output 48 | //it is necessary to make sure that can be found by the 49 | //compiler, and that icu-uc is linked in to the binary. 50 | 51 | #ifdef CXXOPTS_USE_UNICODE 52 | #include 53 | 54 | namespace cxxopts 55 | { 56 | typedef icu::UnicodeString String; 57 | 58 | inline 59 | String 60 | toLocalString(std::string s) 61 | { 62 | return icu::UnicodeString::fromUTF8(std::move(s)); 63 | } 64 | 65 | class UnicodeStringIterator : public 66 | std::iterator 67 | { 68 | public: 69 | 70 | UnicodeStringIterator(const icu::UnicodeString* string, int32_t pos) 71 | : s(string) 72 | , i(pos) 73 | { 74 | } 75 | 76 | value_type 77 | operator*() const 78 | { 79 | return s->char32At(i); 80 | } 81 | 82 | bool 83 | operator==(const UnicodeStringIterator& rhs) const 84 | { 85 | return s == rhs.s && i == rhs.i; 86 | } 87 | 88 | bool 89 | operator!=(const UnicodeStringIterator& rhs) const 90 | { 91 | return !(*this == rhs); 92 | } 93 | 94 | UnicodeStringIterator& 95 | operator++() 96 | { 97 | ++i; 98 | return *this; 99 | } 100 | 101 | UnicodeStringIterator 102 | operator+(int32_t v) 103 | { 104 | return UnicodeStringIterator(s, i + v); 105 | } 106 | 107 | private: 108 | const icu::UnicodeString* s; 109 | int32_t i; 110 | }; 111 | 112 | inline 113 | String& 114 | stringAppend(String&s, String a) 115 | { 116 | return s.append(std::move(a)); 117 | } 118 | 119 | inline 120 | String& 121 | stringAppend(String& s, int n, UChar32 c) 122 | { 123 | for (int i = 0; i != n; ++i) 124 | { 125 | s.append(c); 126 | } 127 | 128 | return s; 129 | } 130 | 131 | template 132 | String& 133 | stringAppend(String& s, Iterator begin, Iterator end) 134 | { 135 | while (begin != end) 136 | { 137 | s.append(*begin); 138 | ++begin; 139 | } 140 | 141 | return s; 142 | } 143 | 144 | inline 145 | size_t 146 | stringLength(const String& s) 147 | { 148 | return s.length(); 149 | } 150 | 151 | inline 152 | std::string 153 | toUTF8String(const String& s) 154 | { 155 | std::string result; 156 | s.toUTF8String(result); 157 | 158 | return result; 159 | } 160 | 161 | inline 162 | bool 163 | empty(const String& s) 164 | { 165 | return s.isEmpty(); 166 | } 167 | } 168 | 169 | namespace std 170 | { 171 | cxxopts::UnicodeStringIterator 172 | begin(const icu::UnicodeString& s) 173 | { 174 | return cxxopts::UnicodeStringIterator(&s, 0); 175 | } 176 | 177 | cxxopts::UnicodeStringIterator 178 | end(const icu::UnicodeString& s) 179 | { 180 | return cxxopts::UnicodeStringIterator(&s, s.length()); 181 | } 182 | } 183 | 184 | //ifdef CXXOPTS_USE_UNICODE 185 | #else 186 | 187 | namespace cxxopts 188 | { 189 | typedef std::string String; 190 | 191 | template 192 | T 193 | toLocalString(T&& t) 194 | { 195 | return t; 196 | } 197 | 198 | inline 199 | size_t 200 | stringLength(const String& s) 201 | { 202 | return s.length(); 203 | } 204 | 205 | inline 206 | String& 207 | stringAppend(String&s, String a) 208 | { 209 | return s.append(std::move(a)); 210 | } 211 | 212 | inline 213 | String& 214 | stringAppend(String& s, size_t n, char c) 215 | { 216 | return s.append(n, c); 217 | } 218 | 219 | template 220 | String& 221 | stringAppend(String& s, Iterator begin, Iterator end) 222 | { 223 | return s.append(begin, end); 224 | } 225 | 226 | template 227 | std::string 228 | toUTF8String(T&& t) 229 | { 230 | return std::forward(t); 231 | } 232 | 233 | inline 234 | bool 235 | empty(const std::string& s) 236 | { 237 | return s.empty(); 238 | } 239 | } 240 | 241 | //ifdef CXXOPTS_USE_UNICODE 242 | #endif 243 | 244 | namespace cxxopts 245 | { 246 | namespace 247 | { 248 | #ifdef _WIN32 249 | const std::string LQUOTE("\'"); 250 | const std::string RQUOTE("\'"); 251 | #else 252 | const std::string LQUOTE("‘"); 253 | const std::string RQUOTE("’"); 254 | #endif 255 | } 256 | 257 | class Value : public std::enable_shared_from_this 258 | { 259 | public: 260 | 261 | virtual void 262 | parse(const std::string& text) const = 0; 263 | 264 | virtual void 265 | parse() const = 0; 266 | 267 | virtual bool 268 | has_arg() const = 0; 269 | 270 | virtual bool 271 | has_default() const = 0; 272 | 273 | virtual bool 274 | is_container() const = 0; 275 | 276 | virtual bool 277 | has_implicit() const = 0; 278 | 279 | virtual std::string 280 | get_default_value() const = 0; 281 | 282 | virtual std::string 283 | get_implicit_value() const = 0; 284 | 285 | virtual std::shared_ptr 286 | default_value(const std::string& value) = 0; 287 | 288 | virtual std::shared_ptr 289 | implicit_value(const std::string& value) = 0; 290 | }; 291 | 292 | class OptionException : public std::exception 293 | { 294 | public: 295 | OptionException(const std::string& message) 296 | : m_message(message) 297 | { 298 | } 299 | 300 | virtual const char* 301 | what() const noexcept 302 | { 303 | return m_message.c_str(); 304 | } 305 | 306 | private: 307 | std::string m_message; 308 | }; 309 | 310 | class OptionSpecException : public OptionException 311 | { 312 | public: 313 | 314 | OptionSpecException(const std::string& message) 315 | : OptionException(message) 316 | { 317 | } 318 | }; 319 | 320 | class OptionParseException : public OptionException 321 | { 322 | public: 323 | OptionParseException(const std::string& message) 324 | : OptionException(message) 325 | { 326 | } 327 | }; 328 | 329 | class option_exists_error : public OptionSpecException 330 | { 331 | public: 332 | option_exists_error(const std::string& option) 333 | : OptionSpecException(u8"Option " + LQUOTE + option + RQUOTE + u8" already exists") 334 | { 335 | } 336 | }; 337 | 338 | class invalid_option_format_error : public OptionSpecException 339 | { 340 | public: 341 | invalid_option_format_error(const std::string& format) 342 | : OptionSpecException(u8"Invalid option format " + LQUOTE + format + RQUOTE) 343 | { 344 | } 345 | }; 346 | 347 | class option_not_exists_exception : public OptionParseException 348 | { 349 | public: 350 | option_not_exists_exception(const std::string& option) 351 | : OptionParseException(u8"Option " + LQUOTE + option + RQUOTE + u8" does not exist") 352 | { 353 | } 354 | }; 355 | 356 | class missing_argument_exception : public OptionParseException 357 | { 358 | public: 359 | missing_argument_exception(const std::string& option) 360 | : OptionParseException( 361 | u8"Option " + LQUOTE + option + RQUOTE + u8" is missing an argument" 362 | ) 363 | { 364 | } 365 | }; 366 | 367 | class option_requires_argument_exception : public OptionParseException 368 | { 369 | public: 370 | option_requires_argument_exception(const std::string& option) 371 | : OptionParseException( 372 | u8"Option " + LQUOTE + option + RQUOTE + u8" requires an argument" 373 | ) 374 | { 375 | } 376 | }; 377 | 378 | class option_not_has_argument_exception : public OptionParseException 379 | { 380 | public: 381 | option_not_has_argument_exception 382 | ( 383 | const std::string& option, 384 | const std::string& arg 385 | ) 386 | : OptionParseException( 387 | u8"Option " + LQUOTE + option + RQUOTE + 388 | u8" does not take an argument, but argument" + 389 | LQUOTE + arg + RQUOTE + " given" 390 | ) 391 | { 392 | } 393 | }; 394 | 395 | class option_not_present_exception : public OptionParseException 396 | { 397 | public: 398 | option_not_present_exception(const std::string& option) 399 | : OptionParseException(u8"Option " + LQUOTE + option + RQUOTE + u8" not present") 400 | { 401 | } 402 | }; 403 | 404 | class argument_incorrect_type : public OptionParseException 405 | { 406 | public: 407 | argument_incorrect_type 408 | ( 409 | const std::string& arg 410 | ) 411 | : OptionParseException( 412 | u8"Argument " + LQUOTE + arg + RQUOTE + u8" failed to parse" 413 | ) 414 | { 415 | } 416 | }; 417 | 418 | class option_required_exception : public OptionParseException 419 | { 420 | public: 421 | option_required_exception(const std::string& option) 422 | : OptionParseException( 423 | u8"Option " + LQUOTE + option + RQUOTE + u8" is required but not present" 424 | ) 425 | { 426 | } 427 | }; 428 | 429 | namespace values 430 | { 431 | namespace 432 | { 433 | std::basic_regex integer_pattern 434 | ("(-)?(0x)?([1-9a-zA-Z][0-9a-zA-Z]*)|((0x)?0)"); 435 | } 436 | 437 | namespace detail 438 | { 439 | template 440 | struct SignedCheck; 441 | 442 | template 443 | struct SignedCheck 444 | { 445 | template 446 | void 447 | operator()(bool negative, U u, const std::string& text) 448 | { 449 | if (negative) 450 | { 451 | if (u > static_cast(-std::numeric_limits::min())) 452 | { 453 | throw argument_incorrect_type(text); 454 | } 455 | } 456 | else 457 | { 458 | if (u > static_cast(std::numeric_limits::max())) 459 | { 460 | throw argument_incorrect_type(text); 461 | } 462 | } 463 | } 464 | }; 465 | 466 | template 467 | struct SignedCheck 468 | { 469 | template 470 | void 471 | operator()(bool, U, const std::string&) {} 472 | }; 473 | 474 | template 475 | void 476 | check_signed_range(bool negative, U value, const std::string& text) 477 | { 478 | SignedCheck::is_signed>()(negative, value, text); 479 | } 480 | } 481 | 482 | template 483 | R 484 | checked_negate(T&& t, const std::string&, std::true_type) 485 | { 486 | // if we got to here, then `t` is a positive number that fits into 487 | // `R`. So to avoid MSVC C4146, we first cast it to `R`. 488 | // See https://github.com/jarro2783/cxxopts/issues/62 for more details. 489 | return -static_cast(t); 490 | } 491 | 492 | template 493 | T 494 | checked_negate(T&&, const std::string& text, std::false_type) 495 | { 496 | throw argument_incorrect_type(text); 497 | } 498 | 499 | template 500 | void 501 | integer_parser(const std::string& text, T& value) 502 | { 503 | std::smatch match; 504 | std::regex_match(text, match, integer_pattern); 505 | 506 | if (match.length() == 0) 507 | { 508 | throw argument_incorrect_type(text); 509 | } 510 | 511 | if (match.length(4) > 0) 512 | { 513 | value = 0; 514 | return; 515 | } 516 | 517 | using US = typename std::make_unsigned::type; 518 | 519 | constexpr auto umax = std::numeric_limits::max(); 520 | constexpr bool is_signed = std::numeric_limits::is_signed; 521 | const bool negative = match.length(1) > 0; 522 | const auto base = match.length(2) > 0 ? 16 : 10; 523 | 524 | auto value_match = match[3]; 525 | 526 | US result = 0; 527 | 528 | for (auto iter = value_match.first; iter != value_match.second; ++iter) 529 | { 530 | int digit = 0; 531 | 532 | if (*iter >= '0' && *iter <= '9') 533 | { 534 | digit = *iter - '0'; 535 | } 536 | else if (base == 16 && *iter >= 'a' && *iter <= 'f') 537 | { 538 | digit = *iter - 'a' + 10; 539 | } 540 | else if (base == 16 && *iter >= 'A' && *iter <= 'F') 541 | { 542 | digit = *iter - 'A' + 10; 543 | } 544 | else 545 | { 546 | throw argument_incorrect_type(text); 547 | } 548 | 549 | if (umax - digit < result * base) 550 | { 551 | throw argument_incorrect_type(text); 552 | } 553 | 554 | result = result * base + digit; 555 | } 556 | 557 | detail::check_signed_range(negative, result, text); 558 | 559 | if (negative) 560 | { 561 | value = checked_negate(result, 562 | text, 563 | std::integral_constant()); 564 | //if (!is_signed) 565 | //{ 566 | // throw argument_incorrect_type(text); 567 | //} 568 | //value = -result; 569 | } 570 | else 571 | { 572 | value = result; 573 | } 574 | } 575 | 576 | template 577 | void stringstream_parser(const std::string& text, T& value) 578 | { 579 | std::stringstream in(text); 580 | in >> value; 581 | if (!in) { 582 | throw argument_incorrect_type(text); 583 | } 584 | } 585 | 586 | inline 587 | void 588 | parse_value(const std::string& text, uint8_t& value) 589 | { 590 | integer_parser(text, value); 591 | } 592 | 593 | inline 594 | void 595 | parse_value(const std::string& text, int8_t& value) 596 | { 597 | integer_parser(text, value); 598 | } 599 | 600 | inline 601 | void 602 | parse_value(const std::string& text, uint16_t& value) 603 | { 604 | integer_parser(text, value); 605 | } 606 | 607 | inline 608 | void 609 | parse_value(const std::string& text, int16_t& value) 610 | { 611 | integer_parser(text, value); 612 | } 613 | 614 | inline 615 | void 616 | parse_value(const std::string& text, uint32_t& value) 617 | { 618 | integer_parser(text, value); 619 | } 620 | 621 | inline 622 | void 623 | parse_value(const std::string& text, int32_t& value) 624 | { 625 | integer_parser(text, value); 626 | } 627 | 628 | inline 629 | void 630 | parse_value(const std::string& text, uint64_t& value) 631 | { 632 | integer_parser(text, value); 633 | } 634 | 635 | inline 636 | void 637 | parse_value(const std::string& text, int64_t& value) 638 | { 639 | integer_parser(text, value); 640 | } 641 | 642 | inline 643 | void 644 | parse_value(const std::string& /*text*/, bool& value) 645 | { 646 | //TODO recognise on, off, yes, no, enable, disable 647 | //so that we can write --long=yes explicitly 648 | value = true; 649 | } 650 | 651 | inline 652 | void 653 | parse_value(const std::string& text, std::string& value) 654 | { 655 | value = text; 656 | } 657 | 658 | // The fallback parser. It uses the stringstream parser to parse all types 659 | // that have not been overloaded explicitly. It has to be placed in the 660 | // source code before all other more specialized templates. 661 | template 662 | void 663 | parse_value(const std::string& text, T& value) { 664 | stringstream_parser(text, value); 665 | } 666 | 667 | template 668 | void 669 | parse_value(const std::string& text, std::vector& value) 670 | { 671 | T v; 672 | parse_value(text, v); 673 | value.push_back(v); 674 | } 675 | 676 | template 677 | struct value_has_arg 678 | { 679 | static constexpr bool value = true; 680 | }; 681 | 682 | template <> 683 | struct value_has_arg 684 | { 685 | static constexpr bool value = false; 686 | }; 687 | 688 | template 689 | struct type_is_container 690 | { 691 | static constexpr bool value = false; 692 | }; 693 | 694 | template 695 | struct type_is_container> 696 | { 697 | static constexpr bool value = true; 698 | }; 699 | 700 | template 701 | class standard_value final : public Value 702 | { 703 | public: 704 | standard_value() 705 | : m_result(std::make_shared()) 706 | , m_store(m_result.get()) 707 | { 708 | } 709 | 710 | standard_value(T* t) 711 | : m_store(t) 712 | { 713 | } 714 | 715 | void 716 | parse(const std::string& text) const 717 | { 718 | parse_value(text, *m_store); 719 | } 720 | 721 | bool 722 | is_container() const 723 | { 724 | return type_is_container::value; 725 | } 726 | 727 | void 728 | parse() const 729 | { 730 | parse_value(m_default_value, *m_store); 731 | } 732 | 733 | bool 734 | has_arg() const 735 | { 736 | return value_has_arg::value; 737 | } 738 | 739 | bool 740 | has_default() const 741 | { 742 | return m_default; 743 | } 744 | 745 | bool 746 | has_implicit() const 747 | { 748 | return m_implicit; 749 | } 750 | 751 | virtual std::shared_ptr 752 | default_value(const std::string& value){ 753 | m_default = true; 754 | m_default_value = value; 755 | return shared_from_this(); 756 | } 757 | 758 | virtual std::shared_ptr 759 | implicit_value(const std::string& value){ 760 | m_implicit = true; 761 | m_implicit_value = value; 762 | return shared_from_this(); 763 | } 764 | 765 | std::string 766 | get_default_value() const 767 | { 768 | return m_default_value; 769 | } 770 | 771 | std::string 772 | get_implicit_value() const 773 | { 774 | return m_implicit_value; 775 | } 776 | 777 | const T& 778 | get() const 779 | { 780 | if (m_store == nullptr) 781 | { 782 | return *m_result; 783 | } 784 | else 785 | { 786 | return *m_store; 787 | } 788 | } 789 | 790 | protected: 791 | std::shared_ptr m_result; 792 | T* m_store; 793 | bool m_default = false; 794 | std::string m_default_value; 795 | bool m_implicit = false; 796 | std::string m_implicit_value; 797 | }; 798 | } 799 | 800 | template 801 | std::shared_ptr 802 | value() 803 | { 804 | return std::make_shared>(); 805 | } 806 | 807 | template 808 | std::shared_ptr 809 | value(T& t) 810 | { 811 | return std::make_shared>(&t); 812 | } 813 | 814 | class OptionAdder; 815 | 816 | class OptionDetails 817 | { 818 | public: 819 | OptionDetails 820 | ( 821 | const String& desc, 822 | std::shared_ptr val 823 | ) 824 | : m_desc(desc) 825 | , m_value(val) 826 | , m_count(0) 827 | { 828 | } 829 | 830 | const String& 831 | description() const 832 | { 833 | return m_desc; 834 | } 835 | 836 | bool 837 | has_arg() const 838 | { 839 | return m_value->has_arg(); 840 | } 841 | 842 | void 843 | parse(const std::string& text) 844 | { 845 | m_value->parse(text); 846 | ++m_count; 847 | } 848 | 849 | void 850 | parse_default() 851 | { 852 | m_value->parse(); 853 | } 854 | 855 | int 856 | count() const 857 | { 858 | return m_count; 859 | } 860 | 861 | const Value& value() const { 862 | return *m_value; 863 | } 864 | 865 | template 866 | const T& 867 | as() const 868 | { 869 | #ifdef CXXOPTS_NO_RTTI 870 | return static_cast&>(*m_value).get(); 871 | #else 872 | return dynamic_cast&>(*m_value).get(); 873 | #endif 874 | } 875 | 876 | private: 877 | String m_desc; 878 | std::shared_ptr m_value; 879 | int m_count; 880 | }; 881 | 882 | struct HelpOptionDetails 883 | { 884 | std::string s; 885 | std::string l; 886 | String desc; 887 | bool has_arg; 888 | bool has_default; 889 | std::string default_value; 890 | bool has_implicit; 891 | std::string implicit_value; 892 | std::string arg_help; 893 | bool is_container; 894 | }; 895 | 896 | struct HelpGroupDetails 897 | { 898 | std::string name; 899 | std::string description; 900 | std::vector options; 901 | }; 902 | 903 | class Options 904 | { 905 | public: 906 | 907 | Options(std::string program, std::string help_string = "") 908 | : m_program(std::move(program)) 909 | , m_help_string(toLocalString(std::move(help_string))) 910 | , m_positional_help("positional parameters") 911 | , m_next_positional(m_positional.end()) 912 | { 913 | } 914 | 915 | inline 916 | Options& 917 | positional_help(std::string help_text) 918 | { 919 | m_positional_help = std::move(help_text); 920 | return *this; 921 | } 922 | 923 | inline 924 | void 925 | parse(int& argc, char**& argv); 926 | 927 | inline 928 | OptionAdder 929 | add_options(std::string group = ""); 930 | 931 | inline 932 | void 933 | add_option 934 | ( 935 | const std::string& group, 936 | const std::string& s, 937 | const std::string& l, 938 | std::string desc, 939 | std::shared_ptr value, 940 | std::string arg_help 941 | ); 942 | 943 | int 944 | count(const std::string& o) const 945 | { 946 | auto iter = m_options.find(o); 947 | if (iter == m_options.end()) 948 | { 949 | return 0; 950 | } 951 | 952 | return iter->second->count(); 953 | } 954 | 955 | const OptionDetails& 956 | operator[](const std::string& option) const 957 | { 958 | auto iter = m_options.find(option); 959 | 960 | if (iter == m_options.end()) 961 | { 962 | throw option_not_present_exception(option); 963 | } 964 | 965 | return *iter->second; 966 | } 967 | 968 | //parse positional arguments into the given option 969 | inline 970 | void 971 | parse_positional(std::string option); 972 | 973 | inline 974 | void 975 | parse_positional(std::vector options); 976 | 977 | inline 978 | std::string 979 | help(const std::vector& groups = {""}) const; 980 | 981 | inline 982 | const std::vector 983 | groups() const; 984 | 985 | inline 986 | const HelpGroupDetails& 987 | group_help(const std::string& group) const; 988 | 989 | private: 990 | 991 | inline 992 | void 993 | add_one_option 994 | ( 995 | const std::string& option, 996 | std::shared_ptr details 997 | ); 998 | 999 | inline 1000 | bool 1001 | consume_positional(std::string a); 1002 | 1003 | inline 1004 | void 1005 | add_to_option(const std::string& option, const std::string& arg); 1006 | 1007 | inline 1008 | void 1009 | parse_option 1010 | ( 1011 | std::shared_ptr value, 1012 | const std::string& name, 1013 | const std::string& arg = "" 1014 | ); 1015 | 1016 | inline 1017 | void 1018 | checked_parse_arg 1019 | ( 1020 | int argc, 1021 | char* argv[], 1022 | int& current, 1023 | std::shared_ptr value, 1024 | const std::string& name 1025 | ); 1026 | 1027 | inline 1028 | String 1029 | help_one_group(const std::string& group) const; 1030 | 1031 | inline 1032 | void 1033 | generate_group_help 1034 | ( 1035 | String& result, 1036 | const std::vector& groups 1037 | ) const; 1038 | 1039 | inline 1040 | void 1041 | generate_all_groups_help(String& result) const; 1042 | 1043 | std::string m_program; 1044 | String m_help_string; 1045 | std::string m_positional_help; 1046 | 1047 | std::map> m_options; 1048 | std::vector m_positional; 1049 | std::vector::iterator m_next_positional; 1050 | std::unordered_set m_positional_set; 1051 | 1052 | //mapping from groups to help options 1053 | std::map m_help; 1054 | }; 1055 | 1056 | class OptionAdder 1057 | { 1058 | public: 1059 | 1060 | OptionAdder(Options& options, std::string group) 1061 | : m_options(options), m_group(std::move(group)) 1062 | { 1063 | } 1064 | 1065 | inline 1066 | OptionAdder& 1067 | operator() 1068 | ( 1069 | const std::string& opts, 1070 | const std::string& desc, 1071 | std::shared_ptr value 1072 | = ::cxxopts::value(), 1073 | std::string arg_help = "" 1074 | ); 1075 | 1076 | private: 1077 | Options& m_options; 1078 | std::string m_group; 1079 | }; 1080 | 1081 | // A helper function for setting required arguments 1082 | inline 1083 | void 1084 | check_required 1085 | ( 1086 | const Options& options, 1087 | const std::vector& required 1088 | ) 1089 | { 1090 | for (auto& r : required) 1091 | { 1092 | if (options.count(r) == 0) 1093 | { 1094 | throw option_required_exception(r); 1095 | } 1096 | } 1097 | } 1098 | 1099 | namespace 1100 | { 1101 | constexpr int OPTION_LONGEST = 30; 1102 | constexpr int OPTION_DESC_GAP = 2; 1103 | 1104 | std::basic_regex option_matcher 1105 | ("--([[:alnum:]][-_[:alnum:]]+)(=(.*))?|-([[:alnum:]]+)"); 1106 | 1107 | std::basic_regex option_specifier 1108 | ("(([[:alnum:]]),)?[ ]*([[:alnum:]][-_[:alnum:]]*)?"); 1109 | 1110 | String 1111 | format_option 1112 | ( 1113 | const HelpOptionDetails& o 1114 | ) 1115 | { 1116 | auto& s = o.s; 1117 | auto& l = o.l; 1118 | 1119 | String result = " "; 1120 | 1121 | if (s.size() > 0) 1122 | { 1123 | result += "-" + toLocalString(s) + ","; 1124 | } 1125 | else 1126 | { 1127 | result += " "; 1128 | } 1129 | 1130 | if (l.size() > 0) 1131 | { 1132 | result += " --" + toLocalString(l); 1133 | } 1134 | 1135 | if (o.has_arg) 1136 | { 1137 | auto arg = o.arg_help.size() > 0 ? toLocalString(o.arg_help) : "arg"; 1138 | 1139 | if (o.has_implicit) 1140 | { 1141 | result += " [=" + arg + "(=" + toLocalString(o.implicit_value) + ")]"; 1142 | } 1143 | else 1144 | { 1145 | result += " " + arg; 1146 | } 1147 | } 1148 | 1149 | return result; 1150 | } 1151 | 1152 | String 1153 | format_description 1154 | ( 1155 | const HelpOptionDetails& o, 1156 | size_t start, 1157 | size_t width 1158 | ) 1159 | { 1160 | auto desc = o.desc; 1161 | 1162 | if (o.has_default) 1163 | { 1164 | desc += toLocalString(" (default: " + o.default_value + ")"); 1165 | } 1166 | 1167 | String result; 1168 | 1169 | auto current = std::begin(desc); 1170 | auto startLine = current; 1171 | auto lastSpace = current; 1172 | 1173 | auto size = size_t{}; 1174 | 1175 | while (current != std::end(desc)) 1176 | { 1177 | if (*current == ' ') 1178 | { 1179 | lastSpace = current; 1180 | } 1181 | 1182 | if (size > width) 1183 | { 1184 | if (lastSpace == startLine) 1185 | { 1186 | stringAppend(result, startLine, current + 1); 1187 | stringAppend(result, "\n"); 1188 | stringAppend(result, start, ' '); 1189 | startLine = current + 1; 1190 | lastSpace = startLine; 1191 | } 1192 | else 1193 | { 1194 | stringAppend(result, startLine, lastSpace); 1195 | stringAppend(result, "\n"); 1196 | stringAppend(result, start, ' '); 1197 | startLine = lastSpace + 1; 1198 | } 1199 | size = 0; 1200 | } 1201 | else 1202 | { 1203 | ++size; 1204 | } 1205 | 1206 | ++current; 1207 | } 1208 | 1209 | //append whatever is left 1210 | stringAppend(result, startLine, current); 1211 | 1212 | return result; 1213 | } 1214 | } 1215 | 1216 | OptionAdder 1217 | Options::add_options(std::string group) 1218 | { 1219 | return OptionAdder(*this, std::move(group)); 1220 | } 1221 | 1222 | OptionAdder& 1223 | OptionAdder::operator() 1224 | ( 1225 | const std::string& opts, 1226 | const std::string& desc, 1227 | std::shared_ptr value, 1228 | std::string arg_help 1229 | ) 1230 | { 1231 | std::match_results result; 1232 | std::regex_match(opts.c_str(), result, option_specifier); 1233 | 1234 | if (result.empty()) 1235 | { 1236 | throw invalid_option_format_error(opts); 1237 | } 1238 | 1239 | const auto& short_match = result[2]; 1240 | const auto& long_match = result[3]; 1241 | 1242 | if (!short_match.length() && !long_match.length()) 1243 | { 1244 | throw invalid_option_format_error(opts); 1245 | } else if (long_match.length() == 1 && short_match.length()) 1246 | { 1247 | throw invalid_option_format_error(opts); 1248 | } 1249 | 1250 | auto option_names = [] 1251 | ( 1252 | const std::sub_match& short_, 1253 | const std::sub_match& long_ 1254 | ) 1255 | { 1256 | if (long_.length() == 1) 1257 | { 1258 | return std::make_tuple(long_.str(), short_.str()); 1259 | } 1260 | else 1261 | { 1262 | return std::make_tuple(short_.str(), long_.str()); 1263 | } 1264 | }(short_match, long_match); 1265 | 1266 | m_options.add_option 1267 | ( 1268 | m_group, 1269 | std::get<0>(option_names), 1270 | std::get<1>(option_names), 1271 | desc, 1272 | value, 1273 | std::move(arg_help) 1274 | ); 1275 | 1276 | return *this; 1277 | } 1278 | 1279 | void 1280 | Options::parse_option 1281 | ( 1282 | std::shared_ptr value, 1283 | const std::string& /*name*/, 1284 | const std::string& arg 1285 | ) 1286 | { 1287 | value->parse(arg); 1288 | } 1289 | 1290 | void 1291 | Options::checked_parse_arg 1292 | ( 1293 | int argc, 1294 | char* argv[], 1295 | int& current, 1296 | std::shared_ptr value, 1297 | const std::string& name 1298 | ) 1299 | { 1300 | if (current + 1 >= argc) 1301 | { 1302 | if (value->value().has_implicit()) 1303 | { 1304 | parse_option(value, name, value->value().get_implicit_value()); 1305 | } 1306 | else 1307 | { 1308 | throw missing_argument_exception(name); 1309 | } 1310 | } 1311 | else 1312 | { 1313 | if (argv[current + 1][0] == '-' && value->value().has_implicit()) 1314 | { 1315 | parse_option(value, name, value->value().get_implicit_value()); 1316 | } 1317 | else 1318 | { 1319 | parse_option(value, name, argv[current + 1]); 1320 | ++current; 1321 | } 1322 | } 1323 | } 1324 | 1325 | void 1326 | Options::add_to_option(const std::string& option, const std::string& arg) 1327 | { 1328 | auto iter = m_options.find(option); 1329 | 1330 | if (iter == m_options.end()) 1331 | { 1332 | throw option_not_exists_exception(option); 1333 | } 1334 | 1335 | parse_option(iter->second, option, arg); 1336 | } 1337 | 1338 | bool 1339 | Options::consume_positional(std::string a) 1340 | { 1341 | while (m_next_positional != m_positional.end()) 1342 | { 1343 | auto iter = m_options.find(*m_next_positional); 1344 | if (iter != m_options.end()) 1345 | { 1346 | if (!iter->second->value().is_container()) 1347 | { 1348 | if (iter->second->count() == 0) 1349 | { 1350 | add_to_option(*m_next_positional, a); 1351 | ++m_next_positional; 1352 | return true; 1353 | } 1354 | else 1355 | { 1356 | ++m_next_positional; 1357 | continue; 1358 | } 1359 | } 1360 | else 1361 | { 1362 | add_to_option(*m_next_positional, a); 1363 | return true; 1364 | } 1365 | } 1366 | ++m_next_positional; 1367 | } 1368 | 1369 | return false; 1370 | } 1371 | 1372 | void 1373 | Options::parse_positional(std::string option) 1374 | { 1375 | parse_positional(std::vector{option}); 1376 | } 1377 | 1378 | void 1379 | Options::parse_positional(std::vector options) 1380 | { 1381 | m_positional = std::move(options); 1382 | m_next_positional = m_positional.begin(); 1383 | 1384 | m_positional_set.insert(m_positional.begin(), m_positional.end()); 1385 | } 1386 | 1387 | void 1388 | Options::parse(int& argc, char**& argv) 1389 | { 1390 | int current = 1; 1391 | 1392 | int nextKeep = 1; 1393 | 1394 | bool consume_remaining = false; 1395 | 1396 | while (current != argc) 1397 | { 1398 | if (strcmp(argv[current], "--") == 0) 1399 | { 1400 | consume_remaining = true; 1401 | ++current; 1402 | break; 1403 | } 1404 | 1405 | std::match_results result; 1406 | std::regex_match(argv[current], result, option_matcher); 1407 | 1408 | if (result.empty()) 1409 | { 1410 | //not a flag 1411 | 1412 | //if true is returned here then it was consumed, otherwise it is 1413 | //ignored 1414 | if (consume_positional(argv[current])) 1415 | { 1416 | } 1417 | else 1418 | { 1419 | argv[nextKeep] = argv[current]; 1420 | ++nextKeep; 1421 | } 1422 | //if we return from here then it was parsed successfully, so continue 1423 | } 1424 | else 1425 | { 1426 | //short or long option? 1427 | if (result[4].length() != 0) 1428 | { 1429 | const std::string& s = result[4]; 1430 | 1431 | for (std::size_t i = 0; i != s.size(); ++i) 1432 | { 1433 | std::string name(1, s[i]); 1434 | auto iter = m_options.find(name); 1435 | 1436 | if (iter == m_options.end()) 1437 | { 1438 | throw option_not_exists_exception(name); 1439 | } 1440 | 1441 | auto value = iter->second; 1442 | 1443 | //if no argument then just add it 1444 | if (!value->has_arg()) 1445 | { 1446 | parse_option(value, name); 1447 | } 1448 | else 1449 | { 1450 | //it must be the last argument 1451 | if (i + 1 == s.size()) 1452 | { 1453 | checked_parse_arg(argc, argv, current, value, name); 1454 | } 1455 | else if (value->value().has_implicit()) 1456 | { 1457 | parse_option(value, name, value->value().get_implicit_value()); 1458 | } 1459 | else 1460 | { 1461 | //error 1462 | throw option_requires_argument_exception(name); 1463 | } 1464 | } 1465 | } 1466 | } 1467 | else if (result[1].length() != 0) 1468 | { 1469 | const std::string& name = result[1]; 1470 | 1471 | auto iter = m_options.find(name); 1472 | 1473 | if (iter == m_options.end()) 1474 | { 1475 | throw option_not_exists_exception(name); 1476 | } 1477 | 1478 | auto opt = iter->second; 1479 | 1480 | //equals provided for long option? 1481 | if (result[3].length() != 0) 1482 | { 1483 | //parse the option given 1484 | 1485 | //but if it doesn't take an argument, this is an error 1486 | if (!opt->has_arg()) 1487 | { 1488 | throw option_not_has_argument_exception(name, result[3]); 1489 | } 1490 | 1491 | parse_option(opt, name, result[3]); 1492 | } 1493 | else 1494 | { 1495 | if (opt->has_arg()) 1496 | { 1497 | //parse the next argument 1498 | checked_parse_arg(argc, argv, current, opt, name); 1499 | } 1500 | else 1501 | { 1502 | //parse with empty argument 1503 | parse_option(opt, name); 1504 | } 1505 | } 1506 | } 1507 | 1508 | } 1509 | 1510 | ++current; 1511 | } 1512 | 1513 | for (auto& opt : m_options) 1514 | { 1515 | auto& detail = opt.second; 1516 | auto& value = detail->value(); 1517 | 1518 | if(!detail->count() && value.has_default()){ 1519 | detail->parse_default(); 1520 | } 1521 | } 1522 | 1523 | if (consume_remaining) 1524 | { 1525 | while (current < argc) 1526 | { 1527 | if (!consume_positional(argv[current])) { 1528 | break; 1529 | } 1530 | ++current; 1531 | } 1532 | 1533 | //adjust argv for any that couldn't be swallowed 1534 | while (current != argc) { 1535 | argv[nextKeep] = argv[current]; 1536 | ++nextKeep; 1537 | ++current; 1538 | } 1539 | } 1540 | 1541 | argc = nextKeep; 1542 | 1543 | } 1544 | 1545 | void 1546 | Options::add_option 1547 | ( 1548 | const std::string& group, 1549 | const std::string& s, 1550 | const std::string& l, 1551 | std::string desc, 1552 | std::shared_ptr value, 1553 | std::string arg_help 1554 | ) 1555 | { 1556 | auto stringDesc = toLocalString(std::move(desc)); 1557 | auto option = std::make_shared(stringDesc, value); 1558 | 1559 | if (s.size() > 0) 1560 | { 1561 | add_one_option(s, option); 1562 | } 1563 | 1564 | if (l.size() > 0) 1565 | { 1566 | add_one_option(l, option); 1567 | } 1568 | 1569 | //add the help details 1570 | auto& options = m_help[group]; 1571 | 1572 | options.options.emplace_back(HelpOptionDetails{s, l, stringDesc, 1573 | value->has_arg(), 1574 | value->has_default(), value->get_default_value(), 1575 | value->has_implicit(), value->get_implicit_value(), 1576 | std::move(arg_help), 1577 | value->is_container()}); 1578 | } 1579 | 1580 | void 1581 | Options::add_one_option 1582 | ( 1583 | const std::string& option, 1584 | std::shared_ptr details 1585 | ) 1586 | { 1587 | auto in = m_options.emplace(option, details); 1588 | 1589 | if (!in.second) 1590 | { 1591 | throw option_exists_error(option); 1592 | } 1593 | } 1594 | 1595 | String 1596 | Options::help_one_group(const std::string& g) const 1597 | { 1598 | typedef std::vector> OptionHelp; 1599 | 1600 | auto group = m_help.find(g); 1601 | if (group == m_help.end()) 1602 | { 1603 | return ""; 1604 | } 1605 | 1606 | OptionHelp format; 1607 | 1608 | size_t longest = 0; 1609 | 1610 | String result; 1611 | 1612 | if (!g.empty()) 1613 | { 1614 | result += toLocalString(" " + g + " options:\n"); 1615 | } 1616 | 1617 | for (const auto& o : group->second.options) 1618 | { 1619 | if (o.is_container && m_positional_set.find(o.l) != m_positional_set.end()) 1620 | { 1621 | continue; 1622 | } 1623 | 1624 | auto s = format_option(o); 1625 | longest = std::max(longest, stringLength(s)); 1626 | format.push_back(std::make_pair(s, String())); 1627 | } 1628 | 1629 | longest = std::min(longest, static_cast(OPTION_LONGEST)); 1630 | 1631 | //widest allowed description 1632 | auto allowed = size_t{76} - longest - OPTION_DESC_GAP; 1633 | 1634 | auto fiter = format.begin(); 1635 | for (const auto& o : group->second.options) 1636 | { 1637 | if (o.is_container && m_positional_set.find(o.l) != m_positional_set.end()) 1638 | { 1639 | continue; 1640 | } 1641 | 1642 | auto d = format_description(o, longest + OPTION_DESC_GAP, allowed); 1643 | 1644 | result += fiter->first; 1645 | if (stringLength(fiter->first) > longest) 1646 | { 1647 | result += '\n'; 1648 | result += toLocalString(std::string(longest + OPTION_DESC_GAP, ' ')); 1649 | } 1650 | else 1651 | { 1652 | result += toLocalString(std::string(longest + OPTION_DESC_GAP - 1653 | stringLength(fiter->first), 1654 | ' ')); 1655 | } 1656 | result += d; 1657 | result += '\n'; 1658 | 1659 | ++fiter; 1660 | } 1661 | 1662 | return result; 1663 | } 1664 | 1665 | void 1666 | Options::generate_group_help 1667 | ( 1668 | String& result, 1669 | const std::vector& print_groups 1670 | ) const 1671 | { 1672 | for (size_t i = 0; i != print_groups.size(); ++i) 1673 | { 1674 | const String& group_help_text = help_one_group(print_groups[i]); 1675 | if (empty(group_help_text)) 1676 | { 1677 | continue; 1678 | } 1679 | result += group_help_text; 1680 | if (i < print_groups.size() - 1) 1681 | { 1682 | result += '\n'; 1683 | } 1684 | } 1685 | } 1686 | 1687 | void 1688 | Options::generate_all_groups_help(String& result) const 1689 | { 1690 | std::vector all_groups; 1691 | all_groups.reserve(m_help.size()); 1692 | 1693 | for (auto& group : m_help) 1694 | { 1695 | all_groups.push_back(group.first); 1696 | } 1697 | 1698 | generate_group_help(result, all_groups); 1699 | } 1700 | 1701 | std::string 1702 | Options::help(const std::vector& help_groups) const 1703 | { 1704 | String result = m_help_string + "\nUsage:\n " + 1705 | toLocalString(m_program) + " [OPTION...]"; 1706 | 1707 | if (m_positional.size() > 0) { 1708 | result += " " + toLocalString(m_positional_help); 1709 | } 1710 | 1711 | result += "\n\n"; 1712 | 1713 | if (help_groups.size() == 0) 1714 | { 1715 | generate_all_groups_help(result); 1716 | } 1717 | else 1718 | { 1719 | generate_group_help(result, help_groups); 1720 | } 1721 | 1722 | return toUTF8String(result); 1723 | } 1724 | 1725 | const std::vector 1726 | Options::groups() const 1727 | { 1728 | std::vector g; 1729 | 1730 | std::transform( 1731 | m_help.begin(), 1732 | m_help.end(), 1733 | std::back_inserter(g), 1734 | [] (const std::map::value_type& pair) 1735 | { 1736 | return pair.first; 1737 | } 1738 | ); 1739 | 1740 | return g; 1741 | } 1742 | 1743 | const HelpGroupDetails& 1744 | Options::group_help(const std::string& group) const 1745 | { 1746 | return m_help.at(group); 1747 | } 1748 | 1749 | } 1750 | 1751 | #if defined(__GNUC__) 1752 | #pragma GCC diagnostic pop 1753 | #endif 1754 | 1755 | #endif //CXX_OPTS_HPP -------------------------------------------------------------------------------- /src/filereader.cpp: -------------------------------------------------------------------------------- 1 | #include "filereader.h" 2 | /* VTC Blockindexer - A utility to build additional indexes to the 3 | Vertcoin blockchain by scanning and indexing the blockfiles 4 | downloaded by Vertcoin Core. 5 | 6 | Copyright (C) 2017 Gert-Jaap Glasbergen 7 | 8 | This program is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program. If not, see . 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | 30 | using namespace std; 31 | 32 | uint64_t VtcBlockIndexer::FileReader::readVarInt(istream& stream) 33 | { 34 | uint8_t prefix = 0; 35 | stream.read(reinterpret_cast(&prefix), sizeof(prefix)); 36 | if(prefix < 253) { 37 | return prefix; 38 | } 39 | 40 | if(prefix == 253) { 41 | uint16_t value = 0; 42 | stream.read(reinterpret_cast(&value), sizeof(value)); 43 | return value; 44 | 45 | } else if (prefix == 254) { 46 | uint32_t value = 0; 47 | stream.read(reinterpret_cast(&value), sizeof(value)); 48 | return value; 49 | 50 | } else if (prefix == 255) { 51 | uint64_t value = 0; 52 | stream.read(reinterpret_cast(&value), sizeof(value)); 53 | return value; 54 | 55 | } else { 56 | // This should never happen, but just in case (and to fix compiler warning) 57 | return 0; 58 | } 59 | 60 | 61 | } 62 | 63 | vector VtcBlockIndexer::FileReader::readHash(istream& stream) { 64 | vector data(32); 65 | stream.read(reinterpret_cast(&data[0]) , 32); 66 | return data; 67 | } 68 | 69 | vector VtcBlockIndexer::FileReader::readString(istream& stream) { 70 | uint64_t length = readVarInt(stream); 71 | 72 | if(length > 0) { 73 | vector data(length); 74 | stream.read(reinterpret_cast(&data[0]) , length); 75 | return data; 76 | } else { 77 | return {}; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/filereader.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* VTC Blockindexer - A utility to build additional indexes to the 5 | Vertcoin blockchain by scanning and indexing the blockfiles 6 | downloaded by Vertcoin Core. 7 | 8 | Copyright (C) 2017 Gert-Jaap Glasbergen 9 | 10 | This program is free software: you can redistribute it and/or modify 11 | it under the terms of the GNU General Public License as published by 12 | the Free Software Foundation, either version 3 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License 21 | along with this program. If not, see . 22 | */ 23 | namespace VtcBlockIndexer { 24 | 25 | /** 26 | * The Util class provides methods to perform various cryptographic operations 27 | */ 28 | 29 | class FileReader { 30 | public: 31 | /** Reads a varint from a ifstream. The first byte determines the size of 32 | * the int. If it is below 0xFD it's a uint8_t, if it's 0xFD it's followed by 33 | * a uint16_t (2 bytes). If it's 0xFE it's followed by a uint32_t (4 bytes), 34 | * and 0xFF means a uint64_t (8 bytes) 35 | * @param stream the stream to read from 36 | */ 37 | static uint64_t readVarInt(std::istream& stream); 38 | 39 | /** Reads a hash (32 bytes) from the ifstream and returns it as vector 40 | * use Utility::hashToHex or ::hashToReverseHex to convert it to hex 41 | * 42 | * @param stream the stream to read from 43 | */ 44 | static std::vector readHash(std::istream& stream); 45 | 46 | /** Reads a string (first a VarInt with the length, then the contents) from 47 | * the ifstream and returns it as vector 48 | * 49 | * @param stream the stream to read from 50 | */ 51 | static std::vector readString(std::istream& stream); 52 | }; 53 | } -------------------------------------------------------------------------------- /src/httpserver.cpp: -------------------------------------------------------------------------------- 1 | /* VTC Blockindexer - A utility to build additional indexes to the 2 | Vertcoin blockchain by scanning and indexing the blockfiles 3 | downloaded by Vertcoin Core. 4 | 5 | Copyright (C) 2017 Gert-Jaap Glasbergen 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | #include "httpserver.h" 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "json.hpp" 31 | #include "utility.h" 32 | using namespace std; 33 | using namespace restbed; 34 | using json = nlohmann::json; 35 | 36 | 37 | VtcBlockIndexer::HttpServer::HttpServer(shared_ptr db, shared_ptr mempoolMonitor, string blocksDir) { 38 | this->db = db; 39 | this->blocksDir = blocksDir; 40 | this->mempoolMonitor = mempoolMonitor; 41 | blockReader.reset(new VtcBlockIndexer::BlockReader(blocksDir)); 42 | scriptSolver = std::make_unique(); 43 | httpClient.reset(new jsonrpc::HttpClient("http://middleware:middleware@" + std::string(std::getenv("COIND_HOST")) + ":8332")); 44 | vertcoind.reset(new VertcoinClient(*httpClient)); 45 | } 46 | 47 | 48 | 49 | void VtcBlockIndexer::HttpServer::getTransaction(const shared_ptr session) { 50 | const auto request = session->get_request(); 51 | 52 | cout << "Looking up txid " << request->get_path_parameter("id") << endl; 53 | 54 | try { 55 | const Json::Value tx = vertcoind->getrawtransaction(request->get_path_parameter("id"), true); 56 | 57 | stringstream body; 58 | body << tx.toStyledString(); 59 | 60 | session->close(OK, body.str(), {{"Content-Type","application/json"},{"Content-Length", std::to_string(body.str().size())}}); 61 | } catch(const jsonrpc::JsonRpcException& e) { 62 | const std::string message(e.what()); 63 | cout << "Not found " << message << endl; 64 | session->close(404, message, {{"Content-Type","application/json"},{"Content-Length", std::to_string(message.size())}}); 65 | } 66 | } 67 | 68 | 69 | void VtcBlockIndexer::HttpServer::getTransactionProof(const shared_ptr session) { 70 | const auto request = session->get_request(); 71 | 72 | std::string blockHash; 73 | std::string txId = request->get_path_parameter("id",""); 74 | leveldb::Status s = this->db->Get(leveldb::ReadOptions(), "tx-" + txId + "-block", &blockHash); 75 | if(!s.ok()) // no key found 76 | { 77 | const std::string message("TX not found"); 78 | session->close(404, message, {{"Content-Length", std::to_string(message.size())}}); 79 | return; 80 | } 81 | 82 | std::string blockHeightString; 83 | s = this->db->Get(leveldb::ReadOptions(), "block-hash-" + blockHash, &blockHeightString); 84 | if(!s.ok()) // no key found 85 | { 86 | const std::string message("Block not found"); 87 | session->close(404, message, {{"Content-Length", std::to_string(message.size())}}); 88 | return; 89 | } 90 | uint64_t blockHeight = stoll(blockHeightString); 91 | json j; 92 | j["txHash"] = txId; 93 | j["blockHash"] = blockHash; 94 | j["blockHeight"] = blockHeight; 95 | json chain = json::array(); 96 | for(uint64_t i = blockHeight+1; --i > 0 && i > blockHeight-10;) { 97 | stringstream blockKey; 98 | blockKey << "block-filePosition-" << setw(8) << setfill('0') << i; 99 | 100 | std::string filePosition; 101 | s = this->db->Get(leveldb::ReadOptions(), blockKey.str(), &filePosition); 102 | if(!s.ok()) // no key found 103 | { 104 | const std::string message("Block not found"); 105 | session->close(404, message, {{"Content-Length", std::to_string(message.size())}}); 106 | return; 107 | } 108 | 109 | Block block = this->blockReader->readBlock(filePosition.substr(0,12),stoll(filePosition.substr(12,12)),i,true); 110 | 111 | json jsonBlock; 112 | jsonBlock["blockHash"] = block.blockHash; 113 | jsonBlock["previousBlockHash"] = block.previousBlockHash; 114 | jsonBlock["merkleRoot"] = block.merkleRoot; 115 | jsonBlock["version"] = block.version; 116 | jsonBlock["time"] = block.time; 117 | jsonBlock["bits"] = block.bits; 118 | jsonBlock["nonce"] = block.nonce; 119 | jsonBlock["height"] = block.height; 120 | chain.push_back(jsonBlock); 121 | 122 | } 123 | j["chain"] = chain; 124 | string body = j.dump(); 125 | 126 | session->close( OK, body, { { "Content-Type", "application/json" }, { "Content-Length", std::to_string(body.size()) } } ); 127 | } 128 | 129 | void VtcBlockIndexer::HttpServer::sync(const shared_ptr session) { 130 | json j; 131 | 132 | const auto request = session->get_request( ); 133 | 134 | string highestBlockString; 135 | this->db->Get(leveldb::ReadOptions(),"highestblock",&highestBlockString); 136 | 137 | j["error"] = nullptr; 138 | j["height"] = stoll(highestBlockString); 139 | try { 140 | const Json::Value blockCount = vertcoind->getblockcount(); 141 | 142 | j["blockChainHeight"] = blockCount.asInt(); 143 | } catch(const jsonrpc::JsonRpcException& e) { 144 | const std::string message(e.what()); 145 | j["error"] = message; 146 | } 147 | 148 | float progress = (float)j["height"].get() / (float)j["blockChainHeight"].get(); 149 | progress *= 100; 150 | j["syncPercentage"] = progress; 151 | if(progress >= 100) { 152 | j["status"] = "finished"; 153 | } else { 154 | j["status"] = "indexing"; 155 | } 156 | 157 | string body = j.dump(); 158 | session->close( OK, body, { { "Content-Type", "application/json" }, { "Content-Length", std::to_string(body.size()) } } ); 159 | } 160 | 161 | void VtcBlockIndexer::HttpServer::getBlocks(const shared_ptr session) { 162 | json j = json::array(); 163 | 164 | const auto request = session->get_request( ); 165 | 166 | string highestBlockString; 167 | this->db->Get(leveldb::ReadOptions(),"highestblock",&highestBlockString); 168 | 169 | 170 | long long limitParam = stoi(request->get_query_parameter("limit","0")); 171 | if(limitParam == 0 || limitParam > 100) 172 | limitParam = 100; 173 | 174 | long long lowestBlock = stoll(highestBlockString)-limitParam; 175 | stringstream lowestBlockString; 176 | lowestBlockString << setw(8) << setfill('0') << lowestBlock; 177 | 178 | string start("block-" + highestBlockString); 179 | string limit("block-" + lowestBlockString.str()); 180 | 181 | leveldb::Iterator* it = this->db->NewIterator(leveldb::ReadOptions()); 182 | for (it->Seek(start); 183 | it->Valid() && it->key().ToString() > limit; 184 | it->Prev()) { 185 | json blockObj; 186 | string blockHeightString = it->key().ToString().substr(6); 187 | blockObj["hash"] = it->value().ToString(); 188 | string blockSizeString; 189 | string blockTxesString; 190 | string blockTimeString; 191 | this->db->Get(leveldb::ReadOptions(),"block-size-" + blockHeightString,&blockSizeString); 192 | this->db->Get(leveldb::ReadOptions(),"block-txcount-" + blockHeightString,&blockTxesString); 193 | this->db->Get(leveldb::ReadOptions(),"block-time-" + blockHeightString,&blockTimeString); 194 | blockObj["height"] = stoll(blockHeightString); 195 | blockObj["size"] = stoll(blockSizeString); 196 | blockObj["time"] = stoll(blockTimeString); 197 | blockObj["txlength"] = stoll(blockTxesString); 198 | blockObj["poolInfo"] = nullptr; 199 | j.push_back(blockObj); 200 | } 201 | 202 | string body = j.dump(); 203 | 204 | session->close( OK, body, { { "Content-Type", "application/json" }, { "Content-Length", std::to_string(body.size()) } } ); 205 | 206 | } 207 | 208 | void VtcBlockIndexer::HttpServer::addressBalance( const shared_ptr< Session > session ) 209 | { 210 | long long balance = 0; 211 | long long unconfirmedBalance = 0; 212 | long long txCount = 0; 213 | long long unconfirmedTxCount = 0; 214 | int txoCount = 0; 215 | const auto request = session->get_request( ); 216 | int details = stoi(request->get_query_parameter("details","0")); 217 | 218 | cout << "Checking balance for address " << request->get_path_parameter( "address" ) << endl; 219 | 220 | string start(request->get_path_parameter( "address" ) + "-txo-00000001"); 221 | string limit(request->get_path_parameter( "address" ) + "-txo-99999999"); 222 | 223 | leveldb::Iterator* it = this->db->NewIterator(leveldb::ReadOptions()); 224 | 225 | for (it->Seek(start); 226 | it->Valid() && it->key().ToString() < limit; 227 | it->Next()) { 228 | 229 | string spentTx; 230 | txoCount++; 231 | txCount++; 232 | string txo = it->value().ToString(); 233 | 234 | leveldb::Status s = this->db->Get(leveldb::ReadOptions(), "txo-" + txo.substr(0,64) + "-" + txo.substr(64,8) + "-spent", &spentTx); 235 | if(!s.ok()) // no key found, not spent. Add balance. 236 | { 237 | balance += stoll(txo.substr(80)); 238 | // check mempool for spenders 239 | string spender = mempoolMonitor->outpointSpend(txo.substr(0,64), stol(txo.substr(64,8))); 240 | if(spender.compare("") == 0) { 241 | unconfirmedBalance += stoll(txo.substr(80)); 242 | } else { 243 | unconfirmedTxCount++; 244 | } 245 | } else { 246 | txCount++; 247 | } 248 | } 249 | assert(it->status().ok()); // Check for any errors found during the scan 250 | delete it; 251 | 252 | cout << "Analyzed " << txoCount << " TXOs - Balance is " << balance << endl; 253 | 254 | // Add mempool transactions 255 | vector mempoolOutputs = mempoolMonitor->getTxos(request->get_path_parameter( "address" )); 256 | for (VtcBlockIndexer::TransactionOutput txo : mempoolOutputs) { 257 | txoCount++; 258 | unconfirmedTxCount++; 259 | string spender = mempoolMonitor->outpointSpend(txo.txHash, txo.index); 260 | cout << "Spender for " << txo.txHash << "/" << txo.index << " = " << spender; 261 | if(spender.compare("") == 0) { 262 | unconfirmedBalance += txo.value; 263 | } else { 264 | unconfirmedTxCount++; 265 | } 266 | } 267 | 268 | cout << "Including mempool: Analyzed " << txoCount << " TXOs - Balance is " << balance << endl; 269 | 270 | if(details != 0) { 271 | json j; 272 | j["balance"] = balance; 273 | j["txCount"] = txCount; 274 | j["unconfirmedBalance"] = unconfirmedBalance; 275 | j["unconfirmedTxCount"] = unconfirmedTxCount; 276 | string body = j.dump(); 277 | session->close( OK, body, { { "Content-Type", "application/json" }, { "Content-Length", std::to_string(body.size()) } } ); 278 | } else { 279 | stringstream body; 280 | body << balance; 281 | 282 | session->close( OK, body.str(), { {"Content-Type","text/plain"}, { "Content-Length", std::to_string(body.str().size()) } } ); 283 | } 284 | 285 | } 286 | 287 | void VtcBlockIndexer::HttpServer::addressTxos( const shared_ptr< Session > session ) 288 | { 289 | json j = json::array(); 290 | 291 | const auto request = session->get_request( ); 292 | 293 | long long sinceBlock = stoll(request->get_path_parameter( "sinceBlock", "0" )); 294 | 295 | int txHashOnly = stoi(request->get_query_parameter("txHashOnly","0")); 296 | int raw = stoi(request->get_query_parameter("raw","0")); 297 | int unspent = stoi(request->get_query_parameter("unspent","0")); 298 | int unconfirmed = stoi(request->get_query_parameter("unconfirmed","0")); 299 | int scripts = stoi(request->get_query_parameter("script","0")); 300 | cout << "Fetching address txos for address " << request->get_path_parameter( "address" ) << endl; 301 | 302 | string start(request->get_path_parameter( "address" ) + "-txo-00000001"); 303 | string limit(request->get_path_parameter( "address" ) + "-txo-99999999"); 304 | 305 | leveldb::Iterator* it = this->db->NewIterator(leveldb::ReadOptions()); 306 | 307 | for (it->Seek(start); 308 | it->Valid() && it->key().ToString() < limit; 309 | it->Next()) { 310 | 311 | string spentTx; 312 | string txo = it->value().ToString(); 313 | 314 | leveldb::Status s = this->db->Get(leveldb::ReadOptions(), "txo-" + txo.substr(0,64) + "-" + txo.substr(64,8) + "-spent", &spentTx); 315 | long long block = stoll(txo.substr(72,8)); 316 | if(block >= sinceBlock) { 317 | json txoObj; 318 | txoObj["height"] = block; 319 | 320 | if(!s.ok()) { 321 | if(unconfirmed) { 322 | string spender = mempoolMonitor->outpointSpend(txo.substr(0,64), stol(txo.substr(64,8))); 323 | if(spender.compare("") == 0) { 324 | txoObj["spender"] = nullptr; 325 | } else { 326 | if(unspent == 1) continue; 327 | txoObj["spender"] = spender; 328 | } 329 | } else { 330 | txoObj["spender"] = nullptr; 331 | } 332 | } else { 333 | if(unspent == 1) continue; 334 | txoObj["spender"] = spentTx.substr(65, 64); 335 | 336 | } 337 | 338 | if(raw != 0) { 339 | try { 340 | const Json::Value tx = vertcoind->getrawtransaction(txo.substr(0,64), false); 341 | txoObj["tx"] = tx.asString(); 342 | } catch(const jsonrpc::JsonRpcException& e) { 343 | const std::string message(e.what()); 344 | session->close(400, message, {{"Content-Type","text/plain"},{"Content-Length", std::to_string(message.size())}}); 345 | cout << "Not found " << message << endl; 346 | return; 347 | } 348 | } 349 | 350 | if(raw == 0 && scripts != 0) { 351 | try { 352 | const Json::Value tx = vertcoind->getrawtransaction(txo.substr(0,64), true); 353 | const Json::Value scriptHex = tx["vout"][stoi(txo.substr(64,8))]["scriptPubKey"]["hex"]; 354 | txoObj["script"] = scriptHex.asString(); 355 | } catch(const jsonrpc::JsonRpcException& e) { 356 | const std::string message(e.what()); 357 | session->close(400, message, {{"Content-Type","text/plain"},{"Content-Length", std::to_string(message.size())}}); 358 | cout << "Not found " << message << endl; 359 | return; 360 | } 361 | } 362 | 363 | 364 | 365 | if(raw != 0 && txoObj["spender"].is_string()) { 366 | try { 367 | const Json::Value tx = vertcoind->getrawtransaction(txoObj["spender"].get(), false); 368 | txoObj["spender"] = tx.asString(); 369 | } catch(const jsonrpc::JsonRpcException& e) { 370 | const std::string message(e.what()); 371 | session->close(400, message, {{"Content-Type","text/plain"},{"Content-Length", std::to_string(message.size())}}); 372 | cout << "Not found " << message << endl; 373 | return; 374 | } 375 | } 376 | 377 | if(raw == 0) { 378 | txoObj["txhash"] = txo.substr(0,64); 379 | } 380 | if(txHashOnly == 0 && raw == 0) { 381 | txoObj["vout"] = stoll(txo.substr(64,8)); 382 | txoObj["value"] = stoll(txo.substr(80)); 383 | } 384 | stringstream ssBlockTimeHeightKey; 385 | string blockTimeStr; 386 | ssBlockTimeHeightKey << "block-time-" << setw(8) << setfill('0') << block; 387 | s = this->db->Get(leveldb::ReadOptions(), ssBlockTimeHeightKey.str(), &blockTimeStr); 388 | txoObj["time"] = stoll(blockTimeStr); 389 | 390 | j.push_back(txoObj); 391 | } 392 | } 393 | assert(it->status().ok()); // Check for any errors found during the scan 394 | delete it; 395 | 396 | if(unconfirmed == 1) { 397 | // Add mempool transactions 398 | vector mempoolOutputs = mempoolMonitor->getTxos(request->get_path_parameter( "address" )); 399 | for (VtcBlockIndexer::TransactionOutput txo : mempoolOutputs) { 400 | json txoObj; 401 | txoObj["txhash"] = txo.txHash; 402 | txoObj["vout"] = txo.index; 403 | txoObj["value"] = txo.value; 404 | txoObj["block"] = 0; 405 | string spender = mempoolMonitor->outpointSpend(txo.txHash, txo.index); 406 | if(spender.compare("") != 0) { 407 | txoObj["spender"] = spender; 408 | } else { 409 | txoObj["spender"] = nullptr; 410 | } 411 | j.push_back(txoObj); 412 | } 413 | } 414 | 415 | string body = j.dump(); 416 | 417 | session->close( OK, body, { { "Content-Type", "application/json" }, { "Content-Length", std::to_string(body.size()) } } ); 418 | } 419 | 420 | void VtcBlockIndexer::HttpServer::outpointSpend( const shared_ptr< Session > session ) 421 | { 422 | json j; 423 | j["error"] = false; 424 | const auto request = session->get_request( ); 425 | int raw = stoi(request->get_query_parameter("raw","0")); 426 | int unconfirmed = stoi(request->get_query_parameter("unconfirmed","0")); 427 | 428 | long long vout = stoll(request->get_path_parameter( "vout", "0" )); 429 | string txid = request->get_path_parameter("txid", ""); 430 | stringstream txBlockKey; 431 | string txBlock; 432 | txBlockKey << "tx-" << txid << "-block"; 433 | leveldb::Status s = this->db->Get(leveldb::ReadOptions(), txBlockKey.str(), &txBlock); 434 | if(!s.ok()) { 435 | j["error"] = true; 436 | j["errorDescription"] = "Transaction ID not found"; 437 | } 438 | else 439 | { 440 | stringstream txoId; 441 | txoId << "txo-" << txid << "-" << setw(8) << setfill('0') << vout << "-spent"; 442 | cout << "Checking outpoint spent " << txoId.str() << endl; 443 | string spentTx; 444 | 445 | s = this->db->Get(leveldb::ReadOptions(), txoId.str(), &spentTx); 446 | j["spent"] = s.ok(); 447 | if(s.ok()) { 448 | j["spender"] = spentTx.substr(65, 64); 449 | 450 | string blockHeightStr; 451 | stringstream blockHashId; 452 | blockHashId << "block-hash-" << spentTx.substr(0,64); 453 | s = this->db->Get(leveldb::ReadOptions(), blockHashId.str(), &blockHeightStr); 454 | if(s.ok()) { 455 | j["height"] = stol(blockHeightStr); 456 | } 457 | } else if(unconfirmed != 0) { 458 | string mempoolSpend = mempoolMonitor->outpointSpend(txid, vout); 459 | if(mempoolSpend.compare("") != 0) { 460 | j["spent"] = true; 461 | j["spender"] = mempoolSpend; 462 | j["height"] = 0; 463 | } 464 | } 465 | 466 | if(raw != 0 && j["spender"].is_string()) { 467 | try { 468 | const Json::Value tx = vertcoind->getrawtransaction(j["spender"].get(), false); 469 | j["spenderRaw"] = tx.asString(); 470 | j["spender"] = nullptr; 471 | } catch(const jsonrpc::JsonRpcException& e) { 472 | const std::string message(e.what()); 473 | session->close(400, message, {{"Content-Type","text/plain"},{"Content-Length", std::to_string(message.size())}}); 474 | cout << "Not found " << message << endl; 475 | return; 476 | } 477 | } 478 | 479 | 480 | 481 | 482 | } 483 | 484 | string body = j.dump(); 485 | 486 | session->close( OK, body, { { "Content-Type", "application/json" }, { "Content-Length", std::to_string(body.size()) } } ); 487 | } 488 | 489 | 490 | void VtcBlockIndexer::HttpServer::outpointSpends( const shared_ptr< Session > session ) 491 | { 492 | const auto request = session->get_request( ); 493 | size_t content_length = 0; 494 | content_length = request->get_header( "Content-Length", 0); 495 | 496 | 497 | 498 | session->fetch( content_length, [ request, this ]( const shared_ptr< Session > session, const Bytes & body ) 499 | { 500 | const auto request = session->get_request( ); 501 | int raw = stoi(request->get_query_parameter("raw","0")); 502 | int unconfirmed = stoi(request->get_query_parameter("unconfirmed","0")); 503 | 504 | 505 | string content =string(body.begin(), body.end()); 506 | json output = json::array(); 507 | json input = json::parse(content); 508 | if(!input.is_null()) { 509 | for (auto& txo : input) { 510 | if(txo.is_object() && txo["txid"].is_string() && txo["vout"].is_number()) { 511 | stringstream txoId; 512 | txoId << "txo-" << txo["txid"].get() << "-" << setw(8) << setfill('0') << txo["vout"].get() << "-spent"; 513 | cout << "Checking outpoint spent " << txoId.str() << endl; 514 | 515 | json j; 516 | j["txid"] = txo["txid"]; 517 | j["vout"] = txo["vout"]; 518 | j["error"] = false; 519 | stringstream txBlockKey; 520 | string txBlock; 521 | txBlockKey << "tx-" << txo["txid"].get() << "-block"; 522 | leveldb::Status s = this->db->Get(leveldb::ReadOptions(), txBlockKey.str(), &txBlock); 523 | if(!s.ok()) { 524 | j["error"] = true; 525 | j["errorDescription"] = "Transaction ID not found"; 526 | } 527 | else 528 | { 529 | string spentTx; 530 | s = this->db->Get(leveldb::ReadOptions(), txoId.str(), &spentTx); 531 | if(s.ok()) { 532 | j["spender"] = spentTx.substr(65, 64); 533 | j["spent"] = true; 534 | string blockHeightStr; 535 | stringstream blockHashId; 536 | blockHashId << "block-hash-" << spentTx.substr(0,64); 537 | s = this->db->Get(leveldb::ReadOptions(), blockHashId.str(), &blockHeightStr); 538 | if(s.ok()) { 539 | j["height"] = stol(blockHeightStr); 540 | } 541 | } else if(unconfirmed != 0) { 542 | string mempoolSpend = mempoolMonitor->outpointSpend( txo["txid"].get(), txo["vout"].get()); 543 | if(mempoolSpend.compare("") != 0) { 544 | json j; 545 | j["spender"] = mempoolSpend; 546 | j["spent"] = true; 547 | j["height"] = 0; 548 | } else { 549 | j["spent"] = false; 550 | } 551 | } 552 | } 553 | 554 | if(raw != 0 && j["spender"].is_string()) { 555 | try { 556 | const Json::Value tx = vertcoind->getrawtransaction(j["spender"].get(), false); 557 | j["spenderRaw"] = tx.asString(); 558 | j["spender"] = nullptr; 559 | } catch(const jsonrpc::JsonRpcException& e) { 560 | const std::string message(e.what()); 561 | cout << "Not found " << message << endl; 562 | } 563 | } 564 | 565 | output.push_back(j); 566 | 567 | } 568 | } 569 | } 570 | 571 | string resultBody = output.dump(); 572 | session->close( OK, resultBody, { { "Content-Type", "application/json" }, { "Content-Length", std::to_string(resultBody.size()) } } ); 573 | } ); 574 | } 575 | 576 | void VtcBlockIndexer::HttpServer::sendRawTransaction( const shared_ptr< Session > session ) 577 | { 578 | const auto request = session->get_request( ); 579 | const size_t content_length = request->get_header( "Content-Length", 0); 580 | session->fetch( content_length, [ request, this ]( const shared_ptr< Session > session, const Bytes & body ) 581 | { 582 | const string rawtx = string(body.begin(), body.end()); 583 | 584 | try { 585 | const auto txid = vertcoind->sendrawtransaction(rawtx); 586 | 587 | session->close(OK, txid, {{"Content-Type","text/plain"}, {"Content-Length", std::to_string(txid.size())}}); 588 | } catch(const jsonrpc::JsonRpcException& e) { 589 | const std::string message(e.what()); 590 | session->close(400, message, {{"Content-Type","text/plain"},{"Content-Length", std::to_string(message.size())}}); 591 | } 592 | }); 593 | } 594 | 595 | void VtcBlockIndexer::HttpServer::run() 596 | { 597 | auto addressBalanceResource = make_shared< Resource >( ); 598 | addressBalanceResource->set_path( "/addressBalance/{address: .*}" ); 599 | addressBalanceResource->set_method_handler( "GET", bind( &VtcBlockIndexer::HttpServer::addressBalance, this, std::placeholders::_1) ); 600 | 601 | auto addressTxosResource = make_shared< Resource >( ); 602 | addressTxosResource->set_path( "/addressTxos/{address: .*}" ); 603 | addressTxosResource->set_method_handler( "GET", bind( &VtcBlockIndexer::HttpServer::addressTxos, this, std::placeholders::_1) ); 604 | 605 | auto addressTxosSinceBlockResource = make_shared< Resource >( ); 606 | addressTxosSinceBlockResource->set_path( "/addressTxosSince/{sinceBlock: ^[0-9]*$}/{address: .*}" ); 607 | addressTxosSinceBlockResource->set_method_handler( "GET", bind( &VtcBlockIndexer::HttpServer::addressTxos, this, std::placeholders::_1) ); 608 | 609 | auto getTransactionResource = make_shared(); 610 | getTransactionResource->set_path( "/getTransaction/{id: [0-9a-f]*}" ); 611 | getTransactionResource->set_method_handler("GET", bind(&VtcBlockIndexer::HttpServer::getTransaction, this, std::placeholders::_1) ); 612 | 613 | auto getTransactionProofResource = make_shared(); 614 | getTransactionProofResource->set_path( "/getTransactionProof/{id: [0-9a-f]*}" ); 615 | getTransactionProofResource->set_method_handler("GET", bind(&VtcBlockIndexer::HttpServer::getTransactionProof, this, std::placeholders::_1) ); 616 | 617 | auto outpointSpendResource = make_shared(); 618 | outpointSpendResource->set_path( "/outpointSpend/{txid: .*}/{vout: .*}" ); 619 | outpointSpendResource->set_method_handler("GET", bind(&VtcBlockIndexer::HttpServer::outpointSpend, this, std::placeholders::_1) ); 620 | 621 | auto outpointSpendsResource = make_shared(); 622 | outpointSpendsResource->set_path( "/outpointSpends" ); 623 | outpointSpendsResource->set_method_handler("POST", bind(&VtcBlockIndexer::HttpServer::outpointSpends, this, std::placeholders::_1) ); 624 | 625 | auto sendRawTransactionResource = make_shared(); 626 | sendRawTransactionResource->set_path( "/sendRawTransaction" ); 627 | sendRawTransactionResource->set_method_handler("POST", bind(&VtcBlockIndexer::HttpServer::sendRawTransaction, this, std::placeholders::_1) ); 628 | 629 | auto blocksResource = make_shared(); 630 | blocksResource->set_path( "/blocks" ); 631 | blocksResource->set_method_handler("GET", bind(&VtcBlockIndexer::HttpServer::getBlocks, this, std::placeholders::_1) ); 632 | 633 | auto syncResource = make_shared(); 634 | syncResource->set_path( "/sync" ); 635 | syncResource->set_method_handler("GET", bind(&VtcBlockIndexer::HttpServer::sync, this, std::placeholders::_1) ); 636 | 637 | 638 | auto settings = make_shared< Settings >( ); 639 | settings->set_port( 8888 ); 640 | settings->set_default_header( "Connection", "close" ); 641 | settings->set_default_header( "Access-Control-Allow-Origin", "*" ); 642 | 643 | Service service; 644 | service.publish( addressBalanceResource ); 645 | service.publish( addressTxosResource ); 646 | service.publish( addressTxosSinceBlockResource ); 647 | service.publish( getTransactionResource ); 648 | service.publish( getTransactionProofResource ); 649 | service.publish( outpointSpendResource ); 650 | service.publish( outpointSpendsResource ); 651 | service.publish( sendRawTransactionResource ); 652 | service.publish( blocksResource ); 653 | service.publish( syncResource ); 654 | service.start( settings ); 655 | } 656 | -------------------------------------------------------------------------------- /src/httpserver.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | /* VTC Blockindexer - A utility to build additional indexes to the 5 | Vertcoin blockchain by scanning and indexing the blockfiles 6 | downloaded by Vertcoin Core. 7 | 8 | Copyright (C) 2017 Gert-Jaap Glasbergen 9 | 10 | This program is free software: you can redistribute it and/or modify 11 | it under the terms of the GNU General Public License as published by 12 | the Free Software Foundation, either version 3 of the License, or 13 | (at your option) any later version. 14 | 15 | This program is distributed in the hope that it will be useful, 16 | but WITHOUT ANY WARRANTY; without even the implied warranty of 17 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 | GNU General Public License for more details. 19 | 20 | You should have received a copy of the GNU General Public License 21 | along with this program. If not, see . 22 | */ 23 | 24 | #include 25 | #include 26 | 27 | #include "leveldb/db.h" 28 | #include "leveldb/write_batch.h" 29 | 30 | #include "vertcoinrpc.h" 31 | #include "blockreader.h" 32 | #include "scriptsolver.h" 33 | #include "mempoolmonitor.h" 34 | 35 | using namespace std; 36 | using namespace restbed; 37 | 38 | namespace VtcBlockIndexer { 39 | 40 | /** 41 | * The HttpServer class contains the methods used to run the HTTP public interface for 42 | * querying the blockchain. 43 | */ 44 | 45 | class HttpServer { 46 | public: 47 | HttpServer(const shared_ptr db, const shared_ptr mempoolMonitor, string blocksDir); 48 | void run(); 49 | /* REST Api for returning the balance of a given address */ 50 | void addressBalance( const shared_ptr< Session > session ); 51 | 52 | /* REST Api for returning the TXOs on a given address */ 53 | void addressTxos( const shared_ptr< Session > session ); 54 | 55 | /* REST Api for returning the transaction details with a given hash */ 56 | void getTransaction(const shared_ptr session); 57 | 58 | /* REST Api for returning the transaction proof for a given hash */ 59 | void getTransactionProof(const shared_ptr session); 60 | 61 | /* REST Api for returning if a given outpoint is spent (and if so, which TX spends it) */ 62 | void outpointSpend( const shared_ptr< Session > session ); 63 | 64 | /* REST Api for returning if a collection of given outpoints is spent (and if so, which TX spends it) */ 65 | void outpointSpends( const shared_ptr< Session > session ); 66 | 67 | /* REST Api for returning list of blocks */ 68 | void getBlocks( const shared_ptr< Session > session ); 69 | 70 | /* REST Api for returning sync status */ 71 | void sync( const shared_ptr< Session > session ); 72 | 73 | /* REST Api for sending a hex transaction on the VTC p2p network*/ 74 | void sendRawTransaction( const shared_ptr< Session > session ); 75 | 76 | private: 77 | shared_ptr db; 78 | unique_ptr vertcoind; 79 | unique_ptr httpClient; 80 | unique_ptr blockReader; 81 | unique_ptr scriptSolver; 82 | shared_ptr mempoolMonitor; 83 | /** Directory containing the blocks 84 | */ 85 | string blocksDir; 86 | 87 | }; 88 | } -------------------------------------------------------------------------------- /src/main.cpp: -------------------------------------------------------------------------------- 1 | /* VTC Blockindexer - A utility to build additional indexes to the 2 | Vertcoin blockchain by scanning and indexing the blockfiles 3 | downloaded by Vertcoin Core. 4 | 5 | Copyright (C) 2017 Gert-Jaap Glasbergen 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include "leveldb/db.h" 31 | #include "leveldb/write_batch.h" 32 | #include "leveldb/filter_policy.h" 33 | #include "leveldb/cache.h" 34 | #include "utility.h" 35 | #include "blockchaintypes.h" 36 | #include "httpserver.h" 37 | #include "mempoolmonitor.h" 38 | #include "blockfilewatcher.h" 39 | #include 40 | #include "cxxopts.hpp" 41 | #include "coinparams.h" 42 | 43 | using namespace std; 44 | 45 | 46 | shared_ptr database; 47 | shared_ptr httpServer; 48 | shared_ptr blockFileWatcher; 49 | shared_ptr mempoolMonitor; 50 | 51 | void runBlockfileWatcher() { 52 | cout << "Starting blockfile watcher..." << endl; 53 | blockFileWatcher->startWatcher(); 54 | } 55 | 56 | void runMempoolMonitor() { 57 | cout << "Starting mempool monitor..." << endl; 58 | mempoolMonitor->startWatcher(); 59 | } 60 | 61 | void openDatabase(std::string indexDir) { 62 | leveldb::DB* db; 63 | leveldb::Options options; 64 | options.create_if_missing = true; 65 | options.block_cache = leveldb::NewLRUCache(300 * 1024 * 1024); 66 | options.filter_policy = leveldb::NewBloomFilterPolicy(10); 67 | leveldb::Status status = leveldb::DB::Open(options, indexDir, &db); 68 | assert(status.ok()); 69 | database.reset(db); 70 | } 71 | 72 | 73 | int main(int argc, char* argv[]) { 74 | cxxopts::Options options("vtc_indexer", "Block file indexer for blockchains"); 75 | 76 | options.add_options() 77 | ("coinParams", "Coin parameters file", cxxopts::value()) 78 | ("indexDir", "Directory to save the indexes [Default: /index]", cxxopts::value()->default_value("/index")) 79 | ("blocksDir", "Directory where the block files are located [Default: /blocks]", cxxopts::value()->default_value("/blocks")) 80 | ; 81 | 82 | options.parse(argc, argv); 83 | 84 | if(options.count("coinParams") == 0) { 85 | cerr << "Coin parameters file not specified. Exiting." << endl; 86 | return -1; 87 | } 88 | 89 | // Open the database 90 | openDatabase(options["indexDir"].as()); 91 | 92 | // Read coin parameters 93 | VtcBlockIndexer::CoinParams::readFromFile(options["coinParams"].as()); 94 | 95 | // Start memory pool monitor on a separate thread 96 | mempoolMonitor = make_shared(); 97 | std::thread mempoolThread(runMempoolMonitor); 98 | 99 | // Start blockfile watcher on separate thread 100 | blockFileWatcher.reset(new VtcBlockIndexer::BlockFileWatcher(options["blocksDir"].as(), database, mempoolMonitor)); 101 | std::thread watcherThread(runBlockfileWatcher); 102 | 103 | // Start webserver on main thread. 104 | httpServer.reset(new VtcBlockIndexer::HttpServer(database, mempoolMonitor, string(argv[1]))); 105 | httpServer->run(); 106 | } 107 | -------------------------------------------------------------------------------- /src/membuf.h: -------------------------------------------------------------------------------- 1 | /* VTC Blockindexer - A utility to build additional indexes to the 2 | Vertcoin blockchain by scanning and indexing the blockfiles 3 | downloaded by Vertcoin Core. 4 | 5 | Copyright (C) 2017 Gert-Jaap Glasbergen 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | 22 | #ifndef MEMBUF_H_INCLUDED 23 | #define MEMBUF_H_INCLUDED 24 | 25 | #include 26 | 27 | class membuf : public std::basic_streambuf { 28 | public: 29 | membuf(const uint8_t *p, size_t l) { 30 | setg((char*)p, (char*)p, (char*)p + l); 31 | } 32 | }; 33 | 34 | class memstream : public std::istream { 35 | public: 36 | memstream(const uint8_t *p, size_t l) : 37 | std::istream(&_buffer), 38 | _buffer(p, l) { 39 | rdbuf(&_buffer); 40 | } 41 | 42 | private: 43 | membuf _buffer; 44 | }; 45 | 46 | #endif //MEMBUF_H_INCLUDED -------------------------------------------------------------------------------- /src/mempoolmonitor.cpp: -------------------------------------------------------------------------------- 1 | /* VTC Blockindexer - A utility to build additional indexes to the 2 | Vertcoin blockchain by scanning and indexing the blockfiles 3 | downloaded by Vertcoin Core. 4 | 5 | Copyright (C) 2017 Gert-Jaap Glasbergen 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | #include "mempoolmonitor.h" 21 | #include "utility.h" 22 | #include "scriptsolver.h" 23 | #include "blockchaintypes.h" 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include "byte_array_buffer.h" 29 | using namespace std; 30 | 31 | // This map keeps the memorypool transactions deserialized in memory. 32 | 33 | 34 | VtcBlockIndexer::MempoolMonitor::MempoolMonitor() { 35 | httpClient.reset(new jsonrpc::HttpClient("http://middleware:middleware@" + std::string(std::getenv("COIND_HOST")) + ":8332")); 36 | vertcoind.reset(new VertcoinClient(*httpClient)); 37 | blockReader.reset(new VtcBlockIndexer::BlockReader("")); 38 | scriptSolver.reset(new VtcBlockIndexer::ScriptSolver()); 39 | } 40 | 41 | void VtcBlockIndexer::MempoolMonitor::startWatcher() { 42 | while(true) { 43 | try { 44 | const Json::Value mempool = vertcoind->getrawmempool(); 45 | for ( uint index = 0; index < mempool.size(); ++index ) 46 | { 47 | if(mempoolTransactions.find(mempool[index].asString()) == mempoolTransactions.end()) { 48 | const Json::Value rawTx = vertcoind->getrawtransaction(mempool[index].asString(), false); 49 | std::vector rawTxBytes = VtcBlockIndexer::Utility::hexToBytes(rawTx.asString()); 50 | 51 | byte_array_buffer streambuf(&rawTxBytes[0], rawTxBytes.size()); 52 | std::istream stream(&streambuf); 53 | 54 | VtcBlockIndexer::Transaction tx = blockReader->readTransaction(stream); 55 | mempoolTransactions[mempool[index].asString()] = tx; 56 | 57 | 58 | for(VtcBlockIndexer::TransactionOutput out : tx.outputs) { 59 | out.txHash = tx.txHash; 60 | vector addresses = scriptSolver->getAddressesFromScript(out.script); 61 | for(string address : addresses) { 62 | if(addressMempoolTransactions.find(address) == addressMempoolTransactions.end()) 63 | { 64 | addressMempoolTransactions[address] = {}; 65 | } 66 | addressMempoolTransactions[address].push_back(out); 67 | } 68 | } 69 | } 70 | } 71 | } catch(const jsonrpc::JsonRpcException& e) { 72 | const std::string message(e.what()); 73 | cout << "Error reading mempool " << message << endl; 74 | } 75 | 76 | std::this_thread::sleep_for(std::chrono::seconds(1)); 77 | } 78 | } 79 | 80 | string VtcBlockIndexer::MempoolMonitor::outpointSpend(string txid, uint32_t vout) { 81 | for (auto kvp : mempoolTransactions) { 82 | VtcBlockIndexer::Transaction tx = kvp.second; 83 | for (VtcBlockIndexer::TransactionInput txi : tx.inputs) { 84 | if(txi.txHash.compare(txid) == 0 && txi.txoIndex == vout) { 85 | return tx.txHash; 86 | } 87 | } 88 | } 89 | return ""; 90 | } 91 | 92 | vector VtcBlockIndexer::MempoolMonitor::getTxos(std::string address) { 93 | if(addressMempoolTransactions.find(address) == addressMempoolTransactions.end()) 94 | { 95 | return {}; 96 | } 97 | return vector(addressMempoolTransactions[address]); 98 | } 99 | 100 | void VtcBlockIndexer::MempoolMonitor::transactionIndexed(std::string txid) { 101 | if(mempoolTransactions.find(txid) != mempoolTransactions.end()) { 102 | mempoolTransactions.erase(txid); 103 | 104 | unordered_map> changedMempoolAddressTxes; 105 | for (auto kvp : addressMempoolTransactions) { 106 | 107 | vector newVector = {}; 108 | bool itemsRemoved = false; 109 | for (VtcBlockIndexer::TransactionOutput txo : kvp.second) { 110 | if(txo.txHash.compare(txid) != 0) { 111 | newVector.push_back(txo); 112 | } else { 113 | itemsRemoved = true; 114 | } 115 | } 116 | 117 | if(itemsRemoved) { 118 | changedMempoolAddressTxes[kvp.first] = newVector; 119 | } 120 | } 121 | 122 | for (auto kvp : changedMempoolAddressTxes) { 123 | addressMempoolTransactions[kvp.first] = kvp.second; 124 | } 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/mempoolmonitor.h: -------------------------------------------------------------------------------- 1 | /* VTC Blockindexer - A utility to build additional indexes to the 2 | Vertcoin blockchain by scanning and indexing the blockfiles 3 | downloaded by Vertcoin Core. 4 | 5 | Copyright (C) 2017 Gert-Jaap Glasbergen 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #include "vertcoinrpc.h" 22 | #include 23 | #include 24 | #include "blockreader.h" 25 | #include "scriptsolver.h" 26 | #include 27 | #ifndef MEMPOOLMONITOR_H_INCLUDED 28 | #define MEMPOOLMONITOR_H_INCLUDED 29 | 30 | using namespace std; 31 | 32 | namespace VtcBlockIndexer { 33 | 34 | /** 35 | * The BlockFileWatcher class provides methods to watch and scan a blocks directory 36 | * and process the blockfiles when changes occur. 37 | */ 38 | 39 | class MempoolMonitor { 40 | public: 41 | /** Constructs a MempoolMonitor instance 42 | */ 43 | MempoolMonitor(); 44 | 45 | /** Starts watching the mempool for new transactions */ 46 | void startWatcher(); 47 | 48 | /** Notify a transaction has been indexed - remove it from the mempool */ 49 | void transactionIndexed(string txid); 50 | 51 | /** Returns the spender txid if an outpoint is spent */ 52 | string outpointSpend(string txid, uint32_t vout); 53 | 54 | /** Returns TXOs in the memorypool matching an address */ 55 | vector getTxos(string address); 56 | 57 | private: 58 | unique_ptr vertcoind; 59 | unique_ptr httpClient; 60 | unordered_map mempoolTransactions; 61 | unordered_map> addressMempoolTransactions; 62 | unique_ptr blockReader; 63 | unique_ptr scriptSolver; 64 | }; 65 | 66 | } 67 | 68 | #endif // MEMPOOLMONITOR_H_INCLUDED -------------------------------------------------------------------------------- /src/scriptsolver.cpp: -------------------------------------------------------------------------------- 1 | /* VTC Blockindexer - A utility to build additional indexes to the 2 | Vertcoin blockchain by scanning and indexing the blockfiles 3 | downloaded by Vertcoin Core. 4 | 5 | Copyright (C) 2017 Gert-Jaap Glasbergen 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | #include "scriptsolver.h" 21 | #include "blockchaintypes.h" 22 | #include "utility.h" 23 | #include 24 | #include 25 | #include "leveldb/db.h" 26 | //#include "hashing.h" 27 | #include 28 | #include 29 | 30 | using namespace std; 31 | VtcBlockIndexer::ScriptSolver::ScriptSolver() { 32 | 33 | } 34 | 35 | vector VtcBlockIndexer::ScriptSolver::getAddressesFromScript(vector script) { 36 | vector addresses; 37 | uint64_t scriptSize = script.size(); 38 | bool parsed = false; 39 | 40 | // The most common output script type that pays to hash160(pubKey) 41 | if( 42 | 43 | (25==scriptSize && 44 | 0x76==script.at(0) && // OP_DUP 45 | 0xA9==script.at(1) && // OP_HASH160 46 | 20==script.at(2) && // OP_PUSHDATA(20) 47 | 48 | (0x88==script.at(scriptSize-2) && // OP_EQUALVERIFY 49 | 0xAC==script.at(scriptSize-1))) // OP_CHECKSIG 50 | 51 | || 52 | // scripts appended with OP_NOP1. Since OP_NOP1 does nothing, this should still be valid. 53 | 54 | (25==scriptSize && 55 | 0x76==script.at(0) && // OP_DUP 56 | 0xA9==script.at(1) && // OP_HASH160 57 | 20==script.at(2) && // OP_PUSHDATA(20) 58 | 59 | (0x88==script.at(scriptSize-2) && // OP_EQUALVERIFY 60 | 0xB0==script.at(scriptSize-1))) // OP_NOP1 61 | 62 | || 63 | 64 | // scripts appended with OP_NOP. Since OP_NOP does nothing, this should still be valid. 65 | 66 | (26==scriptSize && 67 | 0x76==script.at(0) && // OP_DUP 68 | 0xA9==script.at(1) && // OP_HASH160 69 | 20==script.at(2) && // OP_PUSHDATA(20) 70 | 71 | 72 | (0x88==script.at(scriptSize-3) && // OP_EQUALVERIFY 73 | 0xAC==script.at(scriptSize-2) && // OP_CHECKSIG 74 | 0x61==script.at(scriptSize-1))) // OP_NOP 75 | 76 | 77 | 78 | ) { 79 | addresses.push_back(VtcBlockIndexer::Utility::ripeMD160ToP2PKAddress(vector(&script[3], &script[23]))); 80 | parsed = true; 81 | } 82 | 83 | // Output script commonly found in block reward TX, that pays to an explicit pubKey 84 | if( 85 | 67==scriptSize && 86 | 65==script.at(0) && // OP_PUSHDATA(65) 87 | 0xAC==script.at(scriptSize-1) // OP_CHECKSIG 88 | 89 | ) { 90 | addresses.push_back(VtcBlockIndexer::Utility::publicKeyToAddress(vector(&script[1], &script[66]))); 91 | parsed = true; 92 | } 93 | 94 | // Pay to compressed pubkey script 95 | if( 96 | 35==scriptSize && 97 | 0x21==script.at(0) && // OP_PUSHDATA(33) 98 | 0xAC==script.at(scriptSize-1) // OP_CHECKSIG 99 | ) { 100 | addresses.push_back(VtcBlockIndexer::Utility::publicKeyToAddress(vector(&script[1], &script[34]))); 101 | parsed = true; 102 | } 103 | 104 | // P2WSH 105 | if( 106 | 22 == scriptSize && 107 | 0x00 == script.at(0) && 108 | 0x14 == script.at(1) 109 | ) { 110 | addresses.push_back(VtcBlockIndexer::Utility::bech32Address(vector(&script[2], &script[22]))); 111 | parsed = true; 112 | } 113 | 114 | // P2WPKH 115 | if( 116 | 34 == scriptSize && 117 | 0x00 == script.at(0) && 118 | 0x20 == script.at(1) 119 | ) { 120 | addresses.push_back(VtcBlockIndexer::Utility::bech32Address(vector(&script[2], &script[34]))); 121 | parsed = true; 122 | } 123 | 124 | // NULLDATA script. starts with OP_RETURN followed by arbitrary data and no further opcodes. Not needed to be stored in UTXO database. So Ignore. 125 | if( 126 | scriptSize > 0 && 127 | 0x6A == script.at(0) 128 | ) { 129 | uint32_t pos = 1; 130 | 131 | bool foundOpcodes = false; 132 | while(pos < script.size()) { 133 | if(script.at(pos) >= 0x01 && script.at(pos) <= 0x4B) { 134 | pos += script.at(pos) + 1; 135 | } else { 136 | foundOpcodes = true; 137 | break; 138 | } 139 | } 140 | 141 | if(!foundOpcodes) 142 | parsed = true; 143 | } 144 | 145 | // A modern output script type, that pays to hash160(script) 146 | if( 147 | 23 == scriptSize && 148 | 0xA9==script.at(0) && // OP_HASH160 149 | 20==script.at(1) && // OP_PUSHDATA(20) 150 | 0x87==script.at(scriptSize-1) // OP_EQUAL 151 | 152 | 153 | ) { 154 | addresses.push_back(VtcBlockIndexer::Utility::ripeMD160ToP2SHAddress(vector(&script[2], &script[22]))); 155 | parsed = true; 156 | } 157 | 158 | // OP_PUSHDATA(32) + data only (Found in litecoin chain - nonstandard script) 159 | // Public block explorers show these as "unknown" (https://bchain.info/LTC/tx/265278e51d1b29cdce906a858251b7ce15e2dab09de7dede0acb4c629f780b91) 160 | if( 161 | 162 | 33==scriptSize && 163 | 0x20==script.at(0) 164 | 165 | 166 | ) { 167 | 168 | parsed = true; 169 | } 170 | 171 | // OP_PUSHDATA(36) + data only (Found in litecoin chain - nonstandard script) 172 | // Public block explorers show these as "unknown" (http://explorer.litecoin.net/tx/936e8ed1cfca736320fdced61c2d03886b232497ea975e41d101f1d83bb74c44) 173 | if( 174 | 175 | 37==scriptSize && 176 | 0x24==script.at(0) 177 | 178 | 179 | ) { 180 | 181 | parsed = true; 182 | } 183 | 184 | // OP_PUSHDATA(20) + data only. Unparseable (BTC) 185 | // Public block explorers show these as "unknown" (https://blockchain.info/tx/b8fd633e7713a43d5ac87266adc78444669b987a56b3a65fb92d58c2c4b0e84d) 186 | if( 187 | 188 | 24==scriptSize && 189 | 0x14==script.at(0) 190 | 191 | 192 | ) { 193 | 194 | parsed = true; 195 | } 196 | 197 | 198 | // Unknown (seems malformed) output script found on Litecoin in p2pool blocks 199 | // For example https://bchain.info/LTC/tx/8f1220670b5d4ade8f9c6a82fde3d88a28d2e1c290f2edc6d7a7a13aa0352fc7 200 | if( 201 | 6 == scriptSize && 202 | 0x73==script.at(0) && // OP_IFDUP 203 | 0x63==script.at(1) && // OP_IF 204 | 0x72==script.at(2) && // OP_2SWAP 205 | 0x69==script.at(3) && // OP_VERIFY 206 | 0x70==script.at(4) && // OP_2OVER 207 | 0x74==script.at(5) // OP_DEPTH 208 | 209 | 210 | ) { 211 | 212 | parsed = true; 213 | } 214 | 215 | if(isMultiSig(script)) { 216 | uint32_t pos = 1; 217 | while(pos < script.size()-2) { 218 | if(script.at(pos) == 0x21) { 219 | addresses.push_back(VtcBlockIndexer::Utility::publicKeyToAddress(vector(&script[pos+1], &script[pos+34]))); 220 | pos += 34; 221 | parsed = true; 222 | } 223 | else if(script.at(pos) == 0x41) { 224 | addresses.push_back(VtcBlockIndexer::Utility::publicKeyToAddress(vector(&script[pos+1], &script[pos+66]))); 225 | pos += 66; 226 | parsed = true; 227 | } 228 | else 229 | { 230 | pos = script.size(); 231 | } 232 | } 233 | 234 | } 235 | 236 | // A challenge: anyone who can find X such that 0==RIPEMD160(X) stands to earn a bunch of coins 237 | 238 | if( 239 | 0x76==script[0] && // OP_DUP 240 | 0xA9==script[1] && // OP_HASH160 241 | 0x00==script[2] && // OP_0 242 | 0x88==script[3] && // OP_EQUALVERIFY 243 | 0xAC==script[4] // OP_CHECKSIG 244 | ) { 245 | parsed = true; 246 | } 247 | 248 | 249 | 250 | 251 | if(!parsed) { 252 | stringstream ssScript; 253 | for(uint64_t i = 0; i < scriptSize; i++) 254 | { 255 | ssScript << hex << setw(2) << setfill('0') << static_cast(script.at(i)); 256 | } 257 | cout << "Unrecognized script : [" << ssScript.str() << "]" << endl; 258 | } 259 | 260 | return addresses; 261 | /* 262 | 263 | 264 | int m = 0; 265 | int n = 0; 266 | std::vector addresses; 267 | if( 268 | isMultiSig( 269 | m, 270 | n, 271 | addresses, 272 | script, 273 | scriptSize 274 | ) 275 | ) { 276 | packMultiSig(pubKeyHash, addresses, m, n); 277 | addrType[0] = 8; 278 | return 4; 279 | } 280 | 281 | // Broken output scripts that were created by p2pool for a while 282 | if( 283 | 0x73==script[0] && // OP_IFDUP 284 | 0x63==script[1] && // OP_IF 285 | 0x72==script[2] && // OP_2SWAP 286 | 0x69==script[3] && // OP_VERIFY 287 | 0x70==script[4] && // OP_2OVER 288 | 0x74==script[5] // OP_DEPTH 289 | ) { 290 | return -2; 291 | } 292 | 293 | // A non-functional "comment" script 294 | if(isCommentScript(script, scriptSize)) { 295 | return -3; 296 | } 297 | 298 | 299 | return addresses;*/ 300 | } 301 | 302 | bool VtcBlockIndexer::ScriptSolver::isMultiSig(vector script) { 303 | if(script.size() == 0) return false; 304 | return (script.at(script.size()-1) == 0xAE); 305 | } 306 | 307 | int VtcBlockIndexer::ScriptSolver::requiredSignatures(vector script) { 308 | if(!isMultiSig(script)) return -1; 309 | 310 | return (int)script.at(0); 311 | } -------------------------------------------------------------------------------- /src/scriptsolver.h: -------------------------------------------------------------------------------- 1 | /* VTC Blockindexer - A utility to build additional indexes to the 2 | Vertcoin blockchain by scanning and indexing the blockfiles 3 | downloaded by Vertcoin Core. 4 | 5 | Copyright (C) 2017 Gert-Jaap Glasbergen 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | 22 | #ifndef SCRIPTSOLVER_H_INCLUDED 23 | #define SCRIPTSOLVER_H_INCLUDED 24 | 25 | #include 26 | #include 27 | 28 | #include "blockchaintypes.h" 29 | using namespace std; 30 | 31 | namespace VtcBlockIndexer { 32 | 33 | /** 34 | * The ScriptSolver class provides methods to parse the bitcoin script language used in 35 | * transaction outputs and determine the public keys / addresses that can spend it. 36 | */ 37 | 38 | class ScriptSolver { 39 | public: 40 | /** Constructs a BlockIndexer instance using the given block data directory 41 | */ 42 | ScriptSolver(); 43 | 44 | /** Read addresses from script 45 | */ 46 | vector getAddressesFromScript(vector scriptString); 47 | 48 | /** Returns if the script is multisig 49 | */ 50 | bool isMultiSig(vector scriptString); 51 | 52 | /** Returns the number of required signatures 53 | */ 54 | int requiredSignatures(vector scriptString); 55 | }; 56 | 57 | } 58 | 59 | #endif // SCRIPTSOLVER_H_INCLUDED -------------------------------------------------------------------------------- /src/utility.cpp: -------------------------------------------------------------------------------- 1 | #include "utility.h" 2 | /* VTC Blockindexer - A utility to build additional indexes to the 3 | Vertcoin blockchain by scanning and indexing the blockfiles 4 | downloaded by Vertcoin Core. 5 | 6 | Copyright (C) 2017 Gert-Jaap Glasbergen 7 | 8 | This program is free software: you can redistribute it and/or modify 9 | it under the terms of the GNU General Public License as published by 10 | the Free Software Foundation, either version 3 of the License, or 11 | (at your option) any later version. 12 | 13 | This program is distributed in the hope that it will be useful, 14 | but WITHOUT ANY WARRANTY; without even the implied warranty of 15 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 16 | GNU General Public License for more details. 17 | 18 | You should have received a copy of the GNU General Public License 19 | along with this program. If not, see . 20 | */ 21 | 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include "crypto/ripemd160.h" 30 | #include "crypto/bech32.h" 31 | #include "coinparams.h" 32 | #include /* assert */ 33 | 34 | using namespace std; 35 | 36 | namespace 37 | { 38 | /* Global secp256k1_context object used for verification. */ 39 | secp256k1_context* secp256k1_context_verify = NULL; 40 | 41 | typedef std::vector data; 42 | 43 | template 44 | bool convertbits(data& out, const data& in) { 45 | int acc = 0; 46 | int bits = 0; 47 | const int maxv = (1 << tobits) - 1; 48 | const int max_acc = (1 << (frombits + tobits - 1)) - 1; 49 | for (size_t i = 0; i < in.size(); ++i) { 50 | int value = in[i]; 51 | acc = ((acc << frombits) | value) & max_acc; 52 | bits += frombits; 53 | while (bits >= tobits) { 54 | bits -= tobits; 55 | out.push_back((acc >> bits) & maxv); 56 | } 57 | } 58 | if (pad) { 59 | if (bits) out.push_back((acc << (tobits - bits)) & maxv); 60 | } else if (bits >= frombits || ((acc << (tobits - bits)) & maxv)) { 61 | return false; 62 | } 63 | return true; 64 | } 65 | } 66 | 67 | vector VtcBlockIndexer::Utility::sha256(vector input) 68 | { 69 | unique_ptr hash(new unsigned char [SHA256_DIGEST_LENGTH]); 70 | 71 | SHA256_CTX sha256; 72 | SHA256_Init(&sha256); 73 | SHA256_Update(&sha256, &input[0], input.size()); 74 | SHA256_Final(hash.get(), &sha256); 75 | return vector(hash.get(), hash.get()+SHA256_DIGEST_LENGTH); 76 | } 77 | 78 | std::string VtcBlockIndexer::Utility::hashToHex(vector hash) { 79 | stringstream ss; 80 | for(uint i = 0; i < hash.size(); i++) 81 | { 82 | ss << hex << setw(2) << setfill('0') << (int)hash.at(i); 83 | } 84 | return ss.str(); 85 | } 86 | 87 | std::string VtcBlockIndexer::Utility::hashToReverseHex(vector hash) { 88 | if(hash.size() == 0) return ""; 89 | stringstream ss; 90 | for(uint i = hash.size(); i-- > 0;) 91 | { 92 | ss << hex << setw(2) << setfill('0') << (int)hash.at(i); 93 | } 94 | return ss.str(); 95 | } 96 | 97 | void VtcBlockIndexer::Utility::initECCContextIfNeeded() { 98 | if(secp256k1_context_verify == NULL) { 99 | secp256k1_context_verify = secp256k1_context_create(SECP256K1_FLAGS_TYPE_CONTEXT | SECP256K1_FLAGS_BIT_CONTEXT_VERIFY); 100 | } 101 | } 102 | 103 | VtcBlockIndexer::Utility::~Utility() { 104 | if(secp256k1_context_verify != NULL) { 105 | secp256k1_context_destroy(secp256k1_context_verify); 106 | secp256k1_context_verify = NULL; 107 | } 108 | } 109 | 110 | vector VtcBlockIndexer::Utility::decompressPubKey(vector compressedKey) { 111 | 112 | initECCContextIfNeeded(); 113 | secp256k1_pubkey pubkey; 114 | if (!secp256k1_ec_pubkey_parse(secp256k1_context_verify, &pubkey, &compressedKey[0], 33)) { 115 | return {}; 116 | } 117 | unique_ptr pub(new unsigned char[65]); 118 | size_t publen = 65; 119 | secp256k1_ec_pubkey_serialize(secp256k1_context_verify, pub.get(), &publen, &pubkey, SECP256K1_EC_UNCOMPRESSED); 120 | 121 | 122 | vector returnValue (pub.get(), pub.get() + 65); 123 | return returnValue; 124 | } 125 | 126 | 127 | string VtcBlockIndexer::Utility::publicKeyToAddress(vector publicKey) { 128 | vector hashedKey = sha256(publicKey); 129 | vector ripeMD = ripeMD160(hashedKey); 130 | 131 | return ripeMD160ToP2PKAddress(ripeMD); 132 | } 133 | 134 | string VtcBlockIndexer::Utility::ripeMD160ToP2PKAddress(vector ripeMD) { 135 | return ripeMD160ToAddress(VtcBlockIndexer::CoinParams::p2pkhVersion, ripeMD); 136 | } 137 | string VtcBlockIndexer::Utility::ripeMD160ToP2SHAddress(vector ripeMD) { 138 | return ripeMD160ToAddress(VtcBlockIndexer::CoinParams::p2shVersion, ripeMD); 139 | } 140 | 141 | string VtcBlockIndexer::Utility::ripeMD160ToAddress(unsigned char versionByte, vector ripeMD) { 142 | ripeMD.insert(ripeMD.begin(), versionByte); 143 | vector doubleHashedRipeMD = sha256(sha256(ripeMD)); 144 | for(int i = 0; i < 4; i++) { 145 | ripeMD.push_back(doubleHashedRipeMD.at(i)); 146 | } 147 | 148 | string returnValue = base58(ripeMD); 149 | return returnValue; 150 | 151 | } 152 | 153 | vector VtcBlockIndexer::Utility::hexToBytes(const std::string hex) { 154 | vector bytes; 155 | 156 | for (unsigned int i = 0; i < hex.length(); i += 2) { 157 | string byteString = hex.substr(i, 2); 158 | unsigned char byte = (unsigned char) strtol(byteString.c_str(), NULL, 16); 159 | bytes.push_back(byte); 160 | } 161 | 162 | return bytes; 163 | } 164 | 165 | vector VtcBlockIndexer::Utility::ripeMD160(vector in) { 166 | unsigned char hash[CRIPEMD160::OUTPUT_SIZE]; 167 | CRIPEMD160().Write(in.data(), in.size()).Finalize(hash); 168 | return vector(hash, hash + CRIPEMD160::OUTPUT_SIZE); 169 | } 170 | 171 | static const char* pszBase58 = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz"; 172 | 173 | std::string VtcBlockIndexer::Utility::base58(vector in) 174 | { 175 | unsigned char* pbegin = &in[0]; 176 | unsigned char* pend = &in[0] + in.size(); 177 | 178 | // Skip & count leading zeroes. 179 | int zeroes = 0; 180 | int length = 0; 181 | while (pbegin != pend && *pbegin == 0) { 182 | pbegin++; 183 | zeroes++; 184 | } 185 | 186 | 187 | // Allocate enough space in big-endian base58 representation. 188 | int size = (in.size()-zeroes) * 138 / 100 + 1; // log(256) / log(58), rounded up. 189 | 190 | std::vector b58(size); 191 | // Process the bytes. 192 | while (pbegin != pend) { 193 | int carry = *pbegin; 194 | int i = 0; 195 | // Apply "b58 = b58 * 256 + ch". 196 | for (std::vector::reverse_iterator it = b58.rbegin(); (carry != 0 || i < length) && (it != b58.rend()); it++, i++) { 197 | carry += 256 * (*it); 198 | *it = carry % 58; 199 | carry /= 58; 200 | } 201 | 202 | assert(carry == 0); 203 | length = i; 204 | pbegin++; 205 | } 206 | // Skip leading zeroes in base58 result. 207 | std::vector::iterator it = b58.begin() + (size - length); 208 | while (it != b58.end() && *it == 0) 209 | it++; 210 | // Translate the result into a string. 211 | std::string str; 212 | str.reserve(zeroes + (b58.end() - it)); 213 | str.assign(zeroes, '1'); 214 | while (it != b58.end()) 215 | str += pszBase58[*(it++)]; 216 | return str; 217 | } 218 | 219 | string VtcBlockIndexer::Utility::bech32Address(vector in) { 220 | vector enc; 221 | enc.push_back(0); // witness version 222 | if(convertbits<8, 5, true>(enc, in)) { 223 | return bech32::Encode(VtcBlockIndexer::CoinParams::bech32Prefix, enc); 224 | } 225 | else{ 226 | return ""; 227 | } 228 | } 229 | -------------------------------------------------------------------------------- /src/utility.h: -------------------------------------------------------------------------------- 1 | /* VTC Blockindexer - A utility to build additional indexes to the 2 | Vertcoin blockchain by scanning and indexing the blockfiles 3 | downloaded by Vertcoin Core. 4 | 5 | Copyright (C) 2017 Gert-Jaap Glasbergen 6 | 7 | This program is free software: you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation, either version 3 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program. If not, see . 19 | */ 20 | 21 | #include 22 | #include 23 | 24 | using namespace std; 25 | 26 | namespace VtcBlockIndexer { 27 | 28 | /** 29 | * The Utility class provides methods to perform various cryptographic operations 30 | */ 31 | 32 | class Utility { 33 | public: 34 | /** Calculates a single SHA-256 hash over the input 35 | * 36 | * @param input the value to hash 37 | */ 38 | static vector sha256(vector input); 39 | static string hashToHex(vector hash); 40 | static string hashToReverseHex(vector hash); 41 | static vector decompressPubKey(vector compressedKey); 42 | static string publicKeyToAddress(vector publicKey); 43 | static vector ripeMD160(vector in); 44 | static string base58(vector in); 45 | static string ripeMD160ToP2PKAddress(vector ripeMD); 46 | static string ripeMD160ToP2SHAddress(vector ripeMD); 47 | static string bech32Address(vector in); 48 | static vector hexToBytes(string hex); 49 | ~Utility(); 50 | 51 | private: 52 | static string ripeMD160ToAddress(unsigned char versionByte, vector ripeMD); 53 | static void initECCContextIfNeeded(); 54 | Utility() {} 55 | }; 56 | } 57 | -------------------------------------------------------------------------------- /src/vertcoinrpc.h: -------------------------------------------------------------------------------- 1 | #ifndef VERTCOINRPC_INCLUDED_ 2 | #define VERTCOINRPC_INCLUDED_ 3 | 4 | #include 5 | 6 | #include 7 | 8 | namespace VtcBlockIndexer { 9 | class VertcoinClient : public jsonrpc::Client { 10 | public: 11 | VertcoinClient(jsonrpc::IClientConnector &conn, 12 | jsonrpc::clientVersion_t type 13 | = jsonrpc::JSONRPC_CLIENT_V1) : 14 | jsonrpc::Client(conn, type) {} 15 | 16 | Json::Value getrawtransaction(const std::string& id, const bool verbose) 17 | throw (jsonrpc::JsonRpcException) { 18 | Json::Value p; 19 | p.append(id); 20 | p.append(verbose); 21 | const Json::Value result = this->CallMethod("getrawtransaction", p); 22 | if((result.isObject() && verbose) || (result.isString() && !verbose)) { 23 | return result; 24 | } else { 25 | throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, 26 | result.toStyledString()); 27 | } 28 | } 29 | 30 | Json::Value getrawmempool() 31 | throw (jsonrpc::JsonRpcException) { 32 | Json::Value p; 33 | const Json::Value result = this->CallMethod("getrawmempool", p); 34 | if(result.isArray()) { 35 | return result; 36 | } else { 37 | throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, 38 | result.toStyledString()); 39 | } 40 | } 41 | 42 | Json::Value getblockcount() 43 | throw (jsonrpc::JsonRpcException) { 44 | Json::Value p; 45 | const Json::Value result = this->CallMethod("getblockcount", p); 46 | if(result.isNumeric()) { 47 | return result; 48 | } else { 49 | throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, 50 | result.toStyledString()); 51 | } 52 | } 53 | 54 | std::string sendrawtransaction(const std::string& rawTx) 55 | throw (jsonrpc::JsonRpcException) { 56 | Json::Value p; 57 | p.append(rawTx); 58 | const Json::Value result = this->CallMethod("sendrawtransaction", p); 59 | if(result.isString()) { 60 | return result.asString(); 61 | } else { 62 | throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, 63 | result.toStyledString()); 64 | } 65 | } 66 | 67 | Json::Value getblock(const std::string& id, const bool verbose) 68 | throw (jsonrpc::JsonRpcException) { 69 | Json::Value p; 70 | p.append(id); 71 | p.append(verbose); 72 | const Json::Value result = this->CallMethod("getblock", p); 73 | if((result.isObject() && verbose) || (result.isString() && !verbose)) { 74 | return result; 75 | } else { 76 | throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, 77 | result.toStyledString()); 78 | } 79 | } 80 | 81 | std::string getblockhash(const unsigned int height) 82 | throw (jsonrpc::JsonRpcException) { 83 | Json::Value p; 84 | p.append(height); 85 | const Json::Value result = this->CallMethod("getblockhash", p); 86 | if(result.isString()) { 87 | return result.asString(); 88 | } else { 89 | throw jsonrpc::JsonRpcException(jsonrpc::Errors::ERROR_CLIENT_INVALID_RESPONSE, 90 | result.toStyledString()); 91 | } 92 | } 93 | }; 94 | } 95 | 96 | #endif -------------------------------------------------------------------------------- /vertcoin-mainnet.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | services: 3 | 4 | vertcoind-main: 5 | image: lukechilds/vertcoind 6 | restart: always 7 | expose: 8 | - "8332" 9 | - "8333" 10 | ports: 11 | - "5889:5889" 12 | volumes: 13 | - ./data/vtc/main/coind:/data 14 | command: -rpcuser=middleware -rpcpassword=middleware -rpcallowip=0.0.0.0/0 -rpcport=8332 -txindex 15 | 16 | vtc-middleware-cpp-main: 17 | image: vtc-wallet-middleware 18 | restart: always 19 | environment: 20 | - COIND_HOST=vertcoind-main 21 | expose: 22 | - "8888" 23 | volumes: 24 | - ./data/vtc/main/coind/blocks:/blocks 25 | - ./data/vtc/main/index:/index 26 | - ./coins:/coins 27 | command: --coinParams=/coins/vertcoin-mainnet.json 28 | 29 | networks: 30 | default: 31 | external: 32 | name: blockchain-indexer -------------------------------------------------------------------------------- /vertcoin-testnet.yml: -------------------------------------------------------------------------------- 1 | version: "2" 2 | services: 3 | 4 | vertcoind-test: 5 | image: lukechilds/vertcoind 6 | restart: always 7 | expose: 8 | - "8332" 9 | - "8333" 10 | ports: 11 | - "15889:15889" 12 | volumes: 13 | - ./data/vtc/test/coind:/data 14 | command: -rpcuser=middleware -rpcpassword=middleware -testnet -txindex 15 | 16 | vtc-middleware-cpp-test: 17 | image: vtc-wallet-middleware 18 | restart: always 19 | environment: 20 | - COIND_HOST=vertcoind-test 21 | expose: 22 | - "8888" 23 | volumes: 24 | - ./data/vtc/test/coind/testnet3/blocks:/blocks 25 | - ./data/vtc/test/index:/index 26 | - ./coins:/coins 27 | command: --coinParams=/coins/vertcoin-testnet.json 28 | 29 | networks: 30 | default: 31 | external: 32 | name: blockchain-indexer --------------------------------------------------------------------------------