Compare commits

...

67 Commits

Author SHA1 Message Date
Martin Boehm 3cff38942a Add prefix to socketio and websocket test names 2019-05-24 16:29:24 +02:00
Martin Boehm c9c2f3dcbc Refactor totalTokens to usedTokens in API 2019-05-24 16:24:34 +02:00
Martin Boehm 4b2f66cda3 Add test of websocket interface 2019-05-24 15:30:10 +02:00
Martin Boehm 4f9feb4d4f Return error when loading not existing block in ETH #176 2019-05-23 16:05:33 +02:00
Martin Boehm 5ef22e86dc Improve API requests when blockbook in sync with backend 2019-05-23 15:56:40 +02:00
Martin Boehm 70249530ff Disable bitcoin whatthefee fee estimates 2019-05-23 14:52:21 +02:00
Martin Boehm e0c5656e13 Reduce logging of unimportant warnings during block import 2019-05-23 13:56:31 +02:00
Martin Boehm 8903358044 Update documentation 2019-05-22 18:22:26 +02:00
Martin Boehm ec8aed0bde Retry sync in case of error 2019-05-22 18:22:26 +02:00
Scotty0448 697569ffad Add Ravencoin support 2019-05-22 18:22:26 +02:00
motty 07c763fd70 mod binary_url 2019-05-22 18:22:26 +02:00
Jan Hrnko 776114f3b5 Bump bitcoin cash (+testnet) backend 0.19.5 -> 0.19.6 2019-05-22 18:22:26 +02:00
Ján Hrnko 0048c6f237 bump bitcoin cash backend 0.19.4 -> 0.19.5 (#178)
* bump bitcoin cash backend 0.19.4 -> 0.19.5

* bump bitcoin cash testnet backend 0.19.4 -> 0.19.5
2019-05-22 18:22:26 +02:00
TheTrunk ac795cb6b4 integration tests, xx58 port 2019-05-22 18:22:26 +02:00
TheTrunk c70b88a604 fix extract command and parameters download 2019-05-22 18:22:26 +02:00
TheTrunk 462994d909 ZelCash - reuse Zcash integration 2019-05-22 18:22:26 +02:00
CodeFace cb459352d9 Bump Qtum to 0.17.5 2019-05-22 18:22:26 +02:00
Sotiris Blad 78ef4dc4a5 fix getblock & tx for genesis 2019-05-22 18:22:26 +02:00
Martin Boehm 243f2c16bf Wait for backend startup in integration test 2019-05-22 18:22:26 +02:00
Petr Kracík fb9535fc58 Bump Digibyte backend to 7.17.2 and enable integration tests (#172)
* Bump Digibyte to 7.17.2

* Added digibyte to Integration tests
2019-05-22 18:22:26 +02:00
Martin Boehm 3d9ad91c3d Tune bulk connect 2019-05-13 15:56:12 +02:00
Martin Boehm 6b16814d0b Improve performance of utxo indexing 2019-05-12 02:19:51 +02:00
Martin Boehm 31fb59b1a5 Wait for backend startup in integration test 2019-05-07 12:46:31 +02:00
Petr Kracík 9fb2043847 Bump Digibyte backend to 7.17.2 and enable integration tests (#172)
* Bump Digibyte to 7.17.2

* Added digibyte to Integration tests
2019-05-07 12:46:31 +02:00
Martin Boehm f0a396cf0d Log error on eth subscription resubscribe 2019-05-07 12:46:31 +02:00
Martin Boehm 93db48e8ad Wait for backend startup in integration test 2019-05-07 12:46:31 +02:00
Petr Kracík 2967209e7a Bump Digibyte backend to 7.17.2 and enable integration tests (#172)
* Bump Digibyte to 7.17.2

* Added digibyte to Integration tests
2019-05-07 12:46:31 +02:00
Martin Boehm 5685b07bfb Bump golang to version 1.12.4 and rocskdb to 5.18.3 in docker build 2019-05-06 14:42:31 +02:00
Martin Boehm 462ee13855 Use utxo index in API 2019-05-06 14:19:17 +02:00
Martin Boehm 63fa3cccb6 Fix computefeestats flag typo 2019-05-06 08:38:12 +02:00
Martin Boehm e7c05ccfaa Maintain utxo index on disconnect block 2019-05-06 08:37:16 +02:00
Martin Boehm eb7cdab0d9 Add utxos to addressBalance column 2019-05-03 20:46:40 +02:00
Martin Boehm 80bb7c51f8 Update coin configs 2019-05-03 15:11:34 +02:00
Martin Boehm 82cc184741 Trim packr tos link 2019-05-03 15:11:34 +02:00
Petr Kracík 15e29f104c Bump Bitcoin to 0.18.0 2019-05-03 15:11:34 +02:00
Martin Boehm 35379751fd Update monetaryunit and polis config 2019-05-03 15:11:34 +02:00
Sotiris Blad 015423ae37 Add MonetaryUnit (MUE) Support (#166)
* MUE

* mue parser

* mue rpc

* mue

* mue tests

* mue ports

* update

* update

* Create monetaryunitparser_test.go

* Update monetaryunitparser_test.go

* update

* Update monetaryunitparser_test.go

* Update monetaryunitparser_test.go

* compiling

* Update monetaryunitparser_test.go

* update hex

* test

* mue test

* Update monetaryunitparser.go

* Update monetaryunitparser.go

* getblock add

* update sum

* removed testnet
2019-05-03 15:11:34 +02:00
Martin Boehm 164d9b197b Stop indexing OP_RETURN scripts 2019-05-02 14:10:26 +02:00
Martin Boehm e86175a033 Bump required index version to 5 2019-04-30 16:06:56 +02:00
Martin Boehm 839b02dbce Update coin configs 2019-04-29 15:25:34 +02:00
Martin Boehm fa420dc291 Add common issues explanation to documentation 2019-04-29 15:25:34 +02:00
y-chan 3e26b4b6ac fix test data 2019-04-29 15:25:34 +02:00
y-chan 6856d27484 fix miss 2019-04-29 15:25:34 +02:00
y-chan b4a1d8a3ca fix tests for vipstarcoin 2019-04-29 15:25:34 +02:00
y-chan c90672dac9 fix port number and coin name 2019-04-29 15:25:34 +02:00
y-chan 8c1bdc566a fix port number xx90 to xx56 2019-04-29 15:25:34 +02:00
y-chan 399e9d85a4 fix import 2019-04-29 15:25:34 +02:00
y-chan 7c69bbd74c fix username 2019-04-29 15:25:34 +02:00
y-chan 963b260d4c fix errors 2019-04-29 15:25:34 +02:00
y-chan 870e3ffc30 fix miss 2019-04-29 15:25:34 +02:00
y-chan 8ca3c65996 fix tests 2019-04-29 15:25:34 +02:00
y-chan 10e0d2583b Add VIPSTARCOIN 2019-04-29 15:25:34 +02:00
Petr Kracík 8f7ef59646 Bump Ethereum to 1.8.27 2019-04-29 15:25:34 +02:00
Petr Kracík 5e212c9eca Bump Bcash to 0.19.4 2019-04-29 15:25:34 +02:00
Martin Boehm f0882655b9 Add option to compute fee statistics for chosen blocks 2019-04-28 11:54:12 +02:00
Martin Boehm a293aa5673 Return with non zero exit code in case of fatal error 2019-04-23 14:19:36 +02:00
ilmango 77fb7088fa Remove bellcoin-releases.asc 2019-04-23 14:19:36 +02:00
Martin Boehm 0bcd521216 Compare whatTheFee estimates to default 2019-04-18 11:20:16 +02:00
Martin Boehm 0cd043abe5 Make websocket interface fields camelCase 2019-04-18 11:20:16 +02:00
wakiyamap 4a035a2460 add monacoin testdata 2019-04-18 11:20:16 +02:00
romanornr e8a83ccfe2 Viacoin add support 2019-04-18 11:20:16 +02:00
Cronos 32c77fe69a Update Polis Core v1.4.11 2019-04-18 11:20:16 +02:00
Martin Boehm af19559ecd Implement alternative estimateFee using whatthefee.io WIP #153 2019-04-17 15:47:08 +02:00
Martin Boehm d0c20afeb2 Update coin configs 2019-04-17 13:58:32 +02:00
Martin Boehm c12259e266 Bump blockbook version to 0.3.0 2019-04-16 15:21:31 +02:00
Martin Boehm 2bfd9f51fa Fix formatting/linting errors 2019-04-16 15:18:54 +02:00
Martin Boehm 064170c871 Make all API fields camelCase 2019-04-16 14:44:50 +02:00
91 changed files with 5536 additions and 552 deletions

View File

@ -71,6 +71,17 @@ backend-deploy-and-test-dash:
- blockbook
script: ./contrib/scripts/backend-deploy-and-test.sh dash
backend-deploy-and-test-digibyte:
stage: backend-deploy-and-test
only:
refs:
- master
changes:
- configs/coins/digibyte.json
tags:
- blockbook
script: ./contrib/scripts/backend-deploy-and-test.sh digibyte
backend-deploy-and-test-dogecoin:
stage: backend-deploy-and-test
only:

View File

@ -2,8 +2,6 @@
# Blockbook
> **WARNING: Blockbook is currently in the state of heavy development. We may implement at any time backwards incompatible changes that require full reindexation of the database. Also, do not expect this documentation to be always up to date.**
**Blockbook** is back-end service for Trezor wallet. Main features of **Blockbook** are:
- index of addresses and address balances of the connected block chain
@ -17,7 +15,7 @@
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 160 GB of disk space. After initial synchronization, fully synchronized instance uses about 10 GB RAM.
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.
@ -29,14 +27,44 @@ Contribution guide is [here](CONTRIBUTING.md).
## Implemented coins
Blockbook currently supports over 20 coins, among them:
- Bitcoin, Litecoin, Bitcoin Cash, Bgold, ZCash, Dash, Ethereum, Ethereum Classic
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).

View File

@ -21,8 +21,9 @@ func init() {
panic(err)
}
if tosLink, err := box.MustString("tos_link"); err == nil {
tosLink = strings.TrimSpace(tosLink)
if _, err := url.ParseRequestURI(tosLink); err == nil {
Text.TOSLink = strings.TrimSpace(tosLink)
Text.TOSLink = tosLink
} else {
panic(fmt.Sprint("tos_link is not valid URL:", err.Error()))
}

View File

@ -170,22 +170,22 @@ type TokenTransfer struct {
type EthereumSpecific struct {
Status int `json:"status"` // 1 OK, 0 Fail, -1 pending
Nonce uint64 `json:"nonce"`
GasLimit *big.Int `json:"gaslimit"`
GasUsed *big.Int `json:"gasused"`
GasPrice *Amount `json:"gasprice"`
GasLimit *big.Int `json:"gasLimit"`
GasUsed *big.Int `json:"gasUsed"`
GasPrice *Amount `json:"gasPrice"`
}
// Tx holds information about a transaction
type Tx struct {
Txid string `json:"txid"`
Version int32 `json:"version,omitempty"`
Locktime uint32 `json:"locktime,omitempty"`
Locktime uint32 `json:"lockTime,omitempty"`
Vin []Vin `json:"vin"`
Vout []Vout `json:"vout"`
Blockhash string `json:"blockhash,omitempty"`
Blockheight int `json:"blockheight"`
Blockhash string `json:"blockHash,omitempty"`
Blockheight int `json:"blockHeight"`
Confirmations uint32 `json:"confirmations"`
Blocktime int64 `json:"blocktime"`
Blocktime int64 `json:"blockTime"`
Size int `json:"size,omitempty"`
ValueOutSat *Amount `json:"value"`
ValueInSat *Amount `json:"valueIn,omitempty"`
@ -193,8 +193,8 @@ type Tx struct {
Hex string `json:"hex,omitempty"`
CoinSpecificData interface{} `json:"-"`
CoinSpecificJSON json.RawMessage `json:"-"`
TokenTransfers []TokenTransfer `json:"tokentransfers,omitempty"`
EthereumSpecific *EthereumSpecific `json:"ethereumspecific,omitempty"`
TokenTransfers []TokenTransfer `json:"tokenTransfers,omitempty"`
EthereumSpecific *EthereumSpecific `json:"ethereumSpecific,omitempty"`
}
// Paging contains information about paging for address, blocks and block
@ -244,13 +244,13 @@ type Address struct {
UnconfirmedBalanceSat *Amount `json:"unconfirmedBalance"`
UnconfirmedTxs int `json:"unconfirmedTxs"`
Txs int `json:"txs"`
NonTokenTxs int `json:"nontokenTxs,omitempty"`
NonTokenTxs int `json:"nonTokenTxs,omitempty"`
Transactions []*Tx `json:"transactions,omitempty"`
Txids []string `json:"txids,omitempty"`
Nonce string `json:"nonce,omitempty"`
TotalTokens int `json:"totalTokens,omitempty"`
UsedTokens int `json:"usedTokens,omitempty"`
Tokens []Token `json:"tokens,omitempty"`
Erc20Contract *bchain.Erc20Contract `json:"erc20contract,omitempty"`
Erc20Contract *bchain.Erc20Contract `json:"erc20Contract,omitempty"`
// helpers for explorer
Filter string `json:"-"`
XPubAddresses map[string]struct{} `json:"-"`
@ -265,6 +265,7 @@ type Utxo struct {
Confirmations int `json:"confirmations"`
Address string `json:"address,omitempty"`
Path string `json:"path,omitempty"`
Locktime uint32 `json:"lockTime,omitempty"`
}
// Utxos is array of Utxo
@ -293,13 +294,19 @@ type Blocks struct {
// BlockInfo contains extended block header data and a list of block txids
type BlockInfo struct {
bchain.BlockHeader
Version json.Number `json:"version"`
MerkleRoot string `json:"merkleroot"`
Nonce string `json:"nonce"`
Bits string `json:"bits"`
Difficulty string `json:"difficulty"`
Txids []string `json:"tx,omitempty"`
Hash string `json:"hash"`
Prev string `json:"previousBlockHash,omitempty"`
Next string `json:"nextBlockHash,omitempty"`
Height uint32 `json:"height"`
Confirmations int `json:"confirmations"`
Size int `json:"size"`
Time int64 `json:"time,omitempty"`
Version json.Number `json:"version"`
MerkleRoot string `json:"merkleRoot"`
Nonce string `json:"nonce"`
Bits string `json:"bits"`
Difficulty string `json:"difficulty"`
Txids []string `json:"tx,omitempty"`
}
// Block contains information about block
@ -315,10 +322,10 @@ type BlockbookInfo struct {
Coin string `json:"coin"`
Host string `json:"host"`
Version string `json:"version"`
GitCommit string `json:"gitcommit"`
BuildTime string `json:"buildtime"`
GitCommit string `json:"gitCommit"`
BuildTime string `json:"buildTime"`
SyncMode bool `json:"syncMode"`
InitialSync bool `json:"initialsync"`
InitialSync bool `json:"initialSync"`
InSync bool `json:"inSync"`
BestHeight uint32 `json:"bestHeight"`
LastBlockTime time.Time `json:"lastBlockTime"`
@ -332,10 +339,25 @@ type BlockbookInfo struct {
About string `json:"about"`
}
// BackendInfo is used to get information about blockchain
type BackendInfo struct {
Chain string `json:"chain"`
Blocks int `json:"blocks"`
Headers int `json:"headers"`
Bestblockhash string `json:"bestBlockHash"`
Difficulty string `json:"difficulty"`
SizeOnDisk int64 `json:"sizeOnDisk"`
Version string `json:"version"`
Subversion string `json:"subversion"`
ProtocolVersion string `json:"protocolVersion"`
Timeoffset float64 `json:"timeOffset"`
Warnings string `json:"warnings"`
}
// SystemInfo contains information about the running blockbook and backend instance
type SystemInfo struct {
Blockbook *BlockbookInfo `json:"blockbook"`
Backend *bchain.ChainInfo `json:"backend"`
Blockbook *BlockbookInfo `json:"blockbook"`
Backend *BackendInfo `json:"backend"`
}
// MempoolTxid contains information about a transaction in mempool

View File

@ -8,7 +8,10 @@ import (
"bytes"
"encoding/json"
"fmt"
"math"
"math/big"
"os"
"sort"
"strconv"
"time"
@ -173,7 +176,11 @@ func (w *Worker) GetTransactionFromBchainTx(bchainTx *bchain.Tx, height uint32,
}
// mempool transactions are not in TxAddresses but confirmed should be there, log a problem
if bchainTx.Confirmations > 0 {
glog.Warning("DB inconsistency: tx ", bchainVin.Txid, ": not found in txAddresses")
inSync, _, _ := w.is.GetSyncState()
// backend can report tx as confirmed, however blockbook is still syncing (!inSync), in this case do not log a problem
if bchainTx.Confirmations != 1 || inSync {
glog.Warning("DB inconsistency: tx ", bchainVin.Txid, ": not found in txAddresses")
}
}
if len(otx.Vout) > int(vin.Vout) {
vout := &otx.Vout[vin.Vout]
@ -596,7 +603,7 @@ func (w *Worker) getEthereumTypeAddressBalances(addrDesc bchain.AddressDescripto
return ba, tokens, ci, n, nonContractTxs, totalResults, nil
}
func (w *Worker) txFromTxid(txid string, bestheight uint32, option AccountDetails) (*Tx, error) {
func (w *Worker) txFromTxid(txid string, bestheight uint32, option AccountDetails, blockInfo *db.BlockInfo) (*Tx, error) {
var tx *Tx
var err error
// only ChainBitcoinType supports TxHistoryLight
@ -607,19 +614,25 @@ func (w *Worker) txFromTxid(txid string, bestheight uint32, option AccountDetail
}
if ta == nil {
glog.Warning("DB inconsistency: tx ", txid, ": not found in txAddresses")
// as fallback, provide empty TxAddresses to return at least something
ta = &db.TxAddresses{}
// as fallback, get tx from backend
tx, err = w.GetTransaction(txid, false, true)
if err != nil {
return nil, errors.Annotatef(err, "GetTransaction %v", txid)
}
} else {
if blockInfo == nil {
blockInfo, err = w.db.GetBlockInfo(ta.Height)
if err != nil {
return nil, errors.Annotatef(err, "GetBlockInfo %v", ta.Height)
}
if blockInfo == nil {
glog.Warning("DB inconsistency: block height ", ta.Height, ": not found in db")
// provide empty BlockInfo to return the rest of tx data
blockInfo = &db.BlockInfo{}
}
}
tx = w.txFromTxAddress(txid, ta, blockInfo, bestheight)
}
bi, err := w.db.GetBlockInfo(ta.Height)
if err != nil {
return nil, errors.Annotatef(err, "GetBlockInfo %v", ta.Height)
}
if bi == nil {
glog.Warning("DB inconsistency: block height ", ta.Height, ": not found in db")
// provide empty BlockInfo to return the rest of tx data
bi = &db.BlockInfo{}
}
tx = w.txFromTxAddress(txid, ta, bi, bestheight)
} else {
tx, err = w.GetTransaction(txid, false, true)
if err != nil {
@ -680,7 +693,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco
nonce = strconv.Itoa(int(n))
} else {
// ba can be nil if the address is only in mempool!
ba, err = w.db.GetAddrDescBalance(addrDesc)
ba, err = w.db.GetAddrDescBalance(addrDesc, db.AddressBalanceDetailNoUTXO)
if err != nil {
return nil, NewAPIError(fmt.Sprintf("Address not found, %v", err), true)
}
@ -750,7 +763,7 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco
if option == AccountDetailsTxidHistory {
txids = append(txids, txid)
} else {
tx, err := w.txFromTxid(txid, bestheight, option)
tx, err := w.txFromTxid(txid, bestheight, option, nil)
if err != nil {
return nil, err
}
@ -782,7 +795,19 @@ func (w *Worker) GetAddress(address string, page int, txsOnPage int, option Acco
return r, nil
}
func (w *Worker) waitForBackendSync() {
// wait a short time if blockbook is synchronizing with backend
inSync, _, _ := w.is.GetSyncState()
count := 30
for !inSync && count > 0 {
time.Sleep(time.Millisecond * 100)
count--
inSync, _, _ = w.is.GetSyncState()
}
}
func (w *Worker) getAddrDescUtxo(addrDesc bchain.AddressDescriptor, ba *db.AddrBalance, onlyConfirmed bool, onlyMempool bool) (Utxos, error) {
w.waitForBackendSync()
var err error
r := make(Utxos, 0, 8)
spentInMempool := make(map[string]struct{})
@ -822,6 +847,7 @@ func (w *Worker) getAddrDescUtxo(addrDesc bchain.AddressDescriptor, ba *db.AddrB
Txid: bchainTx.Txid,
Vout: int32(i),
AmountSat: (*Amount)(&vout.ValueSat),
Locktime: bchainTx.LockTime,
})
}
}
@ -833,67 +859,38 @@ func (w *Worker) getAddrDescUtxo(addrDesc bchain.AddressDescriptor, ba *db.AddrB
if !onlyMempool {
// get utxo from index
if ba == nil {
ba, err = w.db.GetAddrDescBalance(addrDesc)
ba, err = w.db.GetAddrDescBalance(addrDesc, db.AddressBalanceDetailUTXO)
if err != nil {
return nil, NewAPIError(fmt.Sprintf("Address not found, %v", err), true)
}
}
// ba can be nil if the address is only in mempool!
if ba != nil && !IsZeroBigInt(&ba.BalanceSat) {
outpoints := make([]bchain.Outpoint, 0, 8)
err = w.db.GetAddrDescTransactions(addrDesc, 0, maxUint32, func(txid string, height uint32, indexes []int32) error {
for _, index := range indexes {
// take only outputs
if index >= 0 {
outpoints = append(outpoints, bchain.Outpoint{Txid: txid, Vout: index})
}
}
return nil
})
if err != nil {
return nil, err
}
var lastTxid string
var ta *db.TxAddresses
var checksum big.Int
checksum.Set(&ba.BalanceSat)
if ba != nil && len(ba.Utxos) > 0 {
b, _, err := w.db.GetBestBlock()
if err != nil {
return nil, err
}
bestheight := int(b)
for i := 0; i < len(outpoints) && checksum.Int64() > 0; i++ {
o := outpoints[i]
if lastTxid != o.Txid {
ta, err = w.db.GetTxAddresses(o.Txid)
if err != nil {
return nil, err
}
lastTxid = o.Txid
var checksum big.Int
checksum.Set(&ba.BalanceSat)
// go backwards to get the newest first
for i := len(ba.Utxos) - 1; i >= 0; i-- {
utxo := &ba.Utxos[i]
txid, err := w.chainParser.UnpackTxid(utxo.BtxID)
if err != nil {
return nil, err
}
if ta == nil {
glog.Warning("DB inconsistency: tx ", o.Txid, ": not found in txAddresses")
} else {
if len(ta.Outputs) <= int(o.Vout) {
glog.Warning("DB inconsistency: txAddresses ", o.Txid, " does not have enough outputs")
} else {
if !ta.Outputs[o.Vout].Spent {
v := ta.Outputs[o.Vout].ValueSat
// report only outpoints that are not spent in mempool
_, e := spentInMempool[o.Txid+strconv.Itoa(int(o.Vout))]
if !e {
r = append(r, Utxo{
Txid: o.Txid,
Vout: o.Vout,
AmountSat: (*Amount)(&v),
Height: int(ta.Height),
Confirmations: bestheight - int(ta.Height) + 1,
})
}
checksum.Sub(&checksum, &v)
}
}
_, e := spentInMempool[txid+strconv.Itoa(int(utxo.Vout))]
if !e {
r = append(r, Utxo{
Txid: txid,
Vout: utxo.Vout,
AmountSat: (*Amount)(&utxo.ValueSat),
Height: int(utxo.Height),
Confirmations: bestheight - int(utxo.Height) + 1,
})
}
checksum.Sub(&checksum, &utxo.ValueSat)
}
if checksum.Uint64() != 0 {
glog.Warning("DB inconsistency: ", addrDesc, ": checksum is not zero")
@ -970,6 +967,9 @@ func (w *Worker) GetBlock(bid string, page int, txsOnPage int) (*Block, error) {
} else {
hash = bid
}
if hash == "" {
return nil, NewAPIError("Block not found", true)
}
bi, err := w.chain.GetBlockInfo(hash)
if err != nil {
if err == bchain.ErrBlockNotFound {
@ -991,22 +991,9 @@ func (w *Worker) GetBlock(bid string, page int, txsOnPage int) (*Block, error) {
txs := make([]*Tx, to-from)
txi := 0
for i := from; i < to; i++ {
txid := bi.Txids[i]
if w.chainType == bchain.ChainBitcoinType {
ta, err := w.db.GetTxAddresses(txid)
if err != nil {
return nil, errors.Annotatef(err, "GetTxAddresses %v", txid)
}
if ta == nil {
glog.Warning("DB inconsistency: tx ", txid, ": not found in txAddresses")
continue
}
txs[txi] = w.txFromTxAddress(txid, ta, dbi, bestheight)
} else {
txs[txi], err = w.GetTransaction(txid, false, false)
if err != nil {
return nil, err
}
txs[txi], err = w.txFromTxid(bi.Txids[i], bestheight, AccountDetailsTxHistoryLight, dbi)
if err != nil {
return nil, err
}
txi++
}
@ -1022,19 +1009,85 @@ func (w *Worker) GetBlock(bid string, page int, txsOnPage int) (*Block, error) {
return &Block{
Paging: pg,
BlockInfo: BlockInfo{
BlockHeader: bi.BlockHeader,
Bits: bi.Bits,
Difficulty: string(bi.Difficulty),
MerkleRoot: bi.MerkleRoot,
Nonce: string(bi.Nonce),
Txids: bi.Txids,
Version: bi.Version,
Hash: bi.Hash,
Prev: bi.Prev,
Next: bi.Next,
Height: bi.Height,
Confirmations: bi.Confirmations,
Size: bi.Size,
Time: bi.Time,
Bits: bi.Bits,
Difficulty: string(bi.Difficulty),
MerkleRoot: bi.MerkleRoot,
Nonce: string(bi.Nonce),
Txids: bi.Txids,
Version: bi.Version,
},
TxCount: txCount,
Transactions: txs,
}, nil
}
// ComputeFeeStats computes fee distribution in defined blocks and logs them to log
func (w *Worker) ComputeFeeStats(blockFrom, blockTo int, stopCompute chan os.Signal) error {
bestheight, _, err := w.db.GetBestBlock()
if err != nil {
return errors.Annotatef(err, "GetBestBlock")
}
for block := blockFrom; block <= blockTo; block++ {
hash, err := w.db.GetBlockHash(uint32(block))
if err != nil {
return err
}
bi, err := w.chain.GetBlockInfo(hash)
if err != nil {
return err
}
// process only blocks with enough transactions
if len(bi.Txids) > 20 {
dbi := &db.BlockInfo{
Hash: bi.Hash,
Height: bi.Height,
Time: bi.Time,
}
txids := bi.Txids
if w.chainType == bchain.ChainBitcoinType {
// skip the coinbase transaction
txids = txids[1:]
}
fees := make([]int64, len(txids))
sum := int64(0)
for i, txid := range txids {
select {
case <-stopCompute:
glog.Info("ComputeFeeStats interrupted at height ", block)
return db.ErrOperationInterrupted
default:
tx, err := w.txFromTxid(txid, bestheight, AccountDetailsTxHistoryLight, dbi)
if err != nil {
return err
}
fee := tx.FeesSat.AsInt64()
fees[i] = fee
sum += fee
}
}
sort.Slice(fees, func(i, j int) bool { return fees[i] < fees[j] })
step := float64(len(fees)) / 10
percentils := ""
for i := float64(0); i < float64(len(fees)+1); i += step {
ii := int(math.Round(i))
if ii >= len(fees) {
ii = len(fees) - 1
}
percentils += "," + strconv.FormatInt(fees[ii], 10)
}
glog.Info(block, ",", time.Unix(bi.Time, 0).Format(time.RFC3339), ",", len(bi.Txids), ",", sum, ",", float64(sum)/float64(len(bi.Txids)), percentils)
}
}
return nil
}
// GetSystemInfo returns information about system
func (w *Worker) GetSystemInfo(internal bool) (*SystemInfo, error) {
start := time.Now()
@ -1043,15 +1096,15 @@ func (w *Worker) GetSystemInfo(internal bool) (*SystemInfo, error) {
return nil, errors.Annotatef(err, "GetChainInfo")
}
vi := common.GetVersionInfo()
ss, bh, st := w.is.GetSyncState()
ms, mt, msz := w.is.GetMempoolSyncState()
var dbc []common.InternalStateColumn
var dbs int64
inSync, bestHeight, lastBlockTime := w.is.GetSyncState()
inSyncMempool, lastMempoolTime, mempoolSize := w.is.GetMempoolSyncState()
var columnStats []common.InternalStateColumn
var internalDBSize int64
if internal {
dbc = w.is.GetAllDBColumnStats()
dbs = w.is.DBSizeTotal()
columnStats = w.is.GetAllDBColumnStats()
internalDBSize = w.is.DBSizeTotal()
}
bi := &BlockbookInfo{
blockbookInfo := &BlockbookInfo{
Coin: w.is.Coin,
Host: w.is.Host,
Version: vi.Version,
@ -1059,20 +1112,33 @@ func (w *Worker) GetSystemInfo(internal bool) (*SystemInfo, error) {
BuildTime: vi.BuildTime,
SyncMode: w.is.SyncMode,
InitialSync: w.is.InitialSync,
InSync: ss,
BestHeight: bh,
LastBlockTime: st,
InSyncMempool: ms,
LastMempoolTime: mt,
MempoolSize: msz,
InSync: inSync,
BestHeight: bestHeight,
LastBlockTime: lastBlockTime,
InSyncMempool: inSyncMempool,
LastMempoolTime: lastMempoolTime,
MempoolSize: mempoolSize,
Decimals: w.chainParser.AmountDecimals(),
DbSize: w.db.DatabaseSizeOnDisk(),
DbSizeFromColumns: dbs,
DbColumns: dbc,
DbSizeFromColumns: internalDBSize,
DbColumns: columnStats,
About: Text.BlockbookAbout,
}
backendInfo := &BackendInfo{
Bestblockhash: ci.Bestblockhash,
Blocks: ci.Blocks,
Chain: ci.Chain,
Difficulty: ci.Difficulty,
Headers: ci.Headers,
ProtocolVersion: ci.ProtocolVersion,
SizeOnDisk: ci.SizeOnDisk,
Subversion: ci.Subversion,
Timeoffset: ci.Timeoffset,
Version: ci.Version,
Warnings: ci.Warnings,
}
glog.Info("GetSystemInfo finished in ", time.Since(start))
return &SystemInfo{bi, ci}, nil
return &SystemInfo{blockbookInfo, backendInfo}, nil
}
// GetMempool returns a page of mempool txids
@ -1082,7 +1148,7 @@ func (w *Worker) GetMempool(page int, itemsOnPage int) (*MempoolTxids, error) {
page = 0
}
entries := w.mempool.GetAllEntries()
pg, from, to, page := computePaging(len(entries), page, itemsOnPage)
pg, from, to, _ := computePaging(len(entries), page, itemsOnPage)
r := &MempoolTxids{
Paging: pg,
MempoolSize: len(entries),

View File

@ -160,7 +160,7 @@ func (w *Worker) xpubCheckAndLoadTxids(ad *xpubAddress, filter *AddressFilter, m
func (w *Worker) xpubDerivedAddressBalance(data *xpubData, ad *xpubAddress) (bool, error) {
var err error
if ad.balance, err = w.db.GetAddrDescBalance(ad.addrDesc); err != nil {
if ad.balance, err = w.db.GetAddrDescBalance(ad.addrDesc, db.AddressBalanceDetailUTXO); err != nil {
return false, err
}
if ad.balance != nil {
@ -495,7 +495,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc
if option == AccountDetailsTxidHistory {
txids = append(txids, xpubTxid.txid)
} else {
tx, err := w.txFromTxid(xpubTxid.txid, bestheight, option)
tx, err := w.txFromTxid(xpubTxid.txid, bestheight, option, nil)
if err != nil {
return nil, err
}
@ -505,7 +505,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc
} else {
txCount = int(data.txCountEstimate)
}
totalTokens := 0
usedTokens := 0
var tokens []Token
var xpubAddresses map[string]struct{}
if option > AccountDetailsBasic {
@ -516,7 +516,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc
for i := range da {
ad := &da[i]
if ad.balance != nil {
totalTokens++
usedTokens++
}
if option > AccountDetailsBasic {
token := w.tokenFromXpubAddress(data, ad, ci, i, option)
@ -542,7 +542,7 @@ func (w *Worker) GetXpubAddress(xpub string, page int, txsOnPage int, option Acc
UnconfirmedTxs: unconfirmedTxs,
Transactions: txs,
Txids: txids,
TotalTokens: totalTokens,
UsedTokens: usedTokens,
Tokens: tokens,
XPubAddresses: xpubAddresses,
}

View File

@ -268,6 +268,12 @@ func (p *BaseParser) UnpackTx(buf []byte) (*Tx, uint32, error) {
return &tx, pt.Height, nil
}
// IsAddrDescIndexable returns true if AddressDescriptor should be added to index
// by default all AddressDescriptors are indexable
func (p *BaseParser) IsAddrDescIndexable(addrDesc AddressDescriptor) bool {
return true
}
// DerivationBasePath is unsupported
func (p *BaseParser) DerivationBasePath(xpub string) (string, error) {
return "", errors.New("Not supported")

View File

@ -7,11 +7,13 @@ import (
"github.com/martinboehm/btcutil/chaincfg"
)
// magic numbers
const (
MainnetMagic wire.BitcoinNet = 0xbebacefa
TestnetMagic wire.BitcoinNet = 0x0709110b
)
// chain parameters
var (
MainNetParams chaincfg.Params
TestNetParams chaincfg.Params

View File

@ -18,13 +18,17 @@ import (
"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/vertcoin"
"blockbook/bchain/coins/viacoin"
"blockbook/bchain/coins/vipstarcoin"
"blockbook/bchain/coins/xzc"
"blockbook/bchain/coins/zec"
"blockbook/common"
@ -68,6 +72,7 @@ func init() {
BlockChainFactories["Namecoin"] = namecoin.NewNamecoinRPC
BlockChainFactories["Monacoin"] = monacoin.NewMonacoinRPC
BlockChainFactories["Monacoin Testnet"] = monacoin.NewMonacoinRPC
BlockChainFactories["MonetaryUnit"] = monetaryunit.NewMonetaryUnitRPC
BlockChainFactories["DigiByte"] = digibyte.NewDigiByteRPC
BlockChainFactories["Myriad"] = myriad.NewMyriadRPC
BlockChainFactories["Liquid"] = liquid.NewLiquidRPC
@ -81,8 +86,12 @@ func init() {
BlockChainFactories["Flo"] = flo.NewFloRPC
BlockChainFactories["Bellcoin"] = bellcoin.NewBellcoinRPC
BlockChainFactories["Qtum"] = qtum.NewQtumRPC
BlockChainFactories["Viacoin"] = viacoin.NewViacoinRPC
BlockChainFactories["Qtum Testnet"] = qtum.NewQtumRPC
BlockChainFactories["NULS"] = nuls.NewNulsRPC
BlockChainFactories["VIPSTARCOIN"] = vipstarcoin.NewVIPSTARCOINRPC
BlockChainFactories["ZelCash"] = zec.NewZCashRPC
BlockChainFactories["Ravencoin"] = ravencoin.NewRavencoinRPC
}
// GetCoinNameFromConfig gets coin name and coin shortcut from config file

View File

@ -91,6 +91,15 @@ func (p *BitcoinParser) GetScriptFromAddrDesc(addrDesc bchain.AddressDescriptor)
return addrDesc, nil
}
// IsAddrDescIndexable returns true if AddressDescriptor should be added to index
// empty or OP_RETURN scripts are not indexed
func (p *BitcoinParser) IsAddrDescIndexable(addrDesc bchain.AddressDescriptor) bool {
if len(addrDesc) == 0 || addrDesc[0] == txscript.OP_RETURN {
return false
}
return true
}
// addressToOutputScript converts bitcoin address to ScriptPubKey
func (p *BitcoinParser) addressToOutputScript(address string) ([]byte, error) {
da, err := btcutil.DecodeAddress(address, p.Params)

View File

@ -36,25 +36,27 @@ type BitcoinRPC struct {
// Configuration represents json config file
type Configuration struct {
CoinName string `json:"coin_name"`
CoinShortcut string `json:"coin_shortcut"`
RPCURL string `json:"rpc_url"`
RPCUser string `json:"rpc_user"`
RPCPass string `json:"rpc_pass"`
RPCTimeout int `json:"rpc_timeout"`
Parse bool `json:"parse"`
MessageQueueBinding string `json:"message_queue_binding"`
Subversion string `json:"subversion"`
BlockAddressesToKeep int `json:"block_addresses_to_keep"`
MempoolWorkers int `json:"mempool_workers"`
MempoolSubWorkers int `json:"mempool_sub_workers"`
AddressFormat string `json:"address_format"`
SupportsEstimateFee bool `json:"supports_estimate_fee"`
SupportsEstimateSmartFee bool `json:"supports_estimate_smart_fee"`
XPubMagic uint32 `json:"xpub_magic,omitempty"`
XPubMagicSegwitP2sh uint32 `json:"xpub_magic_segwit_p2sh,omitempty"`
XPubMagicSegwitNative uint32 `json:"xpub_magic_segwit_native,omitempty"`
Slip44 uint32 `json:"slip44,omitempty"`
CoinName string `json:"coin_name"`
CoinShortcut string `json:"coin_shortcut"`
RPCURL string `json:"rpc_url"`
RPCUser string `json:"rpc_user"`
RPCPass string `json:"rpc_pass"`
RPCTimeout int `json:"rpc_timeout"`
Parse bool `json:"parse"`
MessageQueueBinding string `json:"message_queue_binding"`
Subversion string `json:"subversion"`
BlockAddressesToKeep int `json:"block_addresses_to_keep"`
MempoolWorkers int `json:"mempool_workers"`
MempoolSubWorkers int `json:"mempool_sub_workers"`
AddressFormat string `json:"address_format"`
SupportsEstimateFee bool `json:"supports_estimate_fee"`
SupportsEstimateSmartFee bool `json:"supports_estimate_smart_fee"`
XPubMagic uint32 `json:"xpub_magic,omitempty"`
XPubMagicSegwitP2sh uint32 `json:"xpub_magic_segwit_p2sh,omitempty"`
XPubMagicSegwitNative uint32 `json:"xpub_magic_segwit_native,omitempty"`
Slip44 uint32 `json:"slip44,omitempty"`
AlternativeEstimateFee string `json:"alternativeEstimateFee,omitempty"`
AlternativeEstimateFeeParams string `json:"alternativeEstimateFeeParams,omitempty"`
}
// NewBitcoinRPC returns new BitcoinRPC instance.
@ -127,6 +129,14 @@ func (b *BitcoinRPC) Initialize() error {
glog.Info("rpc: block chain ", params.Name)
if b.ChainConfig.AlternativeEstimateFee == "whatthefee" {
if err = InitWhatTheFee(b, b.ChainConfig.AlternativeEstimateFeeParams); err != nil {
glog.Error("InitWhatTheFee error ", err, " Reverting to default estimateFee functionality")
// disable AlternativeEstimateFee logic
b.ChainConfig.AlternativeEstimateFee = ""
}
}
return nil
}

View File

@ -0,0 +1,157 @@
package btc
import (
"blockbook/bchain"
"bytes"
"encoding/json"
"fmt"
"math"
"net/http"
"strconv"
"sync"
"time"
"github.com/golang/glog"
"github.com/juju/errors"
)
// https://whatthefee.io returns
// {"index": [3, 6, 9, 12, 18, 24, 36, 48, 72, 96, 144],
// "columns": ["0.0500", "0.2000", "0.5000", "0.8000", "0.9500"],
// "data": [[60, 180, 280, 400, 440], [20, 120, 180, 380, 440],
// [0, 120, 160, 360, 420], [0, 80, 160, 300, 380], [0, 20, 120, 220, 360],
// [0, 20, 100, 180, 300], [0, 0, 80, 140, 240], [0, 0, 60, 100, 180],
// [0, 0, 40, 60, 140], [0, 0, 20, 20, 60], [0, 0, 0, 0, 20]]}
type whatTheFeeServiceResult struct {
Index []int `json:"index"`
Columns []string `json:"columns"`
Data [][]int `json:"data"`
}
type whatTheFeeParams struct {
URL string `json:"url"`
PeriodSeconds int `periodSeconds:"url"`
}
type whatTheFeeFee struct {
blocks int
feesPerKB []int
}
type whatTheFeeData struct {
params whatTheFeeParams
probabilities []string
fees []whatTheFeeFee
lastSync time.Time
chain bchain.BlockChain
mux sync.Mutex
}
var whatTheFee whatTheFeeData
// InitWhatTheFee initializes https://whatthefee.io handler
func InitWhatTheFee(chain bchain.BlockChain, params string) error {
err := json.Unmarshal([]byte(params), &whatTheFee.params)
if err != nil {
return err
}
if whatTheFee.params.URL == "" || whatTheFee.params.PeriodSeconds == 0 {
return errors.New("Missing parameters")
}
whatTheFee.chain = chain
go whatTheFeeDownloader()
return nil
}
func whatTheFeeDownloader() {
period := time.Duration(whatTheFee.params.PeriodSeconds) * time.Second
timer := time.NewTimer(period)
counter := 0
for {
var data whatTheFeeServiceResult
err := whatTheFeeGetData(&data)
if err != nil {
glog.Error("whatTheFeeGetData ", err)
} else {
if whatTheFeeProcessData(&data) {
if counter%60 == 0 {
whatTheFeeCompareToDefault()
}
counter++
}
}
<-timer.C
timer.Reset(period)
}
}
func whatTheFeeProcessData(data *whatTheFeeServiceResult) bool {
if len(data.Index) == 0 || len(data.Index) != len(data.Data) || len(data.Columns) == 0 {
glog.Errorf("invalid data %+v", data)
return false
}
whatTheFee.mux.Lock()
defer whatTheFee.mux.Unlock()
whatTheFee.probabilities = data.Columns
whatTheFee.fees = make([]whatTheFeeFee, len(data.Index))
for i, blocks := range data.Index {
if len(data.Columns) != len(data.Data[i]) {
glog.Errorf("invalid data %+v", data)
return false
}
fees := make([]int, len(data.Columns))
for j, l := range data.Data[i] {
fees[j] = int(1000 * math.Exp(float64(l)/100))
}
whatTheFee.fees[i] = whatTheFeeFee{
blocks: blocks,
feesPerKB: fees,
}
}
whatTheFee.lastSync = time.Now()
glog.Infof("%+v", whatTheFee.fees)
return true
}
func whatTheFeeGetData(res interface{}) error {
var httpData []byte
httpReq, err := http.NewRequest("GET", whatTheFee.params.URL, bytes.NewBuffer(httpData))
if err != nil {
return err
}
httpRes, err := http.DefaultClient.Do(httpReq)
if httpRes != nil {
defer httpRes.Body.Close()
}
if err != nil {
return err
}
if httpRes.StatusCode != 200 {
return errors.New("whatthefee.io returned status " + strconv.Itoa(httpRes.StatusCode))
}
return safeDecodeResponse(httpRes.Body, &res)
}
func whatTheFeeCompareToDefault() {
output := ""
for _, fee := range whatTheFee.fees {
output += fmt.Sprint(fee.blocks, ",")
for _, wtf := range fee.feesPerKB {
output += fmt.Sprint(wtf, ",")
}
conservative, err := whatTheFee.chain.EstimateSmartFee(fee.blocks, true)
if err != nil {
glog.Error(err)
return
}
economical, err := whatTheFee.chain.EstimateSmartFee(fee.blocks, false)
if err != nil {
glog.Error(err)
return
}
output += fmt.Sprint(conservative.String(), ",", economical.String(), "\n")
}
glog.Info("whatTheFeeCompareToDefault\n", output)
}

View File

@ -10,10 +10,12 @@ import (
"github.com/martinboehm/btcutil/chaincfg"
)
// magic numbers
const (
MainnetMagic wire.BitcoinNet = 0xc0c0c0c0
)
// chain parameters
var (
MainNetParams chaincfg.Params
)

View File

@ -245,7 +245,7 @@ func (b *EthereumRPC) subscribe(f func() (*rpc.ClientSubscription, error)) error
return
}
glog.Error("Subscription error ", e)
timer := time.NewTimer(time.Second)
timer := time.NewTimer(time.Second * 2)
// try in 1 second interval to resubscribe
for {
select {
@ -260,7 +260,8 @@ func (b *EthereumRPC) subscribe(f func() (*rpc.ClientSubscription, error)) error
s = ns
continue Loop
}
timer.Reset(time.Second)
glog.Error("Resubscribe error ", e)
timer.Reset(time.Second * 2)
}
}
}

View File

@ -7,12 +7,14 @@ import (
"github.com/martinboehm/btcutil/chaincfg"
)
// magic numbers
const (
MainnetMagic wire.BitcoinNet = 0xdbb6c0fb
TestnetMagic wire.BitcoinNet = 0x0709110b
RegtestMagic wire.BitcoinNet = 0xdab5bffa
)
// chain parameters
var (
MainNetParams chaincfg.Params
TestNetParams chaincfg.Params

View File

@ -9,11 +9,13 @@ import (
"github.com/martinboehm/btcutil/chaincfg"
)
// magic numbers
const (
MainnetMagic wire.BitcoinNet = 0xd4b4bef9
TestnetMagic wire.BitcoinNet = 0x0709110b
)
// chain parameters
var (
MainNetParams chaincfg.Params
TestNetParams chaincfg.Params

View File

@ -9,10 +9,12 @@ import (
"github.com/juju/errors"
)
// GroestlcoinRPC is an interface to JSON-RPC service
type GroestlcoinRPC struct {
*btc.BitcoinRPC
}
// NewGroestlcoinRPC returns new GroestlcoinRPC instance
func NewGroestlcoinRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) {
b, err := btc.NewBitcoinRPC(config, pushHandler)
if err != nil {

View File

@ -7,12 +7,14 @@ import (
"github.com/martinboehm/btcutil/chaincfg"
)
// magic numbers
const (
MainnetMagic wire.BitcoinNet = 0xdbb6c0fb
TestnetMagic wire.BitcoinNet = 0xf1c8d2fd
RegtestMagic wire.BitcoinNet = 0xdab5bffa
)
// chain parameters
var (
MainNetParams chaincfg.Params
TestNetParams chaincfg.Params

View File

@ -0,0 +1,182 @@
package monetaryunit
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"blockbook/bchain/coins/utils"
"bytes"
"io"
"encoding/hex"
"encoding/json"
"github.com/juju/errors"
"github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg"
)
const (
// Net Magics
MainnetMagic wire.BitcoinNet = 0x91c4fdea
TestnetMagic wire.BitcoinNet = 0x477665bd
)
var (
MainNetParams chaincfg.Params
TestNetParams chaincfg.Params
)
func init() {
// MonetaryUnit mainnet Address encoding magics
MainNetParams = chaincfg.MainNetParams
MainNetParams.Net = MainnetMagic
MainNetParams.PubKeyHashAddrID = []byte{16} // starting with '7'
MainNetParams.ScriptHashAddrID = []byte{76}
MainNetParams.PrivateKeyID = []byte{126}
// MonetaryUnit testnet Address encoding magics
TestNetParams = chaincfg.TestNet3Params
TestNetParams.Net = TestnetMagic
TestNetParams.PubKeyHashAddrID = []byte{139} // starting with 'x' or 'y'
TestNetParams.ScriptHashAddrID = []byte{19}
TestNetParams.PrivateKeyID = []byte{239}
}
// MonetaryUnitParser handle
type MonetaryUnitParser struct {
*btc.BitcoinParser
baseparser *bchain.BaseParser
BitcoinOutputScriptToAddressesFunc btc.OutputScriptToAddressesFunc
}
// NewMonetaryUnitParser returns new MonetaryUnitParser instance
func NewMonetaryUnitParser(params *chaincfg.Params, c *btc.Configuration) *MonetaryUnitParser {
p := &MonetaryUnitParser{
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 MonetaryUnit 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 *MonetaryUnitParser) 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 {
// Skip past AccumulatorCheckpoint which was added in MonetaryUnit block version 4
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 *MonetaryUnitParser) 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 *MonetaryUnitParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
return p.baseparser.UnpackTx(buf)
}
// ParseTx parses byte array containing transaction and returns Tx struct
func (p *MonetaryUnitParser) 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
}
// ParseTxFromJson parses JSON message containing transaction and returns Tx struct
func (p *MonetaryUnitParser) 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 *MonetaryUnitParser) outputScriptToAddresses(script []byte) ([]string, bool, error) {
rv, s, _ := p.BitcoinOutputScriptToAddressesFunc(script)
return rv, s, nil
}
func (p *MonetaryUnitParser) 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
}

View File

@ -0,0 +1,288 @@
// +build unittest
package monetaryunit
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/hex"
"math/big"
"os"
"reflect"
"testing"
"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: "7cTAePV4rJoSpa6eLhUPSuPpGLdsLiWnXf"},
want: "76a9146e2b0a8655786c8c5ea7b9ce478f03e00ecb2f5588ac",
wantErr: false,
},
{
name: "P2PKH2",
args: args{address: "XdmBSxuCLajxZzQQWiY2FEk6XVjiVoqLXW"},
want: "a91421ba6a62ac1d74d2ba921bbc8c9a3ca6e1420a0087",
wantErr: false,
},
{
name: "P2SH1",
args: args{address: "bc1q0v3tadxj6pm3ym9j06v9rfyw0jeh5f8squ3nvt"},
want: "00147b22beb4d2d077126cb27e9851a48e7cb37a24f0",
wantErr: false,
},
{
name: "P2SH2",
args: args{address: "bc1qumpyvyxz25kfjjrvyxn3zlyc2wfc0m3l3gm5pg99c4mxylemfqhsdf5q0k"},
want: "0020e6c24610c2552c99486c21a7117c98539387ee3f8a3740a0a5c576627f3b482f",
wantErr: false,
},
}
parser := NewMonetaryUnitParser(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: "76a9146e2b0a8655786c8c5ea7b9ce478f03e00ecb2f5588ac"},
want: []string{"7cTAePV4rJoSpa6eLhUPSuPpGLdsLiWnXf"},
want2: true,
wantErr: false,
},
{
name: "P2SH",
args: args{script: "a91421ba6a62ac1d74d2ba921bbc8c9a3ca6e1420a0087"},
want: []string{"XdmBSxuCLajxZzQQWiY2FEk6XVjiVoqLXW"},
want2: true,
wantErr: false,
},
{
name: "P2WPKH",
args: args{script: "00147b22beb4d2d077126cb27e9851a48e7cb37a24f0"},
want: []string{"bc1q0v3tadxj6pm3ym9j06v9rfyw0jeh5f8squ3nvt"},
want2: true,
wantErr: false,
},
{
name: "P2WSH",
args: args{script: "0020e6c24610c2552c99486c21a7117c98539387ee3f8a3740a0a5c576627f3b482f"},
want: []string{"bc1qumpyvyxz25kfjjrvyxn3zlyc2wfc0m3l3gm5pg99c4mxylemfqhsdf5q0k"},
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 := NewMonetaryUnitParser(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 = "0a20f05ba72a05c4900ff2a00a0403697750201e41267aeea8a589a7dc7bcc57076e12d30101000000010c396f3768565c707addf85ecf47e04cefb2721d95afd977e13f25904de8336a0100000049483045022100fe1b79f38ca4b9dc2fbc50eac6c9bf050ae5a3ee37da05b950918230eb0a8c7c0220477173b60ec00a8b4b28d5db9fc5d8259eea35f91bbdd60089c5ec1be7b05f3b01ffffffff03000000000000000000dd400def140000002321025d145b77df04c40ceb88ea36828755f8275dc5fecf19d3ecccce2d8198c3407cac00d2496b000000001976a914afe70b2e1bf4199298ed8281767bae22970b415088ac0000000018e6b092e605200028f48d1b32770a0012206a33e84d90253fe177d9af951d72b2ef4ce047cf5ef8dd7a705c5668376f390c18012249483045022100fe1b79f38ca4b9dc2fbc50eac6c9bf050ae5a3ee37da05b950918230eb0a8c7c0220477173b60ec00a8b4b28d5db9fc5d8259eea35f91bbdd60089c5ec1be7b05f3b0128ffffffff0f3a04100022003a520a0514ef0d40dd10011a2321025d145b77df04c40ceb88ea36828755f8275dc5fecf19d3ecccce2d8198c3407cac22223764396a4e79716835694b555a566e516a6d7a6d4541513878444b777a4536536e673a470a046b49d20010021a1976a914afe70b2e1bf4199298ed8281767bae22970b415088ac22223769536a6e57436f41556d4a347656584d6b61457561736e5658537a5a7166504c324001"
)
func init() {
testTx1 = bchain.Tx{
Hex: "01000000010c396f3768565c707addf85ecf47e04cefb2721d95afd977e13f25904de8336a0100000049483045022100fe1b79f38ca4b9dc2fbc50eac6c9bf050ae5a3ee37da05b950918230eb0a8c7c0220477173b60ec00a8b4b28d5db9fc5d8259eea35f91bbdd60089c5ec1be7b05f3b01ffffffff03000000000000000000dd400def140000002321025d145b77df04c40ceb88ea36828755f8275dc5fecf19d3ecccce2d8198c3407cac00d2496b000000001976a914afe70b2e1bf4199298ed8281767bae22970b415088ac00000000",
Blocktime: 1556387942,
Txid: "f05ba72a05c4900ff2a00a0403697750201e41267aeea8a589a7dc7bcc57076e",
LockTime: 0,
Time: 1556387942,
Version: 1,
Vin: []bchain.Vin{
{
ScriptSig: bchain.ScriptSig{
Hex: "483045022100fe1b79f38ca4b9dc2fbc50eac6c9bf050ae5a3ee37da05b950918230eb0a8c7c0220477173b60ec00a8b4b28d5db9fc5d8259eea35f91bbdd60089c5ec1be7b05f3b01",
},
Txid: "6a33e84d90253fe177d9af951d72b2ef4ce047cf5ef8dd7a705c5668376f390c",
Vout: 1,
Sequence: 4294967295,
},
},
Vout: []bchain.Vout{
{
ValueSat: *big.NewInt(0),
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "",
Addresses: []string{
"",
},
},
},
{
ValueSat: *big.NewInt(89909969117),
N: 1,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "21025d145b77df04c40ceb88ea36828755f8275dc5fecf19d3ecccce2d8198c3407cac",
Addresses: []string{
"7d9jNyqh5iKUZVnQjmzmEAQ8xDKwzE6Sng",
},
},
},
{
ValueSat: *big.NewInt(1800000000),
N: 2,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a914afe70b2e1bf4199298ed8281767bae22970b415088ac",
Addresses: []string{
"7iSjnWCoAUmJ4vVXMkaEuasnVXSzZqfPL2",
},
},
},
},
}
}
func Test_PackTx(t *testing.T) {
type args struct {
tx bchain.Tx
height uint32
blockTime int64
parser *MonetaryUnitParser
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "monetaryunit-1",
args: args{
tx: testTx1,
height: 444148,
blockTime: 1556387942,
parser: NewMonetaryUnitParser(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 *MonetaryUnitParser
}
tests := []struct {
name string
args args
want *bchain.Tx
want1 uint32
wantErr bool
}{
{
name: "monetaryunit-1",
args: args{
packedTx: testTxPacked1,
parser: NewMonetaryUnitParser(GetChainParams("main"), &btc.Configuration{}),
},
want: &testTx1,
want1: 444148,
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

@ -0,0 +1,112 @@
package monetaryunit
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json"
"github.com/golang/glog"
)
// MonetaryUnitRPC is an interface to JSON-RPC bitcoind service.
type MonetaryUnitRPC struct {
*btc.BitcoinRPC
}
// NewMonetaryUnitRPC returns new MonetaryUnitRPC instance.
func NewMonetaryUnitRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) {
b, err := btc.NewBitcoinRPC(config, pushHandler)
if err != nil {
return nil, err
}
s := &MonetaryUnitRPC{
b.(*btc.BitcoinRPC),
}
s.RPCMarshaler = btc.JSONMarshalerV1{}
s.ChainConfig.SupportsEstimateFee = true
s.ChainConfig.SupportsEstimateSmartFee = false
return s, nil
}
// Initialize initializes MonetaryUnitRPC instance.
func (b *MonetaryUnitRPC) 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 = NewMonetaryUnitParser(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
}
// Get Block
func (b *MonetaryUnitRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) {
if height == 0 {
var err error
if hash == "" {
hash, err = b.GetBlockHash(height)
if err != nil {
return nil, err
}
}
if !b.ParseBlocks {
return b.GetBlockFull(hash)
}
return b.GetBlockWithoutHeader(hash, height)
}
var err error
if hash == "" && height > 0 {
hash, err = b.GetBlockHash(height)
if err != nil {
return nil, err
}
}
glog.V(1).Info("rpc: getblock (verbosity=1) ", hash)
res := btc.ResGetBlockThin{}
req := btc.CmdGetBlock{Method: "getblock"}
req.Params.BlockHash = hash
req.Params.Verbosity = 1
err = b.Call(&req, &res)
txs := make([]bchain.Tx, 0, len(res.Result.Txids))
for _, txid := range res.Result.Txids {
tx, err := b.GetTransaction(txid)
if err != nil {
if err == bchain.ErrTxNotFound {
glog.Errorf("rpc: getblock: skipping transanction in block %s due error: %s", hash, err)
continue
}
return nil, err
}
txs = append(txs, *tx)
}
block := &bchain.Block{
BlockHeader: res.Result.BlockHeader,
Txs: txs,
}
return block, nil
}

View File

@ -6,19 +6,22 @@ import (
"bytes"
"encoding/binary"
"encoding/json"
"github.com/bsm/go-vlq"
vlq "github.com/bsm/go-vlq"
"github.com/martinboehm/btcutil/base58"
"github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg"
)
// magic numbers
const (
MainnetMagic wire.BitcoinNet = 0xbd6b0cbf
TestnetMagic wire.BitcoinNet = 0xffcae2ce
RegtestMagic wire.BitcoinNet = 0xdcb7c1fc
)
// chain parameters
var (
MainNetParams chaincfg.Params
TestNetParams chaincfg.Params
@ -140,6 +143,7 @@ func (p *NulsParser) UnpackTx(buf []byte) (*bchain.Tx, uint32, error) {
return tx, height, nil
}
// ParseTx parses tx from blob
func (p *NulsParser) ParseTx(b []byte) (*bchain.Tx, error) {
tx := bchain.Tx{}
err := json.Unmarshal(b, &tx)

View File

@ -21,7 +21,7 @@ import (
"github.com/golang/glog"
)
// NulsRPC is an interface to JSON-RPC bitcoind service.
// NulsRPC is an interface to JSON-RPC bitcoind service
type NulsRPC struct {
*btc.BitcoinRPC
client http.Client
@ -30,7 +30,7 @@ type NulsRPC struct {
password string
}
// NewNulsRPC returns new NulsRPC instance.
// NewNulsRPC returns new NulsRPC instance
func NewNulsRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) {
b, err := btc.NewBitcoinRPC(config, pushHandler)
if err != nil {
@ -101,7 +101,7 @@ type CmdGetVersionInfo struct {
MyVersion string `json:"myVersion"`
NewestVersion string `json:"newestVersion"`
NetworkVersion int `json:"networkVersion"`
Infromation string `json:"infromation"`
Information string `json:"information"`
} `json:"data"`
}
@ -227,7 +227,7 @@ func (n *NulsRPC) GetChainInfo() (*bchain.ChainInfo, error) {
Subversion: versionInfo.Data.NewestVersion,
ProtocolVersion: strconv.Itoa(versionInfo.Data.NetworkVersion),
Timeoffset: 0,
Warnings: versionInfo.Data.Infromation,
Warnings: versionInfo.Data.Information,
}
return chainInfo, nil
}
@ -414,7 +414,8 @@ func (n *NulsRPC) GetTransaction(txid string) (*bchain.Tx, error) {
}
blockHeaderHeight := getTx.Tx.BlockHeight
blockHeader, e := n.GetBlockHeaderByHeight(uint32(blockHeaderHeight))
// shouldn't it check the error here?
blockHeader, _ := n.GetBlockHeaderByHeight(uint32(blockHeaderHeight))
if blockHeader != nil {
tx.Blocktime = blockHeader.Time
}

View File

@ -2,16 +2,19 @@ package polis
import (
"blockbook/bchain/coins/btc"
"github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg"
)
// magic numbers
const (
MainnetMagic wire.BitcoinNet = 0xbd6b0cbf
TestnetMagic wire.BitcoinNet = 0xffcae2ce
RegtestMagic wire.BitcoinNet = 0xdcb7c1fc
)
// chain parameters
var (
MainNetParams chaincfg.Params
TestNetParams chaincfg.Params

View File

@ -3,21 +3,20 @@
package polis
import (
"bytes"
"fmt"
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"bytes"
"encoding/hex"
"fmt"
"github.com/martinboehm/btcutil/chaincfg"
"io/ioutil"
"math/big"
"os"
"path/filepath"
"reflect"
"testing"
"github.com/martinboehm/btcutil/chaincfg"
)
type testBlock struct {
size int
time int64

View File

@ -12,11 +12,13 @@ import (
"github.com/martinboehm/btcutil/chaincfg"
)
// magic numbers
const (
MainnetMagic wire.BitcoinNet = 0xf1cfa6d3
TestnetMagic wire.BitcoinNet = 0x0d221506
)
// chain parameters
var (
MainNetParams chaincfg.Params
TestNetParams chaincfg.Params

View File

@ -0,0 +1,91 @@
package ravencoin
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"blockbook/bchain/coins/utils"
"bytes"
"github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg"
)
const (
MainnetMagic wire.BitcoinNet = 0x4e564152
TestnetMagic wire.BitcoinNet = 0x544e5652
)
var (
MainNetParams chaincfg.Params
TestNetParams chaincfg.Params
)
func init() {
MainNetParams = chaincfg.MainNetParams
MainNetParams.Net = MainnetMagic
MainNetParams.PubKeyHashAddrID = []byte{60}
MainNetParams.ScriptHashAddrID = []byte{122}
TestNetParams = chaincfg.TestNet3Params
TestNetParams.Net = TestnetMagic
TestNetParams.PubKeyHashAddrID = []byte{111}
TestNetParams.ScriptHashAddrID = []byte{196}
}
// RavencoinParser handle
type RavencoinParser struct {
*btc.BitcoinParser
}
// NewRavencoinParser returns new RavencoinParser instance
func NewRavencoinParser(params *chaincfg.Params, c *btc.Configuration) *RavencoinParser {
return &RavencoinParser{BitcoinParser: btc.NewBitcoinParser(params, c)}
}
// GetChainParams contains network parameters
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 *RavencoinParser) 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, err
}
err = utils.DecodeTransactions(r, 0, wire.WitnessEncoding, &w)
if err != nil {
return nil, err
}
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
}

View File

@ -0,0 +1,273 @@
// +build unittest
package ravencoin
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/hex"
"math/big"
"os"
"reflect"
"testing"
"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: "RAoGkGhKwzxLnstApumYPD2eTrAJ849cga"},
want: "76a91410a8805f1a6af1a5927088544b0b6ec7d6f0ab8b88ac",
wantErr: false,
},
{
name: "P2PKH2",
args: args{address: "RTq37kPJqMS36tZYunxo2abrBMLeYSCAaa"},
want: "76a914cb78181d62d312fdb9aacca433570150dcf0dec288ac",
wantErr: false,
},
{
name: "P2SH1",
args: args{address: "rCzjkBoY2duVn2WizKxfBedTVWAg6UhfLZ"},
want: "a9144a2a40987c74578ee517d426aa2c43fc568f7e0887",
wantErr: false,
},
{
name: "P2SH2",
args: args{address: "rDzGemZkv9FbDDh5pvWfr7TWtMUnNRRE7T"},
want: "a914550bc2fcc1992afade4d298326ee6a03ab975a9387",
wantErr: false,
},
}
parser := NewRavencoinParser(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)
}
})
}
}
var (
testTx1 bchain.Tx
testTxPacked1 = "000a08848bcae7c30e0200000001c171348ffc8976074fa064e48598a816fce3798afc635fb67d99580e50b8e614000000006a473044022009e07574fa543ad259bd3334eb365c655c96d310c578b64c24d7f77fa7dc591c0220427d8ae6eacd1ca2d1994e9ec49cb322aacdde98e4bdb065e0fce81162fb3aa9012102d46827546548b9b47ae1e9e84fc4e53513e0987eeb1dd41220ba39f67d3bf46affffffff02f8137114000000001976a914587a2afa560ccaeaeb67cb72a0db7e2573a179e488ace0c48110000000001976a914d85e6ab66ab0b2c4cfd40ca3b0a779529da5799288ac00000000"
testTx2 bchain.Tx
testTxPacked2 = "000a08848bcae7c30e02000000029e2e14113b2f55726eebaa440edec707fcec3a31ce28fa125afea1e755fb6850010000006a47304402204034c3862f221551cffb2aa809f621f989a75cdb549c789a5ceb3a82c0bcc21c022001b4638f5d73fdd406a4dd9bf99be3dfca4a572b8f40f09b8fd495a7756c0db70121027a32ef45aef2f720ccf585f6fb0b8a7653db89cacc3320e5b385146851aba705fefffffff3b240ae32c542786876fcf23b4b2ab4c34ef077912898ee529756ed4ba35910000000006a47304402204d442645597b13abb85e96e5acd34eff50a4418822fe6a37ed378cdd24574dff02205ae667c56eab63cc45a51063f15b72136fd76e97c46af29bd28e8c4d405aa211012102cde27d7b29331ea3fef909a8d91f6f7753e99a3dd129914be50df26eed73fab3feffffff028447bf38000000001976a9146d7badec5426b880df25a3afc50e476c2423b34b88acb26b556a740000001976a914b3020d0ab85710151fa509d5d9a4e783903d681888ac83080a00"
)
func init() {
testTx1 = bchain.Tx{
Hex: "0200000001c171348ffc8976074fa064e48598a816fce3798afc635fb67d99580e50b8e614000000006a473044022009e07574fa543ad259bd3334eb365c655c96d310c578b64c24d7f77fa7dc591c0220427d8ae6eacd1ca2d1994e9ec49cb322aacdde98e4bdb065e0fce81162fb3aa9012102d46827546548b9b47ae1e9e84fc4e53513e0987eeb1dd41220ba39f67d3bf46affffffff02f8137114000000001976a914587a2afa560ccaeaeb67cb72a0db7e2573a179e488ace0c48110000000001976a914d85e6ab66ab0b2c4cfd40ca3b0a779529da5799288ac00000000",
Blocktime: 1554837703,
Txid: "d4d3a093586eae0c3668fd288d9e24955928a894c20b551b38dd18c99b123a7c",
LockTime: 0,
Version: 2,
Vin: []bchain.Vin{
{
ScriptSig: bchain.ScriptSig{
Hex: "473044022009e07574fa543ad259bd3334eb365c655c96d310c578b64c24d7f77fa7dc591c0220427d8ae6eacd1ca2d1994e9ec49cb322aacdde98e4bdb065e0fce81162fb3aa9012102d46827546548b9b47ae1e9e84fc4e53513e0987eeb1dd41220ba39f67d3bf46a",
},
Txid: "14e6b8500e58997db65f63fc8a79e3fc16a89885e464a04f077689fc8f3471c1",
Vout: 0,
Sequence: 4294967295,
},
},
Vout: []bchain.Vout{
{
ValueSat: *big.NewInt(342955000),
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a914587a2afa560ccaeaeb67cb72a0db7e2573a179e488ac",
Addresses: []string{
"RHM1tmdvkk7vDoiGxwUJAMNNmDqywZ5tEn",
},
},
},
{
ValueSat: *big.NewInt(276940000),
N: 1,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a914d85e6ab66ab0b2c4cfd40ca3b0a779529da5799288ac",
Addresses: []string{
"RV1F99b9UBBrCM8aNKugsqsDM8iqoCq7Mt",
},
},
},
},
}
testTx2 = bchain.Tx{
Hex: "02000000029e2e14113b2f55726eebaa440edec707fcec3a31ce28fa125afea1e755fb6850010000006a47304402204034c3862f221551cffb2aa809f621f989a75cdb549c789a5ceb3a82c0bcc21c022001b4638f5d73fdd406a4dd9bf99be3dfca4a572b8f40f09b8fd495a7756c0db70121027a32ef45aef2f720ccf585f6fb0b8a7653db89cacc3320e5b385146851aba705fefffffff3b240ae32c542786876fcf23b4b2ab4c34ef077912898ee529756ed4ba35910000000006a47304402204d442645597b13abb85e96e5acd34eff50a4418822fe6a37ed378cdd24574dff02205ae667c56eab63cc45a51063f15b72136fd76e97c46af29bd28e8c4d405aa211012102cde27d7b29331ea3fef909a8d91f6f7753e99a3dd129914be50df26eed73fab3feffffff028447bf38000000001976a9146d7badec5426b880df25a3afc50e476c2423b34b88acb26b556a740000001976a914b3020d0ab85710151fa509d5d9a4e783903d681888ac83080a00",
Blocktime: 1554837703,
Txid: "8e480d5c1bf7f11d1cbe396ab7dc14e01ea4e1aff45de7c055924f61304ad434",
LockTime: 657539,
Version: 2,
Vin: []bchain.Vin{
{
ScriptSig: bchain.ScriptSig{
Hex: "47304402204034c3862f221551cffb2aa809f621f989a75cdb549c789a5ceb3a82c0bcc21c022001b4638f5d73fdd406a4dd9bf99be3dfca4a572b8f40f09b8fd495a7756c0db70121027a32ef45aef2f720ccf585f6fb0b8a7653db89cacc3320e5b385146851aba705",
},
Txid: "5068fb55e7a1fe5a12fa28ce313aecfc07c7de0e44aaeb6e72552f3b11142e9e",
Vout: 1,
Sequence: 4294967294,
},
{
ScriptSig: bchain.ScriptSig{
Hex: "47304402204d442645597b13abb85e96e5acd34eff50a4418822fe6a37ed378cdd24574dff02205ae667c56eab63cc45a51063f15b72136fd76e97c46af29bd28e8c4d405aa211012102cde27d7b29331ea3fef909a8d91f6f7753e99a3dd129914be50df26eed73fab3",
},
Txid: "1059a34bed569752ee98289177f04ec3b42a4b3bf2fc76687842c532ae40b2f3",
Vout: 0,
Sequence: 4294967294,
},
},
Vout: []bchain.Vout{
{
ValueSat: *big.NewInt(952059780),
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a9146d7badec5426b880df25a3afc50e476c2423b34b88ac",
Addresses: []string{
"RKG5tpWwjhtqddTgA3QhUh7QmKcuvBnhBX",
},
},
},
{
ValueSat: *big.NewInt(500000189362),
N: 1,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a914b3020d0ab85710151fa509d5d9a4e783903d681888ac",
Addresses: []string{
"RRbhVMbLfuezHPwUMujTmDFAzv64Y9mJqd",
},
},
},
},
}
}
func Test_PackTx(t *testing.T) {
type args struct {
tx bchain.Tx
height uint32
blockTime int64
parser *RavencoinParser
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "ravencoin-1",
args: args{
tx: testTx1,
height: 657540,
blockTime: 1554837703,
parser: NewRavencoinParser(GetChainParams("main"), &btc.Configuration{}),
},
want: testTxPacked1,
wantErr: false,
},
{
name: "ravencoin-2",
args: args{
tx: testTx2,
height: 657540,
blockTime: 1554837703,
parser: NewRavencoinParser(GetChainParams("main"), &btc.Configuration{}),
},
want: testTxPacked2,
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 *RavencoinParser
}
tests := []struct {
name string
args args
want *bchain.Tx
want1 uint32
wantErr bool
}{
{
name: "ravencoin-1",
args: args{
packedTx: testTxPacked1,
parser: NewRavencoinParser(GetChainParams("main"), &btc.Configuration{}),
},
want: &testTx1,
want1: 657540,
wantErr: false,
},
{
name: "ravencoin-2",
args: args{
packedTx: testTxPacked2,
parser: NewRavencoinParser(GetChainParams("main"), &btc.Configuration{}),
},
want: &testTx2,
want1: 657540,
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

@ -0,0 +1,58 @@
package ravencoin
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json"
"github.com/golang/glog"
)
// RavencoinRPC is an interface to JSON-RPC bitcoind service.
type RavencoinRPC struct {
*btc.BitcoinRPC
}
// NewRavencoinRPC returns new RavencoinRPC instance.
func NewRavencoinRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) {
b, err := btc.NewBitcoinRPC(config, pushHandler)
if err != nil {
return nil, err
}
s := &RavencoinRPC{
b.(*btc.BitcoinRPC),
}
s.RPCMarshaler = btc.JSONMarshalerV1{}
s.ChainConfig.SupportsEstimateFee = false
return s, nil
}
// Initialize initializes RavencoinRPC instance.
func (b *RavencoinRPC) 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 = NewRavencoinParser(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

@ -7,12 +7,14 @@ import (
"github.com/martinboehm/btcutil/chaincfg"
)
// magic numbers
const (
MainnetMagic wire.BitcoinNet = 0xdab5bffb
TestnetMagic wire.BitcoinNet = 0x74726576 // "vert" word
RegtestMagic wire.BitcoinNet = 0xdab5bffc
)
// chain parameters
var (
MainNetParams chaincfg.Params
TestNetParams chaincfg.Params

View File

@ -0,0 +1,101 @@
package viacoin
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"blockbook/bchain/coins/utils"
"bytes"
"github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg"
)
const (
MainnetMagic wire.BitcoinNet = 0xcbc6680f
RegtestMagic wire.BitcoinNet = 0x377b972d
)
var (
MainNetParams chaincfg.Params
RegtestParams chaincfg.Params
)
func init() {
MainNetParams = chaincfg.MainNetParams
MainNetParams.Net = MainnetMagic
// Mainnet address encoding magics
MainNetParams.PubKeyHashAddrID = []byte{71} // base58 prefix: v
MainNetParams.ScriptHashAddrID = []byte{33} // base68 prefix: E
MainNetParams.Bech32HRPSegwit = "via"
RegtestParams = chaincfg.RegressionNetParams
RegtestParams.Net = RegtestMagic
// Regtest address encoding magics
RegtestParams.PubKeyHashAddrID = []byte{111} // base58 prefix: m or n
RegtestParams.ScriptHashAddrID = []byte{196} // base58 prefix: 2
RegtestParams.Bech32HRPSegwit = "tvia"
err := chaincfg.Register(&MainNetParams)
if err == nil {
err = chaincfg.Register(&RegtestParams)
}
if err != nil {
panic(err)
}
}
// ViacoinParser handle
type ViacoinParser struct {
*btc.BitcoinParser
}
// NewViacoinParser returns new VertcoinParser instance
func NewViacoinParser(params *chaincfg.Params, c *btc.Configuration) *ViacoinParser {
return &ViacoinParser{BitcoinParser: btc.NewBitcoinParser(params, c)}
}
func GetChainParams(chain string) *chaincfg.Params {
switch chain {
case "regtest":
return &RegtestParams
default:
return &MainNetParams
}
}
// ParseBlock parses raw block to our Block struct
// it has special handling for Auxpow blocks that cannot be parsed by standard btc wire parse
func (p *ViacoinParser) 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, err
}
if (h.Version & utils.VersionAuxpow) != 0 {
if err = utils.SkipAuxpow(r); err != nil {
return nil, err
}
}
err = utils.DecodeTransactions(r, 0, wire.WitnessEncoding, &w)
if err != nil {
return nil, err
}
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
}

View File

@ -0,0 +1,207 @@
// +build unittest
package viacoin
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/hex"
"math/big"
"os"
"reflect"
"testing"
"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: "VhyGT8kJU9x28dHwjf1jEDG8gMY8yhckDR"},
want: "76a91457757edd001d16528c7aa337b314a7bab303ee8088ac",
wantErr: false,
},
{
name: "P2PKH2",
args: args{address: "VdMPvn7vUTSzbYjiMDs1jku9wAh1Ri2Y1A"},
want: "76a91424cc424c1e5e977175d2b20012554d39024bd68f88ac",
wantErr: false,
},
{
name: "P2SH1",
args: args{address: "EUtqKT17p1LdHTDyGL1b2WPJiUFidS6gVq"},
want: "a91480c7c8faece680bab1ae81a5969815a05b7565f087",
wantErr: false,
},
{
name: "P2SH2",
args: args{address: "EMdC3QPzx2MsJG56x2QbSR727dRM73B1rK"},
want: "a91431098c569891a8ff1fa11d1cbd3d46ca5e245c6b87",
wantErr: false,
},
{
name: "witness_v0_keyhash",
args: args{address: "via1q95qlu98cpj23xy6w9tdnfn65n5vkpkey99g6wl"},
want: "00142d01fe14f80c9513134e2adb34cf549d1960db24",
wantErr: false,
},
}
parser := NewViacoinParser(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)
}
})
}
}
var (
testTx1 bchain.Tx
testTxPacked1 = "004eb96d8bb3b6c6140200000001ddc431a8f5c4e74296de8a6bff796ece148b9bd6827a80ecde8671df41a51fc7000000006a47304402204f929a1e1e40bd352bbd5d3c5ae6c29740e5a8b29dd8c53a15d3eab29aecee7c02206a514e5e4561cfb9330d98f4a4fe1385af56d87eb1d3e1a379d7a50276788cfe0121034a9305644fbcb56d4fc0bc15959b917f7753ae8e581acc97f9cfe771ad1e8249feffffff0200ca9a3b000000001976a91456c7359ed52d61c1ca371d7dc136632148169c5e88acd0e8cc10000000001976a914112e29df5df4866e40ef98e0857036b275380fe088ac6ab94e00"
)
func init() {
testTx1 = bchain.Tx{
Hex: "0200000001ddc431a8f5c4e74296de8a6bff796ece148b9bd6827a80ecde8671df41a51fc7000000006a47304402204f929a1e1e40bd352bbd5d3c5ae6c29740e5a8b29dd8c53a15d3eab29aecee7c02206a514e5e4561cfb9330d98f4a4fe1385af56d87eb1d3e1a379d7a50276788cfe0121034a9305644fbcb56d4fc0bc15959b917f7753ae8e581acc97f9cfe771ad1e8249feffffff0200ca9a3b000000001976a91456c7359ed52d61c1ca371d7dc136632148169c5e88acd0e8cc10000000001976a914112e29df5df4866e40ef98e0857036b275380fe088ac6ab94e00",
Blocktime: 1530319242,
Txid: "d0284c75a389a07cc256e0bb913110d8d8059efd04daa8147ecf2fa0b3bdf6ff",
LockTime: 5159274,
Version: 2,
Vin: []bchain.Vin{
{
ScriptSig: bchain.ScriptSig{
Hex: "47304402204f929a1e1e40bd352bbd5d3c5ae6c29740e5a8b29dd8c53a15d3eab29aecee7c02206a514e5e4561cfb9330d98f4a4fe1385af56d87eb1d3e1a379d7a50276788cfe0121034a9305644fbcb56d4fc0bc15959b917f7753ae8e581acc97f9cfe771ad1e8249",
},
Txid: "c71fa541df7186deec807a82d69b8b14ce6e79ff6b8ade9642e7c4f5a831c4dd",
Vout: 0,
Sequence: 4294967294,
},
},
Vout: []bchain.Vout{
{
ValueSat: *big.NewInt(1000000000),
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a91456c7359ed52d61c1ca371d7dc136632148169c5e88ac",
Addresses: []string{
"VhuffXKNA3j9hgp2JYGrj6uHQ6KUU6zNbS",
},
},
},
{
ValueSat: *big.NewInt(281864400),
N: 1,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a914112e29df5df4866e40ef98e0857036b275380fe088ac",
Addresses: []string{
"VbZfhUMCUJHDjqjby6ynYFPZSNVYhfe4cK",
},
},
},
},
}
}
func Test_PackTx(t *testing.T) {
type args struct {
tx bchain.Tx
height uint32
blockTime int64
parser *ViacoinParser
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "viacoin-1",
args: args{
tx: testTx1,
height: 5159277,
blockTime: 1530319242,
parser: NewViacoinParser(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 *ViacoinParser
}
tests := []struct {
name string
args args
want *bchain.Tx
want1 uint32
wantErr bool
}{
{
name: "viacoin-1",
args: args{
packedTx: testTxPacked1,
parser: NewViacoinParser(GetChainParams("main"), &btc.Configuration{}),
},
want: &testTx1,
want1: 5159277,
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

@ -0,0 +1,73 @@
package viacoin
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json"
"github.com/golang/glog"
)
// ViacoinRPC is an interface to JSON-RPC bitcoind service
type ViacoinRPC struct {
*btc.BitcoinRPC
}
// NewViacoinRPC returns new ViacoinRPC instance
func NewViacoinRPC(config json.RawMessage, pushHandler func(notificationType bchain.NotificationType)) (bchain.BlockChain, error) {
b, err := btc.NewBitcoinRPC(config, pushHandler)
if err != nil {
return nil, err
}
s := &ViacoinRPC{
b.(*btc.BitcoinRPC),
}
s.RPCMarshaler = btc.JSONMarshalerV1{}
s.ChainConfig.SupportsEstimateSmartFee = false
return s, nil
}
// Initialize initializes ViacoinRPC instance.
func (b *ViacoinRPC) 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 = NewViacoinParser(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
}
// GetBlock returns block with given hash
func (b *ViacoinRPC) GetBlock(hash string, height uint32) (*bchain.Block, error) {
var err error
if hash == "" {
hash, err = b.GetBlockHash(height)
if err != nil {
return nil, err
}
}
if !b.ParseBlocks {
return b.GetBlockFull(hash)
}
return b.GetBlockWithoutHeader(hash, height)
}

View File

@ -0,0 +1,150 @@
package vipstarcoin
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"blockbook/bchain/coins/utils"
"bytes"
"encoding/json"
"io"
"github.com/martinboehm/btcd/wire"
"github.com/martinboehm/btcutil/chaincfg"
)
const (
MainnetMagic wire.BitcoinNet = 0x012ce7b5
TestnetMagic wire.BitcoinNet = 0x1a2b3c4d
)
var (
MainNetParams chaincfg.Params
TestNetParams chaincfg.Params
)
func init() {
MainNetParams = chaincfg.MainNetParams
MainNetParams.Net = MainnetMagic
MainNetParams.PubKeyHashAddrID = []byte{70}
MainNetParams.ScriptHashAddrID = []byte{50}
MainNetParams.Bech32HRPSegwit = "vips"
TestNetParams = chaincfg.TestNet3Params
TestNetParams.Net = TestnetMagic
TestNetParams.PubKeyHashAddrID = []byte{132}
TestNetParams.ScriptHashAddrID = []byte{110}
TestNetParams.Bech32HRPSegwit = "tvips"
}
// VIPSTARCOINParser handle
type VIPSTARCOINParser struct {
*btc.BitcoinParser
}
// NewVIPSTARCOINParser returns new VIPSTARCOINParser instance
func NewVIPSTARCOINParser(params *chaincfg.Params, c *btc.Configuration) *VIPSTARCOINParser {
return &VIPSTARCOINParser{BitcoinParser: btc.NewBitcoinParser(params, c)}
}
// GetChainParams contains network parameters for the main VIPSTARCOIN network,
// and the test VIPSTARCOIN 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
}
}
func parseBlockHeader(r io.Reader) (*wire.BlockHeader, error) {
h := &wire.BlockHeader{}
err := h.Deserialize(r)
if err != nil {
return nil, err
}
// hash_state_root 32
// hash_utxo_root 32
// hash_prevout_stake 32
// hash_prevout_n 4
buf := make([]byte, 100)
_, err = io.ReadFull(r, buf)
if err != nil {
return nil, err
}
sigLength, err := wire.ReadVarInt(r, 0)
if err != nil {
return nil, err
}
sigBuf := make([]byte, sigLength)
_, err = io.ReadFull(r, sigBuf)
if err != nil {
return nil, err
}
return h, err
}
func (p *VIPSTARCOINParser) ParseBlock(b []byte) (*bchain.Block, error) {
r := bytes.NewReader(b)
w := wire.MsgBlock{}
h, err := parseBlockHeader(r)
if err != nil {
return nil, err
}
err = utils.DecodeTransactions(r, 0, wire.WitnessEncoding, &w)
if err != nil {
return nil, err
}
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
}
// ParseTxFromJson parses JSON message containing transaction and returns Tx struct
func (p *VIPSTARCOINParser) 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
}

View File

@ -0,0 +1,305 @@
// +build unittest
package vipstarcoin
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/hex"
"math/big"
"os"
"reflect"
"testing"
"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: "VLhjpFadC3pkyydKfMTbdg6JFaYojkubik"},
want: "76a9146e2b0a8655786c8c5ea7b9ce478f03e00ecb2f5588ac",
wantErr: false,
},
{
name: "P2PKH2",
args: args{address: "VEUMnC1iPpr64u4HwE9DRjhwnF98xbS6n4"},
want: "76a91429d27c0c33c87830dda711aa878574bcd9c5247188ac",
wantErr: false,
},
{
name: "P2SH1",
args: args{address: "MAyVr99hsthBJin9sosjdyfeAP3ByxXFxz"},
want: "a91421ba6a62ac1d74d2ba921bbc8c9a3ca6e1420a0087",
wantErr: false,
},
{
name: "P2SH2",
args: args{address: "MV71zj5jJ1b9ijt198JAJrCwDHxu3tCn6g"},
want: "a914e898c1f90c736de7c3570c3391bf5e726c8b59aa87",
wantErr: false,
},
{
name: "P2WPKH",
args: args{address: "vips1q0v3tadxj6pm3ym9j06v9rfyw0jeh5f8s87sgtw"},
want: "00147b22beb4d2d077126cb27e9851a48e7cb37a24f0",
wantErr: false,
},
{
name: "P2WSH",
args: args{address: "vips1qumpyvyxz25kfjjrvyxn3zlyc2wfc0m3l3gm5pg99c4mxylemfqhsnj023l"},
want: "0020e6c24610c2552c99486c21a7117c98539387ee3f8a3740a0a5c576627f3b482f",
wantErr: false,
},
}
parser := NewVIPSTARCOINParser(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: "76a9146e2b0a8655786c8c5ea7b9ce478f03e00ecb2f5588ac"},
want: []string{"VLhjpFadC3pkyydKfMTbdg6JFaYojkubik"},
want2: true,
wantErr: false,
},
{
name: "P2SH",
args: args{script: "a91421ba6a62ac1d74d2ba921bbc8c9a3ca6e1420a0087"},
want: []string{"MAyVr99hsthBJin9sosjdyfeAP3ByxXFxz"},
want2: true,
wantErr: false,
},
{
name: "P2WPKH",
args: args{script: "00147b22beb4d2d077126cb27e9851a48e7cb37a24f0"},
want: []string{"vips1q0v3tadxj6pm3ym9j06v9rfyw0jeh5f8s87sgtw"},
want2: true,
wantErr: false,
},
{
name: "P2WSH",
args: args{script: "0020e6c24610c2552c99486c21a7117c98539387ee3f8a3740a0a5c576627f3b482f"},
want: []string{"vips1qumpyvyxz25kfjjrvyxn3zlyc2wfc0m3l3gm5pg99c4mxylemfqhsnj023l"},
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 := NewVIPSTARCOINParser(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 = "000831508bcbe1ae6002000000000103a733281698ae7d80da17d5a201ab8b6077e83f780cb417c8d12eb9da8343735900000000171600141ee6fc3b04fdd080c03eeb513498b38c2621f4fcfdffffff3976295b67ed10a28016171da4f9a5833fe4ca1f83ffb56f95856aa445ebf46a000000006a473044022012315ff56ad254ed8b099623bd84a106819770970c41c2a138b6cfe4bb332aa602206f5679570c968b77a3f7e8dd14663d0af9aba5b354f113d3a4321b3aeafc03080121030d52fc12b11b9288490ed78b8b07ff025a33fe1577f402ddf30c0f73769363a3fdffffffdd5a7a9852c8ebe1c417a26c54bb58339eb6e1ea1416b9b11314f05f93e69ea1010000006a4730440220149942b3971fb655bd5d76630bb1c8993d3083e4ad631c972156927173e6afb802204590399049f77530251c86ab569f45d2248aa222372b9d1d95963bd67213a5f80121030d52fc12b11b9288490ed78b8b07ff025a33fe1577f402ddf30c0f73769363a3fdffffff0200ca9a3b000000001976a91493c052c292e366221f9ee709c36a9ea441eb984488acb0b20e000000000017a9149695605a5a5e9349e1c99e01b175c5c3baf39bc4870247304402207a2a1cc2f314c8c659a4bcbce099c5adfb217c03fa2b0cfc95bef48c1507901a0220324ab06cf2fe4c9e446a3a12c00fa611a479b5734f62c20b66e919e173a2c699012102bbe6f37b4c44303b2186de6784d02cc5b86a65ca1203821b06a98e243b44c76400004e310800"
)
func init() {
testTx1 = bchain.Tx{
Hex: "02000000000103a733281698ae7d80da17d5a201ab8b6077e83f780cb417c8d12eb9da8343735900000000171600141ee6fc3b04fdd080c03eeb513498b38c2621f4fcfdffffff3976295b67ed10a28016171da4f9a5833fe4ca1f83ffb56f95856aa445ebf46a000000006a473044022012315ff56ad254ed8b099623bd84a106819770970c41c2a138b6cfe4bb332aa602206f5679570c968b77a3f7e8dd14663d0af9aba5b354f113d3a4321b3aeafc03080121030d52fc12b11b9288490ed78b8b07ff025a33fe1577f402ddf30c0f73769363a3fdffffffdd5a7a9852c8ebe1c417a26c54bb58339eb6e1ea1416b9b11314f05f93e69ea1010000006a4730440220149942b3971fb655bd5d76630bb1c8993d3083e4ad631c972156927173e6afb802204590399049f77530251c86ab569f45d2248aa222372b9d1d95963bd67213a5f80121030d52fc12b11b9288490ed78b8b07ff025a33fe1577f402ddf30c0f73769363a3fdffffff0200ca9a3b000000001976a91493c052c292e366221f9ee709c36a9ea441eb984488acb0b20e000000000017a9149695605a5a5e9349e1c99e01b175c5c3baf39bc4870247304402207a2a1cc2f314c8c659a4bcbce099c5adfb217c03fa2b0cfc95bef48c1507901a0220324ab06cf2fe4c9e446a3a12c00fa611a479b5734f62c20b66e919e173a2c699012102bbe6f37b4c44303b2186de6784d02cc5b86a65ca1203821b06a98e243b44c76400004e310800",
Blocktime: 1555835824,
Txid: "93aae65e87ec46cd13b3032e1588c7db75e2b712514696efca5f2bfd80c16632",
LockTime: 536910,
Version: 2,
Vin: []bchain.Vin{
{
ScriptSig: bchain.ScriptSig{
Hex: "1600141ee6fc3b04fdd080c03eeb513498b38c2621f4fc",
},
Txid: "59734383dab92ed1c817b40c783fe877608bab01a2d517da807dae98162833a7",
Vout: 0,
Sequence: 4294967293,
},
{
ScriptSig: bchain.ScriptSig{
Hex: "473044022012315ff56ad254ed8b099623bd84a106819770970c41c2a138b6cfe4bb332aa602206f5679570c968b77a3f7e8dd14663d0af9aba5b354f113d3a4321b3aeafc03080121030d52fc12b11b9288490ed78b8b07ff025a33fe1577f402ddf30c0f73769363a3",
},
Txid: "6af4eb45a46a85956fb5ff831fcae43f83a5f9a41d171680a210ed675b297639",
Vout: 0,
Sequence: 4294967293,
},
{
ScriptSig: bchain.ScriptSig{
Hex: "4730440220149942b3971fb655bd5d76630bb1c8993d3083e4ad631c972156927173e6afb802204590399049f77530251c86ab569f45d2248aa222372b9d1d95963bd67213a5f80121030d52fc12b11b9288490ed78b8b07ff025a33fe1577f402ddf30c0f73769363a3",
},
Txid: "a19ee6935ff01413b1b91614eae1b69e3358bb546ca217c4e1ebc852987a5add",
Vout: 1,
Sequence: 4294967293,
},
},
Vout: []bchain.Vout{
{
ValueSat: *big.NewInt(1000000000),
N: 0,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "76a91493c052c292e366221f9ee709c36a9ea441eb984488ac",
Addresses: []string{
"VQ8Tef3y1hKQnAq5baiyxiScsVnEcPqjdy",
},
},
},
{
ValueSat: *big.NewInt(963248),
N: 1,
ScriptPubKey: bchain.ScriptPubKey{
Hex: "a9149695605a5a5e9349e1c99e01b175c5c3baf39bc487",
Addresses: []string{
"MMdNY4HfcntfZ1EB6zn5Zz7i25yYfocaFT",
},
},
},
},
}
}
func Test_PackTx(t *testing.T) {
type args struct {
tx bchain.Tx
height uint32
blockTime int64
parser *VIPSTARCOINParser
}
tests := []struct {
name string
args args
want string
wantErr bool
}{
{
name: "VIPSTARCOIN-1",
args: args{
tx: testTx1,
height: 536912,
blockTime: 1555835824,
parser: NewVIPSTARCOINParser(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 *VIPSTARCOINParser
}
tests := []struct {
name string
args args
want *bchain.Tx
want1 uint32
wantErr bool
}{
{
name: "VIPSTARCOIN-1",
args: args{
packedTx: testTxPacked1,
parser: NewVIPSTARCOINParser(GetChainParams("main"), &btc.Configuration{}),
},
want: &testTx1,
want1: 536912,
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

@ -0,0 +1,64 @@
package vipstarcoin
import (
"blockbook/bchain"
"blockbook/bchain/coins/btc"
"encoding/json"
"github.com/golang/glog"
)
// VIPSTARCOINRPC is an interface to JSON-RPC bitcoind service.
type VIPSTARCOINRPC struct {
*btc.BitcoinRPC
}
// NewVIPSTARCOINRPC returns new VIPSTARCOINRPC instance.
func NewVIPSTARCOINRPC(config json.RawMessage, pushHandler func(bchain.NotificationType)) (bchain.BlockChain, error) {
b, err := btc.NewBitcoinRPC(config, pushHandler)
if err != nil {
return nil, err
}
s := &VIPSTARCOINRPC{
b.(*btc.BitcoinRPC),
}
s.RPCMarshaler = btc.JSONMarshalerV1{}
s.ChainConfig.SupportsEstimateSmartFee = true
return s, nil
}
// Initialize initializes VIPSTARCOINRPC instance.
func (b *VIPSTARCOINRPC) 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 = NewVIPSTARCOINParser(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
}
// GetTransactionForMempool returns a transaction by the transaction ID
// It could be optimized for mempool, i.e. without block time and confirmations
func (b *VIPSTARCOINRPC) GetTransactionForMempool(txid string) (*bchain.Tx, error) {
return b.GetTransaction(txid)
}

View File

@ -264,6 +264,7 @@ type BlockChainParser interface {
GetAddrDescFromAddress(address string) (AddressDescriptor, error)
GetAddressesFromAddrDesc(addrDesc AddressDescriptor) ([]string, bool, error)
GetScriptFromAddrDesc(addrDesc AddressDescriptor) ([]byte, error)
IsAddrDescIndexable(addrDesc AddressDescriptor) bool
// transactions
PackedTxidLen() int
PackTxid(txid string) ([]byte, error)

View File

@ -33,6 +33,10 @@ const debounceResyncMempoolMs = 1009
// store internal state about once every minute
const storeInternalStatePeriodMs = 59699
// exit codes from the main function
const exitCodeOK = 0
const exitCodeFatal = 255
var (
blockchain = flag.String("blockchaincfg", "", "path to blockchain RPC service configuration json file")
@ -64,8 +68,9 @@ var (
noTxCache = flag.Bool("notxcache", false, "disable tx cache")
computeColumnStats = flag.Bool("computedbstats", false, "compute column stats and exit")
dbStatsPeriodHours = flag.Int("dbstatsperiod", 24, "period of db stats collection in hours, 0 disables stats collection")
computeColumnStats = flag.Bool("computedbstats", false, "compute column stats and exit")
computeFeeStatsFlag = flag.Bool("computefeestats", false, "compute fee stats for blocks in blockheight-blockuntil range and exit")
dbStatsPeriodHours = flag.Int("dbstatsperiod", 24, "period of db stats collection in hours, 0 disables stats collection")
// resync index at least each resyncIndexPeriodMs (could be more often if invoked by message from ZeroMQ)
resyncIndexPeriodMs = flag.Int("resyncindexperiod", 935093, "resync index period in milliseconds")
@ -100,6 +105,11 @@ func init() {
}
func main() {
os.Exit(mainWithExitCode())
}
// allow deferred functions to run even in case of fatal error
func mainWithExitCode() int {
flag.Parse()
defer glog.Flush()
@ -120,20 +130,20 @@ func main() {
if *repair {
if err := db.RepairRocksDB(*dbPath); err != nil {
glog.Errorf("RepairRocksDB %s: %v", *dbPath, err)
return
return exitCodeFatal
}
return
return exitCodeOK
}
if *blockchain == "" {
glog.Error("Missing blockchaincfg configuration parameter")
return
return exitCodeFatal
}
coin, coinShortcut, coinLabel, err := coins.GetCoinNameFromConfig(*blockchain)
if err != nil {
glog.Error("config: ", err)
return
return exitCodeFatal
}
// gspt.SetProcTitle("blockbook-" + normalizeName(coin))
@ -141,49 +151,60 @@ func main() {
metrics, err = common.GetMetrics(coin)
if err != nil {
glog.Error("metrics: ", err)
return
return exitCodeFatal
}
if chain, mempool, err = getBlockChainWithRetry(coin, *blockchain, pushSynchronizationHandler, metrics, 60); err != nil {
if chain, mempool, err = getBlockChainWithRetry(coin, *blockchain, pushSynchronizationHandler, metrics, 120); err != nil {
glog.Error("rpc: ", err)
return
return exitCodeFatal
}
index, err = db.NewRocksDB(*dbPath, *dbCache, *dbMaxOpenFiles, chain.GetChainParser(), metrics)
if err != nil {
glog.Error("rocksDB: ", err)
return
return exitCodeFatal
}
defer index.Close()
internalState, err = newInternalState(coin, coinShortcut, coinLabel, index)
if err != nil {
glog.Error("internalState: ", err)
return
return exitCodeFatal
}
index.SetInternalState(internalState)
if internalState.DbState != common.DbStateClosed {
if internalState.DbState == common.DbStateInconsistent {
glog.Error("internalState: database is in inconsistent state and cannot be used")
return
return exitCodeFatal
}
glog.Warning("internalState: database was left in open state, possibly previous ungraceful shutdown")
}
if *computeFeeStatsFlag {
internalState.DbState = common.DbStateOpen
err = computeFeeStats(chanOsSignal, *blockFrom, *blockUntil, index, chain, txCache, internalState, metrics)
if err != nil && err != db.ErrOperationInterrupted {
glog.Error("computeFeeStats: ", err)
return exitCodeFatal
}
return exitCodeOK
}
if *computeColumnStats {
internalState.DbState = common.DbStateOpen
err = index.ComputeInternalStateColumnStats(chanOsSignal)
if err != nil {
glog.Error("internalState: ", err)
return exitCodeFatal
}
glog.Info("DB size on disk: ", index.DatabaseSizeOnDisk(), ", DB size as computed: ", internalState.DBSizeTotal())
return
return exitCodeOK
}
syncWorker, err = db.NewSyncWorker(index, chain, *syncWorkers, *syncChunk, *blockFrom, *dryRun, chanOsSignal, metrics, internalState)
if err != nil {
glog.Errorf("NewSyncWorker %v", err)
return
return exitCodeFatal
}
// set the DbState to open at this moment, after all important workers are initialized
@ -191,17 +212,20 @@ func main() {
err = index.StoreInternalState(internalState)
if err != nil {
glog.Error("internalState: ", err)
return
return exitCodeFatal
}
if *rollbackHeight >= 0 {
performRollback()
return
err = performRollback()
if err != nil {
return exitCodeFatal
}
return exitCodeOK
}
if txCache, err = db.NewTxCache(index, chain, metrics, internalState, !*noTxCache); err != nil {
glog.Error("txCache ", err)
return
return exitCodeFatal
}
// report BlockbookAppInfo metric, only log possible error
@ -214,7 +238,7 @@ func main() {
internalServer, err = startInternalServer()
if err != nil {
glog.Error("internal server: ", err)
return
return exitCodeFatal
}
}
@ -223,7 +247,7 @@ func main() {
publicServer, err = startPublicServer()
if err != nil {
glog.Error("public server: ", err)
return
return exitCodeFatal
}
}
@ -231,8 +255,11 @@ func main() {
internalState.SyncMode = true
internalState.InitialSync = true
if err := syncWorker.ResyncIndex(nil, true); err != nil {
glog.Error("resyncIndex ", err)
return
if err != db.ErrOperationInterrupted {
glog.Error("resyncIndex ", err)
return exitCodeFatal
}
return exitCodeOK
}
// initialize mempool after the initial sync is complete
var addrDescForOutpoint bchain.AddrDescForOutpointFunc
@ -242,12 +269,12 @@ func main() {
err = chain.InitializeMempool(addrDescForOutpoint, onNewTxAddr)
if err != nil {
glog.Error("initializeMempool ", err)
return
return exitCodeFatal
}
var mempoolCount int
if mempoolCount, err = mempool.Resync(); err != nil {
glog.Error("resyncMempool ", err)
return
return exitCodeFatal
}
internalState.FinishedMempoolSync(mempoolCount)
go syncIndexLoop()
@ -272,8 +299,11 @@ func main() {
if !*synchronize {
if err = syncWorker.ConnectBlocksParallel(height, until); err != nil {
glog.Error("connectBlocksParallel ", err)
return
if err != db.ErrOperationInterrupted {
glog.Error("connectBlocksParallel ", err)
return exitCodeFatal
}
return exitCodeOK
}
}
}
@ -290,6 +320,7 @@ func main() {
<-chanSyncMempoolDone
<-chanStoreInternalStateDone
}
return exitCodeOK
}
func getBlockChainWithRetry(coin string, configfile string, pushHandler func(bchain.NotificationType), metrics *common.Metrics, seconds int) (bchain.BlockChain, bchain.Mempool, error) {
@ -355,11 +386,11 @@ func startPublicServer() (*server.PublicServer, error) {
return publicServer, err
}
func performRollback() {
func performRollback() error {
bestHeight, bestHash, err := index.GetBestBlock()
if err != nil {
glog.Error("rollbackHeight: ", err)
return
return err
}
if uint32(*rollbackHeight) > bestHeight {
glog.Infof("nothing to rollback, rollbackHeight %d, bestHeight: %d", *rollbackHeight, bestHeight)
@ -369,16 +400,17 @@ func performRollback() {
hash, err := index.GetBlockHash(height)
if err != nil {
glog.Error("rollbackHeight: ", err)
return
return err
}
hashes = append(hashes, hash)
}
err = syncWorker.DisconnectBlocks(uint32(*rollbackHeight), bestHeight, hashes)
if err != nil {
glog.Error("rollbackHeight: ", err)
return
return err
}
}
return nil
}
func blockbookAppInfoMetric(db *db.RocksDB, chain bchain.BlockChain, txCache *db.TxCache, is *common.InternalState, metrics *common.Metrics) error {
@ -464,7 +496,12 @@ func syncIndexLoop() {
// resync index about every 15 minutes if there are no chanSyncIndex requests, with debounce 1 second
tickAndDebounce(time.Duration(*resyncIndexPeriodMs)*time.Millisecond, debounceResyncIndexMs*time.Millisecond, chanSyncIndex, func() {
if err := syncWorker.ResyncIndex(onNewBlockHash, false); err != nil {
glog.Error("syncIndexLoop ", errors.ErrorStack(err))
glog.Error("syncIndexLoop ", errors.ErrorStack(err), ", will retry...")
// retry once in case of random network error, after a slight delay
time.Sleep(time.Millisecond * 2500)
if err := syncWorker.ResyncIndex(onNewBlockHash, false); err != nil {
glog.Error("syncIndexLoop ", errors.ErrorStack(err))
}
}
})
glog.Info("syncIndexLoop stopped")
@ -592,3 +629,16 @@ func normalizeName(s string) string {
s = strings.Replace(s, " ", "-", -1)
return s
}
// computeFeeStats computes fee distribution in defined blocks
func computeFeeStats(stopCompute chan os.Signal, blockFrom, blockTo int, db *db.RocksDB, chain bchain.BlockChain, txCache *db.TxCache, is *common.InternalState, metrics *common.Metrics) error {
start := time.Now()
glog.Info("computeFeeStats start")
api, err := api.NewWorker(db, chain, mempool, txCache, is)
if err != nil {
return err
}
err = api.ComputeFeeStats(blockFrom, blockTo, stopCompute)
glog.Info("computeFeeStats finished in ", time.Since(start))
return err
}

View File

@ -9,8 +9,8 @@ RUN apt-get update && \
liblz4-dev graphviz && \
apt-get clean
ENV GOLANG_VERSION=go1.11.4.linux-amd64
ENV ROCKSDB_VERSION=v5.17.2
ENV GOLANG_VERSION=go1.12.4.linux-amd64
ENV ROCKSDB_VERSION=v5.18.3
ENV GOPATH=/go
ENV PATH=$PATH:$GOPATH/bin
ENV CGO_CFLAGS="-I/opt/rocksdb/include"

View File

@ -1,55 +0,0 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
Version: GnuPG v1
mQENBFxvq4EBCACwQu2Q3N5GbaykDr/bavLcjZK+IieXuwsVUX1NTqpyMFmnaKGr
WBq9tVnTFWzEj65eShv2YlhMCzok2oJaJW7lWogWZW0kXQoLRGMiHpGifAvQaHHp
Jz/YEHgSdnHRyHuqvq6iQl5HCgIhse0HCkdVDIARZ2RpWAa2MH1kdBvsdhxEW+rW
pg5/cGdyO6aF0n9d31ed8BmYUi6jLnlrcYzp9oWgd5f3PbqVkOL3yim1C+DqEKjC
FnM3wYmm55qH9sVHjdtWPJ+JBYkdY+3vXjhlJyuvRo4sZHX4g8+tUN1c+yr4PGP5
RzttWQYW2jneS8xuAn6NiL35/PRBYaa4OlgdABEBAAG0Jll1dG8gQXNoaWRhIDx5
dXRvX3RldHVvdGFAeWFob28uY28uanA+iQE4BBMBAgAiBQJcb6uBAhsDBgsJCAcD
AgYVCAIJCgsEFgIDAQIeAQIXgAAKCRCTE4QDUcV0n+PsB/9ZCxPNc7wGTr6decZw
A6tM19JyuE+GNnrm6LohO2gFU9sdazh4vQBLunqEAB2xRsGByNyfZRHxMzRisSd5
4nOcXj5L7Ox7AZQ09JgkQia+Ev06LnfZlqiKswlmeSSjlD5pw7zvUlTIFbwxFkXF
wwpHbwWhms4W6qSmNcLqVyaYlc4yIyBkhE4co4nuUFZCb9taUqsfgyVzFRMMzENY
EZBw/SS34UbSH8I1P8JUqTHi7nywsA9NvLCEivjDxgjJvjoaFr0IHoeSJMKrWYak
m7IIS/9ycFDgpJPcmGNwQZA/QVtddWxVQ+S4n1PEb2abW1hbW0nHLnERJhnTmIzz
ksTIuQENBFxvq4EBCADG7m1MHPaUz0enyr0Ev7PUYJYdKq/ocBP7YLIL/v+npzHK
qWG4fi2vKocxzhQw71tk68KiubyoF0DpG1CqHKY9stel6eYz7n6jfJn0wHuSF5qy
FuwexWWcyMkXCYwbGDNIAPW/VZprSMfXRV8Qp+88FPBuSMN+xEQICViIyLxIjH2r
YkMmwU8aEBXVFruZCFMnWXhTYxc57LOq3iZe+4/0IWjLK0leEla44AE64xaowCIl
3I5ovEtV8ZH4cxZxncnQVKt4QBoh/4q4JEdWq6a9hddz3B8rWd68MUaS1jnK/feq
PuKRF1df6P6TD3L25VLdO2gz5wzvqDFXfac+0m5zABEBAAGJAR8EGAECAAkFAlxv
q4ECGwwACgkQkxOEA1HFdJ93Vwf7B6NdEj+dw9xSMIX3/MvUBITCeFAoJf/G4b6F
kl8WFKNgr1qBYntWy5aBPiKpiQj11RvlNSzB517eSVf0htfqaQKV5B9xn5cf8xZV
sadWQWKqCE/BS7Q3/7YngnOLr2GJHW7WOczJOmU9CfxZhC7HRKOP6YonIuiVHsYt
AFOmbw24ZOxq3YGpndj8+f4E3C+LlM2Ffvs12HDFiLiP4JfT0GXqIhoqnUTpBj5D
8ZmH7JfpMBmwNoTREud3JFP21ZxjMtMdaGdEfg5Ybrj47omI64pvT3P7Tl2XRZ9v
JHjObnDob2wXmUnFEYoqrd5JOzdrMTegG+JQUuvdW3WpIrlnuZkBDQRcb62kAQgA
0J16/+sX2QImAiktkh8CNiLepa9LcSQcdYEihG65bd0fCvgaDE8uRKVe61n9gJmG
hQWWfNgHkpYjbuOBvUpXqGbT8n1hTZ+3h2UsJQf7fOHYQcRMOCDcvjM74cRQV8jo
IczM6Vx03KB1cdx3MjxlL2fXevmhkvMWaGt2sWUsHoidRdv2VqtrtC7BsVHErc+S
GacE2iTki36ZlKy/Jya7T8rmJXGVmRZTvhHVqxk4hhQ6ZM1VbmS9tJirOg5lVcTg
nPf55EGsoyfOi2ChICjpshK7xcbwPE8CU8VyVjUkdIUL5RPYBhZ+Jx5Lcfpe0hpo
IN8OTFwYACz0+dmaz87J5wARAQABtCZ5dXRvdGV0dW90YSA8eXV0b190ZXR1b3Rh
QHlhaG9vLmNvLmpwPokBOAQTAQIAIgUCXG+tpAIbAwYLCQgHAwIGFQgCCQoLBBYC
AwECHgECF4AACgkQGHTFO+CYR2n/HwgAn5c2bkwS4e97HpVcRUCfu3HUytGNgoC6
AfbbSk6D1rlwwQyC7yaBaPAfnNvyRbr+wUp9ZSwR5M6CqDQpdWum1C4jWxsQRQsa
NkYcSOsphLT7rvllp0pHv5ZP2F66hIqk60vJiaID2GOYCQW0sefAa6qRKhySSVav
PrwjhvLib6UDAr62jLs0z1adItx3C53JiLGjekicGlEDKykssJ0GXZ2Q6ezqPMkR
Emgh0mUREWN+PFbx1prVfmPzDGkVXQRsKQjtBbI0bKV5BEmPCveJT0Vk/QzvHuEH
3ne/7UarD4eWujXKnDDg6KCAL96rNnn6hzXr4MvHfIvNt/6M2NSXNLkBDQRcb62k
AQgAzz9FlDykb7ZGGu1IBMdWQfxZ2B8U4dCf1VIjR3k+uW9JBrYVUv3nIochi8Q5
XjPZ3vEAoymIDGU2fZj8EXZnIZX6F82QLWpRfWtgLjjx8VUIXMcN50TERDDNc5ID
sBpzv+YwOg+0ADGi4P/e+ej6XLWcO3NcNgASns/J6RHug2NhXhpcM0NWSxUeioKP
cZR7gvi7c1dE1avRq6y3+aDF6iVLN6RoTz/0I4519DhbMhtsCX75DC60z9cD1Sz1
TmEd6XnjRctC6xw6Hg6vME501xMFcCXSJ52wapX+7ICfx8uA9vBKvq1mwYuBJOTp
QkaYRVc0iIVFSFxbta+ZGXGCDQARAQABiQEfBBgBAgAJBQJcb62kAhsMAAoJEBh0
xTvgmEdpogAH/0sfGVu+QnXNnpEytNyEUcLKdR0H2972QVYfP0a2yTO6FCWse0gI
aBvsFI/L3oYaJklgQ2X5YgpLisH1T5WDzaqck2lKo9Ql8Xt/VeKQDyShsmKM2hMZ
8GUI9mtHzuI1piau9CvcaKIz8crtvAC5foTzZwp/9W9VRO+oD5HfqXU8c4LrUvHt
oDPPQZTxpaTOBiEO7cyAgcmYwHzPWdPPZ3ItOk63SagQNvZLgr/RnnpIBxrninen
MfIYZe1jDaxRBGK/5S+2/1VwuAjY/wwVPrg7Lm/ehOhMdGET8+AqTubzTOrfjOMn
XiqzrjeWScdNunn68L6hKIi+5h+FDfR+Bg8=
=U3MK
-----END PGP PUBLIC KEY BLOCK-----

View File

@ -22,10 +22,10 @@
"package_name": "backend-bcash",
"package_revision": "satoshilabs-1",
"system_user": "bcash",
"version": "0.19.3",
"binary_url": "https://download.bitcoinabc.org/0.19.3/linux/bitcoin-abc-0.19.3-x86_64-linux-gnu.tar.gz",
"version": "0.19.6",
"binary_url": "https://download.bitcoinabc.org/0.19.6/linux/bitcoin-abc-0.19.6-x86_64-linux-gnu.tar.gz",
"verification_type": "sha256",
"verification_source": "4d784ea2e603e2dbf8d74f70263127a1fc1489a30d2ead60c0fc521d9e444076",
"verification_source": "b84aad1c0061fcafdef1b89cb75e2fc79be8f0a872fad8b1c40eebe59652a2d2",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [
"bin/bitcoin-qt"
@ -49,7 +49,7 @@
"additional_params": "",
"block_chain": {
"parse": true,
"subversion": "/Bitcoin ABC:0.19.1/",
"subversion": "/Bitcoin ABC:0.19.6/",
"address_format": "cashaddr",
"mempool_workers": 8,
"mempool_sub_workers": 2,
@ -60,7 +60,7 @@
}
},
"meta": {
"package_maintainer": "Petr Kracik",
"package_maintainer_email": "petr.kracik@satoshilabs.com"
"package_maintainer": "IT",
"package_maintainer_email": "it@satoshilabs.com"
}
}
}

View File

@ -22,10 +22,10 @@
"package_name": "backend-bcash-testnet",
"package_revision": "satoshilabs-1",
"system_user": "bcash",
"version": "0.19.3",
"binary_url": "https://download.bitcoinabc.org/0.19.3/linux/bitcoin-abc-0.19.3-x86_64-linux-gnu.tar.gz",
"version": "0.19.6",
"binary_url": "https://download.bitcoinabc.org/0.19.6/linux/bitcoin-abc-0.19.6-x86_64-linux-gnu.tar.gz",
"verification_type": "sha256",
"verification_source": "4d784ea2e603e2dbf8d74f70263127a1fc1489a30d2ead60c0fc521d9e444076",
"verification_source": "b84aad1c0061fcafdef1b89cb75e2fc79be8f0a872fad8b1c40eebe59652a2d2",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [
"bin/bitcoin-qt"
@ -49,7 +49,7 @@
"additional_params": "",
"block_chain": {
"parse": true,
"subversion": "/Bitcoin ABC:0.17.1/",
"subversion": "/Bitcoin ABC:0.19.6/",
"address_format": "cashaddr",
"mempool_workers": 8,
"mempool_sub_workers": 2,
@ -60,7 +60,7 @@
}
},
"meta": {
"package_maintainer": "Petr Kracik",
"package_maintainer_email": "petr.kracik@satoshilabs.com"
"package_maintainer": "IT",
"package_maintainer_email": "it@satoshilabs.com"
}
}
}

View File

@ -22,10 +22,10 @@
"package_name": "backend-bitcoin",
"package_revision": "satoshilabs-1",
"system_user": "bitcoin",
"version": "0.17.1",
"binary_url": "https://bitcoin.org/bin/bitcoin-core-0.17.1/bitcoin-0.17.1-x86_64-linux-gnu.tar.gz",
"version": "0.18.0",
"binary_url": "https://bitcoin.org/bin/bitcoin-core-0.18.0/bitcoin-0.18.0-x86_64-linux-gnu.tar.gz",
"verification_type": "gpg-sha256",
"verification_source": "https://bitcoin.org/bin/bitcoin-core-0.17.1/SHA256SUMS.asc",
"verification_source": "https://bitcoin.org/bin/bitcoin-core-0.18.0/SHA256SUMS.asc",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [
"bin/bitcoin-qt"
@ -58,11 +58,14 @@
"xpub_magic": 76067358,
"xpub_magic_segwit_p2sh": 77429938,
"xpub_magic_segwit_native": 78792518,
"additional_params": {}
"additional_params": {
"alternativeEstimateFee": "whatthefee-disabled",
"alternativeEstimateFeeParams": "{\"url\": \"https://whatthefee.io/data.json\", \"periodSeconds\": 60}"
}
}
},
"meta": {
"package_maintainer": "Jakub Matys",
"package_maintainer_email": "jakub.matys@satoshilabs.com"
"package_maintainer": "Martin Bohm",
"package_maintainer_email": "martin.bohm@satoshilabs.com"
}
}
}

View File

@ -22,10 +22,10 @@
"package_name": "backend-bitcoin-testnet",
"package_revision": "satoshilabs-1",
"system_user": "bitcoin",
"version": "0.17.1",
"binary_url": "https://bitcoin.org/bin/bitcoin-core-0.17.1/bitcoin-0.17.1-x86_64-linux-gnu.tar.gz",
"version": "0.18.0",
"binary_url": "https://bitcoin.org/bin/bitcoin-core-0.18.0/bitcoin-0.18.0-x86_64-linux-gnu.tar.gz",
"verification_type": "gpg-sha256",
"verification_source": "https://bitcoin.org/bin/bitcoin-core-0.17.1/SHA256SUMS.asc",
"verification_source": "https://bitcoin.org/bin/bitcoin-core-0.18.0/SHA256SUMS.asc",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [
"bin/bitcoin-qt"
@ -63,7 +63,7 @@
}
},
"meta": {
"package_maintainer": "Jakub Matys",
"package_maintainer_email": "jakub.matys@satoshilabs.com"
"package_maintainer": "Martin Bohm",
"package_maintainer_email": "martin.bohm@satoshilabs.com"
}
}

View File

@ -65,4 +65,4 @@
"package_maintainer": "Petr Kracik",
"package_maintainer_email": "petr.kracik@satoshilabs.com"
}
}
}

View File

@ -65,4 +65,4 @@
"package_maintainer": "Petr Kracik",
"package_maintainer_email": "petr.kracik@satoshilabs.com"
}
}
}

View File

@ -22,10 +22,10 @@
"package_name": "backend-digibyte",
"package_revision": "satoshilabs-1",
"system_user": "digibyte",
"version": "6.16.5.1",
"binary_url": "https://github.com/digibyte/digibyte/releases/download/v6.16.5.1/digibyte-6.16.5-x86_64-linux-gnu.tar.gz",
"version": "7.17.2",
"binary_url": "https://github.com/digibyte/digibyte/releases/download/v7.17.2/digibyte-7.17.2-x86_64-linux-gnu.tar.gz",
"verification_type": "sha256",
"verification_source": "dd6bed0228087fbb51f08be55cbc08a0e3251acfe1be3249b634447837ecd857",
"verification_source": "caa8ecc42cbebbd2c43e742c7ecc2dd21d76a9e2db23676af428b67b131f6413",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [
"bin/digibyte-qt"
@ -63,7 +63,7 @@
}
},
"meta": {
"package_maintainer": "Martin Boehm",
"package_maintainer": "Martin Bohm",
"package_maintainer_email": "martin.bohm@satoshilabs.com"
}
}

View File

@ -21,10 +21,10 @@
"package_name": "backend-ethereum",
"package_revision": "satoshilabs-1",
"system_user": "ethereum",
"version": "1.8.25-14ae1246",
"binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.8.25-14ae1246.tar.gz",
"version": "1.8.27-4bcc0a37",
"binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.8.27-4bcc0a37.tar.gz",
"verification_type": "gpg",
"verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.8.25-14ae1246.tar.gz.asc",
"verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.8.27-4bcc0a37.tar.gz.asc",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [],
"exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --ipcdisable --syncmode full --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 38336 --ws --wsaddr 0.0.0.0 --wsport {{.Ports.BackendRPC}} --wsorigins \"*\" --rpc --rpcport 8136 -rpcaddr 0.0.0.0 --rpccorsdomain \"*\" --rpcvhosts \"*\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'",
@ -59,4 +59,4 @@
"package_maintainer": "Petr Kracik",
"package_maintainer_email": "petr.kracik@satoshilabs.com"
}
}
}

View File

@ -20,10 +20,10 @@
"package_name": "backend-ethereum-testnet-ropsten",
"package_revision": "satoshilabs-1",
"system_user": "ethereum",
"version": "1.8.25-14ae1246",
"binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.8.25-14ae1246.tar.gz",
"version": "1.8.27-4bcc0a37",
"binary_url": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.8.27-4bcc0a37.tar.gz",
"verification_type": "gpg",
"verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.8.25-14ae1246.tar.gz.asc",
"verification_source": "https://gethstore.blob.core.windows.net/builds/geth-linux-amd64-1.8.27-4bcc0a37.tar.gz.asc",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [],
"exec_command_template": "/bin/sh -c '{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/geth --testnet --syncmode full --ipcdisable --cache 1024 --nat none --datadir {{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend --port 48336 --ws --wsaddr 0.0.0.0 --wsport {{.Ports.BackendRPC}} --wsorigins \"*\" 2>>{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/{{.Coin.Alias}}.log'",
@ -58,4 +58,4 @@
"package_maintainer": "Petr Kracik",
"package_maintainer_email": "petr.kracik@satoshilabs.com"
}
}
}

View File

@ -23,7 +23,7 @@
"package_revision": "satoshilabs-1",
"system_user": "fujicoin",
"version": "0.16.3",
"binary_url": "https://www.fujicoin.org/fujicoin/3.0/fujicoin-3.0-v0.16.3.tar.gz",
"binary_url": "https://www.fujicoin.org/fujicoin/3.0/fujicoin-blockbook.tar.gz",
"verification_type": "gpg-sha256",
"verification_source": "https://www.fujicoin.org/fujicoin/3.0/SHA256SUMS.asc",
"extract_command": "tar -C backend -xf",

View File

@ -1,7 +1,7 @@
{
"coin": {
"name": "Litecoin Testnet",
"shortcut": "TLTC",
"shortcut": "tLTC",
"label": "Litecoin Testnet",
"alias": "litecoin_testnet"
},

View File

@ -0,0 +1,67 @@
{
"coin": {
"name": "MonetaryUnit",
"shortcut": "MUE",
"label": "MonetaryUnit",
"alias": "monetaryunit"
},
"ports": {
"backend_rpc": 8057,
"backend_message_queue": 38357,
"blockbook_internal": 9057,
"blockbook_public": 9157
},
"ipc": {
"rpc_url_template": "http://127.0.0.1:{{.Ports.BackendRPC}}",
"rpc_user": "rpc",
"rpc_pass": "monetaryunitrpc",
"rpc_timeout": 25,
"message_queue_binding_template": "tcp://127.0.0.1:{{.Ports.BackendMessageQueue}}"
},
"backend": {
"package_name": "backend-monetaryunit",
"package_revision": "satoshilabs-1",
"system_user": "monetaryunit",
"version": "2.1.4",
"binary_url": "https://github.com/muecoin/MUE/releases/download/v2.1.4/mon-2.1.4-x86_64-linux-gnu.tar.gz",
"verification_type": "sha256",
"verification_source": "1c6885f3e5bb1efcc8c84e7910ec57bf76573983f2026ea7c5a7a919b7abcc11",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [
"bin/monetaryunit-qt"
],
"exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/monetaryunitd -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid",
"logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/*.log",
"postinst_script_template": "",
"service_type": "forking",
"service_additional_params_template": "",
"protect_memory": false,
"mainnet": true,
"server_config_file": "bitcoin_like.conf",
"client_config_file": "bitcoin_like_client.conf",
"additional_params": {
"whitelist": "127.0.0.1"
}
},
"blockbook": {
"package_name": "blockbook-monetaryunit",
"system_user": "blockbook-monetaryunit",
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
"explorer_url": "",
"additional_params": "",
"block_chain": {
"parse": true,
"mempool_workers": 8,
"mempool_sub_workers": 2,
"block_addresses_to_keep": 300,
"xpub_magic": 76067358,
"slip44": 31,
"additional_params": {}
}
},
"meta": {
"package_maintainer": "sotblad",
"package_maintainer_email": "swthrhs@protonmail.com"
}
}

View File

@ -1,9 +1,9 @@
{
"coin": {
"name": "Polis",
"shortcut": "POLIS",
"label": "Polis",
"alias": "polis"
"name": "Polis",
"shortcut": "POLIS",
"label": "Polis",
"alias": "polis"
},
"ports": {
"backend_rpc": 8067,
@ -22,14 +22,14 @@
"package_name": "backend-polis",
"package_revision": "satoshilabs-1",
"system_user": "polis",
"version": "1.4.10",
"binary_url": "https://github.com/polispay/polis/releases/download/v1.4.10/poliscore-1.4.10-x86_64-linux-gnu.tar.gz",
"version": "1.4.11",
"binary_url": "https://github.com/polispay/polis/releases/download/v1.4.11/poliscore-1.4.11-x86_64-linux-gnu.tar.gz",
"verification_type": "sha256",
"verification_source": "9688beeb4c789c2b20f4277f7363fcc986fb0e6cc75127179663272642fafcd3",
"verification_source": "c9d8c2ca3aaea699c129e0543f143cb35e5c00259ea55b0e2ed2b5dd76b28f3e",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [
"bin/polis-qt"
],
"bin/polis-qt"
],
"exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/polisd -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid",
"logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/*.log",
"postinst_script_template": "",
@ -40,7 +40,7 @@
"server_config_file": "bitcoin_like.conf",
"client_config_file": "bitcoin_like_client.conf",
"additional_params": {
"mempoolexpiry": 72
"mempoolexpiry": 72
}
},
"blockbook": {
@ -52,10 +52,12 @@
"additional_params": "",
"block_chain": {
"parse": true,
"subversion": "/Polis Core:1.4.10/",
"subversion": "/Polis Core:1.4.11/",
"mempool_workers": 8,
"mempool_sub_workers": 2,
"block_addresses_to_keep": 300,
"xpub_magic": 65166718,
"slip44": 1997,
"additional_params": {}
}
},
@ -63,4 +65,4 @@
"package_maintainer": "Cronos",
"package_maintainer_email": "eabz@polispay.org"
}
}
}

View File

@ -22,10 +22,10 @@
"package_name": "backend-qtum",
"package_revision": "satoshilabs-1",
"system_user": "qtum",
"version": "0.17.2",
"binary_url": "https://github.com/qtumproject/qtum/releases/download/mainnet-ignition-v0.17.2/qtum-0.17.2-x86_64-linux-gnu.tar.gz",
"version": "0.17.5",
"binary_url": "https://github.com/qtumproject/qtum/releases/download/mainnet-ignition-v0.17.5/qtum-0.17.5-x86_64-linux-gnu.tar.gz",
"verification_type": "sha256",
"verification_source": "f51926bf704c541e3ead5e5a14da6a4807cb7b761c23830aa37f044824e4fb33",
"verification_source": "824bb5963056ff03c1546ed857193960ca9560f5b15ec664a3970978c9267542",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [
"bin/qtum-qt"

View File

@ -22,10 +22,10 @@
"package_name": "backend-qtum-testnet",
"package_revision": "satoshilabs-1",
"system_user": "qtum",
"version": "0.17.2",
"binary_url": "https://github.com/qtumproject/qtum/releases/download/mainnet-ignition-v0.17.2/qtum-0.17.2-x86_64-linux-gnu.tar.gz",
"version": "0.17.5",
"binary_url": "https://github.com/qtumproject/qtum/releases/download/mainnet-ignition-v0.17.5/qtum-0.17.5-x86_64-linux-gnu.tar.gz",
"verification_type": "sha256",
"verification_source": "f51926bf704c541e3ead5e5a14da6a4807cb7b761c23830aa37f044824e4fb33",
"verification_source": "824bb5963056ff03c1546ed857193960ca9560f5b15ec664a3970978c9267542",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [
"bin/qtum-qt"

View File

@ -0,0 +1,67 @@
{
"coin": {
"name": "Ravencoin",
"shortcut": "RVN",
"label": "Ravencoin",
"alias": "ravencoin"
},
"ports": {
"backend_rpc": 8059,
"backend_message_queue": 38359,
"blockbook_internal": 9059,
"blockbook_public": 9159
},
"ipc": {
"rpc_url_template": "http://127.0.0.1:{{.Ports.BackendRPC}}",
"rpc_user": "rpc",
"rpc_pass": "rpc",
"rpc_timeout": 25,
"message_queue_binding_template": "tcp://127.0.0.1:{{.Ports.BackendMessageQueue}}"
},
"backend": {
"package_name": "backend-ravencoin",
"package_revision": "satoshilabs-1",
"system_user": "ravencoin",
"version": "2.2.2.0",
"binary_url": "https://github.com/RavenProject/Ravencoin/releases/download/v2.2.2/raven-2.2.2.0-x86_64-linux-gnu.tar.gz",
"verification_type": "sha256",
"verification_source": "f6e2c20e9741a9485a8b8b34044bd5b2cdd8c5d5871b04272ec96a4ebf6bcf2e",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [
"bin/raven-qt"
],
"exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/ravend -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid",
"logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/*.log",
"postinst_script_template": "",
"service_type": "forking",
"service_additional_params_template": "",
"protect_memory": true,
"mainnet": true,
"server_config_file": "bitcoin_like.conf",
"client_config_file": "bitcoin_like_client.conf",
"additional_params": {
"deprecatedrpc": "estimatefee"
}
},
"blockbook": {
"package_name": "blockbook-ravencoin",
"system_user": "blockbook-ravencoin",
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
"explorer_url": "",
"additional_params": "",
"block_chain": {
"parse": true,
"mempool_workers": 8,
"mempool_sub_workers": 2,
"block_addresses_to_keep": 300,
"xpub_magic": 76067358,
"slip44": 175,
"additional_params": {}
}
},
"meta": {
"package_maintainer": "Scotty",
"package_maintainer_email": "scotty.rvn@gmail.com"
}
}

View File

@ -0,0 +1,72 @@
{
"coin": {
"name": "Viacoin",
"shortcut": "VIA",
"label": "Viacoin",
"alias": "viacoin"
},
"ports": {
"backend_rpc": 8055,
"backend_message_queue": 38355,
"blockbook_internal": 9055,
"blockbook_public": 9155
},
"ipc": {
"rpc_url_template": "http://127.0.0.1:{{.Ports.BackendRPC}}",
"rpc_user": "rpc",
"rpc_pass": "rpcp",
"rpc_timeout": 25,
"message_queue_binding_template": "tcp://127.0.0.1:{{.Ports.BackendMessageQueue}}"
},
"backend": {
"package_name": "backend-viacoin",
"package_revision": "satoshilabs-1",
"system_user": "viacoin",
"version": "1.14-beta-1",
"binary_url": "https://github.com/viacoin/viacoin/releases/download/v0.15.2/viacoin-0.15.2-x86_64-linux-gnu.tar.gz",
"verification_type": "sha256",
"verification_source": "bdbd432645a8b4baadddb7169ea4bef3d03f80dc2ce53dce5783d8582ac63bab",
"extract_command": "tar -C backend --strip 1 -xf",
"exclude_files": [
"bin/viacoin-qt"
],
"exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/bin/viacoind -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid",
"logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/*.log",
"postinst_script_template": "",
"service_type": "forking",
"service_additional_params_template": "",
"protect_memory": false,
"mainnet": true,
"server_config_file": "bitcoin_like.conf",
"client_config_file": "bitcoin_like_client.conf",
"additional_params": {
"discover": 0,
"rpcthreads": 16,
"upnp": 0,
"whitelist": "127.0.0.1"
}
},
"blockbook": {
"package_name": "blockbook-viacoin",
"system_user": "blockbook-viacoin",
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
"explorer_url": "",
"additional_params": "-resyncindexperiod=30011 -resyncmempoolperiod=2011",
"block_chain": {
"parse": true,
"mempool_workers": 8,
"mempool_sub_workers": 2,
"block_addresses_to_keep": 300,
"xpub_magic": 76067358,
"xpub_magic_segwit_p2sh": 77429938,
"xpub_magic_segwit_native": 78792518,
"slip44": 14,
"additional_params": {}
}
},
"meta": {
"package_maintainer": "Romano",
"package_maintainer_email": "romanornr@gmail.com"
}
}

View File

@ -0,0 +1,66 @@
{
"coin": {
"name": "VIPSTARCOIN",
"shortcut": "VIPS",
"label": "VIPSTARCOIN",
"alias": "VIPSTARCOIN"
},
"ports": {
"backend_rpc": 8056,
"backend_message_queue": 38356,
"blockbook_internal": 9056,
"blockbook_public": 9156
},
"ipc": {
"rpc_url_template": "http://127.0.0.1:{{.Ports.BackendRPC}}",
"rpc_user": "rpc",
"rpc_pass": "rpc",
"rpc_timeout": 25,
"message_queue_binding_template": "tcp://127.0.0.1:{{.Ports.BackendMessageQueue}}"
},
"backend": {
"package_name": "backend-vipstarcoin",
"package_revision": "satoshilabs-1",
"system_user": "vipstarcoin",
"version": "1.0.2",
"binary_url": "https://github.com/y-chan/VIPSTARCOIN/releases/download/v1.0.2/VIPSTARCOIN-1.0.2-x86_64-linux-gnu.tar.gz",
"verification_type": "sha256",
"verification_source": "0bb608cf9103a896f2c1bf7f189db78f717019c2cc622f36581129876c65f131",
"extract_command": "tar -C backend --strip 1 -xzvf",
"exclude_files": [
"VIPSTARCOIN-qt"
],
"exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/VIPSTARCOINd -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid",
"logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/*.log",
"postinst_script_template": "",
"service_type": "forking",
"service_additional_params_template": "",
"protect_memory": true,
"mainnet": true,
"server_config_file": "bitcoin_like.conf",
"client_config_file": "bitcoin_like_client.conf",
"additional_params": {
"whitelist": "127.0.0.1"
}
},
"blockbook": {
"package_name": "blockbook-vipstarcoin",
"system_user": "blockbook-vipstarcoin",
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
"explorer_url": "",
"additional_params": "",
"block_chain": {
"parse": true,
"mempool_workers": 8,
"mempool_sub_workers": 2,
"block_addresses_to_keep": 300,
"slip44": 1919,
"additional_params": {}
}
},
"meta": {
"package_maintainer": "y-chan",
"package_maintainer_email": "yuto_tetuota@yahoo.co.jp"
}
}

View File

@ -64,4 +64,4 @@
"package_maintainer": "Petr Kracik",
"package_maintainer_email": "petr.kracik@satoshilabs.com"
}
}
}

View File

@ -64,4 +64,4 @@
"package_maintainer": "Petr Kracik",
"package_maintainer_email": "petr.kracik@satoshilabs.com"
}
}
}

View File

@ -0,0 +1,70 @@
{
"coin": {
"name": "ZelCash",
"shortcut": "ZEL",
"label": "ZelCash",
"alias": "zelcash"
},
"ports": {
"backend_rpc": 8058,
"backend_message_queue": 38358,
"blockbook_internal": 9058,
"blockbook_public": 9158
},
"ipc": {
"rpc_url_template": "http://127.0.0.1:{{.Ports.BackendRPC}}",
"rpc_user": "rpc",
"rpc_pass": "rpc",
"rpc_timeout": 25,
"message_queue_binding_template": "tcp://127.0.0.1:{{.Ports.BackendMessageQueue}}"
},
"backend": {
"package_name": "backend-zelcash",
"package_revision": "satoshilabs-1",
"system_user": "zelcash",
"version": "3.1.1",
"binary_url": "https://github.com/zelcash/zelcash/releases/download/v3.1.1/ZelCash-Linux.tar.gz",
"verification_type": "sha256",
"verification_source": "513101f44f3f6d40182c108fd8bf5c7a0336022173abd13ca24758776b2ee0d3",
"extract_command": "tar -C backend -xf",
"exclude_files": [],
"exec_command_template": "{{.Env.BackendInstallPath}}/{{.Coin.Alias}}/zelcashd -datadir={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend -conf={{.Env.BackendInstallPath}}/{{.Coin.Alias}}/{{.Coin.Alias}}.conf -pid=/run/{{.Coin.Alias}}/{{.Coin.Alias}}.pid",
"logrotate_files_template": "{{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend/*.log",
"postinst_script_template": "HOME={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend {{.Env.BackendInstallPath}}/{{.Coin.Alias}}/fetch-params.sh",
"service_type": "forking",
"service_additional_params_template": "Environment=\"HOME={{.Env.BackendDataPath}}/{{.Coin.Alias}}/backend\"",
"protect_memory": false,
"mainnet": true,
"server_config_file": "bitcoin_like.conf",
"client_config_file": "bitcoin_like_client.conf",
"additional_params": {
"addnode": [
"explorer.zel.cash",
"explorer2.zel.cash",
"explorer.zelcash.online",
"explorer-asia.zel.cash"
]
}
},
"blockbook": {
"package_name": "blockbook-zelcash",
"system_user": "blockbook-zelcash",
"internal_binding_template": ":{{.Ports.BlockbookInternal}}",
"public_binding_template": ":{{.Ports.BlockbookPublic}}",
"explorer_url": "",
"additional_params": "",
"block_chain": {
"parse": true,
"mempool_workers": 4,
"mempool_sub_workers": 8,
"block_addresses_to_keep": 300,
"xpub_magic": 76067358,
"slip44": 19167,
"additional_params": {}
}
},
"meta": {
"package_maintainer": "Tadeas Kmenta",
"package_maintainer_email": "tadeas.kmenta@zel.cash"
}
}

View File

@ -1,5 +1,5 @@
{
"version": "0.2.2",
"version": "0.3.0",
"backend_install_path": "/opt/coins/nodes",
"backend_data_path": "/opt/coins/data",
"blockbook_install_path": "/opt/coins/blockbook",

View File

@ -32,10 +32,10 @@ type BulkConnect struct {
}
const (
maxBulkAddresses = 200000
maxBulkAddresses = 80000
maxBulkTxAddresses = 500000
partialStoreAddresses = maxBulkTxAddresses / 10
maxBulkBalances = 800000
maxBulkBalances = 700000
partialStoreBalances = maxBulkBalances / 10
maxBulkAddrContracts = 1200000
partialStoreAddrContracts = maxBulkAddrContracts / 10

View File

@ -10,8 +10,10 @@ import (
"math/big"
"os"
"path/filepath"
"sort"
"strconv"
"time"
"unsafe"
vlq "github.com/bsm/go-vlq"
"github.com/golang/glog"
@ -19,7 +21,7 @@ import (
"github.com/tecbot/gorocksdb"
)
const dbVersion = 4
const dbVersion = 5
const packedHeightBytes = 4
const maxAddrDescLen = 1024
@ -42,6 +44,18 @@ type connectBlockStats struct {
balancesMiss int
}
// AddressBalanceDetail specifies what data are returned by GetAddressBalance
type AddressBalanceDetail int
const (
// AddressBalanceDetailNoUTXO returns address balance without utxos
AddressBalanceDetailNoUTXO = 0
// AddressBalanceDetailUTXO returns address balance with utxos
AddressBalanceDetailUTXO = 1
// addressBalanceDetailUTXOIndexed returns address balance with utxos and index for updates, used only internally
addressBalanceDetailUTXOIndexed = 2
)
// RocksDB handle
type RocksDB struct {
path string
@ -71,7 +85,8 @@ const (
)
// common columns
var cfNames = []string{"default", "height", "addresses", "blockTxs", "transactions"}
var cfNames []string
var cfBaseNames = []string{"default", "height", "addresses", "blockTxs", "transactions"}
// type specific columns
var cfNamesBitcoinType = []string{"addressBalance", "txAddresses"}
@ -102,6 +117,7 @@ func openDB(path string, c *gorocksdb.Cache, openFiles int) (*gorocksdb.DB, []*g
func NewRocksDB(path string, cacheSize, maxOpenFiles int, parser bchain.BlockChainParser, metrics *common.Metrics) (d *RocksDB, err error) {
glog.Infof("rocksdb: opening %s, required data version %v, cache size %v, max open files %v", path, dbVersion, cacheSize, maxOpenFiles)
cfNames = append([]string{}, cfBaseNames...)
chainType := parser.GetChainType()
if chainType == bchain.ChainBitcoinType {
cfNames = append(cfNames, cfNamesBitcoinType...)
@ -383,11 +399,21 @@ type TxAddresses struct {
Outputs []TxOutput
}
// Utxo holds information about unspent transaction output
type Utxo struct {
BtxID []byte
Vout int32
Height uint32
ValueSat big.Int
}
// AddrBalance stores number of transactions and balances of an address
type AddrBalance struct {
Txs uint32
SentSat big.Int
BalanceSat big.Int
Utxos []Utxo
utxosMap map[string]int
}
// ReceivedSat computes received amount from total balance and sent amount
@ -397,6 +423,61 @@ func (ab *AddrBalance) ReceivedSat() *big.Int {
return &r
}
// addUtxo
func (ab *AddrBalance) addUtxo(u *Utxo) {
ab.Utxos = append(ab.Utxos, *u)
l := len(ab.Utxos)
if l >= 16 {
if len(ab.utxosMap) == 0 {
ab.utxosMap = make(map[string]int, 32)
for i := 0; i < l; i++ {
s := string(ab.Utxos[i].BtxID)
if _, e := ab.utxosMap[s]; !e {
ab.utxosMap[s] = i
}
}
} else {
s := string(u.BtxID)
if _, e := ab.utxosMap[s]; !e {
ab.utxosMap[s] = l - 1
}
}
}
}
// markUtxoAsSpent finds outpoint btxID:vout in utxos and marks it as spent
// for small number of utxos the linear search is done, for larger number there is a hashmap index
// it is much faster than removing the utxo from the slice as it would cause in memory copy operations
func (ab *AddrBalance) markUtxoAsSpent(btxID []byte, vout int32) {
if len(ab.utxosMap) == 0 {
for i := range ab.Utxos {
utxo := &ab.Utxos[i]
if utxo.Vout == vout && *(*int)(unsafe.Pointer(&utxo.BtxID[0])) == *(*int)(unsafe.Pointer(&btxID[0])) && bytes.Equal(utxo.BtxID, btxID) {
// mark utxo as spent by setting vout=-1
utxo.Vout = -1
return
}
}
} else {
if i, e := ab.utxosMap[string(btxID)]; e {
l := len(ab.Utxos)
for ; i < l; i++ {
utxo := &ab.Utxos[i]
if utxo.Vout == vout {
if bytes.Equal(utxo.BtxID, btxID) {
// mark utxo as spent by setting vout=-1
utxo.Vout = -1
return
} else {
break
}
}
}
}
}
glog.Errorf("Utxo %s:%d not found, using in map %v", hex.EncodeToString(btxID), vout, len(ab.utxosMap) != 0)
}
type blockTxs struct {
btxID []byte
inputs []outpoint
@ -422,7 +503,7 @@ func (d *RocksDB) GetAndResetConnectBlockStats() string {
func (d *RocksDB) processAddressesBitcoinType(block *bchain.Block, addresses addressesMap, txAddressesMap map[string]*TxAddresses, balances map[string]*AddrBalance) error {
blockTxIDs := make([][]byte, len(block.Txs))
blockTxAddresses := make([]*TxAddresses, len(block.Txs))
// first process all outputs so that inputs can point to txs in this block
// first process all outputs so that inputs can refer to txs in this block
for txi := range block.Txs {
tx := &block.Txs[txi]
btxID, err := d.chainParser.PackTxid(tx.Txid)
@ -442,33 +523,41 @@ func (d *RocksDB) processAddressesBitcoinType(block *bchain.Block, addresses add
if err != nil {
// do not log ErrAddressMissing, transactions can be without to address (for example eth contracts)
if err != bchain.ErrAddressMissing {
glog.Warningf("rocksdb: addrDesc: %v - height %d, tx %v, output %v", err, block.Height, tx.Txid, output)
glog.Warningf("rocksdb: addrDesc: %v - height %d, tx %v, output %v, error %v", err, block.Height, tx.Txid, output, err)
}
} else {
glog.Infof("rocksdb: height %d, tx %v, vout %v, skipping addrDesc of length %d", block.Height, tx.Txid, i, len(addrDesc))
glog.V(1).Infof("rocksdb: height %d, tx %v, vout %v, skipping addrDesc of length %d", block.Height, tx.Txid, i, len(addrDesc))
}
continue
}
tao.AddrDesc = addrDesc
strAddrDesc := string(addrDesc)
ab, e := balances[strAddrDesc]
if !e {
ab, err = d.GetAddrDescBalance(addrDesc)
if err != nil {
return err
if d.chainParser.IsAddrDescIndexable(addrDesc) {
strAddrDesc := string(addrDesc)
balance, e := balances[strAddrDesc]
if !e {
balance, err = d.GetAddrDescBalance(addrDesc, addressBalanceDetailUTXOIndexed)
if err != nil {
return err
}
if balance == nil {
balance = &AddrBalance{}
}
balances[strAddrDesc] = balance
d.cbs.balancesMiss++
} else {
d.cbs.balancesHit++
}
if ab == nil {
ab = &AddrBalance{}
balance.BalanceSat.Add(&balance.BalanceSat, &output.ValueSat)
balance.addUtxo(&Utxo{
BtxID: btxID,
Vout: int32(i),
Height: block.Height,
ValueSat: output.ValueSat,
})
counted := addToAddressesMap(addresses, strAddrDesc, btxID, int32(i))
if !counted {
balance.Txs++
}
balances[strAddrDesc] = ab
d.cbs.balancesMiss++
} else {
d.cbs.balancesHit++
}
ab.BalanceSat.Add(&ab.BalanceSat, &output.ValueSat)
counted := addToAddressesMap(addresses, strAddrDesc, btxID, int32(i))
if !counted {
ab.Txs++
}
}
}
@ -510,45 +599,48 @@ func (d *RocksDB) processAddressesBitcoinType(block *bchain.Block, addresses add
glog.Warningf("rocksdb: height %d, tx %v, input tx %v vout %v is out of bounds of stored tx", block.Height, tx.Txid, input.Txid, input.Vout)
continue
}
ot := &ita.Outputs[int(input.Vout)]
if ot.Spent {
spentOutput := &ita.Outputs[int(input.Vout)]
if spentOutput.Spent {
glog.Warningf("rocksdb: height %d, tx %v, input tx %v vout %v is double spend", block.Height, tx.Txid, input.Txid, input.Vout)
}
tai.AddrDesc = ot.AddrDesc
tai.ValueSat = ot.ValueSat
tai.AddrDesc = spentOutput.AddrDesc
tai.ValueSat = spentOutput.ValueSat
// mark the output as spent in tx
ot.Spent = true
if len(ot.AddrDesc) == 0 {
spentOutput.Spent = true
if len(spentOutput.AddrDesc) == 0 {
if !logged {
glog.Warningf("rocksdb: height %d, tx %v, input tx %v vout %v skipping empty address", block.Height, tx.Txid, input.Txid, input.Vout)
glog.V(1).Infof("rocksdb: height %d, tx %v, input tx %v vout %v skipping empty address", block.Height, tx.Txid, input.Txid, input.Vout)
logged = true
}
continue
}
strAddrDesc := string(ot.AddrDesc)
ab, e := balances[strAddrDesc]
if !e {
ab, err = d.GetAddrDescBalance(ot.AddrDesc)
if err != nil {
return err
if d.chainParser.IsAddrDescIndexable(spentOutput.AddrDesc) {
strAddrDesc := string(spentOutput.AddrDesc)
balance, e := balances[strAddrDesc]
if !e {
balance, err = d.GetAddrDescBalance(spentOutput.AddrDesc, addressBalanceDetailUTXOIndexed)
if err != nil {
return err
}
if balance == nil {
balance = &AddrBalance{}
}
balances[strAddrDesc] = balance
d.cbs.balancesMiss++
} else {
d.cbs.balancesHit++
}
if ab == nil {
ab = &AddrBalance{}
counted := addToAddressesMap(addresses, strAddrDesc, spendingTxid, ^int32(i))
if !counted {
balance.Txs++
}
balances[strAddrDesc] = ab
d.cbs.balancesMiss++
} else {
d.cbs.balancesHit++
balance.BalanceSat.Sub(&balance.BalanceSat, &spentOutput.ValueSat)
balance.markUtxoAsSpent(btxID, int32(input.Vout))
if balance.BalanceSat.Sign() < 0 {
d.resetValueSatToZero(&balance.BalanceSat, spentOutput.AddrDesc, "balance")
}
balance.SentSat.Add(&balance.SentSat, &spentOutput.ValueSat)
}
counted := addToAddressesMap(addresses, strAddrDesc, spendingTxid, ^int32(i))
if !counted {
ab.Txs++
}
ab.BalanceSat.Sub(&ab.BalanceSat, &ot.ValueSat)
if ab.BalanceSat.Sign() < 0 {
d.resetValueSatToZero(&ab.BalanceSat, ot.AddrDesc, "balance")
}
ab.SentSat.Add(&ab.SentSat, &ot.ValueSat)
}
}
return nil
@ -598,19 +690,16 @@ func (d *RocksDB) storeTxAddresses(wb *gorocksdb.WriteBatch, am map[string]*TxAd
}
func (d *RocksDB) storeBalances(wb *gorocksdb.WriteBatch, abm map[string]*AddrBalance) error {
// allocate buffer big enough for number of txs + 2 bigints
buf := make([]byte, vlq.MaxLen32+2*maxPackedBigintBytes)
// allocate buffer initial buffer
buf := make([]byte, 1024)
varBuf := make([]byte, maxPackedBigintBytes)
for addrDesc, ab := range abm {
// balance with 0 transactions is removed from db - happens in disconnect
// balance with 0 transactions is removed from db - happens on disconnect
if ab == nil || ab.Txs <= 0 {
wb.DeleteCF(d.cfh[cfAddressBalance], bchain.AddressDescriptor(addrDesc))
} else {
l := packVaruint(uint(ab.Txs), buf)
ll := packBigint(&ab.SentSat, buf[l:])
l += ll
ll = packBigint(&ab.BalanceSat, buf[l:])
l += ll
wb.PutCF(d.cfh[cfAddressBalance], bchain.AddressDescriptor(addrDesc), buf[:l])
buf = packAddrBalance(ab, buf, varBuf)
wb.PutCF(d.cfh[cfAddressBalance], bchain.AddressDescriptor(addrDesc), buf)
}
}
return nil
@ -704,7 +793,7 @@ func (d *RocksDB) getBlockTxs(height uint32) ([]blockTxs, error) {
}
// GetAddrDescBalance returns AddrBalance for given addrDesc
func (d *RocksDB) GetAddrDescBalance(addrDesc bchain.AddressDescriptor) (*AddrBalance, error) {
func (d *RocksDB) GetAddrDescBalance(addrDesc bchain.AddressDescriptor, detail AddressBalanceDetail) (*AddrBalance, error) {
val, err := d.db.GetCF(d.ro, d.cfh[cfAddressBalance], addrDesc)
if err != nil {
return nil, err
@ -715,23 +804,16 @@ func (d *RocksDB) GetAddrDescBalance(addrDesc bchain.AddressDescriptor) (*AddrBa
if len(buf) < 3 {
return nil, nil
}
txs, l := unpackVaruint(buf)
sentSat, sl := unpackBigint(buf[l:])
balanceSat, _ := unpackBigint(buf[l+sl:])
return &AddrBalance{
Txs: uint32(txs),
SentSat: sentSat,
BalanceSat: balanceSat,
}, nil
return unpackAddrBalance(buf, d.chainParser.PackedTxidLen(), detail)
}
// GetAddressBalance returns address balance for an address or nil if address not found
func (d *RocksDB) GetAddressBalance(address string) (*AddrBalance, error) {
func (d *RocksDB) GetAddressBalance(address string, detail AddressBalanceDetail) (*AddrBalance, error) {
addrDesc, err := d.chainParser.GetAddrDescFromAddress(address)
if err != nil {
return nil, err
}
return d.GetAddrDescBalance(addrDesc)
return d.GetAddrDescBalance(addrDesc, detail)
}
func (d *RocksDB) getTxAddresses(btxID []byte) (*TxAddresses, error) {
@ -816,6 +898,68 @@ func appendTxOutput(txo *TxOutput, buf []byte, varBuf []byte) []byte {
return buf
}
func unpackAddrBalance(buf []byte, txidUnpackedLen int, detail AddressBalanceDetail) (*AddrBalance, error) {
txs, l := unpackVaruint(buf)
sentSat, sl := unpackBigint(buf[l:])
balanceSat, bl := unpackBigint(buf[l+sl:])
l = l + sl + bl
ab := &AddrBalance{
Txs: uint32(txs),
SentSat: sentSat,
BalanceSat: balanceSat,
}
if detail != AddressBalanceDetailNoUTXO {
// estimate the size of utxos to avoid reallocation
ab.Utxos = make([]Utxo, 0, len(buf[l:])/txidUnpackedLen+3)
// ab.utxosMap = make(map[string]int, cap(ab.Utxos))
for len(buf[l:]) >= txidUnpackedLen+3 {
btxID := append([]byte(nil), buf[l:l+txidUnpackedLen]...)
l += txidUnpackedLen
vout, ll := unpackVaruint(buf[l:])
l += ll
height, ll := unpackVaruint(buf[l:])
l += ll
valueSat, ll := unpackBigint(buf[l:])
l += ll
u := Utxo{
BtxID: btxID,
Vout: int32(vout),
Height: uint32(height),
ValueSat: valueSat,
}
if detail == AddressBalanceDetailUTXO {
ab.Utxos = append(ab.Utxos, u)
} else {
ab.addUtxo(&u)
}
}
}
return ab, nil
}
func packAddrBalance(ab *AddrBalance, buf, varBuf []byte) []byte {
buf = buf[:0]
l := packVaruint(uint(ab.Txs), varBuf)
buf = append(buf, varBuf[:l]...)
l = packBigint(&ab.SentSat, varBuf)
buf = append(buf, varBuf[:l]...)
l = packBigint(&ab.BalanceSat, varBuf)
buf = append(buf, varBuf[:l]...)
for _, utxo := range ab.Utxos {
// if Vout < 0, utxo is marked as spent
if utxo.Vout >= 0 {
buf = append(buf, utxo.BtxID...)
l = packVaruint(uint(utxo.Vout), varBuf)
buf = append(buf, varBuf[:l]...)
l = packVaruint(uint(utxo.Height), varBuf)
buf = append(buf, varBuf[:l]...)
l = packBigint(&utxo.ValueSat, varBuf)
buf = append(buf, varBuf[:l]...)
}
}
return buf
}
func unpackTxAddresses(buf []byte) (*TxAddresses, error) {
ta := TxAddresses{}
height, l := unpackVaruint(buf)
@ -1030,15 +1174,17 @@ func (d *RocksDB) writeHeight(wb *gorocksdb.WriteBatch, height uint32, bi *Block
// Disconnect blocks
func (d *RocksDB) disconnectTxAddresses(wb *gorocksdb.WriteBatch, height uint32, txid string, inputs []outpoint, txa *TxAddresses,
func (d *RocksDB) disconnectTxAddresses(wb *gorocksdb.WriteBatch, height uint32, btxID []byte, inputs []outpoint, txa *TxAddresses,
txAddressesToUpdate map[string]*TxAddresses, balances map[string]*AddrBalance) error {
var err error
var balance *AddrBalance
addresses := make(map[string]struct{})
getAddressBalance := func(addrDesc bchain.AddressDescriptor) (*AddrBalance, error) {
var err error
s := string(addrDesc)
b, fb := balances[s]
if !fb {
b, err = d.GetAddrDescBalance(addrDesc)
b, err = d.GetAddrDescBalance(addrDesc, addressBalanceDetailUTXOIndexed)
if err != nil {
return nil, err
}
@ -1048,33 +1194,16 @@ func (d *RocksDB) disconnectTxAddresses(wb *gorocksdb.WriteBatch, height uint32,
}
for i, t := range txa.Inputs {
if len(t.AddrDesc) > 0 {
input := &inputs[i]
s := string(t.AddrDesc)
_, exist := addresses[s]
if !exist {
addresses[s] = struct{}{}
}
b, err := getAddressBalance(t.AddrDesc)
if err != nil {
return err
}
if b != nil {
// subtract number of txs only once
if !exist {
b.Txs--
}
b.SentSat.Sub(&b.SentSat, &t.ValueSat)
if b.SentSat.Sign() < 0 {
d.resetValueSatToZero(&b.SentSat, t.AddrDesc, "sent amount")
}
b.BalanceSat.Add(&b.BalanceSat, &t.ValueSat)
} else {
ad, _, _ := d.chainParser.GetAddressesFromAddrDesc(t.AddrDesc)
glog.Warningf("Balance for address %s (%s) not found", ad, t.AddrDesc)
}
s = string(inputs[i].btxID)
sa, exist := txAddressesToUpdate[s]
if !exist {
sa, err = d.getTxAddresses(inputs[i].btxID)
s = string(input.btxID)
sa, found := txAddressesToUpdate[s]
if !found {
sa, err = d.getTxAddresses(input.btxID)
if err != nil {
return err
}
@ -1082,34 +1211,65 @@ func (d *RocksDB) disconnectTxAddresses(wb *gorocksdb.WriteBatch, height uint32,
txAddressesToUpdate[s] = sa
}
}
var inputHeight uint32
if sa != nil {
sa.Outputs[inputs[i].index].Spent = false
sa.Outputs[input.index].Spent = false
inputHeight = sa.Height
}
if d.chainParser.IsAddrDescIndexable(t.AddrDesc) {
balance, err = getAddressBalance(t.AddrDesc)
if err != nil {
return err
}
if balance != nil {
// subtract number of txs only once
if !exist {
balance.Txs--
}
balance.SentSat.Sub(&balance.SentSat, &t.ValueSat)
if balance.SentSat.Sign() < 0 {
d.resetValueSatToZero(&balance.SentSat, t.AddrDesc, "sent amount")
}
balance.BalanceSat.Add(&balance.BalanceSat, &t.ValueSat)
balance.Utxos = append(balance.Utxos, Utxo{
BtxID: input.btxID,
Vout: input.index,
Height: inputHeight,
ValueSat: t.ValueSat,
})
} else {
ad, _, _ := d.chainParser.GetAddressesFromAddrDesc(t.AddrDesc)
glog.Warningf("Balance for address %s (%s) not found", ad, t.AddrDesc)
}
}
}
}
for _, t := range txa.Outputs {
for i, t := range txa.Outputs {
if len(t.AddrDesc) > 0 {
s := string(t.AddrDesc)
_, exist := addresses[s]
if !exist {
addresses[s] = struct{}{}
}
b, err := getAddressBalance(t.AddrDesc)
if err != nil {
return err
}
if b != nil {
// subtract number of txs only once
if !exist {
b.Txs--
if d.chainParser.IsAddrDescIndexable(t.AddrDesc) {
balance, err := getAddressBalance(t.AddrDesc)
if err != nil {
return err
}
b.BalanceSat.Sub(&b.BalanceSat, &t.ValueSat)
if b.BalanceSat.Sign() < 0 {
d.resetValueSatToZero(&b.BalanceSat, t.AddrDesc, "balance")
if balance != nil {
// subtract number of txs only once
if !exist {
balance.Txs--
}
balance.BalanceSat.Sub(&balance.BalanceSat, &t.ValueSat)
if balance.BalanceSat.Sign() < 0 {
d.resetValueSatToZero(&balance.BalanceSat, t.AddrDesc, "balance")
}
balance.markUtxoAsSpent(btxID, int32(i))
} else {
ad, _, _ := d.chainParser.GetAddressesFromAddrDesc(t.AddrDesc)
glog.Warningf("Balance for address %s (%s) not found", ad, t.AddrDesc)
}
} else {
ad, _, _ := d.chainParser.GetAddressesFromAddrDesc(t.AddrDesc)
glog.Warningf("Balance for address %s (%s) not found", ad, t.AddrDesc)
}
}
}
@ -1146,19 +1306,19 @@ func (d *RocksDB) DisconnectBlockRangeBitcoinType(lower uint32, higher uint32) e
// when connecting block, amount is first in tx on the output side, then in another tx on the input side
// when disconnecting, it must be done backwards
for i := len(blockTxs) - 1; i >= 0; i-- {
txid := blockTxs[i].btxID
s := string(txid)
btxID := blockTxs[i].btxID
s := string(btxID)
txsToDelete[s] = struct{}{}
txa, err := d.getTxAddresses(txid)
txa, err := d.getTxAddresses(btxID)
if err != nil {
return err
}
if txa == nil {
ut, _ := d.chainParser.UnpackTxid(txid)
ut, _ := d.chainParser.UnpackTxid(btxID)
glog.Warning("TxAddress for txid ", ut, " not found")
continue
}
if err := d.disconnectTxAddresses(wb, height, s, blockTxs[i].inputs, txa, txAddressesToUpdate, balances); err != nil {
if err := d.disconnectTxAddresses(wb, height, btxID, blockTxs[i].inputs, txa, txAddressesToUpdate, balances); err != nil {
return err
}
}
@ -1167,7 +1327,7 @@ func (d *RocksDB) DisconnectBlockRangeBitcoinType(lower uint32, higher uint32) e
wb.DeleteCF(d.cfh[cfHeight], key)
}
d.storeTxAddresses(wb, txAddressesToUpdate)
d.storeBalances(wb, balances)
d.storeBalancesDisconnect(wb, balances)
for s := range txsToDelete {
b := []byte(s)
wb.DeleteCF(d.cfh[cfTransactions], b)
@ -1180,6 +1340,27 @@ func (d *RocksDB) DisconnectBlockRangeBitcoinType(lower uint32, higher uint32) e
return err
}
func (d *RocksDB) storeBalancesDisconnect(wb *gorocksdb.WriteBatch, balances map[string]*AddrBalance) {
for _, b := range balances {
if b != nil {
// remove spent utxos
us := make([]Utxo, 0, len(b.Utxos))
for _, u := range b.Utxos {
// remove utxos marked as spent
if u.Vout >= 0 {
us = append(us, u)
}
}
b.Utxos = us
// sort utxos by height
sort.SliceStable(b.Utxos, func(i, j int) bool {
return b.Utxos[i].Height < b.Utxos[j].Height
})
}
}
d.storeBalances(wb, balances)
}
func dirSize(path string) (int64, error) {
var size int64
err := filepath.Walk(path, func(_ string, info os.FileInfo, err error) error {

View File

@ -99,6 +99,11 @@ func uintToHex(i uint32) string {
return hex.EncodeToString(buf)
}
func hexToBytes(h string) []byte {
b, _ := hex.DecodeString(h)
return b
}
func addressKeyHex(a string, height uint32, d *RocksDB) string {
return dbtestdata.AddressToPubKeyHex(a, d.chainParser) + uintToHex(^height)
}
@ -221,11 +226,36 @@ func verifyAfterBitcoinTypeBlock1(t *testing.T, d *RocksDB, afterDisconnect bool
}
}
if err := checkColumn(d, cfAddressBalance, []keyPair{
{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr1, d.chainParser), "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB1T1A1), nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr2, d.chainParser), "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB1T1A2), nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr3, d.chainParser), "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB1T2A3), nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr4, d.chainParser), "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB1T2A4), nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr5, d.chainParser), "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB1T2A5), nil},
{
dbtestdata.AddressToPubKeyHex(dbtestdata.Addr1, d.chainParser),
"01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB1T1A1) +
dbtestdata.TxidB1T1 + varuintToHex(0) + varuintToHex(225493) + bigintToHex(dbtestdata.SatB1T1A1),
nil,
},
{
dbtestdata.AddressToPubKeyHex(dbtestdata.Addr2, d.chainParser),
"01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB1T1A2) +
dbtestdata.TxidB1T1 + varuintToHex(1) + varuintToHex(225493) + bigintToHex(dbtestdata.SatB1T1A2),
nil,
},
{
dbtestdata.AddressToPubKeyHex(dbtestdata.Addr3, d.chainParser),
"01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB1T2A3) +
dbtestdata.TxidB1T2 + varuintToHex(0) + varuintToHex(225493) + bigintToHex(dbtestdata.SatB1T2A3),
nil,
},
{
dbtestdata.AddressToPubKeyHex(dbtestdata.Addr4, d.chainParser),
"01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB1T2A4) +
dbtestdata.TxidB1T2 + varuintToHex(1) + varuintToHex(225493) + bigintToHex(dbtestdata.SatB1T2A4),
nil,
},
{
dbtestdata.AddressToPubKeyHex(dbtestdata.Addr5, d.chainParser),
"01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB1T2A5) +
dbtestdata.TxidB1T2 + varuintToHex(2) + varuintToHex(225493) + bigintToHex(dbtestdata.SatB1T2A5),
nil,
},
}); err != nil {
{
t.Fatal(err)
@ -315,9 +345,10 @@ func verifyAfterBitcoinTypeBlock2(t *testing.T, d *RocksDB) {
"02" +
inputAddressToPubKeyHexWithLength(dbtestdata.Addr3, t, d) + bigintToHex(dbtestdata.SatB1T2A3) +
inputAddressToPubKeyHexWithLength(dbtestdata.Addr2, t, d) + bigintToHex(dbtestdata.SatB1T1A2) +
"02" +
"03" +
spentAddressToPubKeyHexWithLength(dbtestdata.Addr6, t, d) + bigintToHex(dbtestdata.SatB2T1A6) +
addressToPubKeyHexWithLength(dbtestdata.Addr7, t, d) + bigintToHex(dbtestdata.SatB2T1A7),
addressToPubKeyHexWithLength(dbtestdata.Addr7, t, d) + bigintToHex(dbtestdata.SatB2T1A7) +
hex.EncodeToString([]byte{byte(len(dbtestdata.TxidB2T1Output3OpReturn))}) + dbtestdata.TxidB2T1Output3OpReturn + bigintToHex(dbtestdata.SatZero),
nil,
},
{
@ -355,16 +386,62 @@ func verifyAfterBitcoinTypeBlock2(t *testing.T, d *RocksDB) {
}
}
if err := checkColumn(d, cfAddressBalance, []keyPair{
{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr1, d.chainParser), "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB1T1A1), nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr2, d.chainParser), "02" + bigintToHex(dbtestdata.SatB1T1A2) + bigintToHex(dbtestdata.SatZero), nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr3, d.chainParser), "02" + bigintToHex(dbtestdata.SatB1T2A3) + bigintToHex(dbtestdata.SatZero), nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr4, d.chainParser), "02" + bigintToHex(dbtestdata.SatB1T2A4) + bigintToHex(dbtestdata.SatZero), nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr5, d.chainParser), "02" + bigintToHex(dbtestdata.SatB1T2A5) + bigintToHex(dbtestdata.SatB2T3A5), nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr6, d.chainParser), "02" + bigintToHex(dbtestdata.SatB2T1A6) + bigintToHex(dbtestdata.SatZero), nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr7, d.chainParser), "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB2T1A7), nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr8, d.chainParser), "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB2T2A8), nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.Addr9, d.chainParser), "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB2T2A9), nil},
{dbtestdata.AddressToPubKeyHex(dbtestdata.AddrA, d.chainParser), "01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB2T4AA), nil},
{
dbtestdata.AddressToPubKeyHex(dbtestdata.Addr1, d.chainParser),
"01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB1T1A1) +
dbtestdata.TxidB1T1 + varuintToHex(0) + varuintToHex(225493) + bigintToHex(dbtestdata.SatB1T1A1),
nil,
},
{
dbtestdata.AddressToPubKeyHex(dbtestdata.Addr2, d.chainParser),
"02" + bigintToHex(dbtestdata.SatB1T1A2) + bigintToHex(dbtestdata.SatZero),
nil,
},
{
dbtestdata.AddressToPubKeyHex(dbtestdata.Addr3, d.chainParser),
"02" + bigintToHex(dbtestdata.SatB1T2A3) + bigintToHex(dbtestdata.SatZero),
nil,
},
{
dbtestdata.AddressToPubKeyHex(dbtestdata.Addr4, d.chainParser),
"02" + bigintToHex(dbtestdata.SatB1T2A4) + bigintToHex(dbtestdata.SatZero),
nil,
},
{
dbtestdata.AddressToPubKeyHex(dbtestdata.Addr5, d.chainParser),
"02" + bigintToHex(dbtestdata.SatB1T2A5) + bigintToHex(dbtestdata.SatB2T3A5) +
dbtestdata.TxidB2T3 + varuintToHex(0) + varuintToHex(225494) + bigintToHex(dbtestdata.SatB2T3A5),
nil,
},
{
dbtestdata.AddressToPubKeyHex(dbtestdata.Addr6, d.chainParser),
"02" + bigintToHex(dbtestdata.SatB2T1A6) + bigintToHex(dbtestdata.SatZero),
nil,
},
{
dbtestdata.AddressToPubKeyHex(dbtestdata.Addr7, d.chainParser),
"01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB2T1A7) +
dbtestdata.TxidB2T1 + varuintToHex(1) + varuintToHex(225494) + bigintToHex(dbtestdata.SatB2T1A7),
nil,
},
{
dbtestdata.AddressToPubKeyHex(dbtestdata.Addr8, d.chainParser),
"01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB2T2A8) +
dbtestdata.TxidB2T2 + varuintToHex(0) + varuintToHex(225494) + bigintToHex(dbtestdata.SatB2T2A8),
nil,
},
{
dbtestdata.AddressToPubKeyHex(dbtestdata.Addr9, d.chainParser),
"01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB2T2A9) +
dbtestdata.TxidB2T2 + varuintToHex(1) + varuintToHex(225494) + bigintToHex(dbtestdata.SatB2T2A9),
nil,
},
{
dbtestdata.AddressToPubKeyHex(dbtestdata.AddrA, d.chainParser),
"01" + bigintToHex(dbtestdata.SatZero) + bigintToHex(dbtestdata.SatB2T4AA) +
dbtestdata.TxidB2T4 + varuintToHex(0) + varuintToHex(225494) + bigintToHex(dbtestdata.SatB2T4AA),
nil,
},
}); err != nil {
{
t.Fatal(err)
@ -579,7 +656,7 @@ func TestRocksDB_Index_BitcoinType(t *testing.T) {
verifyAfterBitcoinTypeBlock2(t, d)
// test public methods for address balance and tx addresses
ab, err := d.GetAddressBalance(dbtestdata.Addr5)
ab, err := d.GetAddressBalance(dbtestdata.Addr5, AddressBalanceDetailUTXO)
if err != nil {
t.Fatal(err)
}
@ -587,6 +664,14 @@ func TestRocksDB_Index_BitcoinType(t *testing.T) {
Txs: 2,
SentSat: *dbtestdata.SatB1T2A5,
BalanceSat: *dbtestdata.SatB2T3A5,
Utxos: []Utxo{
Utxo{
BtxID: hexToBytes(dbtestdata.TxidB2T3),
Vout: 0,
Height: 225494,
ValueSat: *dbtestdata.SatB2T3A5,
},
},
}
if !reflect.DeepEqual(ab, abw) {
t.Errorf("GetAddressBalance() = %+v, want %+v", ab, abw)
@ -624,6 +709,11 @@ func TestRocksDB_Index_BitcoinType(t *testing.T) {
Spent: false,
ValueSat: *dbtestdata.SatB2T1A7,
},
{
AddrDesc: hexToBytes(dbtestdata.TxidB2T1Output3OpReturn),
Spent: false,
ValueSat: *dbtestdata.SatZero,
},
},
}
if !reflect.DeepEqual(ta, taw) {
@ -906,3 +996,78 @@ func Test_packTxAddresses_unpackTxAddresses(t *testing.T) {
})
}
}
func Test_packAddrBalance_unpackAddrBalance(t *testing.T) {
parser := bitcoinTestnetParser()
tests := []struct {
name string
hex string
data *AddrBalance
}{
{
name: "no utxos",
hex: "7b060b44cc1af8520514faf980ac",
data: &AddrBalance{
BalanceSat: *big.NewInt(90110001324),
SentSat: *big.NewInt(12390110001234),
Txs: 123,
Utxos: []Utxo{},
},
},
{
name: "utxos",
hex: "7b060b44cc1af8520514faf980ac00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa38400c87c440060b2fd12177a6effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac750098faf659010105e2e48aeabdd9b75def7b48d756ba304713c2aba7b522bf9dbc893fc4231b0782c6df6d84ccd88552087e9cba87a275ffff",
data: &AddrBalance{
BalanceSat: *big.NewInt(90110001324),
SentSat: *big.NewInt(12390110001234),
Txs: 123,
Utxos: []Utxo{
{
BtxID: hexToBytes(dbtestdata.TxidB1T1),
Vout: 12,
Height: 123456,
ValueSat: *big.NewInt(12390110001234 - 90110001324),
},
{
BtxID: hexToBytes(dbtestdata.TxidB1T2),
Vout: 0,
Height: 52345689,
ValueSat: *big.NewInt(1),
},
{
BtxID: hexToBytes(dbtestdata.TxidB2T3),
Vout: 5353453,
Height: 1234567890,
ValueSat: *big.NewInt(9123372036854775807),
},
},
},
},
{
name: "empty",
hex: "000000",
data: &AddrBalance{
Utxos: []Utxo{},
},
},
}
varBuf := make([]byte, maxPackedBigintBytes)
buf := make([]byte, 32)
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
b := packAddrBalance(tt.data, buf, varBuf)
hex := hex.EncodeToString(b)
if !reflect.DeepEqual(hex, tt.hex) {
t.Errorf("packTxAddresses() = %v, want %v", hex, tt.hex)
}
got1, err := unpackAddrBalance(b, parser.PackedTxidLen(), AddressBalanceDetailUTXO)
if err != nil {
t.Errorf("unpackTxAddresses() error = %v", err)
return
}
if !reflect.DeepEqual(got1, tt.data) {
t.Errorf("unpackTxAddresses() = %+v, want %+v", got1, tt.data)
}
})
}
}

View File

@ -45,6 +45,9 @@ func NewSyncWorker(db *RocksDB, chain bchain.BlockChain, syncWorkers, syncChunk
var errSynced = errors.New("synced")
// ErrOperationInterrupted is returned when operation is interrupted by OS signal
var ErrOperationInterrupted = errors.New("ErrOperationInterrupted")
// ResyncIndex synchronizes index to the top of the blockchain
// onNewBlock is called when new block is connected, but not in initial parallel sync
func (w *SyncWorker) ResyncIndex(onNewBlock bchain.OnNewBlockFunc, initialSync bool) error {
@ -202,7 +205,8 @@ func (w *SyncWorker) connectBlocks(onNewBlock bchain.OnNewBlockFunc, initialSync
for {
select {
case <-w.chanOsSignal:
return errors.Errorf("connectBlocks interrupted at height %d", lastRes.block.Height)
glog.Info("connectBlocks interrupted at height ", lastRes.block.Height)
return ErrOperationInterrupted
case res := <-bch:
if res == empty {
break ConnectLoop
@ -325,7 +329,8 @@ ConnectLoop:
for h := lower; h <= higher; {
select {
case <-w.chanOsSignal:
err = errors.Errorf("connectBlocksParallel interrupted at height %d", h)
glog.Info("connectBlocksParallel interrupted at height ", h)
err = ErrOperationInterrupted
// signal all workers to terminate their loops (error loops are interrupted below)
close(terminating)
break ConnectLoop

View File

@ -25,7 +25,7 @@ Socket.io interface is provided at `/socket.io/`. The interface also can be expl
The legacy API is provided as is and will not be further developed.
The legacy API is currently (Blockbook v0.2.1) also accessible without the */v1/* prefix, however in the future versions the version less access will be removed.
The legacy API is currently (Blockbook v0.3.0) also accessible without the */v1/* prefix, however in the future versions the version less access will be removed.
## API V2
@ -41,6 +41,7 @@ Common principles used in API V2:
The following methods are supported:
- [Status](#status)
- [Get block hash](#get-block-hash)
- [Get transaction](#get-transaction)
- [Get transaction specific](#get-transaction-specific)
@ -50,6 +51,50 @@ The following methods are supported:
- [Get block](#get-block)
- [Send transaction](#send-transaction)
#### Status page
Status page returns current status of Blockbook and connected backend.
```
GET /api
```
Response:
```javascript
{
"blockbook": {
"coin": "Bitcoin",
"host": "blockbook",
"version": "0.3.0",
"gitCommit": "3d9ad91",
"buildTime": "2019-05-17T14:34:00+00:00",
"syncMode": true,
"initialSync": false,
"inSync": true,
"bestHeight": 577261,
"lastBlockTime": "2019-05-22T18:03:33.547762973+02:00",
"inSyncMempool": true,
"lastMempoolTime": "2019-05-22T18:10:10.27929383+02:00",
"mempoolSize": 17348,
"decimals": 8,
"dbSize": 191887866502,
"about": "Blockbook - blockchain indexer for TREZOR wallet https://trezor.io/. Do not use for any other purpose."
},
"backend": {
"chain": "main",
"blocks": 577261,
"headers": 577261,
"bestBlockHash": "0000000000000000000ca8c902aa58b3118a7f35d093e25a07f17bcacd91cabf",
"difficulty": "6704632680587.417",
"sizeOnDisk": 250504188580,
"version": "180000",
"subversion": "/Satoshi:0.18.0/",
"protocolVersion": "70015",
"timeOffset": 0,
"warnings": ""
}
}
```
#### Get block hash
```
GET /api/v2/block-index/<block height>
@ -106,10 +151,10 @@ Response for Bitcoin-type coins:
]
}
],
"blockhash": "78d1f3de899a10dd2e580704226ebf9508e95e1706f177fc9c31c47f245d2502",
"blockheight": 2647927,
"blockHash": "78d1f3de899a10dd2e580704226ebf9508e95e1706f177fc9c31c47f245d2502",
"blockHeight": 2647927,
"confirmations": 1,
"blocktime": 1553088212,
"blockTime": 1553088212,
"value": "55795008999999",
"valueIn": "55795108999999",
"fees": "100000000",
@ -117,7 +162,7 @@ Response for Bitcoin-type coins:
}
```
Response for Ethereum-type coins. There is always only one *vin*, only one *vout*, possibly an array of *tokentransfers* and *ethereumspecific* part. Missing is *hex* field:
Response for Ethereum-type coins. There is always only one *vin*, only one *vout*, possibly an array of *tokenTransfers* and *ethereumSpecific* part. Missing is *hex* field:
```javascript
{
@ -139,13 +184,13 @@ Response for Ethereum-type coins. There is always only one *vin*, only one *vout
]
}
],
"blockhash": "0x39df7fb0893200e1e78c04f98691637a89b64e7a3edd96c16f2537e2fd56c414",
"blockheight": 5241585,
"blockHash": "0x39df7fb0893200e1e78c04f98691637a89b64e7a3edd96c16f2537e2fd56c414",
"blockHeight": 5241585,
"confirmations": 3,
"blocktime": 1553088337,
"blockTime": 1553088337,
"value": "0",
"fees": "402501000000000",
"tokentransfers": [
"tokenTransfers": [
{
"type": "ERC20",
"from": "0x9c2e011c0ce0d75c2b62b9c5a0ba0a7456593803",
@ -157,18 +202,18 @@ Response for Ethereum-type coins. There is always only one *vin*, only one *vout
"value": "133800000"
}
],
"ethereumspecific": {
"ethereumSpecific": {
"status": 1,
"nonce": 2830,
"gaslimit": 36591,
"gasused": 36591,
"gasprice": "11000000000"
"gasLimit": 36591,
"gasUsed": 36591,
"gasPrice": "11000000000"
}
}
```
A note about the `blocktime` field:
- for already mined transaction (`confirmations > 0`), the field `blocktime` contains time of the block
A note about the `blockTime` field:
- for already mined transaction (`confirmations > 0`), the field `blockTime` contains time of the block
- for transactions in mempool (`confirmations == 0`), the field contains time when the running instance of Blockbook was first time notified about the transaction. This time may be different in different instances of Blockbook.
#### Get transaction specific
@ -179,7 +224,7 @@ Returns transaction data in the exact format as returned by backend, including a
GET /api/v2/tx-specific/<txid>
```
Response:
Example response:
```javascript
{
@ -321,7 +366,7 @@ Response:
"57833d50969208091bd6c950599a1b5cf9d66d992ae8a8d3560fb943b98ebb23",
"9cfd6295f20e74ddca6dd816c8eb71a91e4da70fe396aca6f8ce09dc2947839f",
],
"totalTokens": 2,
"usedTokens": 2,
"tokens": [
{
"type": "XPUBAddress",
@ -347,12 +392,14 @@ Response:
}
```
Note: *totalTokens* always returns total number of **used** addresses of xpub.
Note: *usedTokens* always returns total number of **used** addresses of xpub.
#### Get utxo
Returns array of unspent transaction outputs of address or xpub, applicable only for Bitcoin-type coins. By default, the list contains both confirmed and unconfirmed transactions. The query parameter *confirmed=true* disables return of unconfirmed transactions. The returned utxos are sorted by block height, newest blocks first. For xpubs the response also contains address and derivation path of the utxo.
Unconfirmed utxos do not have field *height*, the field *confirmations* has value *0* and may contain field *lockTime*, if not zero.
```
GET /api/v2/utxo/<address|xpub>[?confirmed=true]
```
@ -365,8 +412,8 @@ Response:
"txid": "13d26cd939bf5d155b1c60054e02d9c9b832a85e6ec4f2411be44b6b5a2842e9",
"vout": 0,
"value": "1422303206539",
"height": 2648082,
"confirmations": 8
"confirmations": 0,
"lockTime": 2648100
},
{
"txid": "de4f379fdc3ea9be063e60340461a014f372a018d70c3db35701654e7066b3ef",
@ -401,14 +448,14 @@ Response:
"totalPages": 1,
"itemsOnPage": 1000,
"hash": "760f8ed32894ccce9c1ea11c8a019cadaa82bcb434b25c30102dd7e43f326217",
"previousblockhash": "786a1f9f38493d32fd9f9c104d748490a070bc74a83809103bcadd93ae98288f",
"nextblockhash": "151615691b209de41dda4798a07e62db8429488554077552ccb1c4f8c7e9f57a",
"previousBlockHash": "786a1f9f38493d32fd9f9c104d748490a070bc74a83809103bcadd93ae98288f",
"nextBlockHash": "151615691b209de41dda4798a07e62db8429488554077552ccb1c4f8c7e9f57a",
"height": 2648059,
"confirmations": 47,
"size": 951,
"time": 1553096617,
"version": 6422787,
"merkleroot": "6783f6083788c4f69b8af23bd2e4a194cf36ac34d590dfd97e510fe7aebc72c8",
"merkleRoot": "6783f6083788c4f69b8af23bd2e4a194cf36ac34d590dfd97e510fe7aebc72c8",
"nonce": "0",
"bits": "1a063f3b",
"difficulty": "2685605.260733312",
@ -431,10 +478,10 @@ Response:
]
}
],
"blockhash": "760f8ed32894ccce9c1ea11c8a019cadaa82bcb434b25c30102dd7e43f326217",
"blockheight": 2648059,
"blockHash": "760f8ed32894ccce9c1ea11c8a019cadaa82bcb434b25c30102dd7e43f326217",
"blockHeight": 2648059,
"confirmations": 47,
"blocktime": 1553096617,
"blockTime": 1553096617,
"value": "1000100000000",
"valueIn": "0",
"fees": "0"
@ -467,10 +514,10 @@ Response:
]
}
],
"blockhash": "760f8ed32894ccce9c1ea11c8a019cadaa82bcb434b25c30102dd7e43f326217",
"blockheight": 2648059,
"blockHash": "760f8ed32894ccce9c1ea11c8a019cadaa82bcb434b25c30102dd7e43f326217",
"blockHeight": 2648059,
"confirmations": 47,
"blocktime": 1553096617,
"blockTime": 1553096617,
"value": "1277495845202",
"valueIn": "1277595845202",
"fees": "100000000"
@ -508,4 +555,22 @@ or in case of error
### Websocket API
Websocket interface is provided at `/websocket/`. The interface also can be explored using Blockbook Websocket Test Page found at `/test-websocket.html`.
Websocket interface is provided at `/websocket/`. The interface can be explored using Blockbook Websocket Test Page found at `/test-websocket.html`.
The websocket interface provides the following requests:
- getInfo
- getBlockHash
- getAccountInfo
- getAccountUtxo
- getTransaction
- getTransactionSpecific
- estimateFee
- sendTransaction
The client can subscribe to the following events:
- new block added to blockchain
- new transaction for given address (list of addresses)
There can be always only one subscription of given event per connection, i.e. new list of addresses replaces previous list of addresses.

View File

@ -26,6 +26,11 @@
| Koto | 9051 | 9151 | 8051 | 38351 |
| Bellcoin | 9052 | 9152 | 8052 | 38352 |
| NULS | 9053 | 9153 | 8053 | 38353 |
| Viacoin | 9055 | 9155 | 8055 | 38355 |
| VIPSTARCOIN | 9056 | 9156 | 8056 | 38356 |
| MonetaryUnit | 9057 | 9157 | 8057 | 38357 |
| ZelCash | 9058 | 9158 | 8058 | 38358 |
| Ravencoin | 9059 | 9159 | 8059 | 38359 |
| Flo | 9066 | 9166 | 8066 | 38366 |
| Polis | 9067 | 9167 | 8067 | 38367 |
| Qtum | 9088 | 9188 | 8088 | 38388 |

View File

@ -10,17 +10,18 @@
>- *[]* array
>
>Types used in the description:
>- *[]byte* - array of bytes
>- *[]byte* - variable length array of bytes
>- *[32]byte* - fixed length array of bytes (32 bytes long in this case)
>- *uint32* - unsigned integer, stored as array of 4 bytes in big endian*
>- *vint*, *vuint* - variable length signed/unsigned int
>- *addrDesc* - address descriptor, abstraction of an address.
For Bitcoin type coins it is the transaction output script, stored as variable length array of bytes.
For Ethereum type coins it is fixed size array of 20 bytes.
>- *bigInt* - unsigned big integer, stored as length of the array (1 byte) followed by array of bytes of big int, i.e. *(int_len byte)+(int_value []byte)*. Zero is stored as one byte containing 0.
>- *bigInt* - unsigned big integer, stored as length of the array (1 byte) followed by array of bytes of big int, i.e. *(int_len byte)+(int_value []byte)*. Zero is stored as one byte of value 0.
**Database structure:**
The database structure described here is of Blockbook version **0.2.0** (data format version 4).
The database structure described here is of Blockbook version **0.3.0** (internal data format version 5).
The database structure for **Bitcoin type** and **Ethereum type** coins is slightly different. Column families used for both types:
- default, height, addresses, transactions, blockTxs
@ -39,10 +40,10 @@ Column families used only by **Ethereum type** coins:
Most important internal state values are:
- coin - which coin is indexed in DB
- data format version - currently 4
- data format version - currently 5
- dbState - closed, open, inconsistent
Blockbook is on startup checking these values and does not allow to run against wrong coin, data format version and in inconsistent state. The database must be recreated if the internal state does not match.
Blockbook is checking on startup these values and does not allow to run against wrong coin, data format version and in inconsistent state. The database must be recreated if the internal state does not match.
- **height**
@ -66,9 +67,10 @@ Column families used only by **Ethereum type** coins:
- **addressBalance** (used only by Bitcoin type coins)
Maps *addrDesc* to *number of transactions*, *sent amount* and *total balance* of given address.
Maps *addrDesc* to *number of transactions*, *sent amount*, *total balance* and a list of *unspent transactions outputs (UTXOs)*, ordered from oldest to newest
```
(addrDesc []byte) -> (nr_txs vuint)+(sent_amount bigInt)+(balance bigInt)
(addrDesc []byte) -> (nr_txs vuint)+(sent_amount bigInt)+(balance bigInt)+
[]((txid [32]byte)+(vout vuint)+(block_height vuint)+(block_height vuint)+(amount bigInt))
```
- **txAddresses** (used only by Bitcoin type coins)
@ -113,3 +115,6 @@ Column families used only by **Ethereum type** coins:
```
(txid []byte) -> (txdata []byte)
```
The `txid` field as specified in this documentation is a byte array of fixed size with length 32 bytes (*[32]byte*), however some coins may define other fixed size lengths.

View File

@ -42,7 +42,7 @@ It perfectly fits with layered test definitions. For example, you can:
* run tests for single coin `make test-integration ARGS="-run=TestIntegration/bitcoin/"`
* run single test suite `make test-integration ARGS="-run=TestIntegration//sync/"`
* run single test `make test-integration ARGS="-run=TestIntegration//sync/HandleFork"`
* run tests for set of coins `make test-integration ARGS="-run='TestIntegration/(bcash|bgold|bitcoin|dash|dogecoin|litecoin|vertcoin|zcash)/'"`
* run tests for set of coins `make test-integration ARGS="-run='TestIntegration/(bcash|bgold|bitcoin|dash|dogecoin|litecoin|vertcoin|zcash|zelcash)/'"`
Test fixtures are defined in *testdata* directory in package of particular test suite. They are separate JSON files named
by coin. File schemes are very similar with verbose results of CLI tools and are described below. Integration tests

File diff suppressed because one or more lines are too long

View File

@ -300,6 +300,18 @@ var requestHandlers = map[string]func(*WebsocketServer, *websocketChannel, *webs
},
}
func sendResponse(c *websocketChannel, req *websocketReq, data interface{}) {
defer func() {
if r := recover(); r != nil {
glog.Error("Client ", c.id, ", onRequest ", req.Method, " recovered from panic: ", r)
}
}()
c.out <- &websocketRes{
ID: req.ID,
Data: data,
}
}
func (s *WebsocketServer) onRequest(c *websocketChannel, req *websocketReq) {
var err error
var data interface{}
@ -313,10 +325,7 @@ func (s *WebsocketServer) onRequest(c *websocketChannel, req *websocketReq) {
}
// nil data means no response
if data != nil {
c.out <- &websocketRes{
ID: req.ID,
Data: data,
}
sendResponse(c, req, data)
}
}()
t := time.Now()
@ -426,9 +435,9 @@ func (s *WebsocketServer) getInfo() (interface{}, error) {
Shortcut string `json:"shortcut"`
Decimals int `json:"decimals"`
Version string `json:"version"`
BestHeight int `json:"bestheight"`
BestHash string `json:"besthash"`
Block0Hash string `json:"block0hash"`
BestHeight int `json:"bestHeight"`
BestHash string `json:"bestHash"`
Block0Hash string `json:"block0Hash"`
Testnet bool `json:"testnet"`
}
return &info{

View File

@ -27,10 +27,10 @@
</tr>
<tr>
<td>Used XPUB Addresses</td>
<td class="data">{{$addr.TotalTokens}}</td>
<td class="data">{{$addr.UsedTokens}}</td>
</tr>
<tr>
{{- if or $addr.Tokens $addr.TotalTokens -}}
{{- if or $addr.Tokens $addr.UsedTokens -}}
<td>{{if $data.NonZeroBalanceTokens}}XPUB Addresses with Balance{{else}}XPUB Addresses{{end}}</td>
<td style="padding: 0;">
<table class="table data-table">

View File

@ -8,6 +8,7 @@ import (
"github.com/golang/glog"
)
// Txids, Xpubs and Addresses
const (
TxidB1T1 = "00b2c06055e5e90e9c82bd4181fde310104391a7fa4f289b1704e5d90caa3840"
TxidB1T2 = "effd9ef509383d536b1c8af5bf434c8efbf521a4f2befd4022bbd68694b4ac75"
@ -28,8 +29,11 @@ const (
Addr8 = "2N6utyMZfPNUb1Bk8oz7p2JqJrXkq83gegu" // a91495e9fbe306449c991d314afe3c3567d5bf78efd287, xpub m/49'/1'/33'/1/3
Addr9 = "mmJx9Y8ayz9h14yd9fgCW1bUKoEpkBAquP" // 76a9143f8ba3fda3ba7b69f5818086e12223c6dd25e3c888ac
AddrA = "mzVznVsCHkVHX9UN8WPFASWUUHtxnNn4Jj" // 76a914d03c0d863d189b23b061a95ad32940b65837609f88ac
TxidB2T1Output3OpReturn = "6a072020f1686f6a20"
)
// Amounts in satoshis
var (
SatZero = big.NewInt(0)
SatB1T1A1 = big.NewInt(100000000)
@ -45,6 +49,7 @@ var (
SatB2T4AA = big.NewInt(1360030331)
)
// AddressToPubKeyHex is a utility conversion function
func AddressToPubKeyHex(addr string, parser bchain.BlockChainParser) string {
if addr == "" {
return ""
@ -56,6 +61,7 @@ func AddressToPubKeyHex(addr string, parser bchain.BlockChainParser) string {
return hex.EncodeToString(b)
}
// GetTestBitcoinTypeBlock1 returns block #1
func GetTestBitcoinTypeBlock1(parser bchain.BlockChainParser) *bchain.Block {
return &bchain.Block{
BlockHeader: bchain.BlockHeader{
@ -122,6 +128,7 @@ func GetTestBitcoinTypeBlock1(parser bchain.BlockChainParser) *bchain.Block {
}
}
// GetTestBitcoinTypeBlock2 returns block #2
func GetTestBitcoinTypeBlock2(parser bchain.BlockChainParser) *bchain.Block {
return &bchain.Block{
BlockHeader: bchain.BlockHeader{
@ -161,6 +168,13 @@ func GetTestBitcoinTypeBlock2(parser bchain.BlockChainParser) *bchain.Block {
},
ValueSat: *SatB2T1A7,
},
{
N: 2,
ScriptPubKey: bchain.ScriptPubKey{
Hex: TxidB2T1Output3OpReturn, // OP_RETURN script
},
ValueSat: *SatZero,
},
},
Blocktime: 22549400000,
Time: 22549400000,

View File

@ -5,6 +5,7 @@ import (
"encoding/hex"
)
// Addresses
const (
EthAddr3e = "3e3a3d69dc66ba10737f531ed088954a9ec89d97"
EthAddr55 = "555ee11fbddc0e49a9bab358a8941ad95ffdb48f"
@ -42,6 +43,7 @@ func unpackTxs(packed []string, parser bchain.BlockChainParser) []bchain.Tx {
return r
}
// GetTestEthereumTypeBlock1 returns block #1
func GetTestEthereumTypeBlock1(parser bchain.BlockChainParser) *bchain.Block {
return &bchain.Block{
BlockHeader: bchain.BlockHeader{
@ -55,6 +57,7 @@ func GetTestEthereumTypeBlock1(parser bchain.BlockChainParser) *bchain.Block {
}
}
// GetTestEthereumTypeBlock2 returns block #2
func GetTestEthereumTypeBlock2(parser bchain.BlockChainParser) *bchain.Block {
return &bchain.Block{
BlockHeader: bchain.BlockHeader{

View File

@ -17,7 +17,7 @@ func NewFakeBlockChain(parser bchain.BlockChainParser) (bchain.BlockChain, error
return &fakeBlockChain{&bchain.BaseChain{Parser: parser}}, nil
}
func (b *fakeBlockChain) CreateMempool(chain bchain.BlockChain) (bchain.Mempool, error) {
func (c *fakeBlockChain) CreateMempool(chain bchain.BlockChain) (bchain.Mempool, error) {
return bchain.NewMempoolBitcoinType(chain, 1, 1), nil
}
@ -189,6 +189,6 @@ func (c *fakeBlockChain) GetChainParser() bchain.BlockChainParser {
}
// GetMempoolTransactions returns transactions in mempool
func (b *fakeBlockChain) GetMempoolTransactions() ([]string, error) {
func (c *fakeBlockChain) GetMempoolTransactions() ([]string, error) {
return nil, errors.New("Not implemented")
}

View File

@ -19,6 +19,7 @@ import (
"sort"
"strings"
"testing"
"time"
"github.com/martinboehm/btcutil/chaincfg"
)
@ -81,7 +82,7 @@ func runTests(t *testing.T, coin string, cfg map[string]json.RawMessage) {
if err == notConnectedError {
t.Fatal(err)
}
t.Fatalf("Cannot make blockchain config: %s", err)
t.Fatalf("Cannot init blockchain: %s", err)
}
for test, c := range cfg {
@ -153,7 +154,7 @@ func initBlockChain(coinName string, cfg json.RawMessage) (bchain.BlockChain, bc
return nil, nil, fmt.Errorf("Factory function not found")
}
cli, err := factory(cfg, func(_ bchain.NotificationType) {})
chain, err := factory(cfg, func(_ bchain.NotificationType) {})
if err != nil {
if isNetError(err) {
return nil, nil, notConnectedError
@ -161,25 +162,32 @@ func initBlockChain(coinName string, cfg json.RawMessage) (bchain.BlockChain, bc
return nil, nil, fmt.Errorf("Factory function failed: %s", err)
}
err = cli.Initialize()
if err != nil {
for i := 0; ; i++ {
err = chain.Initialize()
if err == nil {
break
}
if isNetError(err) {
return nil, nil, notConnectedError
}
return nil, nil, fmt.Errorf("BlockChain initialization failed: %s", err)
// wait max 5 minutes for backend to startup
if i > 5*60 {
return nil, nil, fmt.Errorf("BlockChain initialization failed: %s", err)
}
time.Sleep(time.Millisecond * 1000)
}
mempool, err := cli.CreateMempool(cli)
mempool, err := chain.CreateMempool(chain)
if err != nil {
return nil, nil, fmt.Errorf("Mempool creation failed: %s", err)
}
err = cli.InitializeMempool(nil, nil)
err = chain.InitializeMempool(nil, nil)
if err != nil {
return nil, nil, fmt.Errorf("Mempool initialization failed: %s", err)
}
return cli, mempool, nil
return chain, mempool, nil
}
func isNetError(err error) bool {

View File

@ -1,7 +1,7 @@
{
"blockHeight": 1205643,
"blockHash": "60c80ce4f0a90f7b217bef8fab32ce2e499a4dc2067f4a6062048e3b940d76c6",
"blockTime": 1514835214,
"blockTime": 1514835214,
"blockTxs": [
"38b4a919ab0f8b4570e1f26717740846b7994ba22f94c51d96eb32bc45c5886e",
"7f793aff2f74ca9cec6df08a7c0fcaa1ea301468459b46edbed693a871832298",
@ -52,4 +52,4 @@
]
}
}
}
}

View File

@ -0,0 +1,52 @@
{
"blockHeight": 447973,
"blockHash": "c81fe0f3524938c618156449d27ede51c2c76b73e00e83cdb9b599181ca505d5",
"blockTime": 1556541002,
"blockTxs": [
"062f1f851b5d350f860a2c96c98b6dac8030f84bb02df42b5608619abf6049c8",
"72502137b0924c69400eec74deacc57e9290f32a6ba2f5559f17256c9c06a347"
],
"txDetails": {
"72502137b0924c69400eec74deacc57e9290f32a6ba2f5559f17256c9c06a347": {
"hex": "0100000001ac2e21335c11482009e10d78ddf0378e2e778f09ec01077d3ac7747ac4f3eae5010000004847304402201de5db6155e047897ea2fed2a8cbc1d717fb8c5b1a18964cd4942c800db1a218022035a6c8aed82c582db22f4835dbd86a283c7ed3b84eaf1f68932b910dfd08d1be01ffffffff030000000000000000008e36f7c90d00000023210219e5714cbba5933a9c09bc93330efece366f61910ee1cc38b245e420063ca3f2ac00d2496b000000001976a914a1f4fccad310761f842f3cdcc41b5ca3220fd30488ac00000000",
"txid": "72502137b0924c69400eec74deacc57e9290f32a6ba2f5559f17256c9c06a347",
"blocktime": 1556541002,
"time": 1556541002,
"locktime": 0,
"version": 1,
"vin": [
{
"txid": "e5eaf3c47a74c73a7d0701ec098f772e8e37f0dd780de1092048115c33212eac",
"vout": 1,
"scriptSig": {
"hex": "47304402201de5db6155e047897ea2fed2a8cbc1d717fb8c5b1a18964cd4942c800db1a218022035a6c8aed82c582db22f4835dbd86a283c7ed3b84eaf1f68932b910dfd08d1be01"
},
"sequence": 4294967295
}
],
"vout": [
{
"value": 0.00000000,
"n": 0,
"scriptPubKey": {
"hex": ""
}
},
{
"value": 592.22996622,
"n": 1,
"scriptPubKey": {
"hex": "210219e5714cbba5933a9c09bc93330efece366f61910ee1cc38b245e420063ca3f2ac"
}
},
{
"value": 18,
"n": 2,
"scriptPubKey": {
"hex": "76a914a1f4fccad310761f842f3cdcc41b5ca3220fd30488ac"
}
}
]
}
}
}

119
tests/rpc/testdata/ravencoin.json vendored 100644
View File

@ -0,0 +1,119 @@
{
"blockHeight": 707554,
"blockHash": "000000000000429dbd10e090e00384199e85555d501cfa47720e5261c66942a6",
"blockTime": 1557858845,
"blockTxs": [
"194a89653371595337c306e559d71a94a72f3395c8801189cb396ce6330636cd",
"58ce37e86e5c17e3c08ef14c55d994aaf4c11003fd749aa597b141c89d22c582",
"9d3e19d6f147c362410fa361b0cae9690e8de1c727e7a99bc145e7d95580a314",
"739b0ef5a5d8314f84f61105b107b7534dc9748b288fcd06dc8087783ce26e20"
],
"txDetails": {
"58ce37e86e5c17e3c08ef14c55d994aaf4c11003fd749aa597b141c89d22c582": {
"hex": "02000000025496421e6a3627ec4a5eab571ff22b3a1a5aa7555d48b7013976eef18443a115000000006a473044022027e2ef2d5057e03864373e6a3bbf8206af18bd64c817a42979f784688a703c4d02203230b32182046d01a16ea765f85219fd3ba8b2d731eb4a0446d968da170da5a5012102f721c9e757b10fe342869b92ba76c4fbcc74d634fc9536b9c068fc033488d655feffffff886c20d01cc0647f0bff7fea3506d88fd7a49dcc17de2938846f6a52055d1f6e800000006b483045022100cf6c8d2452c5f146e317a77ccafdf6cb516c3050f9d6e71d0d834db8a02d1cc7022041823ee041589a1fcdb43d5ce121cb6470506f6726a4f667d6d20af894a034260121035e5dfde430f2d5f832c4df4a9a251293b0ba29995aee49a6908586de22cbc8c3feffffff0295117782570600001976a9147f7d88fa55fab938161953faf6bd3d026cd078dc88acf2420f00000000001976a914e8fd48114696f4e6ee5b3e5d532c408a19f691b788ac92cb0a00",
"txid": "58ce37e86e5c17e3c08ef14c55d994aaf4c11003fd749aa597b141c89d22c582",
"blocktime": 1557858845,
"time": 1557858845,
"locktime": 707474,
"version": 2,
"vin": [
{
"txid": "15a14384f1ee763901b7485d55a75a1a3a2bf21f57ab5e4aec27366a1e429654",
"vout": 0,
"sequence": 4294967294,
"scriptSig": {
"hex": "473044022027e2ef2d5057e03864373e6a3bbf8206af18bd64c817a42979f784688a703c4d02203230b32182046d01a16ea765f85219fd3ba8b2d731eb4a0446d968da170da5a5012102f721c9e757b10fe342869b92ba76c4fbcc74d634fc9536b9c068fc033488d655"
}
},
{
"txid": "6e1f5d05526a6f843829de17cc9da4d78fd80635ea7fff0b7f64c01cd0206c88",
"vout": 128,
"sequence": 4294967294,
"scriptSig": {
"hex": "483045022100cf6c8d2452c5f146e317a77ccafdf6cb516c3050f9d6e71d0d834db8a02d1cc7022041823ee041589a1fcdb43d5ce121cb6470506f6726a4f667d6d20af894a034260121035e5dfde430f2d5f832c4df4a9a251293b0ba29995aee49a6908586de22cbc8c3"
}
}
],
"vout": [
{
"value": 69729.20762773,
"n": 0,
"scriptPubKey": {
"hex": "76a9147f7d88fa55fab938161953faf6bd3d026cd078dc88ac",
"addresses": [
"RLuJHD2GRHzTbwc47Cg43oAJQZRMMVcU7S"
]
}
},
{
"value": 0.01000178,
"n": 1,
"scriptPubKey": {
"hex": "76a914e8fd48114696f4e6ee5b3e5d532c408a19f691b788ac",
"addresses": [
"RWX8GL2MHTewUvEcJMwu1cSZEhrSitG9Nz"
]
}
}
]
},
"9d3e19d6f147c362410fa361b0cae9690e8de1c727e7a99bc145e7d95580a314": {
"hex": "0200000004152c754032d6d014e21a10f68cd0d1f62b29ad3997bc637f434f513240a6393a370000006b483045022100f397fe990bc6871408a8178a4cac7afc27a989abd8dc32536d4b392e2d46d7d502205aafc72f6b76bfee3373da3786492c8d24107d1005751f25e6be4f8788d85a8e012103f8a3801f6e45a63821c41c8cd5baa1aca5760e2dac0edce67ea1721e3a63a35dfeffffff1d8cedc545c969e9fe0b74465c47081bc25d9c423a1668c9ede948c5627aa78f020000006a4730440220580ccb55105732dca7ae5e338c9d9af1651c1d8a9d167a0dfea41b977c38f67502205a5fe94fb9190a97f8bff5ee94e603323da36c0383a1d6fbf27b499bf900a24d012103f8a3801f6e45a63821c41c8cd5baa1aca5760e2dac0edce67ea1721e3a63a35dfeffffffb832371edbbd6ae9498ec3f33cc61333d1cfb0497f4600cbc0a05ceae82e39e7640000006b483045022100906a567b719038f7b5e0750e5c8ff4b1356dd28ecd867edfb7bf65623b50c64d02203bfe4bd0d08b4caff78112aa63bb4fbfb6804c428488dd5ac30266f0f2ba1fa9012103f8a3801f6e45a63821c41c8cd5baa1aca5760e2dac0edce67ea1721e3a63a35dfeffffffc20447c40b451c4ccb643863a3d06ecbf96681d8f5dace42a80a0e1b7627ec62660000006b483045022100e9912569e224fee0f9febfc1a9356918229461d323c891d93b21e2396bf8e3e2022053c814c6661b3fdabc838d0ea77585b9c340f659fbe902dc5cf7559939350756012103f8a3801f6e45a63821c41c8cd5baa1aca5760e2dac0edce67ea1721e3a63a35dfeffffff0269ef8a00000000001976a9148273705545d167297b7278503447d809d9e84b2e88ac003c9476520200001976a9147cf41bc3cdba1b8edf811877ab1cc9493847a3e488ace0cb0a00",
"txid": "9d3e19d6f147c362410fa361b0cae9690e8de1c727e7a99bc145e7d95580a314",
"blocktime": 1557858845,
"time": 1557858845,
"locktime": 707552,
"version": 2,
"vin": [
{
"txid": "3a39a64032514f437f63bc9739ad292bf6d1d08cf6101ae214d0d63240752c15",
"vout": 55,
"sequence": 4294967294,
"scriptSig": {
"hex": "483045022100f397fe990bc6871408a8178a4cac7afc27a989abd8dc32536d4b392e2d46d7d502205aafc72f6b76bfee3373da3786492c8d24107d1005751f25e6be4f8788d85a8e012103f8a3801f6e45a63821c41c8cd5baa1aca5760e2dac0edce67ea1721e3a63a35d"
}
},
{
"txid": "8fa77a62c548e9edc968163a429c5dc21b08475c46740bfee969c945c5ed8c1d",
"vout": 2,
"sequence": 4294967294,
"scriptSig": {
"hex": "4730440220580ccb55105732dca7ae5e338c9d9af1651c1d8a9d167a0dfea41b977c38f67502205a5fe94fb9190a97f8bff5ee94e603323da36c0383a1d6fbf27b499bf900a24d012103f8a3801f6e45a63821c41c8cd5baa1aca5760e2dac0edce67ea1721e3a63a35d"
}
},
{
"txid": "e7392ee8ea5ca0c0cb00467f49b0cfd13313c63cf3c38e49e96abddb1e3732b8",
"vout": 100,
"sequence": 4294967294,
"scriptSig": {
"hex": "483045022100906a567b719038f7b5e0750e5c8ff4b1356dd28ecd867edfb7bf65623b50c64d02203bfe4bd0d08b4caff78112aa63bb4fbfb6804c428488dd5ac30266f0f2ba1fa9012103f8a3801f6e45a63821c41c8cd5baa1aca5760e2dac0edce67ea1721e3a63a35d"
}
},
{
"txid": "62ec27761b0e0aa842cedaf5d88166f9cb6ed0a3633864cb4c1c450bc44704c2",
"vout": 102,
"sequence": 4294967294,
"scriptSig": {
"hex": "483045022100e9912569e224fee0f9febfc1a9356918229461d323c891d93b21e2396bf8e3e2022053c814c6661b3fdabc838d0ea77585b9c340f659fbe902dc5cf7559939350756012103f8a3801f6e45a63821c41c8cd5baa1aca5760e2dac0edce67ea1721e3a63a35d"
}
}
],
"vout": [
{
"value": 0.09105257,
"n": 0,
"scriptPubKey": {
"hex": "76a9148273705545d167297b7278503447d809d9e84b2e88ac"
}
},
{
"value": 25532.00000000,
"n": 1,
"scriptPubKey": {
"hex": "76a9147cf41bc3cdba1b8edf811877ab1cc9493847a3e488ac"
}
}
]
}
}
}

36
tests/rpc/testdata/viacoin.json vendored 100644
View File

@ -0,0 +1,36 @@
{
"blockHeight": 6197648,
"blockHash": "7bfaf2048ce581f05ea6c87cb043bb5908e6afa83ef49621bf558a027cfe367e",
"blockTime": 1555275169,
"blockTxs": [
"79f2c2690cae8e3e4631a30462197316228fe0e5cf4f2f2eee904d8dcd0206c6"
],
"txDetails": {
"79f2c2690cae8e3e4631a30462197316228fe0e5cf4f2f2eee904d8dcd0206c6": {
"hex": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff090390915e045cb39da1ffffffff01ca9a3b00000000001976a91424cc424c1e5e977175d2b20012554d39024bd68f88ac00000000",
"txid": "79f2c2690cae8e3e4631a30462197316228fe0e5cf4f2f2eee904d8dcd0206c6",
"blocktime": 1555275169,
"time": 1555275169,
"locktime": 0,
"version": 1,
"vin": [
{
"coinbase": "0390915e045cb39da1",
"sequence": 4294967295
}
],
"vout": [
{
"value": 0.0390625,
"n": 0,
"scriptPubKey": {
"hex": "76a91424cc424c1e5e977175d2b20012554d39024bd68f88ac",
"addresses": [
"VdMPvn7vUTSzbYjiMDs1jku9wAh1Ri2Y1A"
]
}
}
]
}
}
}

View File

@ -0,0 +1,53 @@
{
"blockHeight": 537194,
"blockHash": "2ec778923888428f75e89c28625e22ebb11ce71b16b909314c661a646fa98bfc",
"blockTime": 1555853392,
"blockTxs": [
"43087ee6460ab870f42712ed95d047b52d460227e2d2d62f70f70d290a1ee813",
"a7de2ccebee8c090bf286177842944a0db0e2733b084fa56572dda0c2d4e3216",
"d7ef9e537f6eb6d02f99f3eee65a8b4f75b5c5c5906247dff386e625b1aabef3"
],
"txDetails": {
"a7de2ccebee8c090bf286177842944a0db0e2733b084fa56572dda0c2d4e3216": {
"hex": "02000000018226c05828e931e6e24a48cc7615c6aa8964b17ffaa6acb6ef34987eba136e0a0200000049483045022100ca50e41692a9b83622a5ce9e8626f2c7a529c9a32bfb2aea8bd038ad36124bce022034381e1394e536f5c24ce4576dc86eee1c3239baadbe0e41365b1f2eee4f277601ffffffff030000000000000000000013f506556e0000232102474875b1a3bb3f974ce93bcaccc639a1e12072e128ed389107894d9aa9babe39ac60b60507556e0000232102474875b1a3bb3f974ce93bcaccc639a1e12072e128ed389107894d9aa9babe39ac00000000",
"txid": "a7de2ccebee8c090bf286177842944a0db0e2733b084fa56572dda0c2d4e3216",
"blocktime": 1555853392,
"time": 1555853392,
"locktime": 0,
"version": 2,
"vin": [
{
"txid": "0a6e13ba7e9834efb6aca6fa7fb16489aac61576cc484ae2e631e92858c02682",
"vout": 2,
"scriptSig": {
"hex": "483045022100ca50e41692a9b83622a5ce9e8626f2c7a529c9a32bfb2aea8bd038ad36124bce022034381e1394e536f5c24ce4576dc86eee1c3239baadbe0e41365b1f2eee4f277601"
},
"sequence": 4294967295
}
],
"vout": [
{
"value": 0.00000000,
"n": 0,
"scriptPubKey": {
"hex": ""
}
},
{
"value": 1213114.68000000,
"n": 1,
"scriptPubKey": {
"hex": "2102474875b1a3bb3f974ce93bcaccc639a1e12072e128ed389107894d9aa9babe39ac"
}
},
{
"value": 1213114.69090400,
"n": 2,
"scriptPubKey": {
"hex": "2102474875b1a3bb3f974ce93bcaccc639a1e12072e128ed389107894d9aa9babe39ac"
}
}
]
}
}
}

189
tests/rpc/testdata/zelcash.json vendored 100644
View File

@ -0,0 +1,189 @@
{
"blockHeight": 331897,
"blockHash": "000000241b30db061faa43420fdcc9e85a8c29c2f331c523523b32ce8529e6b6",
"blockTime": 1557270310,
"blockTxs": [
"21a06cd256d3f7e754f174721c1f44aac5cd3cdbbdc23087d2efec1a33288d20",
"e44bc8dc1b96af8e3c2d13211fa8c18de1644888445890bb61d618b055833c14",
"625c91358d9add9cbdaf2e8df9633e31d6dec0095121e4483b7d783fb0d8d828",
"d33ad66ecc5bdb9ad5430412f7c5b5e71eb266d4accb83d6b5209a30e0886360",
"d8e714f869542d9c183e971341933443abfb70f72883f3419fdb76563126329c",
"01af9a84d83e9676d235c4f1cf54040e67438c8e0a2b9a066a963d8bf32f69b2",
"23f82502fd9a8f8d29248607cf5025c5190b03dbfc594aca907fe11198ff1599",
"6b06458bfd6c1356c56ee87dc3956b947c75788b8944d028efb83be19dfe49b7",
"2af1c680c223b76d457a16c4fb05da2c83c3579802a8528ed3ed498e985a44f6",
"8b6a1054b148dc4beb5237002b627219d06dddfa4b4bebea5adfe15f20b48559",
"0b5b3a7ce7761877494a3f4b2b307d1a96648bf23239fa66dab7f4fbe068ebc2",
"8d669c48e4a513c94c9684fb8b6ec4c83e1e8bd24b42b4654e1c77726a028fed"
],
"txDetails": {
"625c91358d9add9cbdaf2e8df9633e31d6dec0095121e4483b7d783fb0d8d828": {
"hex": "0400008085202f89055d311a6096bd3bde1384373427e42b6b77c67e7895e071f15e00287157ff53f3000000006a473044022025fbea8e0eb05fe70b4501b441f3f50f259f109ea3a77bbc153e99aca9675a440220220a0a7dcb966a390c4a0a4f6fc1a0c7e59e2aad9a35f8cb0bf9de2b5248b991012102a0e47d64b0381c54abc5c535267a873bfba496a11346d1e4556749ba66a7b10ffeffffff8a0105244aebdaac02b8b26c26eb56c84661f03f6f0f2fb5656476b14f4aee5c000000006a4730440220183302d02c48dd325b0d41e4de6b2db203f1266ed3c2d6455a2e1e1201ade326022014528b7f948c0413890ee643dc13c5d112a81726a279d2049f482e9c0c47f307012103e32ac7699f228dfc6bd02b08ce5fe1869032f69dd78febfa396c3aad4a8b1f83feffffff8cc971fc0fab8348a8827bee546494dc6a5e749aa83f8811fc8c1297c9dbf1f2660000006a47304402205ca3ef28b263aa353c0451b2a3caaecf5a57aa6484f6ee07c7ccb82f79a3254902205b46464b11fbba5be703e32d52c02b2b124d0f98cc1d9387722d5472eefe014401210254d683cf6818c2c99882a24b546d6eb63d76f13413cb0a30ee5be0484fadd2d8feffffff24deb18b787528e3978d3b39f7b3886e6e89416146d7f42c72ce0c3d97f05c75000000006b483045022100b14ef898e3bf63ddce85e5ebde4eaa44d3f050d8994d24b8aff4a128cdd7d5a3022035a449d3ac32b98fdbd7c8ae0d2718b4039289e68543700f1dafd1aa91798f6f012102a10e9edbed2a96babea70b32e4f3197abaf21308a0809ffc344c0cc5a408aceffeffffffba73225c4b9f1368671825dc00e1fbc1b8c10172bcac5150e7ea2e01c628eb3d000000006a473044022074e2032663b19902d84c1ed16c452b8abda3fb07fa94283f5ab648e7f23bb9f1022017bbf83a921ffce04d3e5ccf365e08a447a2872c08d60bf2276df802efbce1f201210392dce1050001ea1f2a9a2d824c06fdc70c0e3bf2424ff45acd0512fc3f91e526feffffff02c045436a740000001976a91441283444bab7ea0a9fdd3e3890a927896cd3feb988accf430f00000000001976a914550597ca228081b4b3fb94cbbe534184fee9335288ac6e1005008d1005000000000000000000000000",
"txid": "625c91358d9add9cbdaf2e8df9633e31d6dec0095121e4483b7d783fb0d8d828",
"blocktime": 1557270310,
"time": 1557270310,
"locktime": 331886,
"version": 4,
"vin": [
{
"txid": "f353ff577128005ef171e095787ec6776b2be42734378413de3bbd96601a315d",
"vout": 0,
"sequence": 4294967294,
"n": 0,
"scriptSig": {
"hex": "473044022025fbea8e0eb05fe70b4501b441f3f50f259f109ea3a77bbc153e99aca9675a440220220a0a7dcb966a390c4a0a4f6fc1a0c7e59e2aad9a35f8cb0bf9de2b5248b991012102a0e47d64b0381c54abc5c535267a873bfba496a11346d1e4556749ba66a7b10f",
"asm": "3044022025fbea8e0eb05fe70b4501b441f3f50f259f109ea3a77bbc153e99aca9675a440220220a0a7dcb966a390c4a0a4f6fc1a0c7e59e2aad9a35f8cb0bf9de2b5248b991[ALL] 02a0e47d64b0381c54abc5c535267a873bfba496a11346d1e4556749ba66a7b10f"
},
"addr": "t1a6kYQgo1syNbwPNbCm2EAhVqz6JFD5FzN",
"valueSat": 305000000000,
"value": 3050,
"doubleSpentTxID": null
},
{
"txid": "5cee4a4fb1766465b52f0f6f3ff06146c856eb266cb2b802acdaeb4a2405018a",
"vout": 0,
"sequence": 4294967294,
"n": 1,
"scriptSig": {
"hex": "4730440220183302d02c48dd325b0d41e4de6b2db203f1266ed3c2d6455a2e1e1201ade326022014528b7f948c0413890ee643dc13c5d112a81726a279d2049f482e9c0c47f307012103e32ac7699f228dfc6bd02b08ce5fe1869032f69dd78febfa396c3aad4a8b1f83",
"asm": "30440220183302d02c48dd325b0d41e4de6b2db203f1266ed3c2d6455a2e1e1201ade326022014528b7f948c0413890ee643dc13c5d112a81726a279d2049f482e9c0c47f307[ALL] 03e32ac7699f228dfc6bd02b08ce5fe1869032f69dd78febfa396c3aad4a8b1f83"
},
"addr": "t1KAwQCWUngaEY63esqpL5ptQ5JkfRhFR9R",
"valueSat": 22162500000,
"value": 221.625,
"doubleSpentTxID": null
},
{
"txid": "f2f1dbc997128cfc11883fa89a745e6adc946454ee7b82a84883ab0ffc71c98c",
"vout": 102,
"sequence": 4294967294,
"n": 2,
"scriptSig": {
"hex": "47304402205ca3ef28b263aa353c0451b2a3caaecf5a57aa6484f6ee07c7ccb82f79a3254902205b46464b11fbba5be703e32d52c02b2b124d0f98cc1d9387722d5472eefe014401210254d683cf6818c2c99882a24b546d6eb63d76f13413cb0a30ee5be0484fadd2d8",
"asm": "304402205ca3ef28b263aa353c0451b2a3caaecf5a57aa6484f6ee07c7ccb82f79a3254902205b46464b11fbba5be703e32d52c02b2b124d0f98cc1d9387722d5472eefe0144[ALL] 0254d683cf6818c2c99882a24b546d6eb63d76f13413cb0a30ee5be0484fadd2d8"
},
"addr": "t1VoyaSqyh8CxHWQ7CupAY51G2HnKf7EVTw",
"valueSat": 1059791034,
"value": 10.59791034,
"doubleSpentTxID": null
},
{
"txid": "755cf0973d0cce722cf4d7466141896e6e88b3f7393b8d97e32875788bb1de24",
"vout": 0,
"sequence": 4294967294,
"n": 3,
"scriptSig": {
"hex": "483045022100b14ef898e3bf63ddce85e5ebde4eaa44d3f050d8994d24b8aff4a128cdd7d5a3022035a449d3ac32b98fdbd7c8ae0d2718b4039289e68543700f1dafd1aa91798f6f012102a10e9edbed2a96babea70b32e4f3197abaf21308a0809ffc344c0cc5a408acef",
"asm": "3045022100b14ef898e3bf63ddce85e5ebde4eaa44d3f050d8994d24b8aff4a128cdd7d5a3022035a449d3ac32b98fdbd7c8ae0d2718b4039289e68543700f1dafd1aa91798f6f[ALL] 02a10e9edbed2a96babea70b32e4f3197abaf21308a0809ffc344c0cc5a408acef"
},
"addr": "t1P92twuXiU6ryBa1eRKCaKUU6sfAwwhECt",
"valueSat": 157715260493,
"value": 1577.15260493,
"doubleSpentTxID": null
},
{
"txid": "3deb28c6012eeae75051acbc7201c1b8c1fbe100dc25186768139f4b5c2273ba",
"vout": 0,
"sequence": 4294967294,
"n": 4,
"scriptSig": {
"hex": "473044022074e2032663b19902d84c1ed16c452b8abda3fb07fa94283f5ab648e7f23bb9f1022017bbf83a921ffce04d3e5ccf365e08a447a2872c08d60bf2276df802efbce1f201210392dce1050001ea1f2a9a2d824c06fdc70c0e3bf2424ff45acd0512fc3f91e526",
"asm": "3044022074e2032663b19902d84c1ed16c452b8abda3fb07fa94283f5ab648e7f23bb9f1022017bbf83a921ffce04d3e5ccf365e08a447a2872c08d60bf2276df802efbce1f2[ALL] 0392dce1050001ea1f2a9a2d824c06fdc70c0e3bf2424ff45acd0512fc3f91e526"
},
"addr": "t1Vc2NKkijULG8LCRjCyDCT8Ryc7v6zCY1X",
"valueSat": 14062450000,
"value": 140.6245,
"doubleSpentTxID": null
}
],
"vout": [
{
"value": "4999.99000000",
"n": 0,
"scriptPubKey": {
"hex": "76a91441283444bab7ea0a9fdd3e3890a927896cd3feb988ac",
"asm": "OP_DUP OP_HASH160 41283444bab7ea0a9fdd3e3890a927896cd3feb9 OP_EQUALVERIFY OP_CHECKSIG",
"addresses": [
"t1Pp86EqzX3WgojH8jeNfQm28TcsKwkHghe"
],
"type": "pubkeyhash"
}
},
{
"value": "0.01000399",
"n": 1,
"scriptPubKey": {
"hex": "76a914550597ca228081b4b3fb94cbbe534184fee9335288ac",
"asm": "OP_DUP OP_HASH160 550597ca228081b4b3fb94cbbe534184fee93352 OP_EQUALVERIFY OP_CHECKSIG",
"addresses": [
"t1RdA9F99sXJttasG8VxKXNh2WxgLN963Hu"
],
"type": "pubkeyhash"
}
}
]
},
"01af9a84d83e9676d235c4f1cf54040e67438c8e0a2b9a066a963d8bf32f69b2": {
"hex": "0400008085202f8901c8c4858e2bb7d1243a03ad21fb84422d7678d9aec0d33a2318f3c84046443993000000006a473044022060b8a51b6deba492a7ea5beb26f9cb06c0bdecd6f678700e0ef7bcbe30fc66c70220684f2f1299926fe96bdefb0388bad63b5473265f1ee684dab66e8549fae3e63d012103fa2199f5eecadffc0f5069f5fd041217d85eb5889ffde4636f3b8974b473abc7feffffff03baf48380040000001976a91478a3a632d414b2eb129ae4edc6ced6cf862387ac88ac4e5f2251000000001976a9146a71e4371a442270dfd4b29de8a981e6cb4ad4d988acb18b746b000000001976a914a568940c7c4c961d1ff6fdf7d7f97aeb5db2cbfc88ac6e1005008d1005000000000000000000000000",
"txid": "01af9a84d83e9676d235c4f1cf54040e67438c8e0a2b9a066a963d8bf32f69b2",
"blocktime": 1557270310,
"time": 1557270310,
"locktime": 331886,
"version": 4,
"vin": [
{
"txid": "9339444640c8f318233ad3c0aed978762d4284fb21ad033a24d1b72b8e85c4c8",
"vout": 0,
"sequence": 4294967294,
"n": 0,
"scriptSig": {
"hex": "473044022060b8a51b6deba492a7ea5beb26f9cb06c0bdecd6f678700e0ef7bcbe30fc66c70220684f2f1299926fe96bdefb0388bad63b5473265f1ee684dab66e8549fae3e63d012103fa2199f5eecadffc0f5069f5fd041217d85eb5889ffde4636f3b8974b473abc7",
"asm": "3044022060b8a51b6deba492a7ea5beb26f9cb06c0bdecd6f678700e0ef7bcbe30fc66c70220684f2f1299926fe96bdefb0388bad63b5473265f1ee684dab66e8549fae3e63d[ALL] 03fa2199f5eecadffc0f5069f5fd041217d85eb5889ffde4636f3b8974b473abc7"
},
"addr": "t1gdnENdKNKFDvXijGUG7xdnGyj4To8LCpR",
"valueSat": 22500008143,
"value": 225.00008143,
"doubleSpentTxID": null
}
],
"vout": [
{
"value": "193.36000698",
"n": 0,
"scriptPubKey": {
"hex": "76a91478a3a632d414b2eb129ae4edc6ced6cf862387ac88ac",
"asm": "OP_DUP OP_HASH160 78a3a632d414b2eb129ae4edc6ced6cf862387ac OP_EQUALVERIFY OP_CHECKSIG",
"addresses": [
"t1UsV8uGDgP3jAJtYRwhYbgj2jLZYgjASS8"
],
"type": "pubkeyhash"
}
},
{
"value": "13.61207118",
"n": 1,
"scriptPubKey": {
"hex": "76a9146a71e4371a442270dfd4b29de8a981e6cb4ad4d988ac",
"asm": "OP_DUP OP_HASH160 6a71e4371a442270dfd4b29de8a981e6cb4ad4d9 OP_EQUALVERIFY OP_CHECKSIG",
"addresses": [
"t1TaS4zDnaVbL2kbmYtbqXwTn2jiuEDTJr9"
],
"type": "pubkeyhash"
}
},
{
"value": "18.02800049",
"n": 2,
"scriptPubKey": {
"hex": "76a914a568940c7c4c961d1ff6fdf7d7f97aeb5db2cbfc88ac",
"asm": "OP_DUP OP_HASH160 a568940c7c4c961d1ff6fdf7d7f97aeb5db2cbfc OP_EQUALVERIFY OP_CHECKSIG",
"addresses": [
"t1YxCnDW38Nf8ZMgw6QzQWq4YkS5KTxaGLE"
],
"type": "pubkeyhash"
}
}
]
}
}
}

View File

@ -25,10 +25,8 @@ func testConnectBlocks(t *testing.T, h *TestHandler) {
close(ch)
}
}, true)
if err != nil {
if !strings.HasPrefix(err.Error(), "connectBlocks interrupted at height") {
t.Fatal(err)
}
if err != nil && err != db.ErrOperationInterrupted {
t.Fatal(err)
}
height, _, err := d.GetBestBlock()

194
tests/sync/testdata/monacoin.json vendored 100644
View File

@ -0,0 +1,194 @@
{
"connectBlocks": {
"syncRanges": [
{"lower": 1146969, "upper": 1146989}
],
"blocks": {
"1146989": {
"height": 1146989,
"hash": "89300c393ff5d0737ee0908312116293142ba12ba6f4e7866d5bdae74062c0f3",
"noTxs": 3,
"txDetails": [
{
"txid": "04101c2f87fdb3186daea6652adbf7a14e6d96b32e211e6010af95a5c3fbc793",
"version": 2,
"vin": [
{
"txid": "58a8738f43223f797f39a08ffff8263019de79b39d8b9fbd20ab2a97dc2bc4f2",
"vout": 0,
"scriptSig": {
"hex": "4830450221009bc21d3cbe11daf4d6c4ea5dffed73c6f273138804c6408dda0d08967eaed7bb02202538b6252a2a50b07a24cd9471330a6af7147f4d73a3843921e869c258039c3b0121020b66bda439797df0437250366d01050fb3f04c89922614ce709145fecc5990ba"
},
"sequence": 4294967294
}
],
"vout": [
{
"value": 0.16009212,
"n": 0,
"scriptPubKey": {
"hex": "76a914f0d067d2d3f909f258ba8f267c73c25b00b7805988ac"
}
},
{
"value": 0.20135788,
"n": 1,
"scriptPubKey": {
"hex": "76a9144b3447769a424eeab9c5b345eb61ff83a1bfd6d388ac"
}
}
],
"hex": "0200000001f2c42bdc972aab20bd9f8b9db379de193026f8ff8fa0397f793f22438f73a858000000006b4830450221009bc21d3cbe11daf4d6c4ea5dffed73c6f273138804c6408dda0d08967eaed7bb02202538b6252a2a50b07a24cd9471330a6af7147f4d73a3843921e869c258039c3b0121020b66bda439797df0437250366d01050fb3f04c89922614ce709145fecc5990bafeffffff02fc47f400000000001976a914f0d067d2d3f909f258ba8f267c73c25b00b7805988ac6c3f3301000000001976a9144b3447769a424eeab9c5b345eb61ff83a1bfd6d388ac49801100",
"time": 1509278441,
"blocktime": 1509278441
},
{
"txid": "baea1aae7aa6d5b9fcb2f5080a3442e464b39517e2a4747738e355bd4ba3395b",
"version": 1,
"vin": [
{
"txid": "818569f5d26b308e718335fd8dd39f4e6062b6ebafe5f4f47db2501765063f76",
"vout": 0,
"scriptSig": {
"hex": "473044022004597274a8c0e80bcfa6902a54e71705c58dfca52f8fbaccf45382b692ed03ae022030744ff630c84bce24e3bad145a7637ae0842afb25a5f78ab138a668040c42980121036e519e7902ee865f62ae343750fe321320abc786f4d15079210bf8383d39a7d0"
},
"sequence": 4294967294
},
{
"txid": "374b169916146bf3ca7f03df684b30cc6a9ead40f03b57bcd8e812dc188c51a1",
"vout": 0,
"scriptSig": {
"hex": "4730440220120b544600f5808540c1d986576b035cb3824790e25e8434a46d578858c0c6e202201be659f515a3be767d58e5e071670dc77febdc9874647cc4acf315d955e35da40121036209de32f750d3d6beb2639859b904695bb93ef7c657056a54a5ebcb77236f31"
},
"sequence": 4294967294
},
{
"txid": "8598c3712a2478da4175cd31c7565f54380d2a596b1799dc3ea866ad2ce0327a",
"vout": 0,
"scriptSig": {
"hex": "47304402201c87042b4676f757e955f27117efed7848a16053738dc541021826d5ce0d8d0402205142de70603d539de744d127f659cdf0c615bd1d225becb0eea3afeca46a578f01210227bb2affe1862313de777a5b4a7869ade68b96e6d76b6e42c3f3a09acf8ac6cd"
},
"sequence": 4294967294
},
{
"txid": "240f20c2a4f10c41393bc65749bf35da7a30f885e02bf229f789eabee62337b7",
"vout": 0,
"scriptSig": {
"hex": "473044022036d0db7996589ed3d07832caadae94e26aa092edbbad7e91d6a649e336204d9502205ecd7d28c32104108e4f9b7dfa73c4c672ce804ffd19ecb1a55a4ecf363c2ee00121022149d2f9e89f14d87a39edfc723167493c7c3daab520e7f5508571c5bcb45d64"
},
"sequence": 4294967294
},
{
"txid": "2fbf88b64e75c0b8c40b45ab560172ec481601aa177dd9be3aed0df0cf366eed",
"vout": 0,
"scriptSig": {
"hex": "473044022003419bfbcfd4b2f58d2855c3d6a24d3c7b972eb220d99ab3cc29b5e3099c3961022033692a741896a23953311cf7a0526e278bec403a45a72e8b634ac8879a6b3a0c01210208ed7a041af3570af8a2922693b3c45c8f9752e85085a7bcbc19de5e5b01942f"
},
"sequence": 4294967294
}
],
"vout": [
{
"value": 0.04504649,
"n": 0,
"scriptPubKey": {
"hex": "76a9149a2d8259e69098c1540439f0318032705649c98988ac"
}
},
{
"value": 0.01063583,
"n": 1,
"scriptPubKey": {
"hex": "76a91409d0be3d3a93e6368d499ad44fe885bbde50838388ac"
}
}
],
"hex": "0100000005763f06651750b27df4f4e5afebb662604e9fd38dfd3583718e306bd2f5698581000000006a473044022004597274a8c0e80bcfa6902a54e71705c58dfca52f8fbaccf45382b692ed03ae022030744ff630c84bce24e3bad145a7637ae0842afb25a5f78ab138a668040c42980121036e519e7902ee865f62ae343750fe321320abc786f4d15079210bf8383d39a7d0feffffffa1518c18dc12e8d8bc573bf040ad9e6acc304b68df037fcaf36b141699164b37000000006a4730440220120b544600f5808540c1d986576b035cb3824790e25e8434a46d578858c0c6e202201be659f515a3be767d58e5e071670dc77febdc9874647cc4acf315d955e35da40121036209de32f750d3d6beb2639859b904695bb93ef7c657056a54a5ebcb77236f31feffffff7a32e02cad66a83edc99176b592a0d38545f56c731cd7541da78242a71c39885010000006a47304402201c87042b4676f757e955f27117efed7848a16053738dc541021826d5ce0d8d0402205142de70603d539de744d127f659cdf0c615bd1d225becb0eea3afeca46a578f01210227bb2affe1862313de777a5b4a7869ade68b96e6d76b6e42c3f3a09acf8ac6cdfeffffffb73723e6beea89f729f22be085f8307ada35bf4957c63b39410cf1a4c2200f24000000006a473044022036d0db7996589ed3d07832caadae94e26aa092edbbad7e91d6a649e336204d9502205ecd7d28c32104108e4f9b7dfa73c4c672ce804ffd19ecb1a55a4ecf363c2ee00121022149d2f9e89f14d87a39edfc723167493c7c3daab520e7f5508571c5bcb45d64feffffffed6e36cff00ded3abed97d17aa011648ec720156ab450bc4b8c0754eb688bf2f000000006a473044022003419bfbcfd4b2f58d2855c3d6a24d3c7b972eb220d99ab3cc29b5e3099c3961022033692a741896a23953311cf7a0526e278bec403a45a72e8b634ac8879a6b3a0c01210208ed7a041af3570af8a2922693b3c45c8f9752e85085a7bcbc19de5e5b01942ffeffffff0249bc4400000000001976a9149a2d8259e69098c1540439f0318032705649c98988ac9f3a1000000000001976a91409d0be3d3a93e6368d499ad44fe885bbde50838388ac6c801100",
"blockhash": "89300c393ff5d0737ee0908312116293142ba12ba6f4e7866d5bdae74062c0f3",
"confirmations": 3,
"time": 1538739377,
"blocktime": 1538739377
}
]
},
"1146985": {
"height": 1146985,
"hash": "d4189fe9b1d920190b95344fbc171092e6703c25a34e80c6aa61b91e3930b146",
"noTxs": 1,
"txDetails": [
{
"txid": "f14ce8ee631c7961e1cc55202057b98fb75e4aa407b829745f8d08d14c08e4f2",
"version": 1,
"vin": [
{
"txid": "",
"vout": 0,
"scriptSig": {
}
}
],
"vout": [
{
"value": 0,
"n": 0,
"scriptPubKey": {
"hex": "6a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf9"
}
},
{
"value": 25,
"n": 1,
"scriptPubKey": {
"hex": "76a9143a4616efd134b1576d0f97906cff2b65ad600ea688ac"
}
},
{
"value": 0,
"n": 2,
"scriptPubKey": {
"hex": "76a91422851477d63a085dbc2398c8430af1c09e7343f688ac"
}
}
],
"hex": "010000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff200369801104b5c0f5590840100763000000000d2f6e6f64655374726174756d2f00000000030000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf900f90295000000001976a9143a4616efd134b1576d0f97906cff2b65ad600ea688ac00000000000000001976a91422851477d63a085dbc2398c8430af1c09e7343f688ac0120000000000000000000000000000000000000000000000000000000000000000000000000",
"time": 1509277877,
"blocktime": 1509277877
}
]
}
}
},
"handleFork": {
"syncRanges": [
{"lower": 1146985, "upper": 1146989}
],
"fakeBlocks": {
"1146987": {
"height": 1146987,
"hash": "f13a74fd3762c7f86e635b41b2e554f9acdb2fe0a2b40b03af84e215ccb8c884"
},
"1146988": {
"height": 1146988,
"hash": "45e05a4bf0da7f891f94f2186338da9c4a064cc297fca862059bcb9f84793264"
},
"1146989": {
"height": 1146989,
"hash": "f4e09ff63368321cdbe6db43dab9b098b47d7a38b55ebebaeb45660e7032b894"
}
},
"realBlocks": {
"1146987": {
"height": 1146987,
"hash": "33f03f1159c1b63e9e807dfb4972dc640a21b94405c2246e4e35793e3527b6aa"
},
"1146988": {
"height": 1146988,
"hash": "9511f1decd43ee60620c4131e48fe8e2e35ce8e12f015c26bea8312208b15a5e"
},
"1146989": {
"height": 1146989,
"hash": "89300c393ff5d0737ee0908312116293142ba12ba6f4e7866d5bdae74062c0f3"
}
}
}
}

View File

@ -0,0 +1,109 @@
{
"connectBlocks": {
"syncRanges": [{ "lower": 447973, "upper": 447973 }],
"blocks": {
"447973": {
"height": 447973,
"hash": "c81fe0f3524938c618156449d27ede51c2c76b73e00e83cdb9b599181ca505d5",
"noTxs": 2,
"txDetails": [
{
"hex": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff0603e5d5060101ffffffff0100000000000000000000000000",
"txid": "062f1f851b5d350f860a2c96c98b6dac8030f84bb02df42b5608619abf6049c8",
"version": 1,
"locktime": 0,
"vin": [
{
"coinbase": "03e5d5060101",
"sequence": 4294967295
}
],
"vout": [
{
"value": 0,
"n": 0,
"scriptPubKey": {
"asm": "",
"hex": "",
"type": "nonstandard"
}
}
],
"blockhash": "c81fe0f3524938c618156449d27ede51c2c76b73e00e83cdb9b599181ca505d5",
"confirmations": 61,
"time": 1556541002,
"blocktime": 1556541002
},
{
"txid": "72502137b0924c69400eec74deacc57e9290f32a6ba2f5559f17256c9c06a347",
"version": 1,
"locktime": 0,
"vin": [
{
"txid": "e5eaf3c47a74c73a7d0701ec098f772e8e37f0dd780de1092048115c33212eac",
"vout": 1,
"scriptSig": {
"asm": "304402201de5db6155e047897ea2fed2a8cbc1d717fb8c5b1a18964cd4942c800db1a218022035a6c8aed82c582db22f4835dbd86a283c7ed3b84eaf1f68932b910dfd08d1be01",
"hex": "47304402201de5db6155e047897ea2fed2a8cbc1d717fb8c5b1a18964cd4942c800db1a218022035a6c8aed82c582db22f4835dbd86a283c7ed3b84eaf1f68932b910dfd08d1be01"
},
"sequence": 4294967295
}
],
"vout": [
{
"value": 0,
"n": 0,
"scriptPubKey": {
"asm": "",
"hex": "",
"type": "nonstandard"
}
},
{
"value": 592.22996622,
"n": 1,
"scriptPubKey": {
"asm": "0219e5714cbba5933a9c09bc93330efece366f61910ee1cc38b245e420063ca3f2 OP_CHECKSIG",
"hex": "210219e5714cbba5933a9c09bc93330efece366f61910ee1cc38b245e420063ca3f2ac",
"reqSigs": 1,
"type": "pubkey",
"addresses": ["7onpME5risJYB6JrcfHGUPdnbsYUL4yhkf"]
}
},
{
"value": 18,
"n": 2,
"scriptPubKey": {
"asm": "OP_DUP OP_HASH160 a1f4fccad310761f842f3cdcc41b5ca3220fd304 OP_EQUALVERIFY OP_CHECKSIG",
"hex": "76a914a1f4fccad310761f842f3cdcc41b5ca3220fd30488ac",
"reqSigs": 1,
"type": "pubkeyhash",
"addresses": ["7hB12ehKqJZZHbyin154CsXehPvdcQWgLX"]
}
}
],
"blockhash": "c81fe0f3524938c618156449d27ede51c2c76b73e00e83cdb9b599181ca505d5",
"confirmations": 65,
"time": 1556541002,
"blocktime": 1556541002
}
]
}
}
},
"handleFork": {
"syncRanges": [{ "lower": 447973, "upper": 447973 }],
"fakeBlocks": {
"447973": {
"height": 447973,
"hash": "4bdd44aa35b2f0db83d2e244561e5bf059b89f3b418c31935d898f597418bf78"
}
},
"realBlocks": {
"447973": {
"height": 447973,
"hash": "c81fe0f3524938c618156449d27ede51c2c76b73e00e83cdb9b599181ca505d5"
}
}
}
}

View File

@ -0,0 +1,331 @@
{
"connectBlocks": {
"syncRanges": [
{"lower": 702610, "upper": 702619},
{"lower": 707540, "upper": 707548}
],
"blocks": {
"702610": {
"height": 702610,
"hash": "00000000000042b3cd2d763d46320fc0a4fa973f11dec48f730da1992a1b4a19",
"noTxs": 6,
"txDetails": [
{
"hex": "02000000026bba01cbc000c54057b8558ac0acae555638a8338d85c7553723ebeacf32578a000000006a473044022024103908c0cf880d0f58bd00a825bc2ded66b42626da62c594de95860385f0f3022005b9f5fa339fd236d777f7bad4058955b3d5792a0dbc44dfe855db2b9fb3d0d1012102c90a0c3a4e95987e7d9e8d1bf743707340ec876d478dcf42effcd1e9e77ecd34feffffffea8457394368f8cd3915a5aff8faf628d111b376af1a3f8bc34e36b807dac34a030000006a4730440220515cb94883c0823d66dac740beb68a1510c46471ca2eaab2924ccf79d5d1db470220644a5fe3972d2f3bda6e6a3744dbb233c6d3afae76d54722f0b84458ef488af4012102c90a0c3a4e95987e7d9e8d1bf743707340ec876d478dcf42effcd1e9e77ecd34feffffff02b21d842b050000001976a91493f0a84f3e4fdcfea587d0c68f6236ec4ba8c0db88ac005cb2ec220000001976a914ee121fca023b92d0369eeb3a9dae9de2610b287088ac90b80a00",
"txid": "32e7361f430957d58bc0e99ed0b115c673bc7c0755c92c925ba80802ad1182f5",
"blocktime": 1557559421,
"time": 1557559421,
"version": 2,
"vin": [
{
"txid": "8a5732cfeaeb233755c7858d33a8385655aeacc08a55b85740c500c0cb01ba6b",
"vout": 0,
"sequence": 4294967294,
"scriptSig": {
"hex": "473044022024103908c0cf880d0f58bd00a825bc2ded66b42626da62c594de95860385f0f3022005b9f5fa339fd236d777f7bad4058955b3d5792a0dbc44dfe855db2b9fb3d0d1012102c90a0c3a4e95987e7d9e8d1bf743707340ec876d478dcf42effcd1e9e77ecd34"
}
},
{
"txid": "4ac3da07b8364ec38b3f1aaf76b311d128f6faf8afa51539cdf86843395784ea",
"vout": 3,
"sequence": 4294967294,
"scriptSig": {
"hex": "4730440220515cb94883c0823d66dac740beb68a1510c46471ca2eaab2924ccf79d5d1db470220644a5fe3972d2f3bda6e6a3744dbb233c6d3afae76d54722f0b84458ef488af4012102c90a0c3a4e95987e7d9e8d1bf743707340ec876d478dcf42effcd1e9e77ecd34"
}
}
],
"vout": [
{
"value": 222.04915122,
"n": 0,
"scriptPubKey": {
"hex": "76a91493f0a84f3e4fdcfea587d0c68f6236ec4ba8c0db88ac"
}
},
{
"value": 1500.00000000,
"n": 1,
"scriptPubKey": {
"hex": "76a914ee121fca023b92d0369eeb3a9dae9de2610b287088ac"
}
}
]
},
{
"hex": "020000000164e38b17ec20ca8a1071c100070e556cdb995b307040c8ac834e1b3e68dee349010000006b483045022100f161513ce7b96df7d772c1e34f71ce0515ff97d64c49cd3c1766406280adcd5802203b51e896b675f336b4600534ae67980b2193f6c39e1380facc7d69c34d0156060121021d06c7124eb7be62b3d4bf8f094510c29760a5f06c1b62c51c9f5df51857d27dfeffffff02e07866eb130000001976a914c889edba47f299e5a79dddbcad6fb9a1b566fc7888ac72abea26a40700001976a9141737a3b2e5b6e9e20067ec56f7db7d29014b1ce588ac91b80a00",
"txid": "2e9bd5cc2afa7f5b9de22b39149c4341bc189a7b9ef7df49876173bdbc85b802",
"blocktime": 1557559421,
"time": 1557559421,
"version": 2,
"vin": [
{
"txid": "49e3de683e1b4e83acc84070305b99db6c550e0700c171108aca20ec178be364",
"vout": 1,
"sequence": 4294967294,
"scriptSig": {
"hex": "483045022100f161513ce7b96df7d772c1e34f71ce0515ff97d64c49cd3c1766406280adcd5802203b51e896b675f336b4600534ae67980b2193f6c39e1380facc7d69c34d0156060121021d06c7124eb7be62b3d4bf8f094510c29760a5f06c1b62c51c9f5df51857d27d"
}
}
],
"vout": [
{
"value": 855.5374000,
"n": 0,
"scriptPubKey": {
"hex": "76a914c889edba47f299e5a79dddbcad6fb9a1b566fc7888ac"
}
},
{
"value": 84016.08944498,
"n": 1,
"scriptPubKey": {
"hex": "76a9141737a3b2e5b6e9e20067ec56f7db7d29014b1ce588ac"
}
}
]
}
]
},
"702611": {
"height": 702611,
"hash": "0000000000001c8c9218bad5a338daa93d16695a2bbfd1d35c32aba8094ccc07",
"noTxs": 2,
"txDetails": [
{
"hex": "02000000018f7a8b54bf2cc90ef51937601f63aa7bb9948a4891a8fdd970327efa86c3c6cf030000006b483045022100971099caabc08e507171f65648ea8d0b13ad1d0471479d4744cf2e41c02ab9aa0220652161d0cdd1d379249d2914c3ff2702680535647157c518ea7357d7cec0313a01210339ac9143c476ede4a7b39ec03d7b85b36c4d571ebe84f0d5caabb6948d34f43ffeffffff05b0da0e1e000000001976a914d1d3d3c7064a9aa29077f322b498b5ca6317bb5388ac3b3b740b090000001976a9142fdede3c1573c646ca13b59f4456b4e439a6ac3588ac6531e81f000000001976a9149d9bddb3e257cc565b775ac5147c8f74d1e2a9b788ac00d4121f000000001976a91407e5d84e1805d96781f9571095767f0103f68fda88ac72b85a22000000001976a914b5a99ad52450f8c2a8e81ab2cfa4e156936e3da888ac91b80a00",
"txid": "1579ab536518550f2fc71609b0c007ba6f78167ecc361362e3a4480357946598",
"blocktime": 1557559435,
"time": 1557559435,
"version": 2,
"vin": [
{
"txid": "cfc6c386fa7e3270d9fda891488a94b97baa631f603719f50ec92cbf548b7a8f",
"vout": 3,
"sequence": 4294967295,
"scriptSig": {
"hex": "483045022100971099caabc08e507171f65648ea8d0b13ad1d0471479d4744cf2e41c02ab9aa0220652161d0cdd1d379249d2914c3ff2702680535647157c518ea7357d7cec0313a01210339ac9143c476ede4a7b39ec03d7b85b36c4d571ebe84f0d5caabb6948d34f43f"
}
}
],
"vout": [
{
"value": 5.04289968,
"n": 0,
"scriptPubKey": {
"hex": "76a914d1d3d3c7064a9aa29077f322b498b5ca6317bb5388ac"
}
},
{
"value": 388.46872379,
"n": 1,
"scriptPubKey": {
"hex": "76a9142fdede3c1573c646ca13b59f4456b4e439a6ac3588ac"
}
},
{
"value": 5.35310693,
"n": 2,
"scriptPubKey": {
"hex": "76a9149d9bddb3e257cc565b775ac5147c8f74d1e2a9b788ac"
}
},
{
"value": 5.21327616,
"n": 3,
"scriptPubKey": {
"hex": "76a91407e5d84e1805d96781f9571095767f0103f68fda88ac"
}
},
{
"value": 5.76370802,
"n": 4,
"scriptPubKey": {
"hex": "76a914b5a99ad52450f8c2a8e81ab2cfa4e156936e3da888ac"
}
}
]
}
]
},
"702619": {
"height": 702619,
"hash": "00000000000009aa5f637d634a18b42d62ae820daa341b5386b29248e2a42356",
"noTxs": 3,
"txDetails": [
{
"hex": "0200000001b93ae2aff06bc580e26331d6d59d2f3ae910cdc549993bb3a21acb53db3a50c80a0000006b483045022100d0083a5968774f7f01de6cc72f8fa04e458ea5ce4cccc58e73254966d8645cf9022056f10deaeaa44eb89f4b0204a16140a49237a1e79cd4d017837ab6719eb14959012102b1e1ef3235d31b84cbd22e0fd8c049202724e7e9382729ad2b881cda664639dcfeffffff03289d0ad5030000001976a91475fceb999dc2625360ad647894eec8f46882802a88ac50dc8956020000001976a9141cc4e68334b4a15aae8406ed296308c4f463d2ac88ac80d1cc67060000001976a914aedf3be432c995a5c054d102c16624b0e64bd04c88ac99b80a00",
"txid": "0d4774305ee1be1d88334218379bf0669004c416e828e6fdceafcd320612bc81",
"blocktime": 1557559657,
"time": 1557559657,
"version": 2,
"vin": [
{
"txid": "c8503adb53cb1aa2b33b9949c5cd10e93a2f9dd5d63163e280c56bf0afe23ab9",
"vout": 10,
"sequence": 4294967294,
"scriptSig": {
"hex": "483045022100d0083a5968774f7f01de6cc72f8fa04e458ea5ce4cccc58e73254966d8645cf9022056f10deaeaa44eb89f4b0204a16140a49237a1e79cd4d017837ab6719eb14959012102b1e1ef3235d31b84cbd22e0fd8c049202724e7e9382729ad2b881cda664639dc"
}
}
],
"vout": [
{
"value": 164.59144488,
"n": 0,
"scriptPubKey": {
"hex": "76a91475fceb999dc2625360ad647894eec8f46882802a88ac"
}
},
{
"value": 100.41810000,
"n": 1,
"scriptPubkey": {
"hex": "76a9141cc4e68334b4a15aae8406ed296308c4f463d2ac88ac"
}
},
{
"value": 275.11280000,
"n": 2,
"scriptPubkey": {
"hex": "76a914aedf3be432c995a5c054d102c16624b0e64bd04c88ac"
}
}
]
}
]
},
"707548": {
"height": 707548,
"hash": "0000000000001deb9c03419e16bcf4d3bba37a68eab17b1d9e80d833d59eaa2f",
"noTxs": 2,
"txDetails": [
{
"hex": "020000000001010000000000000000000000000000000000000000000000000000000000000000ffffffff1503ddcb0a044208db5c014f10509801000000000000ffffffff020088526a740000001976a914055594c199fafa48f6ba46a3ada7cfc3c9d6eb1d88ac0000000000000000266a24aa21a9ede2f61c3f71d1defd3fa999dfa36953755c690689799962b48bebd836974e8cf90120000000000000000000000000000000000000000000000000000000000000000000000000",
"txid": "4e99215422b368329b7edc738461d9ee864fa9a9b95fcc2c07fa8b17cc0da49c",
"blocktime": 1557858226,
"time": 1557858226,
"version": 2,
"vin": [
{
"txid": "d7924d630473a8ed5934db65c06e364e76f5237ca7673d38b6f2a8aa52c52997",
"vout": 0,
"sequence": 4294967295,
"scriptSig": {
"hex": "483045022100952c376e729d5d9f2973bb4ad305e370d6a3331e61e3b9f8831c25c7bbb47c6002201f7c43ddc733d8aee44df51f431a3a0ae473ce95a98597602e4a87bc9e3874870121039e0b146c77f4c85cef15c2f51ad18ffa3141e08ee3ea55a41af68408ecb5e458"
}
}
],
"vout": [
{
"value": 33.51362274,
"n": 0,
"scriptPubKey": {
"hex": "76a9142f366c3fddc3dc8ce4f9a2b97042f5bb6159a6ab88ac"
}
},
{
"value": 10.10052689,
"n": 1,
"scriptPubKey": {
"hex": "76a9144e4802c29f3b6b3ab0a32ce6314efa1e069141cc88ac"
}
},
{
"value": 800.26159654,
"n": 2,
"scriptPubKey": {
"hex": "76a914f72a0f212c006992e36e3d3cac889f3e1a5b12c288ac"
}
},
{
"value": 8.59088871,
"n": 3,
"scriptPubKey": {
"hex": "76a91438e85a0dc5ae187322239d7cba044664cbc587f888ac"
}
},
{
"value": 6.09864291,
"n": 4,
"scriptPubKey": {
"hex": "76a914cf349fa004830769fa76e698033f0a00777f1fc588ac"
}
},
{
"value": 9.36995447,
"n": 5,
"scriptPubKey": {
"hex": "76a914f43058a7559aef593985759588695e0198ead7f688ac"
}
},
{
"value": 6.90847110,
"n": 6,
"scriptPubKey": {
"hex": "76a9140484e38edeb95813ba2d0919493de1710a66189d88ac"
}
},
{
"value": 7.45144308,
"n": 7,
"scriptPubKey": {
"hex": "76a914b9dd0d2742a248ceef9c88630cd9f8a00747076188ac"
}
}
]
}
]
}
}
},
"handleFork": {
"syncRanges": [
{"lower": 702610, "upper": 702619},
{"lower": 707540, "upper": 707548}
],
"fakeBlocks": {
"702617": {
"height": 702617,
"hash": "0000000000002934ba9dc2f595a159f59b658c27247186bc90fe06798fad141e"
},
"702618": {
"height": 702618,
"hash": "000000000000605b9334525e1082de041c35c3bd25a62fe1cb73cdb241787884"
},
"702619": {
"height": 702619,
"hash": "0000000000004cc962adcfa5380f3664a34df55ddef94bc27c59064890c29bea"
},
"707547": {
"height": 707547,
"hash": "000000000000468a219597a5e99fafa3e9e9fafd4f52b1b3f09796b3cf8b0125"
},
"707548": {
"height": 707548,
"hash": "000000000000111258e9313f60f56a809c2ec63b81373af4eb5c2c5b140aff12"
}
},
"realBlocks": {
"702617": {
"height": 702617,
"hash": "000000000000208422f13be75d8e9dba367245ca11b5ce7a7a3d1f88ca31a2c4"
},
"702618": {
"height": 702618,
"hash": "00000000000047cdfb5ecb000b59a9f7622280356008f9cc05e15321626528ff"
},
"702619": {
"height": 702619,
"hash": "00000000000009aa5f637d634a18b42d62ae820daa341b5386b29248e2a42356"
},
"707547": {
"height": 707547,
"hash": "00000000000031c13e3c865d5f7861f28aa02bf997716471f364db6b69b6244f"
},
"707548": {
"height": 707548,
"hash": "0000000000001deb9c03419e16bcf4d3bba37a68eab17b1d9e80d833d59eaa2f"
}
}
}
}

View File

@ -0,0 +1,71 @@
{
"connectBlocks": {
"syncRanges": [
{"lower": 1503776, "upper": 1503796}
],
"blocks": {
"1503796": {
"height": 1503796,
"hash": "f907e10100b3ec0be53c4dd40d0efb563a9d1de1d2b7a156ffec03b8d364ffb4",
"noTxs": 1,
"txDetails": [
{
"txid": "d819588eab648eb7d99f2acc2e4cf21c546a5b15d7090620f703fd9a0d384e59",
"version": 1,
"vin": [
{
"coinbase": "0334f2160101",
"sequence": 4294967295
}
],
"vout": [
{
"value": 5.00000000,
"n": 0,
"scriptPubKey": {
"hex": "76a914d02029d9dffe8f7cbe08122cacb8f57596132f3488ac"
}
}
],
"hex": "01000000010000000000000000000000000000000000000000000000000000000000000000ffffffff060334f2160101ffffffff010065cd1d000000001976a914d02029d9dffe8f7cbe08122cacb8f57596132f3488ac00000000",
"time": 1442385302,
"blocktime": 1442385302
}
]
}
}
},
"handleFork": {
"syncRanges": [
{"lower": 1503790, "upper": 1503796}
],
"fakeBlocks": {
"1503794": {
"height": 1503794,
"hash": "a1b30026cfd15e4f46c5ffbd57f615d71e5c5d2366b079131123bf055d9e812f"
},
"1503795": {
"height": 1503795,
"hash": "2d92315148dd3568c1a9ab7bf6bd893ed0ca012372f69e139e026453f23d92c4"
},
"1503796": {
"height": 1503796,
"hash": "dbc84d9af0a74252f337cbcc2fdd6295db53c2cea2bd94ba8afe66f24bc53a5b"
}
},
"realBlocks": {
"1503794": {
"height": 1503794,
"hash": "9f1ad78ac7a7f4bf2ca32a2be4117495510ec517525209c854b51d9b7bfefa95"
},
"1503795": {
"height": 1503795,
"hash": "88084fc69161eb5868bd146d78fb10fe7d80a474029b23728809e0a744bcf5dd"
},
"1503796": {
"height": 1503796,
"hash": "f907e10100b3ec0be53c4dd40d0efb563a9d1de1d2b7a156ffec03b8d364ffb4"
}
}
}
}

View File

@ -0,0 +1,96 @@
{
"connectBlocks": {
"syncRanges": [
{"lower": 538519, "upper": 538539}
],
"blocks": {
"538539": {
"height": 538539,
"hash": "7f0cf13f99d7c96c0f2334433516e5d3621b34f60d76ee9dc98698b855a319fd",
"noTxs": 3,
"txDetails": [
{
"hex": "0200000001ceebca2fcfb99dcd667d795a99badc9dd37fb5c4738e6d137ed5126b444479b70100000049483045022100e6ebe75139a56532c0795a10f3457e5f9b05b8a625cd36abfa27688a236882e602207fbbaf6045761ba67af3107e4e557a62309067f32a02183d846e16bfac81b6eb01ffffffff040000000000000000008062f92dda160000232103069114b4f300ed8836a5ff75054a4a3a2bc21e0b51ea4d1f41f7e61f51472dadacc01c132eda160000232103069114b4f300ed8836a5ff75054a4a3a2bc21e0b51ea4d1f41f7e61f51472dadac80769000000000001976a914b5749d2f105a91593b956a121fcf94e59278d56288ac00000000",
"txid": "5ac7264773b2e44648e0861e1d19e3a727824ac9ad1cc2030710c62ad6cd53a1",
"version": 2,
"vin": [
{
"txid": "b77944446b12d57e136d8e73c4b57fd39ddcba995a797d66cd9db9cf2fcaebce",
"vout": 1,
"sequence": 4294967295,
"scriptSig": {
"hex": "483045022100e6ebe75139a56532c0795a10f3457e5f9b05b8a625cd36abfa27688a236882e602207fbbaf6045761ba67af3107e4e557a62309067f32a02183d846e16bfac81b6eb01"
}
}
],
"vout": [
{
"value": 0.00000000,
"n": 0,
"scriptPubKey": {
"hex": ""
}
},
{
"value": 251263.30000000,
"n": 1,
"scriptPubKey": {
"hex": "2103069114b4f300ed8836a5ff75054a4a3a2bc21e0b51ea4d1f41f7e61f51472dadac"
}
},
{
"value": 251263.31686080,
"n": 2,
"scriptPubKey": {
"hex": "2103069114b4f300ed8836a5ff75054a4a3a2bc21e0b51ea4d1f41f7e61f51472dadac"
}
},
{
"value": 0.09467520,
"n": 3,
"scriptPubKey": {
"hex": "76a914b5749d2f105a91593b956a121fcf94e59278d56288ac"
}
}
],
"time": 1555935872,
"blocktime": 1555935872
}
]
}
}
},
"handleFork": {
"syncRanges": [
{"lower": 538519, "upper": 538538}
],
"fakeBlocks": {
"538536": {
"height": 538536,
"hash": "00000000000389fc8b2989ae94a083ad6091cd9cfd9bc92ad02989e2a940e49b"
},
"538537": {
"height": 538537,
"hash": "7ef85808d790718fdf0e12e056f7bb4e1fa5f5ff2dd2f45878c07cc7f2199e81"
},
"538538": {
"height": 538538,
"hash": "0000000000235d6c42a23810896a078e2fb41850cad11fd003a9d412eb1487d3"
}
},
"realBlocks": {
"538536": {
"height": 538536,
"hash": "434ad83e19ccaa4fe769559b7f98564b5cfb30f72819a003372394633b01368a"
},
"538537": {
"height": 538537,
"hash": "1fe91c1b068f202158517249463e19d297a7d89ad1db4011c70cf6e89123b39f"
},
"538538": {
"height": 538538,
"hash": "0000000000ad20d8225f83a4572a20129fcb186de3b2733b6ce0a46dd17c0c74"
}
}
}
}

441
tests/sync/testdata/zelcash.json vendored 100644

File diff suppressed because one or more lines are too long

View File

@ -86,8 +86,9 @@
},
"monacoin": {
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync",
"EstimateSmartFee", "EstimateFee"]
},
"EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"],
"sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"]
},
"myriad": {
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync",
"EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"]
@ -131,8 +132,33 @@
"EstimateSmartFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"],
"sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"]
},
"viacoin": {
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync",
"EstimateSmartFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"],
"sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"]
},
"nuls": {
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"],
"sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"]
},
"vipstarcoin": {
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync",
"EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"],
"sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"]
},
"monetaryunit": {
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync",
"EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"],
"sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"]
},
"zelcash": {
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync",
"EstimateSmartFee", "EstimateFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"],
"sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"]
},
"ravencoin": {
"rpc": ["GetBlock", "GetBlockHash", "GetTransaction", "GetTransactionForMempool", "MempoolSync",
"EstimateSmartFee", "GetBestBlockHash", "GetBestBlockHeight", "GetBlockHeader"],
"sync": ["ConnectBlocksParallel", "ConnectBlocks", "HandleFork"]
}
}