Compare commits

..

15 Commits

Author SHA1 Message Date
Martin Boehm b6e3f26eb2 Insert utxos in fixUtxo in the growing order 2020-02-23 00:24:05 +01:00
Martin Boehm 72e48ce658 Automatically check for UTXO inconsitencies 2020-02-16 23:07:50 +01:00
Martin Boehm 9cfe1f3d25 Fix order of utxos 2020-02-14 16:54:51 +01:00
Martin Boehm 839f67f6eb Fix incorrect order of utxos 2020-02-14 16:33:36 +01:00
Martin Boehm 267c2b22c1 Check the order of utxos 2020-02-14 10:04:12 +01:00
Martin Boehm 5a801ed038 Insert utxos in the right order in disconnect block 2020-02-09 11:46:13 +01:00
Martin Boehm 3c90059994 Alter disconnect block procedure to avoid possible utxo db inconsistency 2020-01-31 09:29:54 +01:00
Martin Boehm b4d0e2e819 Alter logging of a possible db inconsitency in addresses column 2020-01-29 23:54:20 +01:00
Martin Boehm a5a5edef9d Optimize and sort correctly fixed utxos 2020-01-29 20:46:58 +01:00
Martin Boehm 923234e97d Improve storing/loading of block info to db 2020-01-29 20:45:22 +01:00
Vladyslav Burzakovskyy 466d89a670 balanceHistory: remove empty currencies from the list, fix typo 2020-01-28 21:04:19 +01:00
Martin Boehm 358858b418 Fix errors in utxo db 2020-01-27 14:33:58 +01:00
Martin Boehm f903a1d6f9 Handle possible address descriptor mismatch in db column addresses 2020-01-27 14:33:13 +01:00
Martin Boehm 7df8f05b31 Check for error in utxo DB 2020-01-26 20:12:12 +01:00
Martin Boehm af1d3fceaa Add load address by serialized address descriptor 2020-01-25 22:17:17 +01:00
273 changed files with 3648 additions and 7892 deletions

1
.gitignore vendored
View File

@ -8,7 +8,6 @@ debug*
docker/blockbook docker/blockbook
build/pkg-defs build/pkg-defs
build/blockbook build/blockbook
build/blockchaincfg.json
build/ldb build/ldb
build/sst_dump build/sst_dump
build/*.deb build/*.deb

View File

@ -3,8 +3,8 @@
Blockbook is back-end service for Trezor wallet. Although it is open source, the design and development of the core packages Blockbook is back-end service for Trezor wallet. Although it is open source, the design and development of the core packages
is done by Trezor developers in order to keep Blockbook compatible with Trezor. is done by Trezor developers in order to keep Blockbook compatible with Trezor.
Bug fixes and support for new coins are welcome. **Please take note that non-fixing pull requests that change base Bug fixes and support for new coins are welcome. Please take note that non-fixing pull requests that change base
packages or another coin code will not be accepted.** If you have a need to change some of the existing code, please file packages or another coin code will not be accepted. If you have a need to change some of the existing code, please file
an issue and discuss your request with Blockbook maintainers. an issue and discuss your request with Blockbook maintainers.
## Development environment ## Development environment

298
Gopkg.lock generated 100644
View File

@ -0,0 +1,298 @@
# This file is autogenerated, do not edit; changes may be undone by the next 'dep ensure'.
[[projects]]
branch = "master"
name = "github.com/Groestlcoin/go-groestl-hash"
packages = ["groestl","hash"]
revision = "790653ac190c4029ee200e82a8f21b5d1afaf7d6"
[[projects]]
digest = "1:8b13694e3e7b33c6f389f367b6a001a1184b051e34880fdd9e58a2207d9f6573"
name = "github.com/allegro/bigcache"
packages = [
".",
"queue",
]
pruneopts = ""
revision = "69ea0af04088faa57adb9ac683934277141e92a5"
version = "v2.0.0"
[[projects]]
branch = "master"
digest = "1:10f6df61e4d3de150f3c11c3c6791c5702382c7f4aa983bcab9f49a73fea44a3"
name = "github.com/aristanetworks/goarista"
packages = ["monotime"]
pruneopts = ""
revision = "8e7d5b18fe7ad671e07097d5445dbc70422663b2"
[[projects]]
branch = "master"
name = "github.com/agl/ed25519"
packages = [".","edwards25519"]
revision = "5312a61534124124185d41f09206b9fef1d88403"
[[projects]]
branch = "master"
name = "github.com/beorn7/perks"
packages = ["quantile"]
revision = "3a771d992973f24aa725d07868b467d1ddfceafb"
[[projects]]
branch = "master"
name = "github.com/bsm/go-vlq"
packages = ["."]
revision = "ec6e8d4f5f4ec0f6e808ffc7f4dcc7516d4d7d49"
[[projects]]
branch = "master"
name = "github.com/martinboehm/btcd"
packages = ["blockchain","btcec","chaincfg","chaincfg/chainhash","database","txscript","wire"]
revision = "8e7c0427fee5d4778c5d4eb987150369e3ca1d0e"
[[projects]]
branch = "master"
name = "github.com/btcsuite/btclog"
packages = ["."]
revision = "84c8d2346e9fc8c7b947e243b9c24e6df9fd206a"
[[projects]]
branch = "master"
name = "github.com/dchest/blake256"
packages = ["."]
revision = "dee3fe6eb0e98dc774a94fc231f85baf7c29d360"
[[projects]]
name = "github.com/deckarep/golang-set"
packages = ["."]
revision = "1d4478f51bed434f1dadf96dcd9b43aabac66795"
version = "v1.7"
[[projects]]
branch = "master"
name = "github.com/decred/base58"
packages = ["."]
revision = "dbeddd8aab76c31eb2ea98351a63fa2c6bf46888"
[[projects]]
name = "github.com/decred/dcrd"
packages = ["chaincfg","chaincfg/chainec","chaincfg/chainhash","dcrec","dcrec/edwards","dcrec/secp256k1","dcrec/secp256k1/schnorr","dcrjson","dcrutil","txscript","wire"]
revision = "e3e8c47c68b010dbddeb783ebad32a3a4993dd71"
version = "v1.4.0"
[[projects]]
name = "github.com/decred/slog"
packages = ["."]
revision = "fbd821ef791ba2b8ae945f5d44f4e49396d230c5"
version = "v1.0.0"
[[projects]]
name = "github.com/ethereum/go-ethereum"
packages = [".","common","common/hexutil","common/math","core/types","crypto","crypto/secp256k1","crypto/sha3","ethclient","ethdb","log","metrics","p2p/netutil","params","rlp","rpc","trie"]
revision = "24d727b6d6e2c0cde222fa12155c4a6db5caaf2e"
version = "v1.8.20"
[[projects]]
name = "github.com/go-stack/stack"
packages = ["."]
revision = "259ab82a6cad3992b4e21ff5cac294ccb06474bc"
version = "v1.7.0"
[[projects]]
name = "github.com/gobuffalo/packr"
packages = ["."]
revision = "5a2cbb54c4e7d482e3f518c56f1f86f133d5204f"
version = "v1.13.7"
[[projects]]
name = "github.com/gogo/protobuf"
packages = ["proto"]
revision = "1adfc126b41513cc696b209667c8656ea7aac67c"
version = "v1.0.0"
[[projects]]
branch = "master"
name = "github.com/golang/glog"
packages = ["."]
revision = "23def4e6c14b4da8ac2ed8007337bc5eb5007998"
[[projects]]
name = "github.com/golang/protobuf"
packages = ["proto"]
revision = "925541529c1fa6821df4e44ce2723319eb2be768"
version = "v1.0.0"
[[projects]]
branch = "master"
name = "github.com/golang/snappy"
packages = ["."]
revision = "553a641470496b2327abcac10b36396bd98e45c9"
[[projects]]
name = "github.com/gorilla/websocket"
packages = ["."]
revision = "ea4d1f681babbce9545c9c5f3d5194a789c89f5b"
version = "v1.2.0"
[[projects]]
branch = "master"
name = "github.com/martinboehm/bchutil"
packages = ["."]
revision = "6373f11b6efe1ea81e8713b8788a695b2c144d38"
[[projects]]
branch = "master"
name = "github.com/martinboehm/btcutil"
packages = [".","base58","bech32","chaincfg","hdkeychain","txscript"]
revision = "a3d2b8457b77d37c3813742d4030e199b6e09111"
[[projects]]
branch = "master"
name = "github.com/juju/errors"
packages = ["."]
revision = "c7d06af17c68cd34c835053720b21f6549d9b0ee"
[[projects]]
branch = "master"
name = "github.com/martinboehm/golang-socketio"
packages = [".","protocol","transport"]
revision = "f60b0a8befde091474a624a8ffd81ee9912957b3"
[[projects]]
name = "github.com/matttproud/golang_protobuf_extensions"
packages = ["pbutil"]
revision = "3247c84500bff8d9fb6d579d800f20b3e091582c"
version = "v1.0.0"
[[projects]]
branch = "master"
name = "github.com/mr-tron/base58"
packages = ["base58"]
revision = "c1bdf7c52f59d6685ca597b9955a443ff95eeee6"
[[projects]]
branch = "master"
name = "github.com/pebbe/zmq4"
packages = ["."]
revision = "5b443b6471cea4b4f9f85025530c04c93233f76a"
[[projects]]
name = "github.com/pkg/errors"
packages = ["."]
revision = "645ef00459ed84a119197bfb8d8205042c6df63d"
version = "v0.8.0"
[[projects]]
name = "github.com/prometheus/client_golang"
packages = ["prometheus","prometheus/promhttp"]
revision = "c5b7fccd204277076155f10851dad72b76a49317"
version = "v0.8.0"
[[projects]]
branch = "master"
name = "github.com/prometheus/client_model"
packages = ["go"]
revision = "99fa1f4be8e564e8a6b613da7fa6f46c9edafc6c"
[[projects]]
branch = "master"
name = "github.com/prometheus/common"
packages = ["expfmt","internal/bitbucket.org/ww/goautoneg","model"]
revision = "d0f7cd64bda49e08b22ae8a730aa57aa0db125d6"
[[projects]]
branch = "master"
name = "github.com/prometheus/procfs"
packages = [".","internal/util","nfs","xfs"]
revision = "8b1c2da0d56deffdbb9e48d4414b4e674bd8083e"
[[projects]]
name = "github.com/rs/cors"
packages = ["."]
revision = "feef513b9575b32f84bafa580aad89b011259019"
version = "v1.3.0"
[[projects]]
name = "github.com/schancel/cashaddr-converter"
packages = ["address","baseconv","cashaddress","legacy"]
revision = "0a38f5822f795dc3727b4caacc298e02938d9eb1"
version = "v9"
[[projects]]
branch = "master"
name = "github.com/syndtr/goleveldb"
packages = ["leveldb","leveldb/cache","leveldb/comparer","leveldb/errors","leveldb/filter","leveldb/iterator","leveldb/journal","leveldb/memdb","leveldb/opt","leveldb/storage","leveldb/table","leveldb/util"]
revision = "714f901b98fdb3aa954b4193d8cbd64a28d80cad"
[[projects]]
branch = "master"
name = "github.com/tecbot/gorocksdb"
packages = ["."]
revision = "214b6b7bc0f06812ab5602fdc502a3e619916f38"
[[projects]]
branch = "master"
name = "golang.org/x/crypto"
packages = ["ripemd160", "sha3"]
revision = "a832865fa7ada6126f4c6124ac49f71be71bff2a"
[[projects]]
branch = "master"
name = "golang.org/x/net"
packages = ["websocket"]
revision = "61147c48b25b599e5b561d2e9c4f3e1ef489ca41"
[[projects]]
branch = "v2"
name = "gopkg.in/karalabe/cookiejar.v2"
packages = ["collections/prque"]
revision = "8dcd6a7f4951f6ff3ee9cbb919a06d8925822e57"
[[projects]]
branch = "v2"
name = "gopkg.in/natefinch/npipe.v2"
packages = ["."]
revision = "c1b8fa8bdccecb0b8db834ee0b92fdbcfa606dd6"
[solve-meta]
analyzer-name = "dep"
analyzer-version = 1
input-imports = [
"github.com/bsm/go-vlq",
"github.com/deckarep/golang-set",
"github.com/decred/dcrd/chaincfg",
"github.com/decred/dcrd/dcrjson",
"github.com/decred/dcrd/txscript",
"github.com/ethereum/go-ethereum",
"github.com/ethereum/go-ethereum/common",
"github.com/ethereum/go-ethereum/common/hexutil",
"github.com/ethereum/go-ethereum/core/types",
"github.com/ethereum/go-ethereum/ethclient",
"github.com/ethereum/go-ethereum/rpc",
"github.com/gobuffalo/packr",
"github.com/gogo/protobuf/proto",
"github.com/golang/glog",
"github.com/golang/protobuf/proto",
"github.com/gorilla/websocket",
"github.com/juju/errors",
"github.com/martinboehm/bchutil",
"github.com/martinboehm/btcd/blockchain",
"github.com/martinboehm/btcd/chaincfg/chainhash",
"github.com/martinboehm/btcd/txscript",
"github.com/martinboehm/btcd/wire",
"github.com/martinboehm/btcutil",
"github.com/martinboehm/btcutil/base58",
"github.com/martinboehm/btcutil/chaincfg",
"github.com/martinboehm/btcutil/hdkeychain",
"github.com/martinboehm/btcutil/txscript",
"github.com/martinboehm/golang-socketio",
"github.com/martinboehm/golang-socketio/transport",
"github.com/pebbe/zmq4",
"github.com/prometheus/client_golang/prometheus",
"github.com/prometheus/client_golang/prometheus/promhttp",
"github.com/schancel/cashaddr-converter/address",
"github.com/tecbot/gorocksdb",
]
solver-name = "gps-cdcl"
solver-version = 1

74
Gopkg.toml 100644
View File

@ -0,0 +1,74 @@
# Gopkg.toml example
#
# Refer to https://github.com/golang/dep/blob/master/docs/Gopkg.toml.md
# for detailed Gopkg.toml documentation.
#
# required = ["github.com/user/thing/cmd/thing"]
# ignored = ["github.com/user/project/pkgX", "bitbucket.org/user/project/pkgA/pkgY"]
#
# [[constraint]]
# name = "github.com/user/project"
# version = "1.0.0"
#
# [[constraint]]
# name = "github.com/user/project2"
# branch = "dev"
# source = "github.com/myfork/project2"
#
# [[override]]
# name = "github.com/x/y"
# version = "2.4.0"
[[constraint]]
branch = "master"
name = "github.com/bsm/go-vlq"
[[constraint]]
branch = "master"
name = "github.com/martinboehm/btcd"
[[constraint]]
branch = "master"
name = "github.com/martinboehm/btcutil"
[[constraint]]
branch = "master"
name = "github.com/golang/glog"
[[constraint]]
name = "github.com/gorilla/mux"
version = "1.6.1"
[[constraint]]
branch = "master"
name = "github.com/juju/errors"
[[constraint]]
branch = "master"
name = "github.com/martinboehm/golang-socketio"
[[constraint]]
branch = "master"
name = "github.com/pebbe/zmq4"
[[constraint]]
name = "github.com/prometheus/client_golang"
version = "0.8.0"
[[constraint]]
branch = "master"
name = "github.com/tecbot/gorocksdb"
[[constraint]]
name = "github.com/ethereum/go-ethereum"
version = "1.8.2"
[[constraint]]
name = "github.com/golang/protobuf"
version = "1.0.0"
[[constraint]]
branch = "master"
name = "github.com/martinboehm/bchutil"

View File

@ -1,9 +1,8 @@
BIN_IMAGE = blockbook-build BIN_IMAGE = blockbook-build
DEB_IMAGE = blockbook-build-deb DEB_IMAGE = blockbook-build-deb
PACKAGER = $(shell id -u):$(shell id -g) PACKAGER = $(shell id -u):$(shell id -g)
BASE_IMAGE = $$(awk -F= '$$1=="ID" { print $$2 ;}' /etc/os-release):$$(awk -F= '$$1=="VERSION_ID" { print $$2 ;}' /etc/os-release | tr -d '"')
NO_CACHE = false NO_CACHE = false
TCMALLOC = UPDATE_VENDOR = 1
ARGS ?= ARGS ?=
TARGETS=$(subst .json,, $(shell ls configs/coins)) TARGETS=$(subst .json,, $(shell ls configs/coins))
@ -11,28 +10,28 @@ TARGETS=$(subst .json,, $(shell ls configs/coins))
.PHONY: build build-debug test deb .PHONY: build build-debug test deb
build: .bin-image build: .bin-image
docker run -t --rm -e PACKAGER=$(PACKAGER) -v "$(CURDIR):/src" -v "$(CURDIR)/build:/out" $(BIN_IMAGE) make build ARGS="$(ARGS)" docker run -t --rm -e PACKAGER=$(PACKAGER) -e UPDATE_VENDOR=$(UPDATE_VENDOR) -v $(CURDIR):/src -v $(CURDIR)/build:/out $(BIN_IMAGE) make build ARGS="$(ARGS)"
build-debug: .bin-image build-debug: .bin-image
docker run -t --rm -e PACKAGER=$(PACKAGER) -v "$(CURDIR):/src" -v "$(CURDIR)/build:/out" $(BIN_IMAGE) make build-debug ARGS="$(ARGS)" docker run -t --rm -e PACKAGER=$(PACKAGER) -e UPDATE_VENDOR=$(UPDATE_VENDOR) -v $(CURDIR):/src -v $(CURDIR)/build:/out $(BIN_IMAGE) make build-debug ARGS="$(ARGS)"
test: .bin-image test: .bin-image
docker run -t --rm -e PACKAGER=$(PACKAGER) -v "$(CURDIR):/src" --network="host" $(BIN_IMAGE) make test ARGS="$(ARGS)" docker run -t --rm -e PACKAGER=$(PACKAGER) -e UPDATE_VENDOR=$(UPDATE_VENDOR) -v $(CURDIR):/src --network="host" $(BIN_IMAGE) make test ARGS="$(ARGS)"
test-integration: .bin-image test-integration: .bin-image
docker run -t --rm -e PACKAGER=$(PACKAGER) -v "$(CURDIR):/src" --network="host" $(BIN_IMAGE) make test-integration ARGS="$(ARGS)" docker run -t --rm -e PACKAGER=$(PACKAGER) -e UPDATE_VENDOR=$(UPDATE_VENDOR) -v $(CURDIR):/src --network="host" $(BIN_IMAGE) make test-integration ARGS="$(ARGS)"
test-all: .bin-image test-all: .bin-image
docker run -t --rm -e PACKAGER=$(PACKAGER) -v "$(CURDIR):/src" --network="host" $(BIN_IMAGE) make test-all ARGS="$(ARGS)" docker run -t --rm -e PACKAGER=$(PACKAGER) -e UPDATE_VENDOR=$(UPDATE_VENDOR) -v $(CURDIR):/src --network="host" $(BIN_IMAGE) make test-all ARGS="$(ARGS)"
deb-backend-%: .deb-image deb-backend-%: .deb-image
docker run -t --rm -e PACKAGER=$(PACKAGER) -v "$(CURDIR):/src" -v "$(CURDIR)/build:/out" $(DEB_IMAGE) /build/build-deb.sh backend $* $(ARGS) docker run -t --rm -e PACKAGER=$(PACKAGER) -e UPDATE_VENDOR=$(UPDATE_VENDOR) -v $(CURDIR):/src -v $(CURDIR)/build:/out $(DEB_IMAGE) /build/build-deb.sh backend $* $(ARGS)
deb-blockbook-%: .deb-image deb-blockbook-%: .deb-image
docker run -t --rm -e PACKAGER=$(PACKAGER) -v "$(CURDIR):/src" -v "$(CURDIR)/build:/out" $(DEB_IMAGE) /build/build-deb.sh blockbook $* $(ARGS) docker run -t --rm -e PACKAGER=$(PACKAGER) -e UPDATE_VENDOR=$(UPDATE_VENDOR) -v $(CURDIR):/src -v $(CURDIR)/build:/out $(DEB_IMAGE) /build/build-deb.sh blockbook $* $(ARGS)
deb-%: .deb-image deb-%: .deb-image
docker run -t --rm -e PACKAGER=$(PACKAGER) -v "$(CURDIR):/src" -v "$(CURDIR)/build:/out" $(DEB_IMAGE) /build/build-deb.sh all $* $(ARGS) docker run -t --rm -e PACKAGER=$(PACKAGER) -e UPDATE_VENDOR=$(UPDATE_VENDOR) -v $(CURDIR):/src -v $(CURDIR)/build:/out $(DEB_IMAGE) /build/build-deb.sh all $* $(ARGS)
deb-blockbook-all: clean-deb $(addprefix deb-blockbook-, $(TARGETS)) deb-blockbook-all: clean-deb $(addprefix deb-blockbook-, $(TARGETS))
@ -45,8 +44,8 @@ build-images: clean-images
.bin-image: .bin-image:
@if [ $$(build/tools/image_status.sh $(BIN_IMAGE):latest build/docker) != "ok" ]; then \ @if [ $$(build/tools/image_status.sh $(BIN_IMAGE):latest build/docker) != "ok" ]; then \
echo "Building image $(BIN_IMAGE) from $(BASE_IMAGE)"; \ echo "Building image $(BIN_IMAGE)..."; \
docker build --no-cache=$(NO_CACHE) --build-arg TCMALLOC=$(TCMALLOC) --build-arg BASE_IMAGE=$(BASE_IMAGE) -t $(BIN_IMAGE) build/docker/bin; \ docker build --no-cache=$(NO_CACHE) -t $(BIN_IMAGE) build/docker/bin; \
else \ else \
echo "Image $(BIN_IMAGE) is up to date"; \ echo "Image $(BIN_IMAGE) is up to date"; \
fi fi

View File

@ -1,74 +0,0 @@
[![Go Report Card](https://goreportcard.com/badge/trezor/blockbook)](https://goreportcard.com/report/trezor/blockbook)
# Blockbook
**Blockbook** is back-end service for Trezor wallet. Main features of **Blockbook** are:
- index of addresses and address balances of the connected block chain
- fast searches in the indexes
- simple blockchain explorer
- websocket, API and legacy Bitcore Insight compatible socket.io interfaces
- support of multiple coins (Bitcoin and Ethereum type), with easy extensibility for other coins
- scripts for easy creation of debian packages for backend and blockbook
## Build and installation instructions
Officially supported platform is **Debian Linux** and **AMD64** architecture.
Memory and disk requirements for initial synchronization of **Bitcoin mainnet** are around 32 GB RAM and over 180 GB of disk space. After initial synchronization, fully synchronized instance uses about 10 GB RAM.
Other coins should have lower requirements, depending on the size of their block chain. Note that fast SSD disks are highly
recommended.
User installation guide is [here](https://wiki.trezor.io/User_manual:Running_a_local_instance_of_Trezor_Wallet_backend_(Blockbook)).
Developer build guide is [here](/docs/build.md).
Contribution guide is [here](CONTRIBUTING.md).
## Implemented coins
Blockbook currently supports over 30 coins. The Trezor team implemented
- Bitcoin, Bitcoin Cash, Zcash, Dash, Litecoin, Bitcoin Gold, Ethereum, Ethereum Classic, Dogecoin, Namecoin, Vertcoin, DigiByte, Liquid
the rest of coins were implemented by the community.
Testnets for some coins are also supported, for example:
- Bitcoin Testnet, Bitcoin Cash Testnet, ZCash Testnet, Ethereum Testnet Ropsten
List of all implemented coins is in [the registry of ports](/docs/ports.md).
## Common issues when running Blockbook or implementing additional coins
#### Out of memory when doing initial synchronization
How to reduce memory footprint of the initial sync:
- disable rocksdb cache by parameter `-dbcache=0`, the default size is 500MB
- run blockbook with parameter `-workers=1`. This disables bulk import mode, which caches a lot of data in memory (not in rocksdb cache). It will run about twice as slowly but especially for smaller blockchains it is no problem at all.
Please add your experience to this [issue](https://github.com/trezor/blockbook/issues/43).
#### Error `internalState: database is in inconsistent state and cannot be used`
Blockbook was killed during the initial import, most commonly by OOM killer. By default, Blockbook performs the initial import in bulk import mode, which for performance reasons does not store all the data immediately to the database. If Blockbook is killed during this phase, the database is left in an inconsistent state.
See above how to reduce the memory footprint, delete the database files and run the import again.
Check [this](https://github.com/trezor/blockbook/issues/89) or [this](https://github.com/trezor/blockbook/issues/147) issue for more info.
#### Running on Ubuntu
[This issue](https://github.com/trezor/blockbook/issues/45) discusses how to run Blockbook on Ubuntu. If you have some additional experience with Blockbook on Ubuntu, please add it to [this issue](https://github.com/trezor/blockbook/issues/45).
#### My coin implementation is reporting parse errors when importing blockchain
Your coin's block/transaction data may not be compatible with `BitcoinParser` `ParseBlock`/`ParseTx`, which is used by default. In that case, implement your coin in a similar way we used in case of [zcash](https://github.com/trezor/blockbook/tree/master/bchain/coins/zec) and some other coins. The principle is not to parse the block/transaction data in Blockbook but instead to get parsed transactions as json from the backend.
## Data storage in RocksDB
Blockbook stores data the key-value store RocksDB. Database format is described [here](/docs/rocksdb.md).
## API
Blockbook API is described [here](/docs/api.md).

View File

@ -1,57 +1,74 @@
# Fork [![Go Report Card](https://goreportcard.com/badge/trezor/blockbook)](https://goreportcard.com/report/trezor/blockbook)
Fork of Trezor Blockbook.
# Blockbook
The differences: **Blockbook** is back-end service for Trezor wallet. Main features of **Blockbook** are:
* Just for Ethereum. - index of addresses and address balances of the connected block chain
- fast searches in the indexes
- simple blockchain explorer
- websocket, API and legacy Bitcore Insight compatible socket.io interfaces
- support of multiple coins (Bitcoin and Ethereum type), with easy extensibility for other coins
- scripts for easy creation of debian packages for backend and blockbook
* Use existing `geth --full` server. ## Build and installation instructions
* Don't require `backend-*` package. Officially supported platform is **Debian Linux** and **AMD64** architecture.
* Minimal UI, dark theme. Memory and disk requirements for initial synchronization of **Bitcoin mainnet** are around 32 GB RAM and over 180 GB of disk space. After initial synchronization, fully synchronized instance uses about 10 GB RAM.
Other coins should have lower requirements, depending on the size of their block chain. Note that fast SSD disks are highly
recommended.
* Listen only on localhost, no SSL. User installation guide is [here](https://wiki.trezor.io/User_manual:Running_a_local_instance_of_Trezor_Wallet_backend_(Blockbook)).
* Use spacecruft repos, not github. Developer build guide is [here](/docs/build.md).
* Don't use CDN. Contribution guide is [here](CONTRIBUTING.md).
# Install ## Implemented coins
``` Blockbook currently supports over 30 coins. The Trezor team implemented
# Install docker, etc.
git clone https://spacecruft.org/spacecruft/blockbook
cd blockbook
make deb-blockbook-ethereum
dpkg -i build/blockbook-ethereum_0.3.4_amd64.deb
```
Edit config: - Bitcoin, Bitcoin Cash, Zcash, Dash, Litecoin, Bitcoin Gold, Ethereum, Ethereum Classic, Dogecoin, Namecoin, Vertcoin, DigiByte, Liquid
```
vim /opt/coins/blockbook/ethereum/config/blockchaincfg.json
```
XXX Hardcoded into systemd script, set `geth` node: the rest of coins were implemented by the community.
``` Testnets for some coins are also supported, for example:
vim /lib/systemd/system/blockbook-ethereum.service - Bitcoin Testnet, Bitcoin Cash Testnet, ZCash Testnet, Ethereum Testnet Ropsten
systemctl daemon-reload
```
Start: List of all implemented coins is in [the registry of ports](/docs/ports.md).
```
systemctl start blockbook-ethereum.service
```
Logs: ## Common issues when running Blockbook or implementing additional coins
```
tail -f /opt/coins/blockbook/ethereum/logs/blockbook.INFO
```
# Upstream #### Out of memory when doing initial synchronization
Fork of Trezor Blockbook. See `README-upstream.md`.
* https://github.com/trezor/blockbook How to reduce memory footprint of the initial sync:
- disable rocksdb cache by parameter `-dbcache=0`, the default size is 500MB
- run blockbook with parameter `-workers=1`. This disables bulk import mode, which caches a lot of data in memory (not in rocksdb cache). It will run about twice as slowly but especially for smaller blockchains it is no problem at all.
Please add your experience to this [issue](https://github.com/trezor/blockbook/issues/43).
#### Error `internalState: database is in inconsistent state and cannot be used`
Blockbook was killed during the initial import, most commonly by OOM killer. By default, Blockbook performs the initial import in bulk import mode, which for performance reasons does not store all the data immediately to the database. If Blockbook is killed during this phase, the database is left in an inconsistent state.
See above how to reduce the memory footprint, delete the database files and run the import again.
Check [this](https://github.com/trezor/blockbook/issues/89) or [this](https://github.com/trezor/blockbook/issues/147) issue for more info.
#### Running on Ubuntu
[This issue](https://github.com/trezor/blockbook/issues/45) discusses how to run Blockbook on Ubuntu. If you have some additional experience with Blockbook on Ubuntu, please add it to [this issue](https://github.com/trezor/blockbook/issues/45).
#### My coin implementation is reporting parse errors when importing blockchain
Your coin's block/transaction data may not be compatible with `BitcoinParser` `ParseBlock`/`ParseTx`, which is used by default. In that case, implement your coin in a similar way we used in case of [zcash](https://github.com/trezor/blockbook/tree/master/bchain/coins/zec) and some other coins. The principle is not to parse the block/transaction data in Blockbook but instead to get parsed transactions as json from the backend.
## Data storage in RocksDB
Blockbook stores data the key-value store RocksDB. Database format is described [here](/docs/rocksdb.md).
## API
Blockbook API is described [here](/docs/api.md).

View File

@ -1,16 +1,14 @@
package api package api
import ( import (
"blockbook/bchain"
"blockbook/common"
"blockbook/db"
"encoding/json" "encoding/json"
"errors" "errors"
"math/big" "math/big"
"sort" "sort"
"time" "time"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/eth"
"spacecruft.org/spacecruft/blockbook/common"
"spacecruft.org/spacecruft/blockbook/db"
) )
const maxUint32 = ^uint32(0) const maxUint32 = ^uint32(0)
@ -171,12 +169,11 @@ type TokenTransfer struct {
// EthereumSpecific contains ethereum specific transaction data // EthereumSpecific contains ethereum specific transaction data
type EthereumSpecific struct { type EthereumSpecific struct {
Status eth.TxStatus `json:"status"` // 1 OK, 0 Fail, -1 pending Status int `json:"status"` // 1 OK, 0 Fail, -1 pending
Nonce uint64 `json:"nonce"` Nonce uint64 `json:"nonce"`
GasLimit *big.Int `json:"gasLimit"` GasLimit *big.Int `json:"gasLimit"`
GasUsed *big.Int `json:"gasUsed"` GasUsed *big.Int `json:"gasUsed"`
GasPrice *Amount `json:"gasPrice"` GasPrice *Amount `json:"gasPrice"`
Data string `json:"data,omitempty"`
} }
// Tx holds information about a transaction // Tx holds information about a transaction
@ -196,7 +193,8 @@ type Tx struct {
FeesSat *Amount `json:"fees,omitempty"` FeesSat *Amount `json:"fees,omitempty"`
Hex string `json:"hex,omitempty"` Hex string `json:"hex,omitempty"`
Rbf bool `json:"rbf,omitempty"` Rbf bool `json:"rbf,omitempty"`
CoinSpecificData json.RawMessage `json:"coinSpecificData,omitempty"` CoinSpecificData interface{} `json:"-"`
CoinSpecificJSON json.RawMessage `json:"-"`
TokenTransfers []TokenTransfer `json:"tokenTransfers,omitempty"` TokenTransfers []TokenTransfer `json:"tokenTransfers,omitempty"`
EthereumSpecific *EthereumSpecific `json:"ethereumSpecific,omitempty"` EthereumSpecific *EthereumSpecific `json:"ethereumSpecific,omitempty"`
} }
@ -226,8 +224,6 @@ const (
AddressFilterVoutInputs = -2 AddressFilterVoutInputs = -2
// AddressFilterVoutOutputs specifies that only txs where the address is as output are returned // AddressFilterVoutOutputs specifies that only txs where the address is as output are returned
AddressFilterVoutOutputs = -3 AddressFilterVoutOutputs = -3
// AddressFilterVoutQueryNotNecessary signals that query for transactions is not necessary as there are no transactions for specified contract filter
AddressFilterVoutQueryNotNecessary = -4
// TokensToReturnNonzeroBalance - return only tokens with nonzero balance // TokensToReturnNonzeroBalance - return only tokens with nonzero balance
TokensToReturnNonzeroBalance TokensToReturn = 0 TokensToReturnNonzeroBalance TokensToReturn = 0
@ -303,13 +299,12 @@ func (a Utxos) Less(i, j int) bool {
// BalanceHistory contains info about one point in time of balance history // BalanceHistory contains info about one point in time of balance history
type BalanceHistory struct { type BalanceHistory struct {
Time uint32 `json:"time"` Time uint32 `json:"time"`
Txs uint32 `json:"txs"` Txs uint32 `json:"txs"`
ReceivedSat *Amount `json:"received"` ReceivedSat *Amount `json:"received"`
SentSat *Amount `json:"sent"` SentSat *Amount `json:"sent"`
SentToSelfSat *Amount `json:"sentToSelf"` FiatRates map[string]float64 `json:"rates,omitempty"`
FiatRates map[string]float64 `json:"rates,omitempty"` Txid string `json:"txid,omitempty"`
Txid string `json:"txid,omitempty"`
} }
// BalanceHistories is array of BalanceHistory // BalanceHistories is array of BalanceHistory
@ -331,9 +326,8 @@ func (a BalanceHistories) SortAndAggregate(groupByTime uint32) BalanceHistories
bhs := make(BalanceHistories, 0) bhs := make(BalanceHistories, 0)
if len(a) > 0 { if len(a) > 0 {
bha := BalanceHistory{ bha := BalanceHistory{
ReceivedSat: &Amount{}, SentSat: &Amount{},
SentSat: &Amount{}, ReceivedSat: &Amount{},
SentToSelfSat: &Amount{},
} }
sort.Sort(a) sort.Sort(a)
for i := range a { for i := range a {
@ -346,19 +340,17 @@ func (a BalanceHistories) SortAndAggregate(groupByTime uint32) BalanceHistories
bhs = append(bhs, bha) bhs = append(bhs, bha)
} }
bha = BalanceHistory{ bha = BalanceHistory{
Time: time, Time: time,
ReceivedSat: &Amount{}, SentSat: &Amount{},
SentSat: &Amount{}, ReceivedSat: &Amount{},
SentToSelfSat: &Amount{},
} }
} }
if bha.Txid != bh.Txid { if bha.Txid != bh.Txid {
bha.Txs += bh.Txs bha.Txs += bh.Txs
bha.Txid = bh.Txid bha.Txid = bh.Txid
} }
(*big.Int)(bha.ReceivedSat).Add((*big.Int)(bha.ReceivedSat), (*big.Int)(bh.ReceivedSat))
(*big.Int)(bha.SentSat).Add((*big.Int)(bha.SentSat), (*big.Int)(bh.SentSat)) (*big.Int)(bha.SentSat).Add((*big.Int)(bha.SentSat), (*big.Int)(bh.SentSat))
(*big.Int)(bha.SentToSelfSat).Add((*big.Int)(bha.SentToSelfSat), (*big.Int)(bh.SentToSelfSat)) (*big.Int)(bha.ReceivedSat).Add((*big.Int)(bha.ReceivedSat), (*big.Int)(bh.ReceivedSat))
} }
if bha.Txs > 0 { if bha.Txs > 0 {
bha.Txid = "" bha.Txid = ""
@ -376,19 +368,19 @@ type Blocks struct {
// BlockInfo contains extended block header data and a list of block txids // BlockInfo contains extended block header data and a list of block txids
type BlockInfo struct { type BlockInfo struct {
Hash string `json:"hash"` Hash string `json:"hash"`
Prev string `json:"previousBlockHash,omitempty"` Prev string `json:"previousBlockHash,omitempty"`
Next string `json:"nextBlockHash,omitempty"` Next string `json:"nextBlockHash,omitempty"`
Height uint32 `json:"height"` Height uint32 `json:"height"`
Confirmations int `json:"confirmations"` Confirmations int `json:"confirmations"`
Size int `json:"size"` Size int `json:"size"`
Time int64 `json:"time,omitempty"` Time int64 `json:"time,omitempty"`
Version common.JSONNumber `json:"version"` Version json.Number `json:"version"`
MerkleRoot string `json:"merkleRoot"` MerkleRoot string `json:"merkleRoot"`
Nonce string `json:"nonce"` Nonce string `json:"nonce"`
Bits string `json:"bits"` Bits string `json:"bits"`
Difficulty string `json:"difficulty"` Difficulty string `json:"difficulty"`
Txids []string `json:"tx,omitempty"` Txids []string `json:"tx,omitempty"`
} }
// Block contains information about block // Block contains information about block
@ -421,10 +413,26 @@ type BlockbookInfo struct {
About string `json:"about"` About string `json:"about"`
} }
// BackendInfo is used to get information about blockchain
type BackendInfo struct {
BackendError string `json:"error,omitempty"`
Chain string `json:"chain,omitempty"`
Blocks int `json:"blocks,omitempty"`
Headers int `json:"headers,omitempty"`
BestBlockHash string `json:"bestBlockHash,omitempty"`
Difficulty string `json:"difficulty,omitempty"`
SizeOnDisk int64 `json:"sizeOnDisk,omitempty"`
Version string `json:"version,omitempty"`
Subversion string `json:"subversion,omitempty"`
ProtocolVersion string `json:"protocolVersion,omitempty"`
Timeoffset float64 `json:"timeOffset,omitempty"`
Warnings string `json:"warnings,omitempty"`
}
// SystemInfo contains information about the running blockbook and backend instance // SystemInfo contains information about the running blockbook and backend instance
type SystemInfo struct { type SystemInfo struct {
Blockbook *BlockbookInfo `json:"blockbook"` Blockbook *BlockbookInfo `json:"blockbook"`
Backend *common.BackendInfo `json:"backend"` Backend *BackendInfo `json:"backend"`
} }
// MempoolTxid contains information about a transaction in mempool // MempoolTxid contains information about a transaction in mempool

View File

@ -67,22 +67,20 @@ func TestBalanceHistories_SortAndAggregate(t *testing.T) {
name: "one", name: "one",
a: []BalanceHistory{ a: []BalanceHistory{
{ {
ReceivedSat: (*Amount)(big.NewInt(1)), ReceivedSat: (*Amount)(big.NewInt(1)),
SentSat: (*Amount)(big.NewInt(2)), SentSat: (*Amount)(big.NewInt(2)),
SentToSelfSat: (*Amount)(big.NewInt(1)), Time: 1521514812,
Time: 1521514812, Txid: "00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840",
Txid: "00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840", Txs: 1,
Txs: 1,
}, },
}, },
groupByTime: 3600, groupByTime: 3600,
want: []BalanceHistory{ want: []BalanceHistory{
{ {
ReceivedSat: (*Amount)(big.NewInt(1)), ReceivedSat: (*Amount)(big.NewInt(1)),
SentSat: (*Amount)(big.NewInt(2)), SentSat: (*Amount)(big.NewInt(2)),
SentToSelfSat: (*Amount)(big.NewInt(1)), Time: 1521514800,
Time: 1521514800, Txs: 1,
Txs: 1,
}, },
}, },
}, },
@ -90,76 +88,67 @@ func TestBalanceHistories_SortAndAggregate(t *testing.T) {
name: "aggregate", name: "aggregate",
a: []BalanceHistory{ a: []BalanceHistory{
{ {
ReceivedSat: (*Amount)(big.NewInt(1)), ReceivedSat: (*Amount)(big.NewInt(1)),
SentSat: (*Amount)(big.NewInt(2)), SentSat: (*Amount)(big.NewInt(2)),
SentToSelfSat: (*Amount)(big.NewInt(0)), Time: 1521504812,
Time: 1521504812, Txid: "0011223344556677889900112233445566778899001122334455667788990011",
Txid: "0011223344556677889900112233445566778899001122334455667788990011", Txs: 1,
Txs: 1,
}, },
{ {
ReceivedSat: (*Amount)(big.NewInt(3)), ReceivedSat: (*Amount)(big.NewInt(3)),
SentSat: (*Amount)(big.NewInt(4)), SentSat: (*Amount)(big.NewInt(4)),
SentToSelfSat: (*Amount)(big.NewInt(2)), Time: 1521504812,
Time: 1521504812, Txid: "00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840",
Txid: "00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840", Txs: 1,
Txs: 1,
}, },
{ {
ReceivedSat: (*Amount)(big.NewInt(5)), ReceivedSat: (*Amount)(big.NewInt(5)),
SentSat: (*Amount)(big.NewInt(6)), SentSat: (*Amount)(big.NewInt(6)),
SentToSelfSat: (*Amount)(big.NewInt(3)), Time: 1521514812,
Time: 1521514812, Txid: "00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840",
Txid: "00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840", Txs: 1,
Txs: 1,
}, },
{ {
ReceivedSat: (*Amount)(big.NewInt(7)), ReceivedSat: (*Amount)(big.NewInt(7)),
SentSat: (*Amount)(big.NewInt(8)), SentSat: (*Amount)(big.NewInt(8)),
SentToSelfSat: (*Amount)(big.NewInt(3)), Time: 1521504812,
Time: 1521504812, Txid: "00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840",
Txid: "00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840", Txs: 1,
Txs: 1,
}, },
{ {
ReceivedSat: (*Amount)(big.NewInt(9)), ReceivedSat: (*Amount)(big.NewInt(9)),
SentSat: (*Amount)(big.NewInt(10)), SentSat: (*Amount)(big.NewInt(10)),
SentToSelfSat: (*Amount)(big.NewInt(5)), Time: 1521534812,
Time: 1521534812, Txid: "0011223344556677889900112233445566778899001122334455667788990011",
Txid: "0011223344556677889900112233445566778899001122334455667788990011", Txs: 1,
Txs: 1,
}, },
{ {
ReceivedSat: (*Amount)(big.NewInt(11)), ReceivedSat: (*Amount)(big.NewInt(11)),
SentSat: (*Amount)(big.NewInt(12)), SentSat: (*Amount)(big.NewInt(12)),
SentToSelfSat: (*Amount)(big.NewInt(6)), Time: 1521534812,
Time: 1521534812, Txid: "1122334455667788990011223344556677889900112233445566778899001100",
Txid: "1122334455667788990011223344556677889900112233445566778899001100", Txs: 1,
Txs: 1,
}, },
}, },
groupByTime: 3600, groupByTime: 3600,
want: []BalanceHistory{ want: []BalanceHistory{
{ {
ReceivedSat: (*Amount)(big.NewInt(11)), ReceivedSat: (*Amount)(big.NewInt(11)),
SentSat: (*Amount)(big.NewInt(14)), SentSat: (*Amount)(big.NewInt(14)),
SentToSelfSat: (*Amount)(big.NewInt(5)), Time: 1521504000,
Time: 1521504000, Txs: 2,
Txs: 2,
}, },
{ {
ReceivedSat: (*Amount)(big.NewInt(5)), ReceivedSat: (*Amount)(big.NewInt(5)),
SentSat: (*Amount)(big.NewInt(6)), SentSat: (*Amount)(big.NewInt(6)),
SentToSelfSat: (*Amount)(big.NewInt(3)), Time: 1521514800,
Time: 1521514800, Txs: 1,
Txs: 1,
}, },
{ {
ReceivedSat: (*Amount)(big.NewInt(20)), ReceivedSat: (*Amount)(big.NewInt(20)),
SentSat: (*Amount)(big.NewInt(22)), SentSat: (*Amount)(big.NewInt(22)),
SentToSelfSat: (*Amount)(big.NewInt(11)), Time: 1521532800,
Time: 1521532800, Txs: 2,
Txs: 2,
}, },
}, },
}, },

View File

@ -1,9 +1,8 @@
package api package api
import ( import (
"blockbook/bchain"
"math/big" "math/big"
"spacecruft.org/spacecruft/blockbook/bchain"
) )
// ScriptSigV1 is used for legacy api v1 // ScriptSigV1 is used for legacy api v1

View File

@ -1,6 +1,10 @@
package api package api
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/eth"
"blockbook/common"
"blockbook/db"
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
@ -14,10 +18,6 @@ import (
"github.com/golang/glog" "github.com/golang/glog"
"github.com/juju/errors" "github.com/juju/errors"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/eth"
"spacecruft.org/spacecruft/blockbook/common"
"spacecruft.org/spacecruft/blockbook/db"
) )
// Worker is handle to api worker // Worker is handle to api worker
@ -253,7 +253,32 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe
if err != nil { if err != nil {
glog.Errorf("GetErc20FromTx error %v, %v", err, bchainTx) glog.Errorf("GetErc20FromTx error %v, %v", err, bchainTx)
} }
tokens = w.getTokensFromErc20(ets) tokens = make([]TokenTransfer, len(ets))
for i := range ets {
e := &ets[i]
cd, err := w.chainParser.GetAddrDescFromAddress(e.Contract)
if err != nil {
glog.Errorf("GetAddrDescFromAddress error %v, contract %v", err, e.Contract)
continue
}
erc20c, err := w.chain.EthereumTypeGetErc20ContractInfo(cd)
if err != nil {
glog.Errorf("GetErc20ContractInfo error %v, contract %v", err, e.Contract)
}
if erc20c == nil {
erc20c = &bchain.Erc20Contract{Name: e.Contract}
}
tokens[i] = TokenTransfer{
Type: ERC20TokenType,
Token: e.Contract,
From: e.From,
To: e.To,
Decimals: erc20c.Decimals,
Value: (*Amount)(&e.Tokens),
Name: erc20c.Name,
Symbol: erc20c.Symbol,
}
}
ethTxData := eth.GetEthereumTxData(bchainTx) ethTxData := eth.GetEthereumTxData(bchainTx)
// mempool txs do not have fees yet // mempool txs do not have fees yet
if ethTxData.GasUsed != nil { if ethTxData.GasUsed != nil {
@ -268,14 +293,12 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe
GasUsed: ethTxData.GasUsed, GasUsed: ethTxData.GasUsed,
Nonce: ethTxData.Nonce, Nonce: ethTxData.Nonce,
Status: ethTxData.Status, Status: ethTxData.Status,
Data: ethTxData.Data,
} }
} }
// for now do not return size, we would have to compute vsize of segwit transactions // for now do not return size, we would have to compute vsize of segwit transactions
// size:=len(bchainTx.Hex) / 2 // size:=len(bchainTx.Hex) / 2
var sj json.RawMessage var sj json.RawMessage
// return CoinSpecificData for all mempool transactions or if requested if specificJSON {
if specificJSON || bchainTx.Confirmations == 0 {
sj, err = w.chain.GetTransactionSpecific(bchainTx) sj, err = w.chain.GetTransactionSpecific(bchainTx)
if err != nil { if err != nil {
return nil, err return nil, err
@ -300,140 +323,14 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height int, spe
Rbf: rbf, Rbf: rbf,
Vin: vins, Vin: vins,
Vout: vouts, Vout: vouts,
CoinSpecificData: sj, CoinSpecificData: bchainTx.CoinSpecificData,
CoinSpecificJSON: sj,
TokenTransfers: tokens, TokenTransfers: tokens,
EthereumSpecific: ethSpecific, EthereumSpecific: ethSpecific,
} }
return r, nil return r, nil
} }
// GetTransactionFromMempoolTx converts bchain.MempoolTx to Tx, with limited amount of data
// it is not doing any request to backend or to db
func (w *Worker) GetTransactionFromMempoolTx(mempoolTx *bchain.MempoolTx) (*Tx, error) {
var err error
var valInSat, valOutSat, feesSat big.Int
var pValInSat *big.Int
var tokens []TokenTransfer
var ethSpecific *EthereumSpecific
vins := make([]Vin, len(mempoolTx.Vin))
rbf := false
for i := range mempoolTx.Vin {
bchainVin := &mempoolTx.Vin[i]
vin := &vins[i]
vin.Txid = bchainVin.Txid
vin.N = i
vin.Vout = bchainVin.Vout
vin.Sequence = int64(bchainVin.Sequence)
// detect explicit Replace-by-Fee transactions as defined by BIP125
if bchainVin.Sequence < 0xffffffff-1 {
rbf = true
}
vin.Hex = bchainVin.ScriptSig.Hex
vin.Coinbase = bchainVin.Coinbase
if w.chainType == bchain.ChainBitcoinType {
// bchainVin.Txid=="" is coinbase transaction
if bchainVin.Txid != "" {
vin.ValueSat = (*Amount)(&bchainVin.ValueSat)
vin.AddrDesc = bchainVin.AddrDesc
vin.Addresses, vin.IsAddress, _ = w.chainParser.GetAddressesFromAddrDesc(vin.AddrDesc)
if vin.ValueSat != nil {
valInSat.Add(&valInSat, (*big.Int)(vin.ValueSat))
}
}
} else if w.chainType == bchain.ChainEthereumType {
if len(bchainVin.Addresses) > 0 {
vin.AddrDesc, err = w.chainParser.GetAddrDescFromAddress(bchainVin.Addresses[0])
if err != nil {
glog.Errorf("GetAddrDescFromAddress error %v, tx %v, bchainVin %v", err, mempoolTx.Txid, bchainVin)
}
vin.Addresses = bchainVin.Addresses
vin.IsAddress = true
}
}
}
vouts := make([]Vout, len(mempoolTx.Vout))
for i := range mempoolTx.Vout {
bchainVout := &mempoolTx.Vout[i]
vout := &vouts[i]
vout.N = i
vout.ValueSat = (*Amount)(&bchainVout.ValueSat)
valOutSat.Add(&valOutSat, &bchainVout.ValueSat)
vout.Hex = bchainVout.ScriptPubKey.Hex
vout.AddrDesc, vout.Addresses, vout.IsAddress, err = w.getAddressesFromVout(bchainVout)
if err != nil {
glog.V(2).Infof("getAddressesFromVout error %v, %v, output %v", err, mempoolTx.Txid, bchainVout.N)
}
}
if w.chainType == bchain.ChainBitcoinType {
// for coinbase transactions valIn is 0
feesSat.Sub(&valInSat, &valOutSat)
if feesSat.Sign() == -1 {
feesSat.SetUint64(0)
}
pValInSat = &valInSat
} else if w.chainType == bchain.ChainEthereumType {
if len(mempoolTx.Vout) > 0 {
valOutSat = mempoolTx.Vout[0].ValueSat
}
tokens = w.getTokensFromErc20(mempoolTx.Erc20)
ethTxData := eth.GetEthereumTxDataFromSpecificData(mempoolTx.CoinSpecificData)
ethSpecific = &EthereumSpecific{
GasLimit: ethTxData.GasLimit,
GasPrice: (*Amount)(ethTxData.GasPrice),
GasUsed: ethTxData.GasUsed,
Nonce: ethTxData.Nonce,
Status: ethTxData.Status,
Data: ethTxData.Data,
}
}
r := &Tx{
Blocktime: mempoolTx.Blocktime,
FeesSat: (*Amount)(&feesSat),
Locktime: mempoolTx.LockTime,
Txid: mempoolTx.Txid,
ValueInSat: (*Amount)(pValInSat),
ValueOutSat: (*Amount)(&valOutSat),
Version: mempoolTx.Version,
Hex: mempoolTx.Hex,
Rbf: rbf,
Vin: vins,
Vout: vouts,
TokenTransfers: tokens,
EthereumSpecific: ethSpecific,
}
return r, nil
}
func (w *Worker) getTokensFromErc20(erc20 []bchain.Erc20Transfer) []TokenTransfer {
tokens := make([]TokenTransfer, len(erc20))
for i := range erc20 {
e := &erc20[i]
cd, err := w.chainParser.GetAddrDescFromAddress(e.Contract)
if err != nil {
glog.Errorf("GetAddrDescFromAddress error %v, contract %v", err, e.Contract)
continue
}
erc20c, err := w.chain.EthereumTypeGetErc20ContractInfo(cd)
if err != nil {
glog.Errorf("GetErc20ContractInfo error %v, contract %v", err, e.Contract)
}
if erc20c == nil {
erc20c = &bchain.Erc20Contract{Name: e.Contract}
}
tokens[i] = TokenTransfer{
Type: ERC20TokenType,
Token: e.Contract,
From: e.From,
To: e.To,
Decimals: erc20c.Decimals,
Value: (*Amount)(&e.Tokens),
Name: erc20c.Name,
Symbol: erc20c.Symbol,
}
}
return tokens
}
func (w *Worker) getAddressTxids(addrDesc bchain.AddressDescriptor, mempool bool, filter *AddressFilter, maxResults int) ([]string, error) { func (w *Worker) getAddressTxids(addrDesc bchain.AddressDescriptor, mempool bool, filter *AddressFilter, maxResults int) ([]string, error) {
var err error var err error
txids := make([]string, 0, 4) txids := make([]string, 0, 4)
@ -503,19 +400,6 @@ func (t *Tx) getAddrVoutValue(addrDesc bchain.AddressDescriptor) *big.Int {
} }
return &val return &val
} }
func (t *Tx) getAddrEthereumTypeMempoolInputValue(addrDesc bchain.AddressDescriptor) *big.Int {
var val big.Int
if len(t.Vin) > 0 && len(t.Vout) > 0 && bytes.Equal(t.Vin[0].AddrDesc, addrDesc) {
val.Add(&val, (*big.Int)(t.Vout[0].ValueSat))
// add maximum possible fee (the used value is not yet known)
if t.EthereumSpecific != nil && t.EthereumSpecific.GasLimit != nil && t.EthereumSpecific.GasPrice != nil {
var fees big.Int
fees.Mul((*big.Int)(t.EthereumSpecific.GasPrice), t.EthereumSpecific.GasLimit)
val.Add(&val, &fees)
}
}
return &val
}
func (t *Tx) getAddrVinValue(addrDesc bchain.AddressDescriptor) *big.Int { func (t *Tx) getAddrVinValue(addrDesc bchain.AddressDescriptor) *big.Int {
var val big.Int var val big.Int
@ -612,44 +496,6 @@ func computePaging(count, page, itemsOnPage int) (Paging, int, int, int) {
}, from, to, page }, from, to, page
} }
func (w *Worker) getEthereumToken(index int, addrDesc, contract bchain.AddressDescriptor, details AccountDetails, txs int) (*Token, error) {
var b *big.Int
validContract := true
ci, err := w.chain.EthereumTypeGetErc20ContractInfo(contract)
if err != nil {
return nil, errors.Annotatef(err, "EthereumTypeGetErc20ContractInfo %v", contract)
}
if ci == nil {
ci = &bchain.Erc20Contract{}
addresses, _, _ := w.chainParser.GetAddressesFromAddrDesc(contract)
if len(addresses) > 0 {
ci.Contract = addresses[0]
ci.Name = addresses[0]
}
validContract = false
}
// do not read contract balances etc in case of Basic option
if details >= AccountDetailsTokenBalances && validContract {
b, err = w.chain.EthereumTypeGetErc20ContractBalance(addrDesc, contract)
if err != nil {
// return nil, nil, nil, errors.Annotatef(err, "EthereumTypeGetErc20ContractBalance %v %v", addrDesc, c.Contract)
glog.Warningf("EthereumTypeGetErc20ContractBalance addr %v, contract %v, %v", addrDesc, contract, err)
}
} else {
b = nil
}
return &Token{
Type: ERC20TokenType,
BalanceSat: (*Amount)(b),
Contract: ci.Contract,
Name: ci.Name,
Symbol: ci.Symbol,
Transfers: txs,
Decimals: ci.Decimals,
ContractIndex: strconv.Itoa(index),
}, nil
}
func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescriptor, details AccountDetails, filter *AddressFilter) (*db.AddrBalance, []Token, *bchain.Erc20Contract, uint64, int, int, error) { func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescriptor, details AccountDetails, filter *AddressFilter) (*db.AddrBalance, []Token, *bchain.Erc20Contract, uint64, int, int, error) {
var ( var (
ba *db.AddrBalance ba *db.AddrBalance
@ -668,13 +514,6 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
if err != nil { if err != nil {
return nil, nil, nil, 0, 0, 0, errors.Annotatef(err, "EthereumTypeGetBalance %v", addrDesc) return nil, nil, nil, 0, 0, 0, errors.Annotatef(err, "EthereumTypeGetBalance %v", addrDesc)
} }
var filterDesc bchain.AddressDescriptor
if filter.Contract != "" {
filterDesc, err = w.chainParser.GetAddrDescFromAddress(filter.Contract)
if err != nil {
return nil, nil, nil, 0, 0, 0, NewAPIError(fmt.Sprintf("Invalid contract filter, %v", err), true)
}
}
if ca != nil { if ca != nil {
ba = &db.AddrBalance{ ba = &db.AddrBalance{
Txs: uint32(ca.TotalTxs), Txs: uint32(ca.TotalTxs),
@ -686,6 +525,13 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
if err != nil { if err != nil {
return nil, nil, nil, 0, 0, 0, errors.Annotatef(err, "EthereumTypeGetNonce %v", addrDesc) return nil, nil, nil, 0, 0, 0, errors.Annotatef(err, "EthereumTypeGetNonce %v", addrDesc)
} }
var filterDesc bchain.AddressDescriptor
if filter.Contract != "" {
filterDesc, err = w.chainParser.GetAddrDescFromAddress(filter.Contract)
if err != nil {
return nil, nil, nil, 0, 0, 0, NewAPIError(fmt.Sprintf("Invalid contract filter, %v", err), true)
}
}
if details > AccountDetailsBasic { if details > AccountDetailsBasic {
tokens = make([]Token, len(ca.Contracts)) tokens = make([]Token, len(ca.Contracts))
var j int var j int
@ -697,26 +543,43 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
// filter only transactions of this contract // filter only transactions of this contract
filter.Vout = i + 1 filter.Vout = i + 1
} }
t, err := w.getEthereumToken(i+1, addrDesc, c.Contract, details, int(c.Txs)) validContract := true
ci, err := w.chain.EthereumTypeGetErc20ContractInfo(c.Contract)
if err != nil { if err != nil {
return nil, nil, nil, 0, 0, 0, err return nil, nil, nil, 0, 0, 0, errors.Annotatef(err, "EthereumTypeGetErc20ContractInfo %v", c.Contract)
}
if ci == nil {
ci = &bchain.Erc20Contract{}
addresses, _, _ := w.chainParser.GetAddressesFromAddrDesc(c.Contract)
if len(addresses) > 0 {
ci.Contract = addresses[0]
ci.Name = addresses[0]
}
validContract = false
}
// do not read contract balances etc in case of Basic option
if details >= AccountDetailsTokenBalances && validContract {
b, err = w.chain.EthereumTypeGetErc20ContractBalance(addrDesc, c.Contract)
if err != nil {
// return nil, nil, nil, errors.Annotatef(err, "EthereumTypeGetErc20ContractBalance %v %v", addrDesc, c.Contract)
glog.Warningf("EthereumTypeGetErc20ContractBalance addr %v, contract %v, %v", addrDesc, c.Contract, err)
}
} else {
b = nil
}
tokens[j] = Token{
Type: ERC20TokenType,
BalanceSat: (*Amount)(b),
Contract: ci.Contract,
Name: ci.Name,
Symbol: ci.Symbol,
Transfers: int(c.Txs),
Decimals: ci.Decimals,
ContractIndex: strconv.Itoa(i + 1),
} }
tokens[j] = *t
j++ j++
} }
// special handling if filter has contract tokens = tokens[:j]
// if the address has no transactions with given contract, check the balance, the address may have some balance even without transactions
if len(filterDesc) > 0 && j == 0 && details >= AccountDetailsTokens {
t, err := w.getEthereumToken(0, addrDesc, filterDesc, details, 0)
if err != nil {
return nil, nil, nil, 0, 0, 0, err
}
tokens = []Token{*t}
// switch off query for transactions, there are no transactions
filter.Vout = AddressFilterVoutQueryNotNecessary
} else {
tokens = tokens[:j]
}
} }
ci, err = w.chain.EthereumTypeGetErc20ContractInfo(addrDesc) ci, err = w.chain.EthereumTypeGetErc20ContractInfo(addrDesc)
if err != nil { if err != nil {
@ -730,8 +593,6 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
totalResults = int(ca.NonContractTxs) totalResults = int(ca.NonContractTxs)
} else if filter.Vout > 0 && filter.Vout-1 < len(ca.Contracts) { } else if filter.Vout > 0 && filter.Vout-1 < len(ca.Contracts) {
totalResults = int(ca.Contracts[filter.Vout-1].Txs) totalResults = int(ca.Contracts[filter.Vout-1].Txs)
} else if filter.Vout == AddressFilterVoutQueryNotNecessary {
totalResults = 0
} }
} }
nonContractTxs = int(ca.NonContractTxs) nonContractTxs = int(ca.NonContractTxs)
@ -742,16 +603,6 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
BalanceSat: *b, BalanceSat: *b,
} }
} }
// special handling if filtering for a contract, check the ballance of it
if len(filterDesc) > 0 && details >= AccountDetailsTokens {
t, err := w.getEthereumToken(0, addrDesc, filterDesc, details, 0)
if err != nil {
return nil, nil, nil, 0, 0, 0, err
}
tokens = []Token{*t}
// switch off query for transactions, there are no transactions
filter.Vout = AddressFilterVoutQueryNotNecessary
}
} }
return ba, tokens, ci, n, nonContractTxs, totalResults, nil return ba, tokens, ci, n, nonContractTxs, totalResults, nil
} }
@ -768,7 +619,7 @@ func (w *Worker) txFromTxid(txid string, bestheight uint32, option AccountDetail
if ta == nil { if ta == nil {
glog.Warning("DB inconsistency: tx ", txid, ": not found in txAddresses") glog.Warning("DB inconsistency: tx ", txid, ": not found in txAddresses")
// as fallback, get tx from backend // as fallback, get tx from backend
tx, err = w.GetTransaction(txid, false, false) tx, err = w.GetTransaction(txid, false, true)
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "GetTransaction %v", txid) return nil, errors.Annotatef(err, "GetTransaction %v", txid)
} }
@ -787,7 +638,7 @@ func (w *Worker) txFromTxid(txid string, bestheight uint32, option AccountDetail
tx = w.txFromTxAddress(txid, ta, blockInfo, bestheight) tx = w.txFromTxAddress(txid, ta, blockInfo, bestheight)
} }
} else { } else {
tx, err = w.GetTransaction(txid, false, false) tx, err = w.GetTransaction(txid, false, true)
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "GetTransaction %v", txid) return nil, errors.Annotatef(err, "GetTransaction %v", txid)
} }
@ -876,7 +727,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco
return nil, errors.Annotatef(err, "getAddressTxids %v true", addrDesc) return nil, errors.Annotatef(err, "getAddressTxids %v true", addrDesc)
} }
for _, txid := range txm { for _, txid := range txm {
tx, err := w.GetTransaction(txid, false, true) tx, err := w.GetTransaction(txid, false, false)
// mempool transaction may fail // mempool transaction may fail
if err != nil || tx == nil { if err != nil || tx == nil {
glog.Warning("GetTransaction in mempool: ", err) glog.Warning("GetTransaction in mempool: ", err)
@ -885,12 +736,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco
if tx.Confirmations == 0 { if tx.Confirmations == 0 {
unconfirmedTxs++ unconfirmedTxs++
uBalSat.Add(&uBalSat, tx.getAddrVoutValue(addrDesc)) uBalSat.Add(&uBalSat, tx.getAddrVoutValue(addrDesc))
// ethereum has a different logic - value not in input and add maximum possible fees uBalSat.Sub(&uBalSat, tx.getAddrVinValue(addrDesc))
if w.chainType == bchain.ChainEthereumType {
uBalSat.Sub(&uBalSat, tx.getAddrEthereumTypeMempoolInputValue(addrDesc))
} else {
uBalSat.Sub(&uBalSat, tx.getAddrVinValue(addrDesc))
}
if page == 0 { if page == 0 {
if option == AccountDetailsTxidHistory { if option == AccountDetailsTxidHistory {
txids = append(txids, tx.Txid) txids = append(txids, tx.Txid)
@ -903,7 +749,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco
} }
} }
// get tx history if requested by option or check mempool if there are some transactions for a new address // get tx history if requested by option or check mempool if there are some transactions for a new address
if option >= AccountDetailsTxidHistory && filter.Vout != AddressFilterVoutQueryNotNecessary { if option >= AccountDetailsTxidHistory {
txc, err := w.getAddressTxids(addrDesc, false, filter, (page+1)*txsOnPage) txc, err := w.getAddressTxids(addrDesc, false, filter, (page+1)*txsOnPage)
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "getAddressTxids %v false", addrDesc) return nil, errors.Annotatef(err, "getAddressTxids %v false", addrDesc)
@ -974,7 +820,7 @@ func (w *Worker) balanceHistoryHeightsFromTo(fromTimestamp, toTimestamp int64) (
return fromUnix, fromHeight, toUnix, toHeight return fromUnix, fromHeight, toUnix, toHeight
} }
func (w *Worker) balanceHistoryForTxid(addrDesc bchain.AddressDescriptor, txid string, fromUnix, toUnix uint32, selfAddrDesc map[string]struct{}) (*BalanceHistory, error) { func (w *Worker) balanceHistoryForTxid(addrDesc bchain.AddressDescriptor, txid string, fromUnix, toUnix uint32) (*BalanceHistory, error) {
var time uint32 var time uint32
var err error var err error
var ta *db.TxAddresses var ta *db.TxAddresses
@ -1007,30 +853,17 @@ func (w *Worker) balanceHistoryForTxid(addrDesc bchain.AddressDescriptor, txid s
return nil, nil return nil, nil
} }
bh := BalanceHistory{ bh := BalanceHistory{
Time: time, Time: time,
Txs: 1, Txs: 1,
ReceivedSat: &Amount{}, SentSat: &Amount{},
SentSat: &Amount{}, ReceivedSat: &Amount{},
SentToSelfSat: &Amount{}, Txid: txid,
Txid: txid,
} }
countSentToSelf := false
if w.chainType == bchain.ChainBitcoinType { if w.chainType == bchain.ChainBitcoinType {
// detect if this input is the first of selfAddrDesc
// to not to count sentToSelf multiple times if counting multiple xpub addresses
ownInputIndex := -1
for i := range ta.Inputs { for i := range ta.Inputs {
tai := &ta.Inputs[i] tai := &ta.Inputs[i]
if _, found := selfAddrDesc[string(tai.AddrDesc)]; found {
if ownInputIndex < 0 {
ownInputIndex = i
}
}
if bytes.Equal(addrDesc, tai.AddrDesc) { if bytes.Equal(addrDesc, tai.AddrDesc) {
(*big.Int)(bh.SentSat).Add((*big.Int)(bh.SentSat), &tai.ValueSat) (*big.Int)(bh.SentSat).Add((*big.Int)(bh.SentSat), &tai.ValueSat)
if ownInputIndex == i {
countSentToSelf = true
}
} }
} }
for i := range ta.Outputs { for i := range ta.Outputs {
@ -1038,17 +871,12 @@ func (w *Worker) balanceHistoryForTxid(addrDesc bchain.AddressDescriptor, txid s
if bytes.Equal(addrDesc, tao.AddrDesc) { if bytes.Equal(addrDesc, tao.AddrDesc) {
(*big.Int)(bh.ReceivedSat).Add((*big.Int)(bh.ReceivedSat), &tao.ValueSat) (*big.Int)(bh.ReceivedSat).Add((*big.Int)(bh.ReceivedSat), &tao.ValueSat)
} }
if countSentToSelf {
if _, found := selfAddrDesc[string(tao.AddrDesc)]; found {
(*big.Int)(bh.SentToSelfSat).Add((*big.Int)(bh.SentToSelfSat), &tao.ValueSat)
}
}
} }
} else if w.chainType == bchain.ChainEthereumType { } else if w.chainType == bchain.ChainEthereumType {
var value big.Int var value big.Int
ethTxData := eth.GetEthereumTxData(bchainTx) ethTxData := eth.GetEthereumTxData(bchainTx)
// add received amount only for OK or unknown status (old) transactions // add received amount only for OK transactions
if ethTxData.Status == eth.TxStatusOK || ethTxData.Status == eth.TxStatusUnknown { if ethTxData.Status == 1 {
if len(bchainTx.Vout) > 0 { if len(bchainTx.Vout) > 0 {
bchainVout := &bchainTx.Vout[0] bchainVout := &bchainTx.Vout[0]
value = bchainVout.ValueSat value = bchainVout.ValueSat
@ -1060,9 +888,6 @@ func (w *Worker) balanceHistoryForTxid(addrDesc bchain.AddressDescriptor, txid s
if bytes.Equal(addrDesc, txAddrDesc) { if bytes.Equal(addrDesc, txAddrDesc) {
(*big.Int)(bh.ReceivedSat).Add((*big.Int)(bh.ReceivedSat), &value) (*big.Int)(bh.ReceivedSat).Add((*big.Int)(bh.ReceivedSat), &value)
} }
if _, found := selfAddrDesc[string(txAddrDesc)]; found {
countSentToSelf = true
}
} }
} }
} }
@ -1074,14 +899,9 @@ func (w *Worker) balanceHistoryForTxid(addrDesc bchain.AddressDescriptor, txid s
return nil, err return nil, err
} }
if bytes.Equal(addrDesc, txAddrDesc) { if bytes.Equal(addrDesc, txAddrDesc) {
// add received amount only for OK or unknown status (old) transactions, fees always // add sent amount only for OK transactions, however fees always
if ethTxData.Status == eth.TxStatusOK || ethTxData.Status == eth.TxStatusUnknown { if ethTxData.Status == 1 {
(*big.Int)(bh.SentSat).Add((*big.Int)(bh.SentSat), &value) (*big.Int)(bh.SentSat).Add((*big.Int)(bh.SentSat), &value)
if countSentToSelf {
if _, found := selfAddrDesc[string(txAddrDesc)]; found {
(*big.Int)(bh.SentToSelfSat).Add((*big.Int)(bh.SentToSelfSat), &value)
}
}
} }
var feesSat big.Int var feesSat big.Int
// mempool txs do not have fees yet // mempool txs do not have fees yet
@ -1142,9 +962,8 @@ func (w *Worker) GetBalanceHistory(address string, fromTimestamp, toTimestamp in
if err != nil { if err != nil {
return nil, err return nil, err
} }
selfAddrDesc := map[string]struct{}{string(addrDesc): {}}
for txi := len(txs) - 1; txi >= 0; txi-- { for txi := len(txs) - 1; txi >= 0; txi-- {
bh, err := w.balanceHistoryForTxid(addrDesc, txs[txi], fromUnix, toUnix, selfAddrDesc) bh, err := w.balanceHistoryForTxid(addrDesc, txs[txi], fromUnix, toUnix)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -1774,7 +1593,7 @@ func (w *Worker) GetSystemInfo(internal bool) (*SystemInfo, error) {
DbColumns: columnStats, DbColumns: columnStats,
About: Text.BlockbookAbout, About: Text.BlockbookAbout,
} }
backendInfo := &common.BackendInfo{ backendInfo := &BackendInfo{
BackendError: backendError, BackendError: backendError,
BestBlockHash: ci.Bestblockhash, BestBlockHash: ci.Bestblockhash,
Blocks: ci.Blocks, Blocks: ci.Blocks,
@ -1787,9 +1606,7 @@ func (w *Worker) GetSystemInfo(internal bool) (*SystemInfo, error) {
Timeoffset: ci.Timeoffset, Timeoffset: ci.Timeoffset,
Version: ci.Version, Version: ci.Version,
Warnings: ci.Warnings, Warnings: ci.Warnings,
Consensus: ci.Consensus,
} }
w.is.SetBackendInfo(backendInfo)
glog.Info("GetSystemInfo finished in ", time.Since(start)) glog.Info("GetSystemInfo finished in ", time.Since(start))
return &SystemInfo{blockbookInfo, backendInfo}, nil return &SystemInfo{blockbookInfo, backendInfo}, nil
} }

View File

@ -1,6 +1,8 @@
package api package api
import ( import (
"blockbook/bchain"
"blockbook/db"
"fmt" "fmt"
"math/big" "math/big"
"sort" "sort"
@ -9,8 +11,6 @@ import (
"github.com/golang/glog" "github.com/golang/glog"
"github.com/juju/errors" "github.com/juju/errors"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/db"
) )
const defaultAddressesGap = 20 const defaultAddressesGap = 20
@ -416,7 +416,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc
// the same tx can have multiple addresses from the same xpub, get it from backend it only once // the same tx can have multiple addresses from the same xpub, get it from backend it only once
tx, foundTx := txmMap[txid.txid] tx, foundTx := txmMap[txid.txid]
if !foundTx { if !foundTx {
tx, err = w.GetTransaction(txid.txid, false, true) tx, err = w.GetTransaction(txid.txid, false, false)
// mempool transaction may fail // mempool transaction may fail
if err != nil || tx == nil { if err != nil || tx == nil {
glog.Warning("GetTransaction in mempool: ", err) glog.Warning("GetTransaction in mempool: ", err)
@ -606,18 +606,12 @@ func (w *Worker) GetXpubBalanceHistory(xpub string, fromTimestamp, toTimestamp i
if err != nil { if err != nil {
return nil, err return nil, err
} }
selfAddrDesc := make(map[string]struct{})
for _, da := range [][]xpubAddress{data.addresses, data.changeAddresses} {
for i := range da {
selfAddrDesc[string(da[i].addrDesc)] = struct{}{}
}
}
for _, da := range [][]xpubAddress{data.addresses, data.changeAddresses} { for _, da := range [][]xpubAddress{data.addresses, data.changeAddresses} {
for i := range da { for i := range da {
ad := &da[i] ad := &da[i]
txids := ad.txids txids := ad.txids
for txi := len(txids) - 1; txi >= 0; txi-- { for txi := len(txids) - 1; txi >= 0; txi-- {
bh, err := w.balanceHistoryForTxid(ad.addrDesc, txids[txi].txid, fromUnix, toUnix, selfAddrDesc) bh, err := w.balanceHistoryForTxid(ad.addrDesc, txids[txi].txid, fromUnix, toUnix)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -3,7 +3,6 @@ package bchain
import ( import (
"sort" "sort"
"sync" "sync"
"time"
) )
type addrIndex struct { type addrIndex struct {
@ -28,7 +27,6 @@ type BaseMempool struct {
txEntries map[string]txEntry txEntries map[string]txEntry
addrDescToTx map[string][]Outpoint addrDescToTx map[string][]Outpoint
OnNewTxAddr OnNewTxAddrFunc OnNewTxAddr OnNewTxAddrFunc
OnNewTx OnNewTxFunc
} }
// GetTransactions returns slice of mempool transactions for given address // GetTransactions returns slice of mempool transactions for given address
@ -115,22 +113,3 @@ func (m *BaseMempool) GetTransactionTime(txid string) uint32 {
} }
return e.time return e.time
} }
func (m *BaseMempool) txToMempoolTx(tx *Tx) *MempoolTx {
mtx := MempoolTx{
Hex: tx.Hex,
Blocktime: time.Now().Unix(),
LockTime: tx.LockTime,
Txid: tx.Txid,
Version: tx.Version,
Vout: tx.Vout,
CoinSpecificData: tx.CoinSpecificData,
}
mtx.Vin = make([]MempoolVin, len(tx.Vin))
for i, vin := range tx.Vin {
mtx.Vin[i] = MempoolVin{
Vin: vin,
}
}
return &mtx
}

View File

@ -9,7 +9,6 @@ import (
"github.com/gogo/protobuf/proto" "github.com/gogo/protobuf/proto"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/juju/errors" "github.com/juju/errors"
"spacecruft.org/spacecruft/blockbook/common"
) )
// BaseParser implements data parsing/handling functionality base for all other parsers // BaseParser implements data parsing/handling functionality base for all other parsers
@ -40,9 +39,9 @@ func (p *BaseParser) GetAddrDescForUnknownInput(tx *Tx, input int) AddressDescri
const zeros = "0000000000000000000000000000000000000000" const zeros = "0000000000000000000000000000000000000000"
// AmountToBigInt converts amount in common.JSONNumber (string) to big.Int // AmountToBigInt converts amount in json.Number (string) to big.Int
// it uses string operations to avoid problems with rounding // it uses string operations to avoid problems with rounding
func (p *BaseParser) AmountToBigInt(n common.JSONNumber) (big.Int, error) { func (p *BaseParser) AmountToBigInt(n json.Number) (big.Int, error) {
var r big.Int var r big.Int
s := string(n) s := string(n)
i := strings.IndexByte(s, '.') i := strings.IndexByte(s, '.')

View File

@ -1,12 +1,9 @@
// +build unittest
package bchain package bchain
import ( import (
"encoding/json"
"math/big" "math/big"
"testing" "testing"
"spacecruft.org/spacecruft/blockbook/common"
) )
func NewBaseParser(adp int) *BaseParser { func NewBaseParser(adp int) *BaseParser {
@ -47,7 +44,7 @@ func TestBaseParser_AmountToDecimalString(t *testing.T) {
func TestBaseParser_AmountToBigInt(t *testing.T) { func TestBaseParser_AmountToBigInt(t *testing.T) {
for _, tt := range amounts { for _, tt := range amounts {
t.Run(tt.s, func(t *testing.T) { t.Run(tt.s, func(t *testing.T) {
got, err := NewBaseParser(tt.adp).AmountToBigInt(common.JSONNumber(tt.s)) got, err := NewBaseParser(tt.adp).AmountToBigInt(json.Number(tt.s))
if err != nil { if err != nil {
t.Errorf("BaseParser.AmountToBigInt() error = %v", err) t.Errorf("BaseParser.AmountToBigInt() error = %v", err)
return return

View File

@ -1,6 +1,8 @@
package bch package bch
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"fmt" "fmt"
"github.com/martinboehm/bchutil" "github.com/martinboehm/bchutil"
@ -8,8 +10,6 @@ import (
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"github.com/martinboehm/btcutil/txscript" "github.com/martinboehm/btcutil/txscript"
"github.com/schancel/cashaddr-converter/address" "github.com/schancel/cashaddr-converter/address"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// AddressFormat type is used to specify different formats of address // AddressFormat type is used to specify different formats of address

View File

@ -3,6 +3,8 @@
package bch package bch
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/hex" "encoding/hex"
"math/big" "math/big"
"os" "os"
@ -10,8 +12,6 @@ import (
"testing" "testing"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {

View File

@ -1,6 +1,8 @@
package bch package bch
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"math/big" "math/big"
@ -8,8 +10,6 @@ import (
"github.com/golang/glog" "github.com/golang/glog"
"github.com/juju/errors" "github.com/juju/errors"
"github.com/martinboehm/bchutil" "github.com/martinboehm/bchutil"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// BCashRPC is an interface to JSON-RPC bitcoind service. // BCashRPC is an interface to JSON-RPC bitcoind service.

View File

@ -1,9 +1,10 @@
package bellcoin package bellcoin
import ( import (
"blockbook/bchain/coins/btc"
"github.com/martinboehm/btcd/wire" "github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// magic numbers // magic numbers

View File

@ -3,6 +3,8 @@
package bellcoin package bellcoin
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/hex" "encoding/hex"
"math/big" "math/big"
"os" "os"
@ -10,8 +12,6 @@ import (
"testing" "testing"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {

View File

@ -1,11 +1,11 @@
package bellcoin package bellcoin
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json" "encoding/json"
"github.com/golang/glog" "github.com/golang/glog"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// BellcoinRPC is an interface to JSON-RPC bitcoind service. // BellcoinRPC is an interface to JSON-RPC bitcoind service.

View File

@ -1,10 +1,10 @@
package bitcore package bitcore
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"github.com/martinboehm/btcd/wire" "github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
const ( const (

View File

@ -3,15 +3,14 @@
package bitcore package bitcore
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/hex" "encoding/hex"
"github.com/martinboehm/btcutil/chaincfg"
"math/big" "math/big"
"os" "os"
"reflect" "reflect"
"testing" "testing"
"github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {

View File

@ -1,12 +1,11 @@
package bitcore package bitcore
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json" "encoding/json"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/juju/errors" "github.com/juju/errors"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// BitcoreRPC is an interface to JSON-RPC bitcoind service. // BitcoreRPC is an interface to JSON-RPC bitcoind service.

View File

@ -1,64 +0,0 @@
package bitzeny
import (
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
"github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg"
)
// magic numbers
const (
MainnetMagic wire.BitcoinNet = 0xf9bea5da
TestnetMagic wire.BitcoinNet = 0x594e4559
)
// chain parameters
var (
MainNetParams chaincfg.Params
TestNetParams chaincfg.Params
)
func init() {
MainNetParams = chaincfg.MainNetParams
MainNetParams.Net = MainnetMagic
MainNetParams.PubKeyHashAddrID = []byte{81}
MainNetParams.ScriptHashAddrID = []byte{5}
MainNetParams.Bech32HRPSegwit = "bz"
TestNetParams = chaincfg.TestNet3Params
TestNetParams.Net = TestnetMagic
TestNetParams.PubKeyHashAddrID = []byte{111}
TestNetParams.ScriptHashAddrID = []byte{196}
TestNetParams.Bech32HRPSegwit = "tz"
}
// BitZenyParser handle
type BitZenyParser struct {
*btc.BitcoinParser
}
// NewBitZenyParser returns new BitZenyParser instance
func NewBitZenyParser(params *chaincfg.Params, c *btc.Configuration) *BitZenyParser {
return &BitZenyParser{BitcoinParser: btc.NewBitcoinParser(params, c)}
}
// GetChainParams contains network parameters for the main BitZeny network,
// and the test BitZeny network
func GetChainParams(chain string) *chaincfg.Params {
if !chaincfg.IsRegistered(&MainNetParams) {
err := chaincfg.Register(&MainNetParams)
if err == nil {
err = chaincfg.Register(&TestNetParams)
}
if err != nil {
panic(err)
}
}
switch chain {
case "test":
return &TestNetParams
default:
return &MainNetParams
}
}

View File

@ -1,290 +0,0 @@
// +build unittest
package bitzeny
import (
"encoding/hex"
"math/big"
"os"
"reflect"
"testing"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
"github.com/martinboehm/btcutil/chaincfg"
)
func TestMain(m *testing.M) {
c := m.Run()
chaincfg.ResetParams()
os.Exit(c)
}
func Test_GetAddrDescFromAddress_Mainnet(t *testing.T) {
type args struct {
address string
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "P2PKH1",
args: args{address: "Zw74N1RSU2xV3a7SBERBiCP11fMwX5yvMu"},
want: "76a914d8658ca5c406149071687d370d1d22d972d2f88488ac",
wantErr: false,
},
{
name: "P2PKH2",
args: args{address: "ZiSn1vTSxGu2kFcnkjjm7bYGhT5BVAVfEG"},
want: "76a9144d869697281ad18370313122795e56dfdc3a331388ac",
wantErr: false,
},
{
name: "P2SH1",
args: args{address: "3CZ3357bm1K81StpEDQtEH3ho3ULx19nc8"},
want: "a9147726fc1144eae1b7bd301d87d0a7f846cadb591887",
wantErr: false,
},
{
name: "P2SH2",
args: args{address: "3M1AjZEuBzScbd9pchiGJSVT4yNfwzSmXP"},
want: "a914d3d93b5d7f57b94a4fecde93d4489f2b423fd3c287",
wantErr: false,
},
{
name: "witness_v0_keyhash",
args: args{address: "bz1q7rfrdacyyfwx8gppd8ah9hka8npgqsm44prfnd"},
want: "0014f0d236f704225c63a02169fb72dedd3cc2804375",
wantErr: false,
},
{
name: "witness_v0_scripthashx",
args: args{address: "bz1qd2mspe6m2wpztw4q2mccyvyess6569eu59sfvf0u0vdmdwltr5lse8d7sw"},
want: "00206ab700e75b538225baa056f182309984354d173ca1609625fc7b1bb6bbeb1d3f",
wantErr: false,
},
}
parser := NewBitZenyParser(GetChainParams("main"), &btc.Configuration{})
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := parser.GetAddrDescFromAddress(tt.args.address)
if (err != nil) != tt.wantErr {
t.Errorf("GetAddrDescFromAddress() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("GetAddrDescFromAddress() = %v, want %v", h, tt.want)
}
})
}
}
func Test_GetAddressesFromAddrDesc(t *testing.T) {
type args struct {
script string
}
tests := []struct {
name string
args args
want []string
want2 bool
wantErr bool
}{
{
name: "P2PKH",
args: args{script: "76a914d8658ca5c406149071687d370d1d22d972d2f88488ac"},
want: []string{"Zw74N1RSU2xV3a7SBERBiCP11fMwX5yvMu"},
want2: true,
wantErr: false,
},
{
name: "P2SH",
args: args{script: "a9147726fc1144eae1b7bd301d87d0a7f846cadb591887"},
want: []string{"3CZ3357bm1K81StpEDQtEH3ho3ULx19nc8"},
want2: true,
wantErr: false,
},
{
name: "P2WPKH",
args: args{script: "0014f0d236f704225c63a02169fb72dedd3cc2804375"},
want: []string{"bz1q7rfrdacyyfwx8gppd8ah9hka8npgqsm44prfnd"},
want2: true,
wantErr: false,
},
{
name: "P2WSH",
args: args{script: "00206ab700e75b538225baa056f182309984354d173ca1609625fc7b1bb6bbeb1d3f"},
want: []string{"bz1qd2mspe6m2wpztw4q2mccyvyess6569eu59sfvf0u0vdmdwltr5lse8d7sw"},
want2: true,
wantErr: false,
},
{
name: "OP_RETURN ascii",
args: args{script: "6a0461686f6a"},
want: []string{"OP_RETURN (ahoj)"},
want2: false,
wantErr: false,
},
{
name: "OP_RETURN hex",
args: args{script: "6a072020f1686f6a20"},
want: []string{"OP_RETURN 2020f1686f6a20"},
want2: false,
wantErr: false,
},
}
parser := NewBitZenyParser(GetChainParams("main"), &btc.Configuration{})
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b, _ := hex.DecodeString(tt.args.script)
got, got2, err := parser.GetAddressesFromAddrDesc(b)
if (err != nil) != tt.wantErr {
t.Errorf("outputScriptToAddresses() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("GetAddressesFromAddrDesc() = %v, want %v", got, tt.want)
}
if !reflect.DeepEqual(got2, tt.want2) {
t.Errorf("GetAddressesFromAddrDesc() = %v, want %v", got2, tt.want2)
}
})
}
}
var (
testTx1 bchain.Tx
testTxPacked1 = "001c3f1a8be6859d3e0100000001aef422fb91cd91e556966fed4121ac44017a761d71385596536bb447ae05213e000000006a47304402202341ac4297925257dc72eb418a069c45e76f7070340e27501f6308cc7eff45f802204a347915adceff5f6fc9b8075d95d47887d46b344c7bbc8066d315931b189ad001210228c2520812b7f8c63e7a088c61b6348b22fa0c98812e736a1fd896bc828d3c65feffffff028041f13d000000001976a91478379ea136bb5783b675cd11e412bf0703995aeb88aca9983141000000001976a9144d869697281ad18370313122795e56dfdc3a331388ac193f1c00"
)
func init() {
testTx1 = bchain.Tx{
Hex: "0100000001aef422fb91cd91e556966fed4121ac44017a761d71385596536bb447ae05213e000000006a47304402202341ac4297925257dc72eb418a069c45e76f7070340e27501f6308cc7eff45f802204a347915adceff5f6fc9b8075d95d47887d46b344c7bbc8066d315931b189ad001210228c2520812b7f8c63e7a088c61b6348b22fa0c98812e736a1fd896bc828d3c65feffffff028041f13d000000001976a91478379ea136bb5783b675cd11e412bf0703995aeb88aca9983141000000001976a9144d869697281ad18370313122795e56dfdc3a331388ac193f1c00",
Blocktime: 1583392607,
Txid: "f81c34b300961877328c3aaa7cd5e69068457868309fbf1e92544e3a6a915bcb",
LockTime: 1851161,
Version: 1,
Vin: []bchain.Vin{
{
ScriptSig: bchain.ScriptSig{
Hex: "47304402202341ac4297925257dc72eb418a069c45e76f7070340e27501f6308cc7eff45f802204a347915adceff5f6fc9b8075d95d47887d46b344c7bbc8066d315931b189ad001210228c2520812b7f8c63e7a088c61b6348b22fa0c98812e736a1fd896bc828d3c65",
},
Txid: "3e2105ae47b46b53965538711d767a0144ac2141ed6f9656e591cd91fb22f4ae",
Vout: 0,
Sequence: 4294967294,
},
},
Vout: []bchain.Vout{
{
ValueSat: *big.NewInt(1039221120),
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a91478379ea136bb5783b675cd11e412bf0703995aeb88ac",
Addresses: []string{
"ZnLWULVbAzjy1TSKxGnpkomeeaEDTHk5Nj",
},
},
},
{
ValueSat: *big.NewInt(1093769385),
N: 1,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a9144d869697281ad18370313122795e56dfdc3a331388ac",
Addresses: []string{
"ZiSn1vTSxGu2kFcnkjjm7bYGhT5BVAVfEG",
},
},
},
},
}
}
func Test_PackTx(t *testing.T) {
type args struct {
tx bchain.Tx
height uint32
blockTime int64
parser *BitZenyParser
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "BitZeny-1",
args: args{
tx: testTx1,
height: 1851162,
blockTime: 1583392607,
parser: NewBitZenyParser(GetChainParams("main"), &btc.Configuration{}),
},
want: testTxPacked1,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got, err := tt.args.parser.PackTx(&tt.args.tx, tt.args.height, tt.args.blockTime)
if (err != nil) != tt.wantErr {
t.Errorf("packTx() error = %v, wantErr %v", err, tt.wantErr)
return
}
h := hex.EncodeToString(got)
if !reflect.DeepEqual(h, tt.want) {
t.Errorf("packTx() = %v, want %v", h, tt.want)
}
})
}
}
func Test_UnpackTx(t *testing.T) {
type args struct {
packedTx string
parser *BitZenyParser
}
tests := []struct {
name string
args args
want *bchain.Tx
want1 uint32
wantErr bool
}{
{
name: "BitZeny-1",
args: args{
packedTx: testTxPacked1,
parser: NewBitZenyParser(GetChainParams("main"), &btc.Configuration{}),
},
want: &testTx1,
want1: 1851162,
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b, _ := hex.DecodeString(tt.args.packedTx)
got, got1, err := tt.args.parser.UnpackTx(b)
if (err != nil) != tt.wantErr {
t.Errorf("unpackTx() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !reflect.DeepEqual(got, tt.want) {
t.Errorf("unpackTx() got = %v, want %v", got, tt.want)
}
if got1 != tt.want1 {
t.Errorf("unpackTx() got1 = %v, want %v", got1, tt.want1)
}
})
}
}

View File

@ -1,59 +0,0 @@
package bitzeny
import (
"encoding/json"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
"github.com/golang/glog"
)
// BitZenyRPC is an interface to JSON-RPC bitcoind service.
type BitZenyRPC struct {
*btc.BitcoinRPC
}
// NewBitZenyRPC returns new BitZenyRPC instance.
func NewBitZenyRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) {
b, err := btc.NewBitcoinRPC(config, pushHandler)
if err != nil {
return nil, err
}
s := &BitZenyRPC{
b.(*btc.BitcoinRPC),
}
s.RPCMarshaler = btc.JSONMarshalerV2{}
s.ChainConfig.SupportsEstimateFee = false
return s, nil
}
// Initialize initializes BitZenyRPC instance.
func (b *BitZenyRPC) Initialize() error {
ci, err := b.GetChainInfo()
if err != nil {
return err
}
chainName := ci.Chain
glog.Info("Chain name ", chainName)
params := GetChainParams(chainName)
// always create parser
b.Parser = NewBitZenyParser(params, b.ChainConfig)
// parameters for getInfo request
if params.Net == MainnetMagic {
b.Testnet = false
b.Network = "livenet"
} else {
b.Testnet = true
b.Network = "testnet"
}
glog.Info("rpc: block chain ", params.Name)
return nil
}

View File

@ -1,6 +1,44 @@
package coins package coins
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/bch"
"blockbook/bchain/coins/bellcoin"
"blockbook/bchain/coins/bitcore"
"blockbook/bchain/coins/btc"
"blockbook/bchain/coins/btg"
"blockbook/bchain/coins/cpuchain"
"blockbook/bchain/coins/dash"
"blockbook/bchain/coins/dcr"
"blockbook/bchain/coins/deeponion"
"blockbook/bchain/coins/digibyte"
"blockbook/bchain/coins/divi"
"blockbook/bchain/coins/dogecoin"
"blockbook/bchain/coins/eth"
"blockbook/bchain/coins/flo"
"blockbook/bchain/coins/fujicoin"
"blockbook/bchain/coins/gamecredits"
"blockbook/bchain/coins/grs"
"blockbook/bchain/coins/koto"
"blockbook/bchain/coins/liquid"
"blockbook/bchain/coins/litecoin"
"blockbook/bchain/coins/monacoin"
"blockbook/bchain/coins/monetaryunit"
"blockbook/bchain/coins/myriad"
"blockbook/bchain/coins/namecoin"
"blockbook/bchain/coins/nuls"
"blockbook/bchain/coins/pivx"
"blockbook/bchain/coins/polis"
"blockbook/bchain/coins/qtum"
"blockbook/bchain/coins/ravencoin"
"blockbook/bchain/coins/ritocoin"
"blockbook/bchain/coins/unobtanium"
"blockbook/bchain/coins/vertcoin"
"blockbook/bchain/coins/viacoin"
"blockbook/bchain/coins/vipstarcoin"
"blockbook/bchain/coins/xzc"
"blockbook/bchain/coins/zec"
"blockbook/common"
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
@ -10,48 +48,6 @@ import (
"time" "time"
"github.com/juju/errors" "github.com/juju/errors"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/bch"
"spacecruft.org/spacecruft/blockbook/bchain/coins/bellcoin"
"spacecruft.org/spacecruft/blockbook/bchain/coins/bitcore"
"spacecruft.org/spacecruft/blockbook/bchain/coins/bitzeny"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btg"
"spacecruft.org/spacecruft/blockbook/bchain/coins/cpuchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/dash"
"spacecruft.org/spacecruft/blockbook/bchain/coins/dcr"
"spacecruft.org/spacecruft/blockbook/bchain/coins/deeponion"
"spacecruft.org/spacecruft/blockbook/bchain/coins/digibyte"
"spacecruft.org/spacecruft/blockbook/bchain/coins/divi"
"spacecruft.org/spacecruft/blockbook/bchain/coins/dogecoin"
"spacecruft.org/spacecruft/blockbook/bchain/coins/eth"
"spacecruft.org/spacecruft/blockbook/bchain/coins/firo"
"spacecruft.org/spacecruft/blockbook/bchain/coins/flo"
"spacecruft.org/spacecruft/blockbook/bchain/coins/fujicoin"
"spacecruft.org/spacecruft/blockbook/bchain/coins/gamecredits"
"spacecruft.org/spacecruft/blockbook/bchain/coins/grs"
"spacecruft.org/spacecruft/blockbook/bchain/coins/koto"
"spacecruft.org/spacecruft/blockbook/bchain/coins/liquid"
"spacecruft.org/spacecruft/blockbook/bchain/coins/litecoin"
"spacecruft.org/spacecruft/blockbook/bchain/coins/monacoin"
"spacecruft.org/spacecruft/blockbook/bchain/coins/monetaryunit"
"spacecruft.org/spacecruft/blockbook/bchain/coins/myriad"
"spacecruft.org/spacecruft/blockbook/bchain/coins/namecoin"
"spacecruft.org/spacecruft/blockbook/bchain/coins/nuls"
"spacecruft.org/spacecruft/blockbook/bchain/coins/omotenashicoin"
"spacecruft.org/spacecruft/blockbook/bchain/coins/pivx"
"spacecruft.org/spacecruft/blockbook/bchain/coins/polis"
"spacecruft.org/spacecruft/blockbook/bchain/coins/qtum"
"spacecruft.org/spacecruft/blockbook/bchain/coins/ravencoin"
"spacecruft.org/spacecruft/blockbook/bchain/coins/ritocoin"
"spacecruft.org/spacecruft/blockbook/bchain/coins/snowgem"
"spacecruft.org/spacecruft/blockbook/bchain/coins/trezarcoin"
"spacecruft.org/spacecruft/blockbook/bchain/coins/unobtanium"
"spacecruft.org/spacecruft/blockbook/bchain/coins/vertcoin"
"spacecruft.org/spacecruft/blockbook/bchain/coins/viacoin"
"spacecruft.org/spacecruft/blockbook/bchain/coins/vipstarcoin"
"spacecruft.org/spacecruft/blockbook/bchain/coins/zec"
"spacecruft.org/spacecruft/blockbook/common"
) )
type blockChainFactory func(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) type blockChainFactory func(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error)
@ -62,17 +58,14 @@ var BlockChainFactories = make(map[string]blockChainFactory)
func init() { func init() {
BlockChainFactories["Bitcoin"] = btc.NewBitcoinRPC BlockChainFactories["Bitcoin"] = btc.NewBitcoinRPC
BlockChainFactories["Testnet"] = btc.NewBitcoinRPC BlockChainFactories["Testnet"] = btc.NewBitcoinRPC
BlockChainFactories["Signet"] = btc.NewBitcoinRPC
BlockChainFactories["Zcash"] = zec.NewZCashRPC BlockChainFactories["Zcash"] = zec.NewZCashRPC
BlockChainFactories["Zcash Testnet"] = zec.NewZCashRPC BlockChainFactories["Zcash Testnet"] = zec.NewZCashRPC
BlockChainFactories["Ethereum"] = eth.NewEthereumRPC BlockChainFactories["Ethereum"] = eth.NewEthereumRPC
BlockChainFactories["Ethereum Classic"] = eth.NewEthereumRPC BlockChainFactories["Ethereum Classic"] = eth.NewEthereumRPC
BlockChainFactories["Ethereum Testnet Ropsten"] = eth.NewEthereumRPC BlockChainFactories["Ethereum Testnet Ropsten"] = eth.NewEthereumRPC
BlockChainFactories["Ethereum Testnet Goerli"] = eth.NewEthereumRPC
BlockChainFactories["Bcash"] = bch.NewBCashRPC BlockChainFactories["Bcash"] = bch.NewBCashRPC
BlockChainFactories["Bcash Testnet"] = bch.NewBCashRPC BlockChainFactories["Bcash Testnet"] = bch.NewBCashRPC
BlockChainFactories["Bgold"] = btg.NewBGoldRPC BlockChainFactories["Bgold"] = btg.NewBGoldRPC
BlockChainFactories["Bgold Testnet"] = btg.NewBGoldRPC
BlockChainFactories["Dash"] = dash.NewDashRPC BlockChainFactories["Dash"] = dash.NewDashRPC
BlockChainFactories["Dash Testnet"] = dash.NewDashRPC BlockChainFactories["Dash Testnet"] = dash.NewDashRPC
BlockChainFactories["Decred"] = dcr.NewDecredRPC BlockChainFactories["Decred"] = dcr.NewDecredRPC
@ -90,7 +83,6 @@ func init() {
BlockChainFactories["Monacoin Testnet"] = monacoin.NewMonacoinRPC BlockChainFactories["Monacoin Testnet"] = monacoin.NewMonacoinRPC
BlockChainFactories["MonetaryUnit"] = monetaryunit.NewMonetaryUnitRPC BlockChainFactories["MonetaryUnit"] = monetaryunit.NewMonetaryUnitRPC
BlockChainFactories["DigiByte"] = digibyte.NewDigiByteRPC BlockChainFactories["DigiByte"] = digibyte.NewDigiByteRPC
BlockChainFactories["DigiByte Testnet"] = digibyte.NewDigiByteRPC
BlockChainFactories["Myriad"] = myriad.NewMyriadRPC BlockChainFactories["Myriad"] = myriad.NewMyriadRPC
BlockChainFactories["Liquid"] = liquid.NewLiquidRPC BlockChainFactories["Liquid"] = liquid.NewLiquidRPC
BlockChainFactories["Groestlcoin"] = grs.NewGroestlcoinRPC BlockChainFactories["Groestlcoin"] = grs.NewGroestlcoinRPC
@ -98,7 +90,7 @@ func init() {
BlockChainFactories["PIVX"] = pivx.NewPivXRPC BlockChainFactories["PIVX"] = pivx.NewPivXRPC
BlockChainFactories["PIVX Testnet"] = pivx.NewPivXRPC BlockChainFactories["PIVX Testnet"] = pivx.NewPivXRPC
BlockChainFactories["Polis"] = polis.NewPolisRPC BlockChainFactories["Polis"] = polis.NewPolisRPC
BlockChainFactories["Firo"] = firo.NewFiroRPC BlockChainFactories["Zcoin"] = xzc.NewZcoinRPC
BlockChainFactories["Fujicoin"] = fujicoin.NewFujicoinRPC BlockChainFactories["Fujicoin"] = fujicoin.NewFujicoinRPC
BlockChainFactories["Flo"] = flo.NewFloRPC BlockChainFactories["Flo"] = flo.NewFloRPC
BlockChainFactories["Bellcoin"] = bellcoin.NewBellcoinRPC BlockChainFactories["Bellcoin"] = bellcoin.NewBellcoinRPC
@ -114,12 +106,7 @@ func init() {
BlockChainFactories["CPUchain"] = cpuchain.NewCPUchainRPC BlockChainFactories["CPUchain"] = cpuchain.NewCPUchainRPC
BlockChainFactories["Unobtanium"] = unobtanium.NewUnobtaniumRPC BlockChainFactories["Unobtanium"] = unobtanium.NewUnobtaniumRPC
BlockChainFactories["DeepOnion"] = deeponion.NewDeepOnionRPC BlockChainFactories["DeepOnion"] = deeponion.NewDeepOnionRPC
BlockChainFactories["SnowGem"] = snowgem.NewSnowGemRPC
BlockChainFactories["Bitcore"] = bitcore.NewBitcoreRPC BlockChainFactories["Bitcore"] = bitcore.NewBitcoreRPC
BlockChainFactories["Omotenashicoin"] = omotenashicoin.NewOmotenashiCoinRPC
BlockChainFactories["Omotenashicoin Testnet"] = omotenashicoin.NewOmotenashiCoinRPC
BlockChainFactories["BitZeny"] = bitzeny.NewBitZenyRPC
BlockChainFactories["Trezarcoin"] = trezarcoin.NewTrezarcoinRPC
} }
// GetCoinNameFromConfig gets coin name and coin shortcut from config file // GetCoinNameFromConfig gets coin name and coin shortcut from config file
@ -191,8 +178,8 @@ func (c *blockChainWithMetrics) CreateMempool(chain bchain.BlockChain) (bchain.M
return c.b.CreateMempool(chain) return c.b.CreateMempool(chain)
} }
func (c *blockChainWithMetrics) InitializeMempool(addrDescForOutpoint bchain.AddrDescForOutpointFunc, onNewTxAddr bchain.OnNewTxAddrFunc, onNewTx bchain.OnNewTxFunc) error { func (c *blockChainWithMetrics) InitializeMempool(addrDescForOutpoint bchain.AddrDescForOutpointFunc, onNewTxAddr bchain.OnNewTxAddrFunc) error {
return c.b.InitializeMempool(addrDescForOutpoint, onNewTxAddr, onNewTx) return c.b.InitializeMempool(addrDescForOutpoint, onNewTxAddr)
} }
func (c *blockChainWithMetrics) Shutdown(ctx context.Context) error { func (c *blockChainWithMetrics) Shutdown(ctx context.Context) error {

View File

@ -1,6 +1,7 @@
package btc package btc
import ( import (
"blockbook/bchain"
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"encoding/hex" "encoding/hex"
@ -15,25 +16,8 @@ import (
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"github.com/martinboehm/btcutil/hdkeychain" "github.com/martinboehm/btcutil/hdkeychain"
"github.com/martinboehm/btcutil/txscript" "github.com/martinboehm/btcutil/txscript"
"spacecruft.org/spacecruft/blockbook/bchain"
) )
// temp params for signet(wait btcd commit)
// magic numbers
const (
SignetMagic wire.BitcoinNet = 0x6a70c7f0
)
// chain parameters
var (
SigNetParams chaincfg.Params
)
func init() {
SigNetParams = chaincfg.TestNet3Params
SigNetParams.Net = SignetMagic
}
// OutputScriptToAddressesFunc converts ScriptPubKey to bitcoin addresses // OutputScriptToAddressesFunc converts ScriptPubKey to bitcoin addresses
type OutputScriptToAddressesFunc func(script []byte) ([]string, bool, error) type OutputScriptToAddressesFunc func(script []byte) ([]string, bool, error)
@ -79,8 +63,6 @@ func GetChainParams(chain string) *chaincfg.Params {
return &chaincfg.TestNet3Params return &chaincfg.TestNet3Params
case "regtest": case "regtest":
return &chaincfg.RegressionNetParams return &chaincfg.RegressionNetParams
case "signet":
return &SigNetParams
} }
return &chaincfg.MainNetParams return &chaincfg.MainNetParams
} }

View File

@ -3,6 +3,7 @@
package btc package btc
import ( import (
"blockbook/bchain"
"encoding/hex" "encoding/hex"
"math/big" "math/big"
"os" "os"
@ -10,7 +11,6 @@ import (
"testing" "testing"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {
@ -259,11 +259,10 @@ func TestGetAddressesFromAddrDesc(t *testing.T) {
} }
var ( var (
testTx1, testTx2, testTx3 bchain.Tx testTx1, testTx2 bchain.Tx
testTxPacked1 = "0001e2408ba8d7af5401000000017f9a22c9cbf54bd902400df746f138f37bcf5b4d93eb755820e974ba43ed5f42040000006a4730440220037f4ed5427cde81d55b9b6a2fd08c8a25090c2c2fff3a75c1a57625ca8a7118022076c702fe55969fa08137f71afd4851c48e31082dd3c40c919c92cdbc826758d30121029f6da5623c9f9b68a9baf9c1bc7511df88fa34c6c2f71f7c62f2f03ff48dca80feffffff019c9700000000000017a9146144d57c8aff48492c9dfb914e120b20bad72d6f8773d00700" testTxPacked1 = "0001e2408ba8d7af5401000000017f9a22c9cbf54bd902400df746f138f37bcf5b4d93eb755820e974ba43ed5f42040000006a4730440220037f4ed5427cde81d55b9b6a2fd08c8a25090c2c2fff3a75c1a57625ca8a7118022076c702fe55969fa08137f71afd4851c48e31082dd3c40c919c92cdbc826758d30121029f6da5623c9f9b68a9baf9c1bc7511df88fa34c6c2f71f7c62f2f03ff48dca80feffffff019c9700000000000017a9146144d57c8aff48492c9dfb914e120b20bad72d6f8773d00700"
testTxPacked2 = "0007c91a899ab7da6a010000000001019d64f0c72a0d206001decbffaa722eb1044534c74eee7a5df8318e42a4323ec10000000017160014550da1f5d25a9dae2eafd6902b4194c4c6500af6ffffffff02809698000000000017a914cd668d781ece600efa4b2404dc91fd26b8b8aed8870553d7360000000017a914246655bdbd54c7e477d0ea2375e86e0db2b8f80a8702473044022076aba4ad559616905fa51d4ddd357fc1fdb428d40cb388e042cdd1da4a1b7357022011916f90c712ead9a66d5f058252efd280439ad8956a967e95d437d246710bc9012102a80a5964c5612bb769ef73147b2cf3c149bc0fd4ecb02f8097629c94ab013ffd00000000" testTxPacked2 = "0007c91a899ab7da6a010000000001019d64f0c72a0d206001decbffaa722eb1044534c74eee7a5df8318e42a4323ec10000000017160014550da1f5d25a9dae2eafd6902b4194c4c6500af6ffffffff02809698000000000017a914cd668d781ece600efa4b2404dc91fd26b8b8aed8870553d7360000000017a914246655bdbd54c7e477d0ea2375e86e0db2b8f80a8702473044022076aba4ad559616905fa51d4ddd357fc1fdb428d40cb388e042cdd1da4a1b7357022011916f90c712ead9a66d5f058252efd280439ad8956a967e95d437d246710bc9012102a80a5964c5612bb769ef73147b2cf3c149bc0fd4ecb02f8097629c94ab013ffd00000000"
testTxPacked3 = "00003d818bfda9aa3e02000000000102deb1999a857ab0a13d6b12fbd95ea75b409edde5f2ff747507ce42d9986a8b9d0000000000fdffffff9fd2d3361e203b2375eba6438efbef5b3075531e7e583c7cc76b7294fe7f22980000000000fdffffff02a0860100000000001600148091746745464e7555c31e9a5afceac14a02978ae7fc1c0000000000160014565ea9ff4589d3e05ba149ae6e257752bfdc2a1e0247304402207d67d320a8e813f986b35e9791935fcb736754812b7038686f5de6cfdcda99cd02201c3bb2c178e0056016437ecfe365a7eef84aa9d293ebdc566177af82e22fcdd3012103abb30c1bbe878b07b58dc169b1d061d48c60be8107f632a59778b38bf7ceea5a02473044022044f54a478cfe086e870cb026c9dcd4e14e63778bef569a4d55a6332725cd9a9802202f0e94c04e6f328fc64ad9efe552888c299750d1b8d033324825a3ff29920e030121036fcd433428aa7dc65c4f5408fa31f208c54fe4b4c6c1ae9c39a825ed4f1ac039813d0000"
) )
func init() { func init() {
@ -336,54 +335,6 @@ func init() {
}, },
}, },
} }
testTx3 = bchain.Tx{
Hex: "02000000000102deb1999a857ab0a13d6b12fbd95ea75b409edde5f2ff747507ce42d9986a8b9d0000000000fdffffff9fd2d3361e203b2375eba6438efbef5b3075531e7e583c7cc76b7294fe7f22980000000000fdffffff02a0860100000000001600148091746745464e7555c31e9a5afceac14a02978ae7fc1c0000000000160014565ea9ff4589d3e05ba149ae6e257752bfdc2a1e0247304402207d67d320a8e813f986b35e9791935fcb736754812b7038686f5de6cfdcda99cd02201c3bb2c178e0056016437ecfe365a7eef84aa9d293ebdc566177af82e22fcdd3012103abb30c1bbe878b07b58dc169b1d061d48c60be8107f632a59778b38bf7ceea5a02473044022044f54a478cfe086e870cb026c9dcd4e14e63778bef569a4d55a6332725cd9a9802202f0e94c04e6f328fc64ad9efe552888c299750d1b8d033324825a3ff29920e030121036fcd433428aa7dc65c4f5408fa31f208c54fe4b4c6c1ae9c39a825ed4f1ac039813d0000",
Blocktime: 1607805599,
Txid: "24551a58a1d1fb89d7052e2bbac7cb69a7825ee1e39439befbec8c32148cf735",
LockTime: 15745,
Version: 2,
Vin: []bchain.Vin{
{
ScriptSig: bchain.ScriptSig{
Hex: "",
},
Txid: "9d8b6a98d942ce077574fff2e5dd9e405ba75ed9fb126b3da1b07a859a99b1de",
Vout: 0,
Sequence: 4294967293,
},
{
ScriptSig: bchain.ScriptSig{
Hex: "",
},
Txid: "98227ffe94726bc77c3c587e1e5375305beffb8e43a6eb75233b201e36d3d29f",
Vout: 0,
Sequence: 4294967293,
},
},
Vout: []bchain.Vout{
{
ValueSat: *big.NewInt(100000),
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "00148091746745464e7555c31e9a5afceac14a02978a",
Addresses: []string{
"tb1qszghge69ge8824wrr6d94l82c99q99u2ccgv5w",
},
},
},
{
ValueSat: *big.NewInt(1899751),
N: 1,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "0014565ea9ff4589d3e05ba149ae6e257752bfdc2a1e",
Addresses: []string{
"tb1q2e02nl6938f7qkapfxhxufth22lac2s792vsxp",
},
},
},
},
}
} }
func TestPackTx(t *testing.T) { func TestPackTx(t *testing.T) {
@ -421,17 +372,6 @@ func TestPackTx(t *testing.T) {
want: testTxPacked2, want: testTxPacked2,
wantErr: false, wantErr: false,
}, },
{
name: "signet-1",
args: args{
tx: testTx3,
height: 15745,
blockTime: 1607805599,
parser: NewBitcoinParser(GetChainParams("signet"), &Configuration{}),
},
want: testTxPacked3,
wantErr: false,
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {
@ -480,16 +420,6 @@ func TestUnpackTx(t *testing.T) {
want1: 510234, want1: 510234,
wantErr: false, wantErr: false,
}, },
{
name: "signet-1",
args: args{
packedTx: testTxPacked3,
parser: NewBitcoinParser(GetChainParams("signet"), &Configuration{}),
},
want: &testTx3,
want1: 15745,
wantErr: false,
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {

View File

@ -1,6 +1,7 @@
package btc package btc
import ( import (
"blockbook/bchain"
"bytes" "bytes"
"context" "context"
"encoding/hex" "encoding/hex"
@ -16,8 +17,6 @@ import (
"github.com/golang/glog" "github.com/golang/glog"
"github.com/juju/errors" "github.com/juju/errors"
"github.com/martinboehm/btcd/wire" "github.com/martinboehm/btcd/wire"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/common"
) )
// BitcoinRPC is an interface to JSON-RPC bitcoind service. // BitcoinRPC is an interface to JSON-RPC bitcoind service.
@ -155,13 +154,12 @@ func (b *BitcoinRPC) CreateMempool(chain bchain.BlockChain) (bchain.Mempool, err
} }
// InitializeMempool creates ZeroMQ subscription and sets AddrDescForOutpointFunc to the Mempool // InitializeMempool creates ZeroMQ subscription and sets AddrDescForOutpointFunc to the Mempool
func (b *BitcoinRPC) InitializeMempool(addrDescForOutpoint bchain.AddrDescForOutpointFunc, onNewTxAddr bchain.OnNewTxAddrFunc, onNewTx bchain.OnNewTxFunc) error { func (b *BitcoinRPC) InitializeMempool(addrDescForOutpoint bchain.AddrDescForOutpointFunc, onNewTxAddr bchain.OnNewTxAddrFunc) error {
if b.Mempool == nil { if b.Mempool == nil {
return errors.New("Mempool not created") return errors.New("Mempool not created")
} }
b.Mempool.AddrDescForOutpoint = addrDescForOutpoint b.Mempool.AddrDescForOutpoint = addrDescForOutpoint
b.Mempool.OnNewTxAddr = onNewTxAddr b.Mempool.OnNewTxAddr = onNewTxAddr
b.Mempool.OnNewTx = onNewTx
if b.mq == nil { if b.mq == nil {
mq, err := bchain.NewMQ(b.ChainConfig.MessageQueueBinding, b.pushHandler) mq, err := bchain.NewMQ(b.ChainConfig.MessageQueueBinding, b.pushHandler)
if err != nil { if err != nil {
@ -239,13 +237,13 @@ type CmdGetBlockChainInfo struct {
type ResGetBlockChainInfo struct { type ResGetBlockChainInfo struct {
Error *bchain.RPCError `json:"error"` Error *bchain.RPCError `json:"error"`
Result struct { Result struct {
Chain string `json:"chain"` Chain string `json:"chain"`
Blocks int `json:"blocks"` Blocks int `json:"blocks"`
Headers int `json:"headers"` Headers int `json:"headers"`
Bestblockhash string `json:"bestblockhash"` Bestblockhash string `json:"bestblockhash"`
Difficulty common.JSONNumber `json:"difficulty"` Difficulty json.Number `json:"difficulty"`
SizeOnDisk int64 `json:"size_on_disk"` SizeOnDisk int64 `json:"size_on_disk"`
Warnings string `json:"warnings"` Warnings string `json:"warnings"`
} `json:"result"` } `json:"result"`
} }
@ -258,11 +256,11 @@ type CmdGetNetworkInfo struct {
type ResGetNetworkInfo struct { type ResGetNetworkInfo struct {
Error *bchain.RPCError `json:"error"` Error *bchain.RPCError `json:"error"`
Result struct { Result struct {
Version common.JSONNumber `json:"version"` Version json.Number `json:"version"`
Subversion common.JSONNumber `json:"subversion"` Subversion json.Number `json:"subversion"`
ProtocolVersion common.JSONNumber `json:"protocolversion"` ProtocolVersion json.Number `json:"protocolversion"`
Timeoffset float64 `json:"timeoffset"` Timeoffset float64 `json:"timeoffset"`
Warnings string `json:"warnings"` Warnings string `json:"warnings"`
} `json:"result"` } `json:"result"`
} }
@ -360,8 +358,8 @@ type CmdEstimateSmartFee struct {
type ResEstimateSmartFee struct { type ResEstimateSmartFee struct {
Error *bchain.RPCError `json:"error"` Error *bchain.RPCError `json:"error"`
Result struct { Result struct {
Feerate common.JSONNumber `json:"feerate"` Feerate json.Number `json:"feerate"`
Blocks int `json:"blocks"` Blocks int `json:"blocks"`
} `json:"result"` } `json:"result"`
} }
@ -375,8 +373,8 @@ type CmdEstimateFee struct {
} }
type ResEstimateFee struct { type ResEstimateFee struct {
Error *bchain.RPCError `json:"error"` Error *bchain.RPCError `json:"error"`
Result common.JSONNumber `json:"result"` Result json.Number `json:"result"`
} }
// sendrawtransaction // sendrawtransaction

View File

@ -1,6 +1,7 @@
package btc package btc
import ( import (
"blockbook/bchain"
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
@ -11,8 +12,8 @@ import (
"time" "time"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/juju/errors" "github.com/juju/errors"
"spacecruft.org/spacecruft/blockbook/bchain"
) )
// https://whatthefee.io returns // https://whatthefee.io returns

View File

@ -1,6 +1,9 @@
package btg package btg
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"blockbook/bchain/coins/utils"
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"io" "io"
@ -8,9 +11,6 @@ import (
"github.com/martinboehm/btcd/chaincfg/chainhash" "github.com/martinboehm/btcd/chaincfg/chainhash"
"github.com/martinboehm/btcd/wire" "github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
"spacecruft.org/spacecruft/blockbook/bchain/coins/utils"
) )
const ( const (

View File

@ -3,6 +3,7 @@
package btg package btg
import ( import (
"blockbook/bchain/coins/btc"
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
@ -12,7 +13,6 @@ import (
"testing" "testing"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {

View File

@ -1,11 +1,11 @@
package btg package btg
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json" "encoding/json"
"github.com/golang/glog" "github.com/golang/glog"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// BGoldRPC is an interface to JSON-RPC bitcoind service. // BGoldRPC is an interface to JSON-RPC bitcoind service.

View File

@ -1,9 +1,10 @@
package cpuchain package cpuchain
import ( import (
"blockbook/bchain/coins/btc"
"github.com/martinboehm/btcd/wire" "github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// magic numbers // magic numbers

View File

@ -1,11 +1,11 @@
package cpuchain package cpuchain
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json" "encoding/json"
"github.com/golang/glog" "github.com/golang/glog"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// CPUchainRPC is an interface to JSON-RPC bitcoind service. // CPUchainRPC is an interface to JSON-RPC bitcoind service.

View File

@ -1,10 +1,11 @@
package dash package dash
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"github.com/martinboehm/btcd/wire" "github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
const ( const (

View File

@ -1,8 +1,10 @@
// +build unittest // build unittest
package dash package dash
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
@ -11,9 +13,6 @@ import (
"path/filepath" "path/filepath"
"reflect" "reflect"
"testing" "testing"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
type testBlock struct { type testBlock struct {

View File

@ -1,12 +1,12 @@
package dash package dash
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json" "encoding/json"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/juju/errors" "github.com/juju/errors"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
const firstBlockWithSpecialTransactions = 1028160 const firstBlockWithSpecialTransactions = 1028160

View File

@ -9,19 +9,18 @@ import (
"math/big" "math/big"
"strconv" "strconv"
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"blockbook/bchain/coins/utils"
cfg "github.com/decred/dcrd/chaincfg"
"github.com/decred/dcrd/chaincfg/chainhash" "github.com/decred/dcrd/chaincfg/chainhash"
cfg "github.com/decred/dcrd/chaincfg/v3" "github.com/decred/dcrd/hdkeychain"
"github.com/decred/dcrd/dcrec" "github.com/decred/dcrd/txscript"
"github.com/decred/dcrd/dcrutil/v3"
"github.com/decred/dcrd/hdkeychain/v3"
"github.com/decred/dcrd/txscript/v3"
"github.com/juju/errors" "github.com/juju/errors"
"github.com/martinboehm/btcd/wire" "github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/base58" "github.com/martinboehm/btcutil/base58"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
"spacecruft.org/spacecruft/blockbook/bchain/coins/utils"
) )
const ( const (
@ -66,9 +65,9 @@ func NewDecredParser(params *chaincfg.Params, c *btc.Configuration) *DecredParse
switch d.BitcoinParser.Params.Name { switch d.BitcoinParser.Params.Name {
case "testnet3": case "testnet3":
d.netConfig = cfg.TestNet3Params() d.netConfig = &cfg.TestNet3Params
default: default:
d.netConfig = cfg.MainNetParams() d.netConfig = &cfg.MainNetParams
} }
return d return d
} }
@ -204,10 +203,7 @@ func (p *DecredParser) GetAddrDescFromVout(output *bchain.Vout) (bchain.AddressD
return nil, err return nil, err
} }
const scriptVersion = 0 scriptClass, addresses, _, err := txscript.ExtractPkScriptAddrs(txscript.DefaultScriptVersion, script, p.netConfig)
const treasuryEnabled = true
scriptClass, addresses, _, err := txscript.ExtractPkScriptAddrs(scriptVersion, script,
p.netConfig, treasuryEnabled)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -245,9 +241,7 @@ func (p *DecredParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
} }
func (p *DecredParser) addrDescFromExtKey(extKey *hdkeychain.ExtendedKey) (bchain.AddressDescriptor, error) { func (p *DecredParser) addrDescFromExtKey(extKey *hdkeychain.ExtendedKey) (bchain.AddressDescriptor, error) {
pk := extKey.SerializedPubKey() var addr, err = extKey.Address(p.netConfig)
hash := dcrutil.Hash160(pk)
addr, err := dcrutil.NewAddressPubKeyHash(hash, p.netConfig, dcrec.STEcdsaSecp256k1)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -258,7 +252,7 @@ func (p *DecredParser) addrDescFromExtKey(extKey *hdkeychain.ExtendedKey) (bchai
// listed indexes // listed indexes
func (p *DecredParser) DeriveAddressDescriptors(xpub string, change uint32, func (p *DecredParser) DeriveAddressDescriptors(xpub string, change uint32,
indexes []uint32) ([]bchain.AddressDescriptor, error) { indexes []uint32) ([]bchain.AddressDescriptor, error) {
extKey, err := hdkeychain.NewKeyFromString(xpub, p.netConfig) extKey, err := hdkeychain.NewKeyFromString(xpub)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -289,7 +283,7 @@ func (p *DecredParser) DeriveAddressDescriptorsFromTo(xpub string, change uint32
if toIndex <= fromIndex { if toIndex <= fromIndex {
return nil, errors.New("toIndex<=fromIndex") return nil, errors.New("toIndex<=fromIndex")
} }
extKey, err := hdkeychain.NewKeyFromString(xpub, p.netConfig) extKey, err := hdkeychain.NewKeyFromString(xpub)
if err != nil { if err != nil {
return nil, err return nil, err
} }

View File

@ -3,14 +3,13 @@
package dcr package dcr
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/hex" "encoding/hex"
"math/big" "math/big"
"os" "os"
"reflect" "reflect"
"testing" "testing"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
var ( var (

View File

@ -1,6 +1,7 @@
package dcr package dcr
import ( import (
"blockbook/bchain"
"bytes" "bytes"
"encoding/json" "encoding/json"
"fmt" "fmt"
@ -15,12 +16,11 @@ import (
"sync" "sync"
"time" "time"
"github.com/decred/dcrd/dcrjson/v3" "blockbook/bchain/coins/btc"
"github.com/decred/dcrd/dcrjson"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/juju/errors" "github.com/juju/errors"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
"spacecruft.org/spacecruft/blockbook/common"
) )
// voteBitYes defines the vote bit set when a given block validates the previous // voteBitYes defines the vote bit set when a given block validates the previous
@ -168,61 +168,61 @@ type GetBlockHashResult struct {
type GetBlockResult struct { type GetBlockResult struct {
Error Error `json:"error"` Error Error `json:"error"`
Result struct { Result struct {
Hash string `json:"hash"` Hash string `json:"hash"`
Confirmations int64 `json:"confirmations"` Confirmations int64 `json:"confirmations"`
Size int32 `json:"size"` Size int32 `json:"size"`
Height uint32 `json:"height"` Height uint32 `json:"height"`
Version common.JSONNumber `json:"version"` Version json.Number `json:"version"`
MerkleRoot string `json:"merkleroot"` MerkleRoot string `json:"merkleroot"`
StakeRoot string `json:"stakeroot"` StakeRoot string `json:"stakeroot"`
RawTx []RawTx `json:"rawtx"` RawTx []RawTx `json:"rawtx"`
Tx []string `json:"tx,omitempty"` Tx []string `json:"tx,omitempty"`
STx []string `json:"stx,omitempty"` STx []string `json:"stx,omitempty"`
Time int64 `json:"time"` Time int64 `json:"time"`
Nonce common.JSONNumber `json:"nonce"` Nonce json.Number `json:"nonce"`
VoteBits uint16 `json:"votebits"` VoteBits uint16 `json:"votebits"`
FinalState string `json:"finalstate"` FinalState string `json:"finalstate"`
Voters uint16 `json:"voters"` Voters uint16 `json:"voters"`
FreshStake uint8 `json:"freshstake"` FreshStake uint8 `json:"freshstake"`
Revocations uint8 `json:"revocations"` Revocations uint8 `json:"revocations"`
PoolSize uint32 `json:"poolsize"` PoolSize uint32 `json:"poolsize"`
Bits string `json:"bits"` Bits string `json:"bits"`
SBits float64 `json:"sbits"` SBits float64 `json:"sbits"`
ExtraData string `json:"extradata"` ExtraData string `json:"extradata"`
StakeVersion uint32 `json:"stakeversion"` StakeVersion uint32 `json:"stakeversion"`
Difficulty float64 `json:"difficulty"` Difficulty float64 `json:"difficulty"`
ChainWork string `json:"chainwork"` ChainWork string `json:"chainwork"`
PreviousHash string `json:"previousblockhash"` PreviousHash string `json:"previousblockhash"`
NextHash string `json:"nextblockhash,omitempty"` NextHash string `json:"nextblockhash,omitempty"`
} `json:"result"` } `json:"result"`
} }
type GetBlockHeaderResult struct { type GetBlockHeaderResult struct {
Error Error `json:"error"` Error Error `json:"error"`
Result struct { Result struct {
Hash string `json:"hash"` Hash string `json:"hash"`
Confirmations int64 `json:"confirmations"` Confirmations int64 `json:"confirmations"`
Version common.JSONNumber `json:"version"` Version json.Number `json:"version"`
MerkleRoot string `json:"merkleroot"` MerkleRoot string `json:"merkleroot"`
StakeRoot string `json:"stakeroot"` StakeRoot string `json:"stakeroot"`
VoteBits uint16 `json:"votebits"` VoteBits uint16 `json:"votebits"`
FinalState string `json:"finalstate"` FinalState string `json:"finalstate"`
Voters uint16 `json:"voters"` Voters uint16 `json:"voters"`
FreshStake uint8 `json:"freshstake"` FreshStake uint8 `json:"freshstake"`
Revocations uint8 `json:"revocations"` Revocations uint8 `json:"revocations"`
PoolSize uint32 `json:"poolsize"` PoolSize uint32 `json:"poolsize"`
Bits string `json:"bits"` Bits string `json:"bits"`
SBits float64 `json:"sbits"` SBits float64 `json:"sbits"`
Height uint32 `json:"height"` Height uint32 `json:"height"`
Size uint32 `json:"size"` Size uint32 `json:"size"`
Time int64 `json:"time"` Time int64 `json:"time"`
Nonce uint32 `json:"nonce"` Nonce uint32 `json:"nonce"`
ExtraData string `json:"extradata"` ExtraData string `json:"extradata"`
StakeVersion uint32 `json:"stakeversion"` StakeVersion uint32 `json:"stakeversion"`
Difficulty float64 `json:"difficulty"` Difficulty float64 `json:"difficulty"`
ChainWork string `json:"chainwork"` ChainWork string `json:"chainwork"`
PreviousHash string `json:"previousblockhash,omitempty"` PreviousHash string `json:"previousblockhash,omitempty"`
NextHash string `json:"nextblockhash,omitempty"` NextHash string `json:"nextblockhash,omitempty"`
} `json:"result"` } `json:"result"`
} }
@ -297,8 +297,8 @@ type EstimateSmartFeeResult struct {
} }
type EstimateFeeResult struct { type EstimateFeeResult struct {
Error Error `json:"error"` Error Error `json:"error"`
Result common.JSONNumber `json:"result"` Result json.Number `json:"result"`
} }
type SendRawTransactionResult struct { type SendRawTransactionResult struct {
@ -637,7 +637,7 @@ func (d *DecredRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) {
Version: block.Result.Version, Version: block.Result.Version,
Nonce: block.Result.Nonce, Nonce: block.Result.Nonce,
Bits: block.Result.Bits, Bits: block.Result.Bits,
Difficulty: common.JSONNumber(strconv.FormatFloat(block.Result.Difficulty, 'e', -1, 64)), Difficulty: json.Number(strconv.FormatFloat(block.Result.Difficulty, 'e', -1, 64)),
Txids: block.Result.Tx, Txids: block.Result.Tx,
} }

View File

@ -1,10 +1,11 @@
package deeponion package deeponion
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"github.com/martinboehm/btcd/wire" "github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// magic numbers // magic numbers

View File

@ -3,6 +3,8 @@
package deeponion package deeponion
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/hex" "encoding/hex"
"math/big" "math/big"
"os" "os"
@ -10,8 +12,6 @@ import (
"testing" "testing"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {

View File

@ -1,12 +1,12 @@
package deeponion package deeponion
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json" "encoding/json"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/juju/errors" "github.com/juju/errors"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// DeepOnionRPC is an interface to JSON-RPC bitcoind service. // DeepOnionRPC is an interface to JSON-RPC bitcoind service.

View File

@ -1,21 +1,20 @@
package digibyte package digibyte
import ( import (
"blockbook/bchain/coins/btc"
"github.com/martinboehm/btcd/wire" "github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
const ( const (
// MainnetMagic is mainnet network constant // MainnetMagic is mainnet network constant
MainnetMagic wire.BitcoinNet = 0xdab6c3fa MainnetMagic wire.BitcoinNet = 0xdab6c3fa
TestnetMagic wire.BitcoinNet = 0xddbdc8fd
) )
var ( var (
// MainNetParams are parser parameters for mainnet // MainNetParams are parser parameters for mainnet
MainNetParams chaincfg.Params MainNetParams chaincfg.Params
TestNetParams chaincfg.Params
) )
func init() { func init() {
@ -24,12 +23,6 @@ func init() {
MainNetParams.PubKeyHashAddrID = []byte{30} MainNetParams.PubKeyHashAddrID = []byte{30}
MainNetParams.ScriptHashAddrID = []byte{63} MainNetParams.ScriptHashAddrID = []byte{63}
MainNetParams.Bech32HRPSegwit = "dgb" MainNetParams.Bech32HRPSegwit = "dgb"
TestNetParams = chaincfg.TestNet3Params
TestNetParams.Net = TestnetMagic
TestNetParams.PubKeyHashAddrID = []byte{126}
TestNetParams.ScriptHashAddrID = []byte{140}
TestNetParams.Bech32HRPSegwit = "dgbt"
} }
// DigiByteParser handle // DigiByteParser handle
@ -37,27 +30,18 @@ type DigiByteParser struct {
*btc.BitcoinParser *btc.BitcoinParser
} }
// NewDigiByteParser returns new DigiByteParser instance // NewDigiByteParser returns new VertcoinParser instance
func NewDigiByteParser(params *chaincfg.Params, c *btc.Configuration) *DigiByteParser { func NewDigiByteParser(params *chaincfg.Params, c *btc.Configuration) *DigiByteParser {
return &DigiByteParser{BitcoinParser: btc.NewBitcoinParser(params, c)} return &DigiByteParser{BitcoinParser: btc.NewBitcoinParser(params, c)}
} }
// GetChainParams contains network parameters for the main DigiByte network // GetChainParams contains network parameters for the main DigiByte network
// and the DigiByte Testnet network
func GetChainParams(chain string) *chaincfg.Params { func GetChainParams(chain string) *chaincfg.Params {
if !chaincfg.IsRegistered(&MainNetParams) { if !chaincfg.IsRegistered(&MainNetParams) {
err := chaincfg.Register(&MainNetParams) err := chaincfg.Register(&MainNetParams)
if err == nil {
err = chaincfg.Register(&TestNetParams)
}
if err != nil { if err != nil {
panic(err) panic(err)
} }
} }
switch chain { return &MainNetParams
case "test":
return &TestNetParams
default:
return &MainNetParams
}
} }

View File

@ -3,6 +3,8 @@
package digibyte package digibyte
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/hex" "encoding/hex"
"math/big" "math/big"
"os" "os"
@ -10,8 +12,6 @@ import (
"testing" "testing"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {

View File

@ -1,11 +1,11 @@
package digibyte package digibyte
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json" "encoding/json"
"github.com/golang/glog" "github.com/golang/glog"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// DigiByteRPC is an interface to JSON-RPC bitcoind service. // DigiByteRPC is an interface to JSON-RPC bitcoind service.

View File

@ -1,18 +1,20 @@
package divi package divi
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"blockbook/bchain/coins/utils"
"bytes" "bytes"
"io"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"io"
"math/big" "math/big"
"github.com/juju/errors" "github.com/juju/errors"
"github.com/martinboehm/btcd/wire" "github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
"spacecruft.org/spacecruft/blockbook/bchain/coins/utils"
) )
const ( const (

View File

@ -3,6 +3,8 @@
package divi package divi
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
@ -14,8 +16,6 @@ import (
"testing" "testing"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {

View File

@ -1,11 +1,11 @@
package divi package divi
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json" "encoding/json"
"github.com/golang/glog" "github.com/golang/glog"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// DivicoinRPC is an interface to JSON-RPC bitcoind service. // DivicoinRPC is an interface to JSON-RPC bitcoind service.

View File

@ -1,13 +1,13 @@
package dogecoin package dogecoin
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"blockbook/bchain/coins/utils"
"bytes" "bytes"
"github.com/martinboehm/btcd/wire" "github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
"spacecruft.org/spacecruft/blockbook/bchain/coins/utils"
) )
// magic numbers // magic numbers

View File

@ -3,6 +3,8 @@
package dogecoin package dogecoin
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
@ -14,8 +16,6 @@ import (
"testing" "testing"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {

View File

@ -1,11 +1,11 @@
package dogecoin package dogecoin
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json" "encoding/json"
"github.com/golang/glog" "github.com/golang/glog"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// DogecoinRPC is an interface to JSON-RPC dogecoind service. // DogecoinRPC is an interface to JSON-RPC dogecoind service.

View File

@ -1,6 +1,7 @@
package eth package eth
import ( import (
"blockbook/bchain"
"bytes" "bytes"
"context" "context"
"encoding/hex" "encoding/hex"
@ -12,7 +13,6 @@ import (
ethcommon "github.com/ethereum/go-ethereum/common" ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/juju/errors" "github.com/juju/errors"
"spacecruft.org/spacecruft/blockbook/bchain"
) )
var erc20abi = `[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function","signature":"0x06fdde03"}, var erc20abi = `[{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"type":"function","signature":"0x06fdde03"},
@ -145,26 +145,24 @@ func parseErc20StringProperty(contractDesc bchain.AddressDescriptor, data string
n := parseErc20NumericProperty(contractDesc, data[64:128]) n := parseErc20NumericProperty(contractDesc, data[64:128])
if n != nil { if n != nil {
l := n.Uint64() l := n.Uint64()
if l > 0 && 2*int(l) <= len(data)-128 { if 2*int(l) <= len(data)-128 {
b, err := hex.DecodeString(data[128 : 128+2*l]) b, err := hex.DecodeString(data[128 : 128+2*l])
if err == nil { if err == nil {
return string(b) return string(b)
} }
} }
} }
} } else if len(data) == 64 {
// allow string properties as UTF-8 data // allow string properties as 32 bytes of UTF-8 data
b, err := hex.DecodeString(data) b, err := hex.DecodeString(data)
if err == nil { if err == nil {
i := bytes.Index(b, []byte{0}) i := bytes.Index(b, []byte{0})
if i > 32 { if i > 0 {
i = 32 b = b[:i]
} }
if i > 0 { if utf8.Valid(b) {
b = b[:i] return string(b)
} }
if utf8.Valid(b) {
return string(b)
} }
} }
if glog.V(1) { if glog.V(1) {
@ -183,26 +181,18 @@ func (b *EthereumRPC) EthereumTypeGetErc20ContractInfo(contractDesc bchain.Addre
address := EIP55Address(contractDesc) address := EIP55Address(contractDesc)
data, err := b.ethCall(erc20NameSignature, address) data, err := b.ethCall(erc20NameSignature, address)
if err != nil { if err != nil {
// ignore the error from the eth_call - since geth v1.9.15 they changed the behavior return nil, err
// and returning error "execution reverted" for some non contract addresses
// https://github.com/ethereum/go-ethereum/issues/21249#issuecomment-648647672
glog.Warning(errors.Annotatef(err, "erc20NameSignature %v", address))
return nil, nil
// return nil, errors.Annotatef(err, "erc20NameSignature %v", address)
} }
name := parseErc20StringProperty(contractDesc, data) name := parseErc20StringProperty(contractDesc, data)
if name != "" { if name != "" {
data, err = b.ethCall(erc20SymbolSignature, address) data, err = b.ethCall(erc20SymbolSignature, address)
if err != nil { if err != nil {
glog.Warning(errors.Annotatef(err, "erc20SymbolSignature %v", address)) return nil, err
return nil, nil
// return nil, errors.Annotatef(err, "erc20SymbolSignature %v", address)
} }
symbol := parseErc20StringProperty(contractDesc, data) symbol := parseErc20StringProperty(contractDesc, data)
data, err = b.ethCall(erc20DecimalsSignature, address) data, err = b.ethCall(erc20DecimalsSignature, address)
if err != nil { if err != nil {
glog.Warning(errors.Annotatef(err, "erc20DecimalsSignature %v", address)) return nil, err
// return nil, errors.Annotatef(err, "erc20DecimalsSignature %v", address)
} }
contract = &bchain.Erc20Contract{ contract = &bchain.Erc20Contract{
Contract: address, Contract: address,

View File

@ -3,13 +3,12 @@
package eth package eth
import ( import (
"fmt" "blockbook/bchain"
"blockbook/tests/dbtestdata"
fmt "fmt"
"math/big" "math/big"
"strings" "strings"
"testing" "testing"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/tests/dbtestdata"
) )
func TestErc20_erc20GetTransfersFromLog(t *testing.T) { func TestErc20_erc20GetTransfersFromLog(t *testing.T) {
@ -139,16 +138,6 @@ func TestErc20_parseErc20StringProperty(t *testing.T) {
args: "0x44616920537461626c65636f696e2076312e3020444444444444444444444444", args: "0x44616920537461626c65636f696e2076312e3020444444444444444444444444",
want: "Dai Stablecoin v1.0 DDDDDDDDDDDD", want: "Dai Stablecoin v1.0 DDDDDDDDDDDD",
}, },
{
name: "long",
args: "0x556e6973776170205631000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000",
want: "Uniswap V1",
},
{
name: "garbage",
args: "0x2234880850896048596206002535425366538144616734015984380565810000",
want: "",
},
} }
for _, tt := range tests { for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) { t.Run(tt.name, func(t *testing.T) {

View File

@ -1,6 +1,7 @@
package eth package eth
import ( import (
"blockbook/bchain"
"encoding/hex" "encoding/hex"
"math/big" "math/big"
"strconv" "strconv"
@ -8,7 +9,6 @@ import (
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/golang/protobuf/proto" "github.com/golang/protobuf/proto"
"github.com/juju/errors" "github.com/juju/errors"
"spacecruft.org/spacecruft/blockbook/bchain"
"golang.org/x/crypto/sha3" "golang.org/x/crypto/sha3"
) )
@ -76,6 +76,12 @@ type rpcReceipt struct {
Logs []*rpcLog `json:"logs"` Logs []*rpcLog `json:"logs"`
} }
type rpcEtcReceipt struct {
GasUsed string `json:"gasUsed"`
Status int `json:"status"`
Logs []*rpcLog `json:"logs"`
}
type completeTransaction struct { type completeTransaction struct {
Tx *rpcTransaction `json:"tx"` Tx *rpcTransaction `json:"tx"`
Receipt *rpcReceipt `json:"receipt,omitempty"` Receipt *rpcReceipt `json:"receipt,omitempty"`
@ -96,31 +102,18 @@ func ethNumber(n string) (int64, error) {
return 0, errors.Errorf("Not a number: '%v'", n) return 0, errors.Errorf("Not a number: '%v'", n)
} }
func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, receipt *rpcReceipt, blocktime int64, confirmations uint32, fixEIP55 bool) (*bchain.Tx, error) { func (p *EthereumParser) ethTxToTx(tx *rpcTransaction, receipt *rpcReceipt, blocktime int64, confirmations uint32) (*bchain.Tx, error) {
txid := tx.Hash txid := tx.Hash
var ( var (
fa, ta []string fa, ta []string
err error err error
) )
if len(tx.From) > 2 { if len(tx.From) > 2 {
if fixEIP55 {
tx.From = EIP55AddressFromAddress(tx.From)
}
fa = []string{tx.From} fa = []string{tx.From}
} }
if len(tx.To) > 2 { if len(tx.To) > 2 {
if fixEIP55 {
tx.To = EIP55AddressFromAddress(tx.To)
}
ta = []string{tx.To} ta = []string{tx.To}
} }
if fixEIP55 && receipt != nil && receipt.Logs != nil {
for _, l := range receipt.Logs {
if len(l.Address) > 2 {
l.Address = EIP55AddressFromAddress(l.Address)
}
}
}
ct := completeTransaction{ ct := completeTransaction{
Tx: tx, Tx: tx,
Receipt: receipt, Receipt: receipt,
@ -211,9 +204,6 @@ func EIP55Address(addrDesc bchain.AddressDescriptor) string {
// EIP55AddressFromAddress returns an EIP55-compliant hex string representation of the address // EIP55AddressFromAddress returns an EIP55-compliant hex string representation of the address
func EIP55AddressFromAddress(address string) string { func EIP55AddressFromAddress(address string) string {
if has0xPrefix(address) {
address = address[2:]
}
b, err := hex.DecodeString(address) b, err := hex.DecodeString(address)
if err != nil { if err != nil {
return address return address
@ -311,14 +301,8 @@ func (p *EthereumParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) (
if pt.Receipt.GasUsed, err = hexDecodeBig(r.Receipt.GasUsed); err != nil { if pt.Receipt.GasUsed, err = hexDecodeBig(r.Receipt.GasUsed); err != nil {
return nil, errors.Annotatef(err, "GasUsed %v", r.Receipt.GasUsed) return nil, errors.Annotatef(err, "GasUsed %v", r.Receipt.GasUsed)
} }
if r.Receipt.Status != "" { if pt.Receipt.Status, err = hexDecodeBig(r.Receipt.Status); err != nil {
if pt.Receipt.Status, err = hexDecodeBig(r.Receipt.Status); err != nil { return nil, errors.Annotatef(err, "Status %v", r.Receipt.Status)
return nil, errors.Annotatef(err, "Status %v", r.Receipt.Status)
}
} else {
// unknown status, use 'U' as status bytes
// there is a potential for conflict with value 0x55 but this is not used by any chain at this moment
pt.Receipt.Status = []byte{'U'}
} }
ptLogs := make([]*ProtoCompleteTransaction_ReceiptType_LogType, len(r.Receipt.Logs)) ptLogs := make([]*ProtoCompleteTransaction_ReceiptType_LogType, len(r.Receipt.Logs))
for i, l := range r.Receipt.Logs { for i, l := range r.Receipt.Logs {
@ -385,18 +369,13 @@ func (p *EthereumParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
Topics: topics, Topics: topics,
} }
} }
status := ""
// handle a special value []byte{'U'} as unknown state
if len(pt.Receipt.Status) != 1 || pt.Receipt.Status[0] != 'U' {
status = hexEncodeBig(pt.Receipt.Status)
}
rr = &rpcReceipt{ rr = &rpcReceipt{
GasUsed: hexEncodeBig(pt.Receipt.GasUsed), GasUsed: hexEncodeBig(pt.Receipt.GasUsed),
Status: status, Status: hexEncodeBig(pt.Receipt.Status),
Logs: logs, Logs: logs,
} }
} }
tx, err := p.ethTxToTx(&rt, rr, int64(pt.BlockTime), 0, false) tx, err := p.ethTxToTx(&rt, rr, int64(pt.BlockTime), 0)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }
@ -472,51 +451,40 @@ func (p *EthereumParser) EthereumTypeGetErc20FromTx(tx *bchain.Tx) ([]bchain.Erc
return r, nil return r, nil
} }
// TxStatus is status of transaction
type TxStatus int
// statuses of transaction
const ( const (
TxStatusUnknown = TxStatus(iota - 2) txStatusUnknown = iota - 2
TxStatusPending txStatusPending
TxStatusFailure txStatusFailure
TxStatusOK txStatusOK
) )
// EthereumTxData contains ethereum specific transaction data // EthereumTxData contains ethereum specific transaction data
type EthereumTxData struct { type EthereumTxData struct {
Status TxStatus `json:"status"` // 1 OK, 0 Fail, -1 pending, -2 unknown Status int `json:"status"` // 1 OK, 0 Fail, -1 pending, -2 unknown
Nonce uint64 `json:"nonce"` Nonce uint64 `json:"nonce"`
GasLimit *big.Int `json:"gaslimit"` GasLimit *big.Int `json:"gaslimit"`
GasUsed *big.Int `json:"gasused"` GasUsed *big.Int `json:"gasused"`
GasPrice *big.Int `json:"gasprice"` GasPrice *big.Int `json:"gasprice"`
Data string `json:"data"`
} }
// GetEthereumTxData returns EthereumTxData from bchain.Tx // GetEthereumTxData returns EthereumTxData from bchain.Tx
func GetEthereumTxData(tx *bchain.Tx) *EthereumTxData { func GetEthereumTxData(tx *bchain.Tx) *EthereumTxData {
return GetEthereumTxDataFromSpecificData(tx.CoinSpecificData) etd := EthereumTxData{Status: txStatusPending}
} csd, ok := tx.CoinSpecificData.(completeTransaction)
// GetEthereumTxDataFromSpecificData returns EthereumTxData from coinSpecificData
func GetEthereumTxDataFromSpecificData(coinSpecificData interface{}) *EthereumTxData {
etd := EthereumTxData{Status: TxStatusPending}
csd, ok := coinSpecificData.(completeTransaction)
if ok { if ok {
if csd.Tx != nil { if csd.Tx != nil {
etd.Nonce, _ = hexutil.DecodeUint64(csd.Tx.AccountNonce) etd.Nonce, _ = hexutil.DecodeUint64(csd.Tx.AccountNonce)
etd.GasLimit, _ = hexutil.DecodeBig(csd.Tx.GasLimit) etd.GasLimit, _ = hexutil.DecodeBig(csd.Tx.GasLimit)
etd.GasPrice, _ = hexutil.DecodeBig(csd.Tx.GasPrice) etd.GasPrice, _ = hexutil.DecodeBig(csd.Tx.GasPrice)
etd.Data = csd.Tx.Payload
} }
if csd.Receipt != nil { if csd.Receipt != nil {
switch csd.Receipt.Status { switch csd.Receipt.Status {
case "0x1": case "0x1":
etd.Status = TxStatusOK etd.Status = txStatusOK
case "": // old transactions did not set status case "": // old transactions did not set status
etd.Status = TxStatusUnknown etd.Status = txStatusUnknown
default: default:
etd.Status = TxStatusFailure etd.Status = txStatusFailure
} }
etd.GasUsed, _ = hexutil.DecodeBig(csd.Receipt.GasUsed) etd.GasUsed, _ = hexutil.DecodeBig(csd.Receipt.GasUsed)
} }

View File

@ -3,14 +3,13 @@
package eth package eth
import ( import (
"blockbook/bchain"
"blockbook/tests/dbtestdata"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
"math/big" "math/big"
"reflect" "reflect"
"testing" "testing"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/tests/dbtestdata"
) )
func TestEthParser_GetAddrDescFromAddress(t *testing.T) { func TestEthParser_GetAddrDescFromAddress(t *testing.T) {
@ -68,7 +67,7 @@ func TestEthParser_GetAddrDescFromAddress(t *testing.T) {
} }
} }
var testTx1, testTx2, testTx1Failed, testTx1NoStatus bchain.Tx var testTx1, testTx2 bchain.Tx
func init() { func init() {
@ -156,83 +155,6 @@ func init() {
}, },
}, },
} }
testTx1Failed = bchain.Tx{
Blocktime: 1534858022,
Time: 1534858022,
Txid: "0xcd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b",
Vin: []bchain.Vin{
{
Addresses: []string{"0x3E3a3D69dc66bA10737F531ed088954a9EC89d97"},
},
},
Vout: []bchain.Vout{
{
ValueSat: *big.NewInt(1999622000000000000),
ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f"},
},
},
},
CoinSpecificData: completeTransaction{
Tx: &rpcTransaction{
AccountNonce: "0xb26c",
GasPrice: "0x430e23400",
GasLimit: "0x5208",
To: "0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f",
Value: "0x1bc0159d530e6000",
Payload: "0x",
Hash: "0xcd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b",
BlockNumber: "0x41eee8",
From: "0x3E3a3D69dc66bA10737F531ed088954a9EC89d97",
TransactionIndex: "0xa",
},
Receipt: &rpcReceipt{
GasUsed: "0x5208",
Status: "0x0",
Logs: []*rpcLog{},
},
},
}
testTx1NoStatus = bchain.Tx{
Blocktime: 1534858022,
Time: 1534858022,
Txid: "0xcd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b",
Vin: []bchain.Vin{
{
Addresses: []string{"0x3E3a3D69dc66bA10737F531ed088954a9EC89d97"},
},
},
Vout: []bchain.Vout{
{
ValueSat: *big.NewInt(1999622000000000000),
ScriptPubKey: bchain.ScriptPubKey{
Addresses: []string{"0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f"},
},
},
},
CoinSpecificData: completeTransaction{
Tx: &rpcTransaction{
AccountNonce: "0xb26c",
GasPrice: "0x430e23400",
GasLimit: "0x5208",
To: "0x555Ee11FBDDc0E49A9bAB358A8941AD95fFDB48f",
Value: "0x1bc0159d530e6000",
Payload: "0x",
Hash: "0xcd647151552b5132b2aef7c9be00dc6f73afc5901dde157aab131335baaa853b",
BlockNumber: "0x41eee8",
From: "0x3E3a3D69dc66bA10737F531ed088954a9EC89d97",
TransactionIndex: "0xa",
},
Receipt: &rpcReceipt{
GasUsed: "0x5208",
Status: "",
Logs: []*rpcLog{},
},
},
}
} }
func TestEthereumParser_PackTx(t *testing.T) { func TestEthereumParser_PackTx(t *testing.T) {
@ -266,24 +188,6 @@ func TestEthereumParser_PackTx(t *testing.T) {
}, },
want: dbtestdata.EthTx2Packed, want: dbtestdata.EthTx2Packed,
}, },
{
name: "3",
args: args{
tx: &testTx1Failed,
height: 4321000,
blockTime: 1534858022,
},
want: dbtestdata.EthTx1FailedPacked,
},
{
name: "4",
args: args{
tx: &testTx1NoStatus,
height: 4321000,
blockTime: 1534858022,
},
want: dbtestdata.EthTx1NoStatusPacked,
},
} }
p := NewEthereumParser(1) p := NewEthereumParser(1)
for _, tt := range tests { for _, tt := range tests {
@ -325,18 +229,6 @@ func TestEthereumParser_UnpackTx(t *testing.T) {
want: &testTx2, want: &testTx2,
want1: 4321000, want1: 4321000,
}, },
{
name: "3",
args: args{hex: dbtestdata.EthTx1FailedPacked},
want: &testTx1Failed,
want1: 4321000,
},
{
name: "4",
args: args{hex: dbtestdata.EthTx1NoStatusPacked},
want: &testTx1NoStatus,
want1: 4321000,
},
} }
p := NewEthereumParser(1) p := NewEthereumParser(1)
for _, tt := range tests { for _, tt := range tests {
@ -373,30 +265,3 @@ func TestEthereumParser_UnpackTx(t *testing.T) {
}) })
} }
} }
func TestEthereumParser_GetEthereumTxData(t *testing.T) {
tests := []struct {
name string
tx *bchain.Tx
want string
}{
{
name: "Test empty data",
tx: &testTx1,
want: "0x",
},
{
name: "Test non empty data",
tx: &testTx2,
want: "0xa9059cbb000000000000000000000000555ee11fbddc0e49a9bab358a8941ad95ffdb48f00000000000000000000000000000000000000000000021e19e0c9bab2400000",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := GetEthereumTxData(tt.tx)
if got.Data != tt.want {
t.Errorf("EthereumParser.GetEthereumTxData() = %v, want %v", got.Data, tt.want)
}
})
}
}

View File

@ -1,6 +1,7 @@
package eth package eth
import ( import (
"blockbook/bchain"
"context" "context"
"encoding/json" "encoding/json"
"fmt" "fmt"
@ -11,14 +12,11 @@ import (
ethereum "github.com/ethereum/go-ethereum" ethereum "github.com/ethereum/go-ethereum"
ethcommon "github.com/ethereum/go-ethereum/common" ethcommon "github.com/ethereum/go-ethereum/common"
"github.com/ethereum/go-ethereum/common/hexutil"
ethtypes "github.com/ethereum/go-ethereum/core/types" ethtypes "github.com/ethereum/go-ethereum/core/types"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
"github.com/ethereum/go-ethereum/rpc" "github.com/ethereum/go-ethereum/rpc"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/juju/errors" "github.com/juju/errors"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/common"
) )
// EthereumNet type specifies the type of ethereum network // EthereumNet type specifies the type of ethereum network
@ -29,8 +27,6 @@ const (
MainNet EthereumNet = 1 MainNet EthereumNet = 1
// TestNet is Ropsten test network // TestNet is Ropsten test network
TestNet EthereumNet = 3 TestNet EthereumNet = 3
// TestNetGoerli is Goerli test network
TestNetGoerli EthereumNet = 5
) )
// Configuration represents json config file // Configuration represents json config file
@ -61,6 +57,7 @@ type EthereumRPC struct {
chanNewTx chan ethcommon.Hash chanNewTx chan ethcommon.Hash
newTxSubscription *rpc.ClientSubscription newTxSubscription *rpc.ClientSubscription
ChainConfig *Configuration ChainConfig *Configuration
isETC bool
} }
// NewEthereumRPC returns new EthRPC instance. // NewEthereumRPC returns new EthRPC instance.
@ -92,6 +89,9 @@ func NewEthereumRPC(config json.RawMessage, pushHandler func(bchain.Notification
s.Parser = NewEthereumParser(c.BlockAddressesToKeep) s.Parser = NewEthereumParser(c.BlockAddressesToKeep)
s.timeout = time.Duration(c.RPCTimeout) * time.Second s.timeout = time.Duration(c.RPCTimeout) * time.Second
// detect ethereum classic
s.isETC = s.ChainConfig.CoinName == "Ethereum Classic"
// new blocks notifications handling // new blocks notifications handling
// the subscription is done in Initialize // the subscription is done in Initialize
s.chanNewBlock = make(chan *ethtypes.Header) s.chanNewBlock = make(chan *ethtypes.Header)
@ -162,9 +162,6 @@ func (b *EthereumRPC) Initialize() error {
b.Testnet = true b.Testnet = true
b.Network = "testnet" b.Network = "testnet"
break break
case TestNetGoerli:
b.Testnet = true
b.Network = "goerli"
default: default:
return errors.Errorf("Unknown network id %v", id) return errors.Errorf("Unknown network id %v", id)
} }
@ -183,7 +180,7 @@ func (b *EthereumRPC) CreateMempool(chain bchain.BlockChain) (bchain.Mempool, er
} }
// InitializeMempool creates subscriptions to newHeads and newPendingTransactions // InitializeMempool creates subscriptions to newHeads and newPendingTransactions
func (b *EthereumRPC) InitializeMempool(addrDescForOutpoint bchain.AddrDescForOutpointFunc, onNewTxAddr bchain.OnNewTxAddrFunc, onNewTx bchain.OnNewTxFunc) error { func (b *EthereumRPC) InitializeMempool(addrDescForOutpoint bchain.AddrDescForOutpointFunc, onNewTxAddr bchain.OnNewTxAddrFunc) error {
if b.Mempool == nil { if b.Mempool == nil {
return errors.New("Mempool not created") return errors.New("Mempool not created")
} }
@ -198,7 +195,6 @@ func (b *EthereumRPC) InitializeMempool(addrDescForOutpoint bchain.AddrDescForOu
} }
b.Mempool.OnNewTxAddr = onNewTxAddr b.Mempool.OnNewTxAddr = onNewTxAddr
b.Mempool.OnNewTx = onNewTx
if err = b.subscribeEvents(); err != nil { if err = b.subscribeEvents(); err != nil {
return err return err
@ -210,21 +206,25 @@ func (b *EthereumRPC) InitializeMempool(addrDescForOutpoint bchain.AddrDescForOu
} }
func (b *EthereumRPC) subscribeEvents() error { func (b *EthereumRPC) subscribeEvents() error {
// subscriptions if b.isETC {
if err := b.subscribe(func() (*rpc.ClientSubscription, error) { glog.Info(b.ChainConfig.CoinName, " does not support subscription to newHeads")
// invalidate the previous subscription - it is either the first one or there was an error } else {
b.newBlockSubscription = nil // subscriptions
ctx, cancel := context.WithTimeout(context.Background(), b.timeout) if err := b.subscribe(func() (*rpc.ClientSubscription, error) {
defer cancel() // invalidate the previous subscription - it is either the first one or there was an error
sub, err := b.rpc.EthSubscribe(ctx, b.chanNewBlock, "newHeads") b.newBlockSubscription = nil
if err != nil { ctx, cancel := context.WithTimeout(context.Background(), b.timeout)
return nil, errors.Annotatef(err, "EthSubscribe newHeads") defer cancel()
sub, err := b.rpc.EthSubscribe(ctx, b.chanNewBlock, "newHeads")
if err != nil {
return nil, errors.Annotatef(err, "EthSubscribe newHeads")
}
b.newBlockSubscription = sub
glog.Info("Subscribed to newHeads")
return sub, nil
}); err != nil {
return err
} }
b.newBlockSubscription = sub
glog.Info("Subscribed to newHeads")
return sub, nil
}); err != nil {
return err
} }
if err := b.subscribe(func() (*rpc.ClientSubscription, error) { if err := b.subscribe(func() (*rpc.ClientSubscription, error) {
@ -340,15 +340,19 @@ func (b *EthereumRPC) GetChainInfo() (*bchain.ChainInfo, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
var ver string var ver, protocol string
if err := b.rpc.CallContext(ctx, &ver, "web3_clientVersion"); err != nil { if err := b.rpc.CallContext(ctx, &ver, "web3_clientVersion"); err != nil {
return nil, err return nil, err
} }
if err := b.rpc.CallContext(ctx, &protocol, "eth_protocolVersion"); err != nil {
return nil, err
}
rv := &bchain.ChainInfo{ rv := &bchain.ChainInfo{
Blocks: int(h.Number.Int64()), Blocks: int(h.Number.Int64()),
Bestblockhash: h.Hash().Hex(), Bestblockhash: h.Hash().Hex(),
Difficulty: h.Difficulty.String(), Difficulty: h.Difficulty.String(),
Version: ver, Version: ver,
ProtocolVersion: protocol,
} }
idi := int(id.Uint64()) idi := int(id.Uint64())
if idi == 1 { if idi == 1 {
@ -362,6 +366,12 @@ func (b *EthereumRPC) GetChainInfo() (*bchain.ChainInfo, error) {
func (b *EthereumRPC) getBestHeader() (*ethtypes.Header, error) { func (b *EthereumRPC) getBestHeader() (*ethtypes.Header, error) {
b.bestHeaderLock.Lock() b.bestHeaderLock.Lock()
defer b.bestHeaderLock.Unlock() defer b.bestHeaderLock.Unlock()
// ETC does not have newBlocks subscription, bestHeader must be updated very often (each 1 second)
if b.isETC {
if b.bestHeaderTime.Add(1 * time.Second).Before(time.Now()) {
b.bestHeader = nil
}
}
// if the best header was not updated for 15 minutes, there could be a subscription problem, reconnect RPC // if the best header was not updated for 15 minutes, there could be a subscription problem, reconnect RPC
// do it only in case of normal operation, not initial synchronization // do it only in case of normal operation, not initial synchronization
if b.bestHeaderTime.Add(15*time.Minute).Before(time.Now()) && !b.bestHeaderTime.IsZero() && b.mempoolInitialized { if b.bestHeaderTime.Add(15*time.Minute).Before(time.Now()) && !b.bestHeaderTime.IsZero() && b.mempoolInitialized {
@ -537,7 +547,7 @@ func (b *EthereumRPC) GetBlock(hash string, height uint32) (*bchain.Block, error
btxs := make([]bchain.Tx, len(body.Transactions)) btxs := make([]bchain.Tx, len(body.Transactions))
for i := range body.Transactions { for i := range body.Transactions {
tx := &body.Transactions[i] tx := &body.Transactions[i]
btx, err := b.Parser.ethTxToTx(tx, &rpcReceipt{Logs: logs[tx.Hash]}, bbh.Time, uint32(bbh.Confirmations), true) btx, err := b.Parser.ethTxToTx(tx, &rpcReceipt{Logs: logs[tx.Hash]}, bbh.Time, uint32(bbh.Confirmations))
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "hash %v, height %v, txid %v", hash, height, tx.Hash) return nil, errors.Annotatef(err, "hash %v, height %v, txid %v", hash, height, tx.Hash)
} }
@ -573,8 +583,8 @@ func (b *EthereumRPC) GetBlockInfo(hash string) (*bchain.BlockInfo, error) {
} }
return &bchain.BlockInfo{ return &bchain.BlockInfo{
BlockHeader: *bch, BlockHeader: *bch,
Difficulty: common.JSONNumber(head.Difficulty), Difficulty: json.Number(head.Difficulty),
Nonce: common.JSONNumber(head.Nonce), Nonce: json.Number(head.Nonce),
Txids: txs.Transactions, Txids: txs.Transactions,
}, nil }, nil
} }
@ -603,7 +613,7 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) {
var btx *bchain.Tx var btx *bchain.Tx
if tx.BlockNumber == "" { if tx.BlockNumber == "" {
// mempool tx // mempool tx
btx, err = b.Parser.ethTxToTx(tx, nil, 0, 0, true) btx, err = b.Parser.ethTxToTx(tx, nil, 0, 0)
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid) return nil, errors.Annotatef(err, "txid %v", txid)
} }
@ -624,9 +634,33 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) {
return nil, errors.Annotatef(err, "txid %v", txid) return nil, errors.Annotatef(err, "txid %v", txid)
} }
var receipt rpcReceipt var receipt rpcReceipt
err = b.rpc.CallContext(ctx, &receipt, "eth_getTransactionReceipt", hash) if b.isETC {
if err != nil { var rawReceipt json.RawMessage
return nil, errors.Annotatef(err, "txid %v", txid) var etcReceipt rpcEtcReceipt
err = b.rpc.CallContext(ctx, &rawReceipt, "eth_getTransactionReceipt", hash)
if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid)
}
err = json.Unmarshal(rawReceipt, &etcReceipt)
if err == nil {
receipt.GasUsed = etcReceipt.GasUsed
receipt.Logs = etcReceipt.Logs
if etcReceipt.Status == 0 {
receipt.Status = "0x0"
} else {
receipt.Status = "0x1"
}
} else {
err = json.Unmarshal(rawReceipt, &receipt)
if err != nil {
return nil, errors.Annotatef(err, "unmarshal receipt for txid %v, %v", txid, string(rawReceipt))
}
}
} else {
err = b.rpc.CallContext(ctx, &receipt, "eth_getTransactionReceipt", hash)
if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid)
}
} }
n, err := ethNumber(tx.BlockNumber) n, err := ethNumber(tx.BlockNumber)
if err != nil { if err != nil {
@ -636,7 +670,7 @@ func (b *EthereumRPC) GetTransaction(txid string) (*bchain.Tx, error) {
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid) return nil, errors.Annotatef(err, "txid %v", txid)
} }
btx, err = b.Parser.ethTxToTx(tx, &receipt, time, confirmations, true) btx, err = b.Parser.ethTxToTx(tx, &receipt, time, confirmations)
if err != nil { if err != nil {
return nil, errors.Annotatef(err, "txid %v", txid) return nil, errors.Annotatef(err, "txid %v", txid)
} }
@ -724,18 +758,6 @@ func (b *EthereumRPC) EthereumTypeEstimateGas(params map[string]interface{}) (ui
if ok && len(s) > 0 { if ok && len(s) > 0 {
msg.Data = ethcommon.FromHex(s) msg.Data = ethcommon.FromHex(s)
} }
s, ok = getStringFromMap("value", params)
if ok && len(s) > 0 {
msg.Value, _ = hexutil.DecodeBig(s)
}
s, ok = getStringFromMap("gas", params)
if ok && len(s) > 0 {
msg.Gas, _ = hexutil.DecodeUint64(s)
}
s, ok = getStringFromMap("gasPrice", params)
if ok && len(s) > 0 {
msg.GasPrice, _ = hexutil.DecodeBig(s)
}
return b.client.EstimateGas(ctx, msg) return b.client.EstimateGas(ctx, msg)
} }

View File

@ -1,11 +1,11 @@
// Code generated by protoc-gen-go. DO NOT EDIT. // Code generated by protoc-gen-go. DO NOT EDIT.
// source: bchain/coins/eth/ethtx.proto // source: tx.proto
/* /*
Package eth is a generated protocol buffer package. Package eth is a generated protocol buffer package.
It is generated from these files: It is generated from these files:
bchain/coins/eth/ethtx.proto tx.proto
It has these top-level messages: It has these top-level messages:
ProtoCompleteTransaction ProtoCompleteTransaction
@ -228,34 +228,33 @@ func init() {
proto.RegisterType((*ProtoCompleteTransaction_ReceiptType_LogType)(nil), "eth.ProtoCompleteTransaction.ReceiptType.LogType") proto.RegisterType((*ProtoCompleteTransaction_ReceiptType_LogType)(nil), "eth.ProtoCompleteTransaction.ReceiptType.LogType")
} }
func init() { proto.RegisterFile("bchain/coins/eth/ethtx.proto", fileDescriptor0) } func init() { proto.RegisterFile("tx.proto", fileDescriptor0) }
var fileDescriptor0 = []byte{ var fileDescriptor0 = []byte{
// 409 bytes of a gzipped FileDescriptorProto // 393 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xdf, 0x8a, 0xd4, 0x30, 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x8c, 0x92, 0xdf, 0x8a, 0xd4, 0x30,
0x18, 0xc5, 0xe9, 0x9f, 0x99, 0xd9, 0xfd, 0xa6, 0x8a, 0x04, 0x91, 0x30, 0xec, 0x45, 0x59, 0xbc, 0x14, 0xc6, 0xe9, 0x9f, 0xf9, 0xb3, 0xa7, 0x55, 0x24, 0x88, 0x84, 0xe2, 0x45, 0x59, 0xbc, 0xa8,
0x18, 0xbd, 0xe8, 0xe2, 0xea, 0x0b, 0xac, 0x23, 0xae, 0xc2, 0xb0, 0x0e, 0x31, 0x7a, 0x9f, 0x49, 0x5e, 0x14, 0x5c, 0x7d, 0x81, 0x75, 0xc4, 0x55, 0x18, 0xd6, 0x21, 0x46, 0xef, 0xb3, 0x69, 0xd8,
0xc3, 0x36, 0x38, 0x6d, 0x4a, 0x93, 0x42, 0xf7, 0x8d, 0x7c, 0x21, 0xdf, 0xc5, 0x4b, 0xc9, 0xd7, 0x29, 0xb6, 0x4d, 0x69, 0x52, 0xe8, 0xbe, 0x91, 0x2f, 0xe4, 0xbb, 0x78, 0x29, 0x39, 0x4d, 0xd7,
0x74, 0x1d, 0x11, 0x65, 0x2f, 0x0a, 0xf9, 0x9d, 0x7e, 0xa7, 0x39, 0x27, 0x29, 0x9c, 0xed, 0x65, 0x11, 0x51, 0xbc, 0x3b, 0xbf, 0x6f, 0xce, 0x37, 0xf9, 0xbe, 0xa4, 0xb0, 0xb5, 0x53, 0xd9, 0x0f,
0x25, 0x74, 0x73, 0x21, 0x8d, 0x6e, 0xec, 0x85, 0x72, 0x95, 0x7f, 0xdc, 0x50, 0xb4, 0x9d, 0x71, 0xda, 0x6a, 0x12, 0x29, 0x7b, 0x3c, 0xff, 0xb6, 0x02, 0x7a, 0x70, 0xb8, 0xd3, 0x6d, 0xdf, 0x28,
0x86, 0x24, 0xca, 0x55, 0xe7, 0xdf, 0x67, 0x40, 0x77, 0x1e, 0x37, 0xa6, 0x6e, 0x0f, 0xca, 0x29, 0xab, 0xf8, 0x20, 0x3a, 0x23, 0xa4, 0xad, 0x75, 0x47, 0x72, 0x48, 0xde, 0x34, 0x5a, 0x7e, 0xbd,
0xde, 0x89, 0xc6, 0x0a, 0xe9, 0xb4, 0x69, 0x48, 0x0e, 0xcb, 0xb7, 0x07, 0x23, 0xbf, 0xdd, 0xf4, 0x1e, 0xdb, 0x1b, 0x35, 0xd0, 0x20, 0x0f, 0x8a, 0x07, 0xec, 0x54, 0x22, 0x4f, 0xe1, 0x0c, 0x91,
0xf5, 0x5e, 0x75, 0x34, 0xca, 0xa3, 0xf5, 0x23, 0x76, 0x2c, 0x91, 0x33, 0x38, 0x45, 0xe4, 0xba, 0xd7, 0xad, 0xa2, 0x61, 0x1e, 0x14, 0x31, 0xfb, 0x25, 0x90, 0xd7, 0x10, 0xf2, 0x89, 0x46, 0x79,
0x56, 0x34, 0xce, 0xa3, 0x75, 0xca, 0x7e, 0x0b, 0xe4, 0x0d, 0xc4, 0x7c, 0xa0, 0x49, 0x1e, 0xad, 0x50, 0x24, 0x17, 0xcf, 0x4a, 0x65, 0x8f, 0xe5, 0xdf, 0x8e, 0x2a, 0xf9, 0xc4, 0xef, 0x7a, 0xc5,
0x97, 0x97, 0xcf, 0x0b, 0xe5, 0xaa, 0xe2, 0x5f, 0x5b, 0x15, 0x7c, 0xe0, 0x77, 0xad, 0x62, 0x31, 0x42, 0x3e, 0x91, 0x1d, 0x6c, 0x98, 0x92, 0xaa, 0xee, 0x2d, 0x8d, 0xd1, 0xfa, 0xfc, 0xdf, 0x56,
0x1f, 0xc8, 0x06, 0x16, 0x4c, 0x49, 0xa5, 0x5b, 0x47, 0x53, 0xb4, 0xbe, 0xf8, 0xbf, 0x35, 0x0c, 0xbf, 0x8c, 0xfe, 0xc5, 0x99, 0xfd, 0x08, 0x60, 0x3d, 0xff, 0x27, 0x39, 0x87, 0xf4, 0x52, 0x4a,
0xa3, 0x7f, 0x72, 0xae, 0x7e, 0x46, 0x30, 0x1f, 0xbf, 0x49, 0xce, 0x21, 0xbb, 0x92, 0xd2, 0xf4, 0x3d, 0x76, 0xf6, 0x5a, 0x77, 0x52, 0x61, 0x8d, 0x98, 0xfd, 0xa6, 0x91, 0x0c, 0xb6, 0x57, 0xc2,
0x8d, 0xbb, 0x31, 0x8d, 0x54, 0x58, 0x23, 0x65, 0x7f, 0x68, 0x64, 0x05, 0x27, 0xd7, 0xc2, 0xee, 0x1c, 0x86, 0x5a, 0xce, 0x35, 0x52, 0x76, 0xcf, 0xfe, 0xb7, 0x7d, 0xdd, 0xd6, 0x16, 0xbb, 0xc4,
0x3a, 0x2d, 0xc7, 0x1a, 0x19, 0xbb, 0xe7, 0xf0, 0x6e, 0xab, 0x6b, 0xed, 0xb0, 0x4b, 0xca, 0xee, 0xec, 0x9e, 0xc9, 0x63, 0x58, 0x7d, 0x11, 0xcd, 0xa8, 0x30, 0x69, 0xca, 0x66, 0x20, 0x14, 0x36,
0x99, 0x3c, 0x85, 0xd9, 0x57, 0x71, 0xe8, 0x15, 0x26, 0xcd, 0xd8, 0x08, 0x84, 0xc2, 0x62, 0x27, 0x07, 0x71, 0xd7, 0x68, 0x51, 0xd1, 0x15, 0xea, 0x0b, 0x12, 0x02, 0xf1, 0x7b, 0x61, 0x8e, 0x74,
0xee, 0x0e, 0x46, 0x94, 0x74, 0x86, 0xfa, 0x84, 0x84, 0x40, 0xfa, 0x41, 0xd8, 0x8a, 0xce, 0x51, 0x8d, 0x32, 0xce, 0xe4, 0x21, 0x84, 0x5c, 0xd3, 0x0d, 0x2a, 0x21, 0xd7, 0x6e, 0xe7, 0xdd, 0xa0,
0xc6, 0x35, 0x79, 0x0c, 0x31, 0x37, 0x74, 0x81, 0x4a, 0xcc, 0x8d, 0x9f, 0x79, 0xdf, 0x99, 0x9a, 0x5b, 0xba, 0x9d, 0x77, 0xdc, 0x4c, 0x5e, 0xc0, 0xa3, 0x93, 0xca, 0x1f, 0xba, 0x4a, 0x4d, 0xf4,
0x9e, 0x8c, 0x33, 0x7e, 0x4d, 0x5e, 0xc2, 0x93, 0xa3, 0xca, 0x1f, 0x9b, 0x52, 0x0d, 0xf4, 0x14, 0x0c, 0x9f, 0xe3, 0x0f, 0x3d, 0xfb, 0x1e, 0x40, 0x72, 0x72, 0x27, 0x2e, 0xcd, 0x95, 0x30, 0x9f,
0xaf, 0xe3, 0x2f, 0x7d, 0xf5, 0x23, 0x82, 0xe5, 0xd1, 0x99, 0xf8, 0x34, 0xd7, 0xc2, 0x7e, 0xb1, 0x8d, 0xaa, 0xb0, 0x7a, 0xca, 0x16, 0x24, 0x4f, 0x60, 0xfd, 0xc9, 0x0a, 0x3b, 0x1a, 0xdf, 0xd9,
0xaa, 0xc4, 0xea, 0x19, 0x9b, 0x90, 0x3c, 0x83, 0xf9, 0x67, 0x27, 0x5c, 0x6f, 0x43, 0xe7, 0x40, 0x13, 0xd9, 0x41, 0xb4, 0xd7, 0xb7, 0x34, 0xca, 0xa3, 0x22, 0xb9, 0x78, 0xf9, 0xdf, 0xb7, 0x5f,
0x64, 0x03, 0xc9, 0xd6, 0xdc, 0xd2, 0x24, 0x4f, 0xd6, 0xcb, 0xcb, 0x57, 0x0f, 0x3e, 0xfd, 0x62, 0xee, 0xf5, 0x2d, 0xbe, 0x82, 0x73, 0x67, 0x1f, 0x61, 0xe3, 0xd9, 0x25, 0xb8, 0xac, 0xaa, 0x41,
0x6b, 0x6e, 0xf1, 0x16, 0xbc, 0x7b, 0xf5, 0x09, 0x16, 0x81, 0x7d, 0x82, 0xab, 0xb2, 0xec, 0x94, 0x19, 0xb3, 0x24, 0xf0, 0xe8, 0xba, 0xbe, 0x15, 0x56, 0xf8, 0xf3, 0x71, 0x76, 0xa9, 0xb8, 0xee,
0xb5, 0x53, 0x82, 0x80, 0xbe, 0xeb, 0x3b, 0xe1, 0x44, 0xd8, 0x1f, 0xd7, 0x3e, 0x15, 0x37, 0xad, 0x6b, 0x69, 0x30, 0x40, 0xca, 0x3c, 0xdd, 0xac, 0xf1, 0xb3, 0x7d, 0xf5, 0x33, 0x00, 0x00, 0xff,
0x96, 0x16, 0x03, 0x64, 0x2c, 0xd0, 0x7e, 0x8e, 0xbf, 0xed, 0xeb, 0x5f, 0x01, 0x00, 0x00, 0xff, 0xff, 0xde, 0xd5, 0x28, 0xa3, 0xc2, 0x02, 0x00, 0x00,
0xff, 0xc2, 0x69, 0x8d, 0xdf, 0xd6, 0x02, 0x00, 0x00,
} }

View File

@ -1,64 +0,0 @@
package firo
import (
"bytes"
"io"
"github.com/martinboehm/btcd/chaincfg/chainhash"
"github.com/martinboehm/btcd/wire"
)
// FiroMsgTx encapsulate firo tx and extra
type FiroMsgTx struct {
wire.MsgTx
Extra []byte
}
// TxHash calculate hash of transaction
func (msg *FiroMsgTx) TxHash() chainhash.Hash {
extraSize := uint64(len(msg.Extra))
sizeOfExtraSize := 0
if extraSize != 0 {
sizeOfExtraSize = wire.VarIntSerializeSize(extraSize)
}
// Original payload
buf := bytes.NewBuffer(make([]byte, 0,
msg.SerializeSizeStripped()+sizeOfExtraSize+len(msg.Extra)))
_ = msg.SerializeNoWitness(buf)
// Extra payload
if extraSize != 0 {
wire.WriteVarInt(buf, 0, extraSize)
buf.Write(msg.Extra)
}
return chainhash.DoubleHashH(buf.Bytes())
}
// FiroDecode to decode bitcoin tx and extra
func (msg *FiroMsgTx) FiroDecode(r io.Reader, pver uint32, enc wire.MessageEncoding) error {
if err := msg.MsgTx.BtcDecode(r, pver, enc); err != nil {
return err
}
// extra
version := uint32(msg.Version)
txVersion := version & 0xffff
txType := (version >> 16) & 0xffff
if txVersion == 3 && txType != 0 {
extraSize, err := wire.ReadVarInt(r, 0)
if err != nil {
return err
}
msg.Extra = make([]byte, extraSize)
_, err = io.ReadFull(r, msg.Extra[:])
if err != nil {
return err
}
}
return nil
}

View File

@ -1,10 +1,11 @@
package flo package flo
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"github.com/martinboehm/btcd/wire" "github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// magic numbers // magic numbers

View File

@ -3,13 +3,13 @@
package flo package flo
import ( import (
"blockbook/bchain/coins/btc"
"encoding/hex" "encoding/hex"
"os" "os"
"reflect" "reflect"
"testing" "testing"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {

View File

@ -1,12 +1,13 @@
package flo package flo
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json" "encoding/json"
"github.com/golang/glog"
"github.com/juju/errors" "github.com/juju/errors"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc" "github.com/golang/glog"
) )
// FloRPC is an interface to JSON-RPC bitcoind service. // FloRPC is an interface to JSON-RPC bitcoind service.

View File

@ -1,9 +1,10 @@
package fujicoin package fujicoin
import ( import (
"blockbook/bchain/coins/btc"
"github.com/martinboehm/btcd/wire" "github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
const ( const (

View File

@ -3,6 +3,8 @@
package fujicoin package fujicoin
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/hex" "encoding/hex"
"math/big" "math/big"
"os" "os"
@ -10,8 +12,6 @@ import (
"testing" "testing"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {

View File

@ -1,11 +1,11 @@
package fujicoin package fujicoin
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json" "encoding/json"
"github.com/golang/glog" "github.com/golang/glog"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// FujicoinRPC is an interface to JSON-RPC bitcoind service. // FujicoinRPC is an interface to JSON-RPC bitcoind service.

View File

@ -1,9 +1,10 @@
package gamecredits package gamecredits
import ( import (
"blockbook/bchain/coins/btc"
"github.com/martinboehm/btcd/wire" "github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// magic numbers // magic numbers

View File

@ -3,6 +3,8 @@
package gamecredits package gamecredits
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/hex" "encoding/hex"
"math/big" "math/big"
"os" "os"
@ -10,8 +12,6 @@ import (
"testing" "testing"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {

View File

@ -1,11 +1,11 @@
package gamecredits package gamecredits
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json" "encoding/json"
"github.com/golang/glog" "github.com/golang/glog"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// GameCreditsRPC is an interface to JSON-RPC bitcoind service. // GameCreditsRPC is an interface to JSON-RPC bitcoind service.

View File

@ -1,11 +1,12 @@
package grs package grs
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"github.com/martinboehm/btcd/wire" "github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/base58" "github.com/martinboehm/btcutil/base58"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// magic numbers // magic numbers

View File

@ -3,6 +3,8 @@
package grs package grs
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"math/big" "math/big"
@ -11,8 +13,6 @@ import (
"testing" "testing"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
var ( var (

View File

@ -1,12 +1,12 @@
package grs package grs
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json" "encoding/json"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/juju/errors" "github.com/juju/errors"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// GroestlcoinRPC is an interface to JSON-RPC service // GroestlcoinRPC is an interface to JSON-RPC service

View File

@ -1,10 +1,11 @@
package koto package koto
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"github.com/martinboehm/btcd/wire" "github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// magic numbers // magic numbers

View File

@ -3,6 +3,8 @@
package koto package koto
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"math/big" "math/big"
@ -11,8 +13,6 @@ import (
"testing" "testing"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
var ( var (

View File

@ -1,12 +1,12 @@
package koto package koto
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json" "encoding/json"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/juju/errors" "github.com/juju/errors"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// KotoRPC is an interface to JSON-RPC bitcoind service // KotoRPC is an interface to JSON-RPC bitcoind service

View File

@ -1,15 +1,16 @@
package liquid package liquid
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"strconv" "strconv"
vlq "github.com/bsm/go-vlq" vlq "github.com/bsm/go-vlq"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/martinboehm/btcd/txscript" "github.com/martinboehm/btcd/txscript"
"github.com/martinboehm/btcd/wire" "github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
const ( const (

View File

@ -3,6 +3,8 @@
package liquid package liquid
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/hex" "encoding/hex"
"math/big" "math/big"
"os" "os"
@ -10,8 +12,6 @@ import (
"testing" "testing"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {

View File

@ -1,12 +1,12 @@
package liquid package liquid
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json" "encoding/json"
"github.com/golang/glog" "github.com/golang/glog"
"github.com/juju/errors" "github.com/juju/errors"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// LiquidRPC is an interface to JSON-RPC bitcoind service. // LiquidRPC is an interface to JSON-RPC bitcoind service.

View File

@ -1,9 +1,10 @@
package litecoin package litecoin
import ( import (
"blockbook/bchain/coins/btc"
"github.com/martinboehm/btcd/wire" "github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// magic numbers // magic numbers

View File

@ -3,6 +3,8 @@
package litecoin package litecoin
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/hex" "encoding/hex"
"math/big" "math/big"
"os" "os"
@ -10,8 +12,6 @@ import (
"testing" "testing"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {

View File

@ -1,11 +1,11 @@
package litecoin package litecoin
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json" "encoding/json"
"github.com/golang/glog" "github.com/golang/glog"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// LitecoinRPC is an interface to JSON-RPC bitcoind service. // LitecoinRPC is an interface to JSON-RPC bitcoind service.

View File

@ -1,9 +1,10 @@
package monacoin package monacoin
import ( import (
"blockbook/bchain/coins/btc"
"github.com/martinboehm/btcd/wire" "github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// magic numbers // magic numbers

View File

@ -3,6 +3,8 @@
package monacoin package monacoin
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/hex" "encoding/hex"
"math/big" "math/big"
"os" "os"
@ -10,8 +12,6 @@ import (
"testing" "testing"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {

View File

@ -1,11 +1,11 @@
package monacoin package monacoin
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json" "encoding/json"
"github.com/golang/glog" "github.com/golang/glog"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// MonacoinRPC is an interface to JSON-RPC bitcoind service. // MonacoinRPC is an interface to JSON-RPC bitcoind service.

View File

@ -1,17 +1,18 @@
package monetaryunit package monetaryunit
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"blockbook/bchain/coins/utils"
"bytes" "bytes"
"io"
"encoding/hex" "encoding/hex"
"encoding/json" "encoding/json"
"io"
"github.com/juju/errors" "github.com/juju/errors"
"github.com/martinboehm/btcd/wire" "github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
"spacecruft.org/spacecruft/blockbook/bchain/coins/utils"
) )
const ( const (

View File

@ -3,6 +3,8 @@
package monetaryunit package monetaryunit
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/hex" "encoding/hex"
"math/big" "math/big"
"os" "os"
@ -10,8 +12,6 @@ import (
"testing" "testing"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {

View File

@ -1,11 +1,11 @@
package monetaryunit package monetaryunit
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json" "encoding/json"
"github.com/golang/glog" "github.com/golang/glog"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// MonetaryUnitRPC is an interface to JSON-RPC bitcoind service. // MonetaryUnitRPC is an interface to JSON-RPC bitcoind service.

View File

@ -1,13 +1,13 @@
package myriad package myriad
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"blockbook/bchain/coins/utils"
"bytes" "bytes"
"github.com/martinboehm/btcd/wire" "github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
"spacecruft.org/spacecruft/blockbook/bchain/coins/utils"
) )
// magic numbers // magic numbers

View File

@ -3,6 +3,8 @@
package myriad package myriad
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/hex" "encoding/hex"
"math/big" "math/big"
"os" "os"
@ -10,8 +12,6 @@ import (
"testing" "testing"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {

View File

@ -1,11 +1,11 @@
package myriad package myriad
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json" "encoding/json"
"github.com/golang/glog" "github.com/golang/glog"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// MyriadRPC is an interface to JSON-RPC bitcoind service. // MyriadRPC is an interface to JSON-RPC bitcoind service.

View File

@ -1,13 +1,13 @@
package namecoin package namecoin
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"blockbook/bchain/coins/utils"
"bytes" "bytes"
"github.com/martinboehm/btcd/wire" "github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
"spacecruft.org/spacecruft/blockbook/bchain/coins/utils"
) )
const ( const (

View File

@ -3,6 +3,7 @@
package namecoin package namecoin
import ( import (
"blockbook/bchain/coins/btc"
"bytes" "bytes"
"encoding/hex" "encoding/hex"
"fmt" "fmt"
@ -13,7 +14,6 @@ import (
"testing" "testing"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
func TestMain(m *testing.M) { func TestMain(m *testing.M) {

View File

@ -1,11 +1,11 @@
package namecoin package namecoin
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json" "encoding/json"
"github.com/golang/glog" "github.com/golang/glog"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// NamecoinRPC is an interface to JSON-RPC namecoin service. // NamecoinRPC is an interface to JSON-RPC namecoin service.

View File

@ -1,18 +1,18 @@
package nuls package nuls
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"bytes" "bytes"
"encoding/binary" "encoding/binary"
"encoding/json" "encoding/json"
"errors" "errors"
vlq "github.com/bsm/go-vlq" vlq "github.com/bsm/go-vlq"
"github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/base58" "github.com/martinboehm/btcutil/base58"
"github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg" "github.com/martinboehm/btcutil/chaincfg"
"github.com/martinboehm/btcutil/hdkeychain" "github.com/martinboehm/btcutil/hdkeychain"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
) )
// magic numbers // magic numbers

View File

@ -1,18 +1,16 @@
// +build unittest
package nuls package nuls
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/hex" "encoding/hex"
"encoding/json"
"math/big" "math/big"
"reflect" "reflect"
"testing" "testing"
"github.com/ethereum/go-ethereum/common/hexutil" "github.com/ethereum/go-ethereum/common/hexutil"
"github.com/martinboehm/btcutil/hdkeychain" "github.com/martinboehm/btcutil/hdkeychain"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
"spacecruft.org/spacecruft/blockbook/common"
) )
var ( var (
@ -43,7 +41,7 @@ func init() {
{ {
ValueSat: *big.NewInt(399999000000), ValueSat: *big.NewInt(399999000000),
N: 0, N: 0,
JsonValue: common.JSONNumber("0"), JsonValue: json.Number("0"),
ScriptPubKey: bchain.ScriptPubKey{ ScriptPubKey: bchain.ScriptPubKey{
Hex: "Nse4zpZHsUuU7h5ymv28pcGbwHju3joV", Hex: "Nse4zpZHsUuU7h5ymv28pcGbwHju3joV",
Addresses: []string{ Addresses: []string{
@ -75,7 +73,7 @@ func init() {
{ {
ValueSat: *big.NewInt(400000000000), ValueSat: *big.NewInt(400000000000),
N: 0, N: 0,
JsonValue: common.JSONNumber("0"), JsonValue: json.Number("0"),
ScriptPubKey: bchain.ScriptPubKey{ ScriptPubKey: bchain.ScriptPubKey{
Hex: "Nse4ikjE88g2BgsNwsswTdkSwiSrKjjS", Hex: "Nse4ikjE88g2BgsNwsswTdkSwiSrKjjS",
Addresses: []string{ Addresses: []string{
@ -86,7 +84,7 @@ func init() {
{ {
ValueSat: *big.NewInt(7286565570000), ValueSat: *big.NewInt(7286565570000),
N: 1, N: 1,
JsonValue: common.JSONNumber("0"), JsonValue: json.Number("0"),
ScriptPubKey: bchain.ScriptPubKey{ ScriptPubKey: bchain.ScriptPubKey{
Hex: "Nse119z2oSDJYkFkxmwYDiYtPfBeNkqi", Hex: "Nse119z2oSDJYkFkxmwYDiYtPfBeNkqi",
Addresses: []string{ Addresses: []string{

View File

@ -1,6 +1,8 @@
package nuls package nuls
import ( import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"bytes" "bytes"
"encoding/base64" "encoding/base64"
"encoding/hex" "encoding/hex"
@ -14,10 +16,9 @@ import (
"strconv" "strconv"
"time" "time"
"github.com/golang/glog"
"github.com/juju/errors" "github.com/juju/errors"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc" "github.com/golang/glog"
) )
// NulsRPC is an interface to JSON-RPC bitcoind service // NulsRPC is an interface to JSON-RPC bitcoind service

View File

@ -1,266 +0,0 @@
package omotenashicoin
import (
"bytes"
"encoding/hex"
"encoding/json"
"io"
"math/big"
"github.com/juju/errors"
"github.com/martinboehm/btcd/blockchain"
"github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg"
"spacecruft.org/spacecruft/blockbook/bchain"
"spacecruft.org/spacecruft/blockbook/bchain/coins/btc"
"spacecruft.org/spacecruft/blockbook/bchain/coins/utils"
)
// magic numbers
const (
MainnetMagic wire.BitcoinNet = 0xdda5b5d1
TestnetMagic wire.BitcoinNet = 0x54644363
// Zerocoin op codes
OP_ZEROCOINMINT = 0xc1
OP_ZEROCOINSPEND = 0xc2
)
// chain parameters
var (
MainNetParams chaincfg.Params
TestNetParams chaincfg.Params
)
func init() {
// mainnet Address encoding magics
MainNetParams = chaincfg.MainNetParams
MainNetParams.Net = MainnetMagic
MainNetParams.PubKeyHashAddrID = []byte{63} // char S
MainNetParams.ScriptHashAddrID = []byte{18}
MainNetParams.PrivateKeyID = []byte{191}
// testnet Address encoding magics
TestNetParams = chaincfg.TestNet3Params
TestNetParams.Net = TestnetMagic
TestNetParams.PubKeyHashAddrID = []byte{83}
TestNetParams.ScriptHashAddrID = []byte{18}
TestNetParams.PrivateKeyID = []byte{193}
}
// OmotenashiCoinParser handle
type OmotenashiCoinParser struct {
*btc.BitcoinParser
baseparser *bchain.BaseParser
BitcoinOutputScriptToAddressesFunc btc.OutputScriptToAddressesFunc
}
// NewOmotenashiCoinParser returns new OmotenashiCoinParser instance
func NewOmotenashiCoinParser(params *chaincfg.Params, c *btc.Configuration) *OmotenashiCoinParser {
p := &OmotenashiCoinParser{
BitcoinParser: btc.NewBitcoinParser(params, c),
baseparser: &bchain.BaseParser{},
}
p.BitcoinOutputScriptToAddressesFunc = p.OutputScriptToAddressesFunc
p.OutputScriptToAddressesFunc = p.outputScriptToAddresses
return p
}
// GetChainParams contains network parameters for the main OmotenashiCoin network
func GetChainParams(chain string) *chaincfg.Params {
if !chaincfg.IsRegistered(&MainNetParams) {
err := chaincfg.Register(&MainNetParams)
if err == nil {
err = chaincfg.Register(&TestNetParams)
}
if err != nil {
panic(err)
}
}
switch chain {
case "test":
return &TestNetParams
default:
return &MainNetParams
}
}
// ParseBlock parses raw block to our Block struct
func (p *OmotenashiCoinParser) ParseBlock(b []byte) (*bchain.Block, error) {
r := bytes.NewReader(b)
w := wire.MsgBlock{}
h := wire.BlockHeader{}
err := h.Deserialize(r)
if err != nil {
return nil, errors.Annotatef(err, "Deserialize")
}
if h.Version > 3 && h.Version < 7 {
// Skip past AccumulatorCheckpoint (block version 4, 5 and 6)
r.Seek(32, io.SeekCurrent)
}
err = utils.DecodeTransactions(r, 0, wire.WitnessEncoding, &w)
if err != nil {
return nil, errors.Annotatef(err, "DecodeTransactions")
}
txs := make([]bchain.Tx, len(w.Transactions))
for ti, t := range w.Transactions {
txs[ti] = p.TxFromMsgTx(t, false)
}
return &bchain.Block{
BlockHeader: bchain.BlockHeader{
Size: len(b),
Time: h.Timestamp.Unix(),
},
Txs: txs,
}, nil
}
// PackTx packs transaction to byte array using protobuf
func (p *OmotenashiCoinParser) PackTx(tx *bchain.Tx, height uint32, blockTime int64) ([]byte, error) {
return p.baseparser.PackTx(tx, height, blockTime)
}
// UnpackTx unpacks transaction from protobuf byte array
func (p *OmotenashiCoinParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
return p.baseparser.UnpackTx(buf)
}
// ParseTx parses byte array containing transaction and returns Tx struct
func (p *OmotenashiCoinParser) ParseTx(b []byte) (*bchain.Tx, error) {
t := wire.MsgTx{}
r := bytes.NewReader(b)
if err := t.Deserialize(r); err != nil {
return nil, err
}
tx := p.TxFromMsgTx(&t, true)
tx.Hex = hex.EncodeToString(b)
return &tx, nil
}
// TxFromMsgTx parses tx and adds handling for OP_ZEROCOINSPEND inputs
func (p *OmotenashiCoinParser) TxFromMsgTx(t *wire.MsgTx, parseAddresses bool) bchain.Tx {
vin := make([]bchain.Vin, len(t.TxIn))
for i, in := range t.TxIn {
// extra check to not confuse Tx with single OP_ZEROCOINSPEND input as a coinbase Tx
if !isZeroCoinSpendScript(in.SignatureScript) && blockchain.IsCoinBaseTx(t) {
vin[i] = bchain.Vin{
Coinbase: hex.EncodeToString(in.SignatureScript),
Sequence: in.Sequence,
}
break
}
s := bchain.ScriptSig{
Hex: hex.EncodeToString(in.SignatureScript),
// missing: Asm,
}
txid := in.PreviousOutPoint.Hash.String()
vin[i] = bchain.Vin{
Txid: txid,
Vout: in.PreviousOutPoint.Index,
Sequence: in.Sequence,
ScriptSig: s,
}
}
vout := make([]bchain.Vout, len(t.TxOut))
for i, out := range t.TxOut {
addrs := []string{}
if parseAddresses {
addrs, _, _ = p.OutputScriptToAddressesFunc(out.PkScript)
}
s := bchain.ScriptPubKey{
Hex: hex.EncodeToString(out.PkScript),
Addresses: addrs,
// missing: Asm,
// missing: Type,
}
var vs big.Int
vs.SetInt64(out.Value)
vout[i] = bchain.Vout{
ValueSat: vs,
N: uint32(i),
ScriptPubKey: s,
}
}
tx := bchain.Tx{
Txid: t.TxHash().String(),
Version: t.Version,
LockTime: t.LockTime,
Vin: vin,
Vout: vout,
// skip: BlockHash,
// skip: Confirmations,
// skip: Time,
// skip: Blocktime,
}
return tx
}
// ParseTxFromJson parses JSON message containing transaction and returns Tx struct
func (p *OmotenashiCoinParser) ParseTxFromJson(msg json.RawMessage) (*bchain.Tx, error) {
var tx bchain.Tx
err := json.Unmarshal(msg, &tx)
if err != nil {
return nil, err
}
for i := range tx.Vout {
vout := &tx.Vout[i]
// convert vout.JsonValue to big.Int and clear it, it is only temporary value used for unmarshal
vout.ValueSat, err = p.AmountToBigInt(vout.JsonValue)
if err != nil {
return nil, err
}
vout.JsonValue = ""
if vout.ScriptPubKey.Addresses == nil {
vout.ScriptPubKey.Addresses = []string{}
}
}
return &tx, nil
}
// outputScriptToAddresses converts ScriptPubKey to bitcoin addresses
func (p *OmotenashiCoinParser) outputScriptToAddresses(script []byte) ([]string, bool, error) {
if isZeroCoinSpendScript(script) {
return []string{"Zerocoin Spend"}, false, nil
}
if isZeroCoinMintScript(script) {
return []string{"Zerocoin Mint"}, false, nil
}
rv, s, _ := p.BitcoinOutputScriptToAddressesFunc(script)
return rv, s, nil
}
func (p *OmotenashiCoinParser) GetAddrDescForUnknownInput(tx *bchain.Tx, input int) bchain.AddressDescriptor {
if len(tx.Vin) > input {
scriptHex := tx.Vin[input].ScriptSig.Hex
if scriptHex != "" {
script, _ := hex.DecodeString(scriptHex)
return script
}
}
s := make([]byte, 10)
return s
}
// Checks if script is OP_ZEROCOINMINT
func isZeroCoinMintScript(signatureScript []byte) bool {
return len(signatureScript) > 1 && signatureScript[0] == OP_ZEROCOINMINT
}
// Checks if script is OP_ZEROCOINSPEND
func isZeroCoinSpendScript(signatureScript []byte) bool {
return len(signatureScript) >= 100 && signatureScript[0] == OP_ZEROCOINSPEND
}

Some files were not shown because too many files have changed in this diff Show More