Compare commits
67 Commits
deepcrayon
...
indexv5
Author | SHA1 | Date |
---|---|---|
Martin Boehm | 3cff38942a | |
Martin Boehm | c9c2f3dcbc | |
Martin Boehm | 4b2f66cda3 | |
Martin Boehm | 4f9feb4d4f | |
Martin Boehm | 5ef22e86dc | |
Martin Boehm | 70249530ff | |
Martin Boehm | e0c5656e13 | |
Martin Boehm | 8903358044 | |
Martin Boehm | ec8aed0bde | |
Scotty0448 | 697569ffad | |
motty | 07c763fd70 | |
Jan Hrnko | 776114f3b5 | |
Ján Hrnko | 0048c6f237 | |
TheTrunk | ac795cb6b4 | |
TheTrunk | c70b88a604 | |
TheTrunk | 462994d909 | |
CodeFace | cb459352d9 | |
Sotiris Blad | 78ef4dc4a5 | |
Martin Boehm | 243f2c16bf | |
Petr Kracík | fb9535fc58 | |
Martin Boehm | 3d9ad91c3d | |
Martin Boehm | 6b16814d0b | |
Martin Boehm | 31fb59b1a5 | |
Petr Kracík | 9fb2043847 | |
Martin Boehm | f0a396cf0d | |
Martin Boehm | 93db48e8ad | |
Petr Kracík | 2967209e7a | |
Martin Boehm | 5685b07bfb | |
Martin Boehm | 462ee13855 | |
Martin Boehm | 63fa3cccb6 | |
Martin Boehm | e7c05ccfaa | |
Martin Boehm | eb7cdab0d9 | |
Martin Boehm | 80bb7c51f8 | |
Martin Boehm | 82cc184741 | |
Petr Kracík | 15e29f104c | |
Martin Boehm | 35379751fd | |
Sotiris Blad | 015423ae37 | |
Martin Boehm | 164d9b197b | |
Martin Boehm | e86175a033 | |
Martin Boehm | 839b02dbce | |
Martin Boehm | fa420dc291 | |
y-chan | 3e26b4b6ac | |
y-chan | 6856d27484 | |
y-chan | b4a1d8a3ca | |
y-chan | c90672dac9 | |
y-chan | 8c1bdc566a | |
y-chan | 399e9d85a4 | |
y-chan | 7c69bbd74c | |
y-chan | 963b260d4c | |
y-chan | 870e3ffc30 | |
y-chan | 8ca3c65996 | |
y-chan | 10e0d2583b | |
Petr Kracík | 8f7ef59646 | |
Petr Kracík | 5e212c9eca | |
Martin Boehm | f0882655b9 | |
Martin Boehm | a293aa5673 | |
ilmango | 77fb7088fa | |
Martin Boehm | 0bcd521216 | |
Martin Boehm | 0cd043abe5 | |
wakiyamap | 4a035a2460 | |
romanornr | e8a83ccfe2 | |
Cronos | 32c77fe69a | |
Martin Boehm | af19559ecd | |
Martin Boehm | d0c20afeb2 | |
Martin Boehm | c12259e266 | |
Martin Boehm | 2bfd9f51fa | |
Martin Boehm | 064170c871 |
|
@ -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:
|
||||
|
|
38
README.md
38
README.md
|
@ -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).
|
||||
|
|
|
@ -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()))
|
||||
}
|
||||
|
|
70
api/types.go
70
api/types.go
|
@ -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
|
||||
|
|
276
api/worker.go
276
api/worker.go
|
@ -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),
|
||||
|
|
10
api/xpub.go
10
api/xpub.go
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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")
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
}
|
|
@ -10,10 +10,12 @@ import (
|
|||
"github.com/martinboehm/btcutil/chaincfg"
|
||||
)
|
||||
|
||||
// magic numbers
|
||||
const (
|
||||
MainnetMagic wire.BitcoinNet = 0xc0c0c0c0
|
||||
)
|
||||
|
||||
// chain parameters
|
||||
var (
|
||||
MainNetParams chaincfg.Params
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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
|
||||
}
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
|
|
112
blockbook.go
112
blockbook.go
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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-----
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -65,4 +65,4 @@
|
|||
"package_maintainer": "Petr Kracik",
|
||||
"package_maintainer_email": "petr.kracik@satoshilabs.com"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -65,4 +65,4 @@
|
|||
"package_maintainer": "Petr Kracik",
|
||||
"package_maintainer_email": "petr.kracik@satoshilabs.com"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"coin": {
|
||||
"name": "Litecoin Testnet",
|
||||
"shortcut": "TLTC",
|
||||
"shortcut": "tLTC",
|
||||
"label": "Litecoin Testnet",
|
||||
"alias": "litecoin_testnet"
|
||||
},
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -64,4 +64,4 @@
|
|||
"package_maintainer": "Petr Kracik",
|
||||
"package_maintainer_email": "petr.kracik@satoshilabs.com"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -64,4 +64,4 @@
|
|||
"package_maintainer": "Petr Kracik",
|
||||
"package_maintainer_email": "petr.kracik@satoshilabs.com"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
413
db/rocksdb.go
413
db/rocksdb.go
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
125
docs/api.md
125
docs/api.md
|
@ -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.
|
|
@ -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 |
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
@ -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{
|
||||
|
|
|
@ -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">
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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{
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"blockHeight": 1205643,
|
||||
"blockHash": "60c80ce4f0a90f7b217bef8fab32ce2e499a4dc2067f4a6062048e3b940d76c6",
|
||||
"blockTime": 1514835214,
|
||||
"blockTime": 1514835214,
|
||||
"blockTxs": [
|
||||
"38b4a919ab0f8b4570e1f26717740846b7994ba22f94c51d96eb32bc45c5886e",
|
||||
"7f793aff2f74ca9cec6df08a7c0fcaa1ea301468459b46edbed693a871832298",
|
||||
|
@ -52,4 +52,4 @@
|
|||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
|
@ -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"]
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue