Compare commits
184 Commits
v5.0.0-bet
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8121a2ba96 | ||
|
|
31344d770e | ||
|
|
96677310c2 | ||
|
|
e180672583 | ||
|
|
158d5aefc2 | ||
|
|
e7248320a6 | ||
|
|
60289a644b | ||
|
|
d42e569008 | ||
|
|
6beb2ecd06 | ||
|
|
4d023760ad | ||
|
|
10d9459f26 | ||
|
|
8b07a1e4a5 | ||
|
|
4afb0dfaaa | ||
|
|
f8260541ef | ||
|
|
6b794aa9a3 | ||
|
|
adb81616aa | ||
|
|
3982807b32 | ||
|
|
0a3a1b5ea6 | ||
|
|
df7710ded1 | ||
|
|
27f3993884 | ||
|
|
71bcafb243 | ||
|
|
1b6352573f | ||
|
|
7f86e488e4 | ||
|
|
37e08b0801 | ||
|
|
16bed1b811 | ||
|
|
bca4fe4f97 | ||
|
|
7409dbb77d | ||
|
|
774d830fff | ||
|
|
3a75002efc | ||
|
|
d9579853ad | ||
|
|
3fbcbbe7bc | ||
|
|
dbfe39991f | ||
|
|
6c164993bf | ||
|
|
2145fdb056 | ||
|
|
4472ed8394 | ||
|
|
e13bd5e3e6 | ||
|
|
0283be05db | ||
|
|
25eb992cf1 | ||
|
|
69e9465b93 | ||
|
|
700abe0500 | ||
|
|
0572ee6b35 | ||
|
|
e6826c7dfc | ||
|
|
ece347c825 | ||
|
|
b831cbce7e | ||
|
|
f9e2ed304b | ||
| 00515c5378 | |||
|
|
e3f5de4df5 | ||
|
|
26b65d63a8 | ||
| 4113d9cfd0 | |||
| 7fa2f096df | |||
| fcecf08ac0 | |||
| af6048de93 | |||
| d47b6047e5 | |||
| c525516a95 | |||
| 0eaa4b6fd2 | |||
| 2921e389df | |||
| 522d00bd52 | |||
| 821aae706d | |||
| 18d1a16b0c | |||
| 9c0ec67ccf | |||
| f0768027c9 | |||
| 80f22f731f | |||
| 81a3d5f8ff | |||
| 69fc6790ae | |||
| 5db68b6bb7 | |||
| 091d7aa863 | |||
| 582bdd698a | |||
| 726156843e | |||
| 846f85e2f8 | |||
| ca47013c5a | |||
| 82357f2ecc | |||
| c3cc5f7465 | |||
| 1888b4a4ae | |||
| 1edc88f14b | |||
| 0966ec124b | |||
| 05c6cd7739 | |||
| 2c37e05ff7 | |||
|
|
0cfb80a164 | ||
| 11b0a58351 | |||
| 869a7c21b4 | |||
| 17dd83de10 | |||
| bad1fb2552 | |||
| 5344d9cd5b | |||
| 89c43cd9a7 | |||
| 0d4a7e3e42 | |||
| d689045002 | |||
|
|
1efed08a39 | ||
|
|
56e5cb1f25 | ||
|
|
453d11c64c | ||
|
|
3598c92e0f | ||
|
|
0061285204 | ||
|
|
3b8d992504 | ||
|
|
79fff4051b | ||
|
|
c3d32b7ffc | ||
|
|
861e908080 | ||
|
|
452596702a | ||
|
|
0293a7acb2 | ||
|
|
5ca1e5132b | ||
|
|
a7c8cd4563 | ||
|
|
80912cc83f | ||
|
|
1eb2dbf29a | ||
|
|
d8da495b38 | ||
|
|
8e8e2310a0 | ||
|
|
45abcd6fa0 | ||
|
|
c5e5d5a35e | ||
|
|
9c19c84822 | ||
|
|
9e3850b5c7 | ||
|
|
cbec5630d3 | ||
|
|
02cc97b0a1 | ||
|
|
6cd5093d4f | ||
|
|
edf4b2a4f5 | ||
|
|
6e8d452174 | ||
|
|
cfc422600b | ||
|
|
978679cad7 | ||
|
|
b13b4f667b | ||
|
|
ecf9bbaeea | ||
|
|
4118f2f140 | ||
|
|
59d6cc0867 | ||
|
|
53511b021d | ||
|
|
3a2f8d19de | ||
|
|
10b25628ba | ||
|
|
416c804d8d | ||
|
|
885b366422 | ||
|
|
4dc88e2bc7 | ||
|
|
be533c026f | ||
|
|
dad804eb64 | ||
|
|
66f2eba199 | ||
|
|
bab703a102 | ||
|
|
02006ed18c | ||
|
|
b139c33726 | ||
|
|
1268b47d85 | ||
|
|
2b0815df6c | ||
|
|
6c6e0ad446 | ||
|
|
06bb34123a | ||
|
|
375ee80b5d | ||
|
|
8715aa5071 | ||
|
|
f488a02d2d | ||
|
|
db11257a43 | ||
|
|
f37a3556e3 | ||
|
|
e7ef0db2cb | ||
|
|
ce85a3bede | ||
|
|
b081c0a5f8 | ||
|
|
224733811c | ||
|
|
c75c1fad61 | ||
|
|
383f9f8b12 | ||
|
|
b04ac5466f | ||
|
|
b1f828bea9 | ||
|
|
dc7eacb112 | ||
|
|
d45f6aecac | ||
|
|
210b2fbcd7 | ||
|
|
799675b234 | ||
|
|
b9e4c8a6a5 | ||
|
|
57bc11a592 | ||
|
|
7ac4d51db1 | ||
|
|
6358953553 | ||
|
|
53b970cda7 | ||
|
|
01ebc7a521 | ||
|
|
fe2e29d74a | ||
|
|
0588a5f69d | ||
|
|
118280090c | ||
|
|
8e9ecff905 | ||
|
|
eb637d3125 | ||
|
|
2a2f661529 | ||
|
|
17a4e0f0cc | ||
|
|
e0e8db1d43 | ||
|
|
2fed6b4768 | ||
|
|
9f7587bd71 | ||
|
|
7c8f7f4548 | ||
|
|
e65689ab5d | ||
|
|
34f31ac57d | ||
|
|
76c81c1dab | ||
|
|
bf8f25d9eb | ||
|
|
20a3f6e9fd | ||
|
|
ddda913ccd | ||
|
|
11612e0877 | ||
|
|
0c24271833 | ||
|
|
01a3df31c8 | ||
|
|
4a9658184a | ||
|
|
9db5f2bb34 | ||
|
|
0e421de897 | ||
|
|
6e20b78b12 | ||
|
|
1dd17c92f2 | ||
|
|
aa44f2e18c | ||
|
|
18a52c214d |
7
.gitignore
vendored
7
.gitignore
vendored
@ -21,13 +21,16 @@ coverage/*
|
|||||||
**/*.config
|
**/*.config
|
||||||
**/*.creator
|
**/*.creator
|
||||||
*.log
|
*.log
|
||||||
|
*.tmp
|
||||||
|
*.tmp.*
|
||||||
.DS_Store
|
.DS_Store
|
||||||
bin/bitcoin*
|
bin/florincoin*
|
||||||
bin/SHA256SUMS.asc
|
bin/SHA256SUMS.asc
|
||||||
regtest/data/node1/regtest
|
regtest/data/node1/regtest
|
||||||
regtest/data/node2/regtest
|
regtest/data/node2/regtest
|
||||||
regtest/data/node3/regtest
|
regtest/data/node3/regtest
|
||||||
bitcore-node.json*
|
flocore-node.json*
|
||||||
*.bak
|
*.bak
|
||||||
*.orig
|
*.orig
|
||||||
lib/services/insight-api
|
lib/services/insight-api
|
||||||
|
testnet/*
|
||||||
|
|||||||
@ -22,12 +22,12 @@ coverage/*
|
|||||||
**/*.creator
|
**/*.creator
|
||||||
*.log
|
*.log
|
||||||
.DS_Store
|
.DS_Store
|
||||||
bin/bitcoin*
|
bin/florincoin*
|
||||||
bin/SHA256SUMS.asc
|
bin/SHA256SUMS.asc
|
||||||
regtest/data/node1/regtest
|
regtest/data/node1/regtest
|
||||||
regtest/data/node2/regtest
|
regtest/data/node2/regtest
|
||||||
regtest/data/node3/regtest
|
regtest/data/node3/regtest
|
||||||
bitcore-node.json*
|
flocore-node.json*
|
||||||
*.bak
|
*.bak
|
||||||
*.orig
|
*.orig
|
||||||
lib/services/insight-api
|
lib/services/insight-api
|
||||||
|
|||||||
4
LICENSE
4
LICENSE
@ -1,7 +1,7 @@
|
|||||||
Copyright (c) 2014-2015 BitPay, Inc.
|
Copyright (c) 2014-2015 BitPay, Inc.
|
||||||
|
|
||||||
Parts of this software are based on Bitcoin Core
|
Parts of this software are based on Florincoin Core
|
||||||
Copyright (c) 2009-2015 The Bitcoin Core developers
|
Copyright (c) 2009-2015 The Florincoin Core developers
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
of this software and associated documentation files (the "Software"), to deal
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
|||||||
26
README.md
26
README.md
@ -1,20 +1,20 @@
|
|||||||
Bitcore Node
|
Flocore Node
|
||||||
============
|
============
|
||||||
|
|
||||||
A Bitcoin blockchain indexing and query service. Intended to be used with as a Bitcoin full node or in conjunction with a Bitcoin full node.
|
A Florincoin blockchain indexing and query service. Intended to be used with as a Florincoin full node or in conjunction with a Florincoin full node.
|
||||||
|
|
||||||
## Upgrading from previous versions of Bitcore Node
|
## Upgrading from previous versions of Flocore Node
|
||||||
|
|
||||||
There is no upgrade path from previous versions of Bitcore Node due to the removal of the included Bitcoin Core software. By installing this version, you must resynchronize the indexes from scratch.
|
There is no upgrade path from previous versions of Flocore Node due to the removal of the included Florincoin Core software. By installing this version, you must resynchronize the indexes from scratch.
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install
|
npm install
|
||||||
./bin/bitcore-node start
|
./bin/flocore-node start
|
||||||
```
|
```
|
||||||
|
|
||||||
Note: A default configuration file is placed in the bitcore user's home directory (~/.bitcore/bitcore-node.json). Or, alternatively, you can copy the provided "bitcore-node.json.sample" file to the project's root directory as bitcore-node.json and edit it for your preferences. If you don't have a preferred block source (trusted peer), [Bcoin](https://github.com/bcoin-org/bcoin) will be started automatically and synchronized with the mainnet chain.
|
Note: A default configuration file is placed in the flocore user's home directory (~/.flocore/flocore-node.json). Or, alternatively, you can copy the provided "flocore-node.json.sample" file to the project's root directory as flocore-node.json and edit it for your preferences. If you don't have a preferred block source (trusted peer), [Bcoin](https://github.com/bcoin-org/bcoin) will be started automatically and synchronized with the mainnet chain.
|
||||||
|
|
||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
@ -24,21 +24,21 @@ Note: A default configuration file is placed in the bitcore user's home director
|
|||||||
|
|
||||||
## Configuration
|
## Configuration
|
||||||
|
|
||||||
The main configuration file is called "bitcore-node.json". This file instructs bitcore-node for the following options:
|
The main configuration file is called "flocore-node.json". This file instructs flocore-node for the following options:
|
||||||
|
|
||||||
- location of database files (datadir)
|
- location of database files (datadir)
|
||||||
- tcp port for web services, if configured (port)
|
- tcp port for web services, if configured (port)
|
||||||
- bitcoin network type (e.g. mainnet, testnet3, regtest), (network)
|
- florincoin network type (e.g. mainnet, testnet3, regtest), (network)
|
||||||
- what services to include (services)
|
- what services to include (services)
|
||||||
- the services' configuration (servicesConfig)
|
- the services' configuration (servicesConfig)
|
||||||
|
|
||||||
## Add-on Services
|
## Add-on Services
|
||||||
|
|
||||||
There are several add-on services available to extend the functionality of Bitcore:
|
There are several add-on services available to extend the functionality of Flocore:
|
||||||
|
|
||||||
- [Insight API](https://github.com/bitpay/insight-api)
|
- [Insight API](https://github.com/bitpay/insight-api)
|
||||||
- [Insight UI](https://github.com/bitpay/insight-ui)
|
- [Insight UI](https://github.com/bitpay/insight-ui)
|
||||||
- [Bitcore Wallet Service](https://github.com/bitpay/bitcore-wallet-service)
|
- [Flocore Wallet Service](https://github.com/bitpay/flocore-wallet-service)
|
||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
@ -60,12 +60,12 @@ There are several add-on services available to extend the functionality of Bitco
|
|||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
Please send pull requests for bug fixes, code optimization, and ideas for improvement. For more information on how to contribute, please refer to our [CONTRIBUTING](https://github.com/bitpay/bitcore/blob/master/CONTRIBUTING.md) file.
|
Please send pull requests for bug fixes, code optimization, and ideas for improvement. For more information on how to contribute, please refer to our [CONTRIBUTING](https://github.com/bitpay/flocore/blob/master/CONTRIBUTING.md) file.
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Code released under [the MIT license](https://github.com/bitpay/bitcore-node/blob/master/LICENSE).
|
Code released under [the MIT license](https://github.com/bitpay/flocore-node/blob/master/LICENSE).
|
||||||
|
|
||||||
Copyright 2013-2017 BitPay, Inc.
|
Copyright 2013-2017 BitPay, Inc.
|
||||||
|
|
||||||
- bitcoin: Copyright (c) 2009-2015 Bitcoin Core Developers (MIT License)
|
- florincoin: Copyright (c) 2009-2015 Florincoin Core Developers (MIT License)
|
||||||
|
|||||||
@ -1 +0,0 @@
|
|||||||
mtLDA41NWe9rLm7nuMvAnTs2SbP49cz1ZR
|
|
||||||
@ -1,4 +0,0 @@
|
|||||||
#!/usr/bin/env node
|
|
||||||
|
|
||||||
var bitcore = require('../lib/cli/bitcore');
|
|
||||||
bitcore();
|
|
||||||
4
bin/flocore-node
Executable file
4
bin/flocore-node
Executable file
@ -0,0 +1,4 @@
|
|||||||
|
#!/usr/bin/env node
|
||||||
|
|
||||||
|
var flocore = require('../lib/cli/flocore');
|
||||||
|
flocore();
|
||||||
@ -3,8 +3,8 @@
|
|||||||
var levelup = require('levelup');
|
var levelup = require('levelup');
|
||||||
var leveldown = require('leveldown');
|
var leveldown = require('leveldown');
|
||||||
var Encoding = require('../lib/services/address/encoding');
|
var Encoding = require('../lib/services/address/encoding');
|
||||||
var dbPath = '/Users/chrisk/.bwdb/bitcore-node.db';
|
var dbPath = '/Users/chrisk/.bwdb/flocore-node.db';
|
||||||
var bitcore = require('bitcore-lib');
|
var flocore = require('flocore-lib');
|
||||||
var db = levelup(dbPath, {keyEncoding: 'binary', valueEncoding: 'binary'});
|
var db = levelup(dbPath, {keyEncoding: 'binary', valueEncoding: 'binary'});
|
||||||
|
|
||||||
var prefix = new Buffer('0002', 'hex');
|
var prefix = new Buffer('0002', 'hex');
|
||||||
@ -33,7 +33,7 @@ stream.on('data', function(data) {
|
|||||||
for(var i = 0; i < inputValuesLength / 8; i++) {
|
for(var i = 0; i < inputValuesLength / 8; i++) {
|
||||||
inputValues.push(buffer.readDoubleBE(i * 8 + 14));
|
inputValues.push(buffer.readDoubleBE(i * 8 + 14));
|
||||||
}
|
}
|
||||||
var transaction = new bitcore.Transaction(data.value.slice(inputValues.length * 8 + 14));
|
var transaction = new flocore.Transaction(data.value.slice(inputValues.length * 8 + 14));
|
||||||
transaction.__height = height;
|
transaction.__height = height;
|
||||||
transaction.__inputValues = inputValues;
|
transaction.__inputValues = inputValues;
|
||||||
transaction.__timestamp = timestamp;
|
transaction.__timestamp = timestamp;
|
||||||
|
|||||||
@ -6,6 +6,6 @@
|
|||||||
# e.g. ./contrib/restart_bwdb.sh && tail -f /tmp/bwdb-out
|
# e.g. ./contrib/restart_bwdb.sh && tail -f /tmp/bwdb-out
|
||||||
|
|
||||||
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
|
||||||
pkill -2 -x bitcore
|
pkill -2 -x flocore
|
||||||
wait
|
wait
|
||||||
exec $DIR/../bin/bitcore-node start >> /tmp/bwdb-out 2>&1 &
|
exec $DIR/../bin/flocore-node start >> /tmp/bwdb-out 2>&1 &
|
||||||
|
|||||||
@ -4,14 +4,14 @@ Requires=network.target
|
|||||||
|
|
||||||
[Service]
|
[Service]
|
||||||
Type=simple
|
Type=simple
|
||||||
WorkingDirectory=/usr/opt/bitcore
|
WorkingDirectory=/usr/opt/flocore
|
||||||
ExecStart=/usr/bin/bwdb
|
ExecStart=/usr/bin/bwdb
|
||||||
ExecReload=/bin/kill -HUP $MAINPID
|
ExecReload=/bin/kill -HUP $MAINPID
|
||||||
Restart=on-failure
|
Restart=on-failure
|
||||||
RestartSec=15
|
RestartSec=15
|
||||||
User=bitcore
|
User=flocore
|
||||||
ExecStartPre=/bin/mkdir -p /run/bwdb
|
ExecStartPre=/bin/mkdir -p /run/bwdb
|
||||||
ExecStartPre=/bin/chown bitcore:bitcore /run/bwdb
|
ExecStartPre=/bin/chown flocore:flocore /run/bwdb
|
||||||
ExecStartPre=/bin/chmod 755 /run/bwdb
|
ExecStartPre=/bin/chmod 755 /run/bwdb
|
||||||
PermissionsStartOnly=true
|
PermissionsStartOnly=true
|
||||||
TimeoutStopSec=300
|
TimeoutStopSec=300
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
# Bus
|
# Bus
|
||||||
The bus provides a way to subscribe to events from any of the services running. It's implemented abstract from transport specific implementation. The primary use of the bus in Bitcore Node is for subscribing to events via a web socket.
|
The bus provides a way to subscribe to events from any of the services running. It's implemented abstract from transport specific implementation. The primary use of the bus in Flocore Node is for subscribing to events via a web socket.
|
||||||
|
|
||||||
## Opening/Closing
|
## Opening/Closing
|
||||||
|
|
||||||
@ -20,11 +20,11 @@ bus.close();
|
|||||||
```javascript
|
```javascript
|
||||||
|
|
||||||
// subscribe to all transaction events
|
// subscribe to all transaction events
|
||||||
bus.subscribe('bitcoind/rawtransaction');
|
bus.subscribe('florincoind/rawtransaction');
|
||||||
|
|
||||||
// to subscribe to new block hashes
|
// to subscribe to new block hashes
|
||||||
bus.subscribe('bitcoind/hashblock');
|
bus.subscribe('florincoind/hashblock');
|
||||||
|
|
||||||
// unsubscribe
|
// unsubscribe
|
||||||
bus.unsubscribe('bitcoind/rawtransaction');
|
bus.unsubscribe('florincoind/rawtransaction');
|
||||||
```
|
```
|
||||||
|
|||||||
@ -10,22 +10,22 @@ nvm install v4
|
|||||||
|
|
||||||
## Fork and Download Repositories
|
## Fork and Download Repositories
|
||||||
|
|
||||||
To develop bitcore-node:
|
To develop flocore-node:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd ~
|
cd ~
|
||||||
git clone git@github.com:<yourusername>/bitcore-node.git
|
git clone git@github.com:<yourusername>/flocore-node.git
|
||||||
git clone git@github.com:<yourusername>/bitcore-lib.git
|
git clone git@github.com:<yourusername>/flocore-lib.git
|
||||||
```
|
```
|
||||||
|
|
||||||
To develop bitcoin or to compile from source:
|
To develop florincoin or to compile from source:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone git@github.com:<yourusername>/bitcoin.git
|
git clone git@github.com:<yourusername>/florincoin.git
|
||||||
git fetch origin <branchname>:<branchname>
|
git fetch origin <branchname>:<branchname>
|
||||||
git checkout <branchname>
|
git checkout <branchname>
|
||||||
```
|
```
|
||||||
**Note**: See bitcoin documentation for building bitcoin on your platform.
|
**Note**: See florincoin documentation for building florincoin on your platform.
|
||||||
|
|
||||||
|
|
||||||
## Install Development Dependencies
|
## Install Development Dependencies
|
||||||
@ -46,27 +46,27 @@ brew install zeromq
|
|||||||
## Install and Symlink
|
## Install and Symlink
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd bitcore-lib
|
cd flocore-lib
|
||||||
npm install
|
npm install
|
||||||
cd ../bitcore-node
|
cd ../flocore-node
|
||||||
npm install
|
npm install
|
||||||
```
|
```
|
||||||
**Note**: If you get a message about not being able to download bitcoin distribution, you'll need to compile bitcoind from source, and setup your configuration to use that version.
|
**Note**: If you get a message about not being able to download florincoin distribution, you'll need to compile florincoind from source, and setup your configuration to use that version.
|
||||||
|
|
||||||
|
|
||||||
We now will setup symlinks in `bitcore-node` *(repeat this for any other modules you're planning on developing)*:
|
We now will setup symlinks in `flocore-node` *(repeat this for any other modules you're planning on developing)*:
|
||||||
```bash
|
```bash
|
||||||
cd node_modules
|
cd node_modules
|
||||||
rm -rf bitcore-lib
|
rm -rf flocore-lib
|
||||||
ln -s ~/bitcore-lib
|
ln -s ~/flocore-lib
|
||||||
rm -rf bitcoind-rpc
|
rm -rf florincoind-rpc
|
||||||
ln -s ~/bitcoind-rpc
|
ln -s ~/florincoind-rpc
|
||||||
```
|
```
|
||||||
|
|
||||||
And if you're compiling or developing bitcoin:
|
And if you're compiling or developing florincoin:
|
||||||
```bash
|
```bash
|
||||||
cd ../bin
|
cd ../bin
|
||||||
ln -sf ~/bitcoin/src/bitcoind
|
ln -sf ~/florincoin/src/florincoind
|
||||||
```
|
```
|
||||||
|
|
||||||
## Run Tests
|
## Run Tests
|
||||||
@ -78,19 +78,19 @@ npm install mocha -g
|
|||||||
|
|
||||||
To run all test suites:
|
To run all test suites:
|
||||||
```bash
|
```bash
|
||||||
cd bitcore-node
|
cd flocore-node
|
||||||
npm run regtest
|
npm run regtest
|
||||||
npm run test
|
npm run test
|
||||||
```
|
```
|
||||||
|
|
||||||
To run a specific unit test in watch mode:
|
To run a specific unit test in watch mode:
|
||||||
```bash
|
```bash
|
||||||
mocha -w -R spec test/services/bitcoind.unit.js
|
mocha -w -R spec test/services/florincoind.unit.js
|
||||||
```
|
```
|
||||||
|
|
||||||
To run a specific regtest:
|
To run a specific regtest:
|
||||||
```bash
|
```bash
|
||||||
mocha -R spec regtest/bitcoind.js
|
mocha -R spec regtest/florincoind.js
|
||||||
```
|
```
|
||||||
|
|
||||||
## Running a Development Node
|
## Running a Development Node
|
||||||
@ -102,27 +102,27 @@ cd ~
|
|||||||
mkdir devnode
|
mkdir devnode
|
||||||
cd devnode
|
cd devnode
|
||||||
mkdir node_modules
|
mkdir node_modules
|
||||||
touch bitcore-node.json
|
touch flocore-node.json
|
||||||
touch package.json
|
touch package.json
|
||||||
```
|
```
|
||||||
|
|
||||||
Edit `bitcore-node.json` with something similar to:
|
Edit `flocore-node.json` with something similar to:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"network": "livenet",
|
"network": "livenet",
|
||||||
"port": 3001,
|
"port": 3001,
|
||||||
"services": [
|
"services": [
|
||||||
"bitcoind",
|
"florincoind",
|
||||||
"web",
|
"web",
|
||||||
"insight-api",
|
"insight-api",
|
||||||
"insight-ui",
|
"insight-ui",
|
||||||
"<additional_service>"
|
"<additional_service>"
|
||||||
],
|
],
|
||||||
"servicesConfig": {
|
"servicesConfig": {
|
||||||
"bitcoind": {
|
"florincoind": {
|
||||||
"spawn": {
|
"spawn": {
|
||||||
"datadir": "/home/<youruser>/.bitcoin",
|
"datadir": "/home/<youruser>/.florincoin",
|
||||||
"exec": "/home/<youruser>/bitcoin/src/bitcoind"
|
"exec": "/home/<youruser>/florincoin/src/florincoind"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -135,13 +135,13 @@ Setup symlinks for all of the services and dependencies:
|
|||||||
|
|
||||||
```bash
|
```bash
|
||||||
cd node_modules
|
cd node_modules
|
||||||
ln -s ~/bitcore-lib
|
ln -s ~/flocore-lib
|
||||||
ln -s ~/bitcore-node
|
ln -s ~/flocore-node
|
||||||
ln -s ~/insight-api
|
ln -s ~/insight-api
|
||||||
ln -s ~/insight-ui
|
ln -s ~/insight-ui
|
||||||
```
|
```
|
||||||
|
|
||||||
Make sure that the `<datadir>/bitcoin.conf` has the necessary settings, for example:
|
Make sure that the `<datadir>/florincoin.conf` has the necessary settings, for example:
|
||||||
```
|
```
|
||||||
server=1
|
server=1
|
||||||
whitelist=127.0.0.1
|
whitelist=127.0.0.1
|
||||||
@ -152,11 +152,11 @@ spentindex=1
|
|||||||
zmqpubrawtx=tcp://127.0.0.1:28332
|
zmqpubrawtx=tcp://127.0.0.1:28332
|
||||||
zmqpubhashblock=tcp://127.0.0.1:28332
|
zmqpubhashblock=tcp://127.0.0.1:28332
|
||||||
rpcallowip=127.0.0.1
|
rpcallowip=127.0.0.1
|
||||||
rpcuser=bitcoin
|
rpcuser=florincoin
|
||||||
rpcpassword=local321
|
rpcpassword=local321
|
||||||
```
|
```
|
||||||
|
|
||||||
From within the `devnode` directory with the configuration file, start the node:
|
From within the `devnode` directory with the configuration file, start the node:
|
||||||
```bash
|
```bash
|
||||||
../bitcore-node/bin/bitcore-node start
|
../flocore-node/bin/flocore-node start
|
||||||
```
|
```
|
||||||
12
docs/node.md
12
docs/node.md
@ -14,17 +14,17 @@ A node represents a collection of services that are loaded together. For more in
|
|||||||
|
|
||||||
```js
|
```js
|
||||||
|
|
||||||
var index = require('bitcore-node');
|
var index = require('flocore-node');
|
||||||
var Bitcoin = index.services.Bitcoin;
|
var Florincoin = index.services.Florincoin;
|
||||||
var Node = index.Node;
|
var Node = index.Node;
|
||||||
|
|
||||||
var configuration = {
|
var configuration = {
|
||||||
datadir: '/home/user/.bitcoin',
|
datadir: '/home/user/.florincoin',
|
||||||
network: 'testnet',
|
network: 'testnet',
|
||||||
services: [
|
services: [
|
||||||
{
|
{
|
||||||
name: 'bitcoind',
|
name: 'florincoind',
|
||||||
module: Bitcoin,
|
module: Florincoin,
|
||||||
config: {}
|
config: {}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@ -37,7 +37,7 @@ node.start(function() {
|
|||||||
});
|
});
|
||||||
|
|
||||||
node.on('ready', function() {
|
node.on('ready', function() {
|
||||||
console.log('Bitcoin Node Ready');
|
console.log('Florincoin Node Ready');
|
||||||
});
|
});
|
||||||
|
|
||||||
node.on('error', function(err) {
|
node.on('error', function(err) {
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
# Release Process
|
# Release Process
|
||||||
|
|
||||||
Binaries for bitcoind are distributed for convenience and built deterministically with Gitian, signatures for bitcoind are located at the [gitian.sigs](https://github.com/bitpay/gitian.sigs) respository.
|
Binaries for florincoind are distributed for convenience and built deterministically with Gitian, signatures for florincoind are located at the [gitian.sigs](https://github.com/bitpay/gitian.sigs) respository.
|
||||||
|
|
||||||
## How to Release
|
## How to Release
|
||||||
|
|
||||||
When publishing to npm, the .gitignore file is used to exclude files from the npm publishing process. Be sure that the bitcore-node directory has only the directories and files that you would like to publish to npm. You might need to run the commands below on each platform that you intend to publish (e.g. Mac and Linux).
|
When publishing to npm, the .gitignore file is used to exclude files from the npm publishing process. Be sure that the flocore-node directory has only the directories and files that you would like to publish to npm. You might need to run the commands below on each platform that you intend to publish (e.g. Mac and Linux).
|
||||||
|
|
||||||
To make a release, bump the `version` of the `package.json`:
|
To make a release, bump the `version` of the `package.json`:
|
||||||
|
|
||||||
|
|||||||
@ -1,20 +1,20 @@
|
|||||||
# Scaffold
|
# Scaffold
|
||||||
A collection of functions for creating, managing, starting, stopping and interacting with a Bitcore node.
|
A collection of functions for creating, managing, starting, stopping and interacting with a Flocore node.
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
This function will add a service to a node by installing the necessary dependencies and modifying the `bitcore-node.json` configuration.
|
This function will add a service to a node by installing the necessary dependencies and modifying the `flocore-node.json` configuration.
|
||||||
|
|
||||||
## Start
|
## Start
|
||||||
This function will load a configuration file `bitcore-node.json` and instantiate and start a node based on the configuration.
|
This function will load a configuration file `flocore-node.json` and instantiate and start a node based on the configuration.
|
||||||
|
|
||||||
## Find Config
|
## Find Config
|
||||||
This function will recursively find a configuration `bitcore-node.json` file in parent directories and return the result.
|
This function will recursively find a configuration `flocore-node.json` file in parent directories and return the result.
|
||||||
|
|
||||||
## Default Config
|
## Default Config
|
||||||
This function will return a default configuration with the default services based on environment variables, and will default to using the standard `/home/user/.bitcoin` data directory.
|
This function will return a default configuration with the default services based on environment variables, and will default to using the standard `/home/user/.florincoin` data directory.
|
||||||
|
|
||||||
## Uninstall
|
## Uninstall
|
||||||
This function will remove a service from a node by uninstalling the necessary dependencies and modifying the `bitcore-node.json` configuration.
|
This function will remove a service from a node by uninstalling the necessary dependencies and modifying the `flocore-node.json` configuration.
|
||||||
|
|
||||||
## Call Method
|
## Call Method
|
||||||
This function will call an API method on a node via the JSON-RPC interface.
|
This function will call an API method on a node via the JSON-RPC interface.
|
||||||
|
|||||||
@ -1,16 +1,16 @@
|
|||||||
# Services
|
# Services
|
||||||
Bitcore Node has a service module system that can start up additional services that can include additional:
|
Flocore Node has a service module system that can start up additional services that can include additional:
|
||||||
- Blockchain indexes (e.g. querying balances for addresses)
|
- Blockchain indexes (e.g. querying balances for addresses)
|
||||||
- API methods
|
- API methods
|
||||||
- HTTP routes
|
- HTTP routes
|
||||||
- Event types to publish and subscribe
|
- Event types to publish and subscribe
|
||||||
|
|
||||||
The `bitcore-node.json` file describes which services will load for a node:
|
The `flocore-node.json` file describes which services will load for a node:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"services": [
|
"services": [
|
||||||
"bitcoind", "web"
|
"florincoind", "web"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
@ -20,36 +20,36 @@ Services correspond with a Node.js module as described in 'package.json', for ex
|
|||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bitcore-lib": "^0.13.7",
|
"flocore-lib": "^0.13.7",
|
||||||
"bitcore-node": "^0.2.0",
|
"flocore-node": "^0.2.0",
|
||||||
"insight-api": "^3.0.0"
|
"insight-api": "^3.0.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
_Note:_ If you already have a bitcore-node database, and you want to query data from previous blocks in the blockchain, you will need to reindex. Reindexing right now means deleting your bitcore-node database and resyncing.
|
_Note:_ If you already have a flocore-node database, and you want to query data from previous blocks in the blockchain, you will need to reindex. Reindexing right now means deleting your flocore-node database and resyncing.
|
||||||
|
|
||||||
## Using Services Programmatically
|
## Using Services Programmatically
|
||||||
If, instead, you would like to run a custom node, you can include services by including them in your configuration object when initializing a new node.
|
If, instead, you would like to run a custom node, you can include services by including them in your configuration object when initializing a new node.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
//Require bitcore
|
//Require flocore
|
||||||
var bitcore = require('bitcore-node');
|
var flocore = require('flocore-node');
|
||||||
|
|
||||||
//Services
|
//Services
|
||||||
var Bitcoin = bitcore.services.Bitcoin;
|
var Florincoin = flocore.services.Florincoin;
|
||||||
var Web = bitcore.services.Web;
|
var Web = flocore.services.Web;
|
||||||
|
|
||||||
var myNode = new bitcore.Node({
|
var myNode = new flocore.Node({
|
||||||
network: 'regtest'
|
network: 'regtest'
|
||||||
services: [
|
services: [
|
||||||
{
|
{
|
||||||
name: 'bitcoind',
|
name: 'florincoind',
|
||||||
module: Bitcoin,
|
module: Florincoin,
|
||||||
config: {
|
config: {
|
||||||
spawn: {
|
spawn: {
|
||||||
datadir: '/home/<username>/.bitcoin',
|
datadir: '/home/<username>/.florincoin',
|
||||||
exec: '/home/<username>/bitcore-node/bin/bitcoind'
|
exec: '/home/<username>/flocore-node/bin/florincoind'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -67,7 +67,7 @@ var myNode = new bitcore.Node({
|
|||||||
Now that you've loaded your services you can access them via `myNode.services.<service-name>.<method-name>`. For example if you wanted to check the balance of an address, you could access the address service like so.
|
Now that you've loaded your services you can access them via `myNode.services.<service-name>.<method-name>`. For example if you wanted to check the balance of an address, you could access the address service like so.
|
||||||
|
|
||||||
```js
|
```js
|
||||||
myNode.services.bitcoind.getAddressBalance('1HB5XMLmzFVj8ALj6mfBsbifRoD4miY36v', false, function(err, total) {
|
myNode.services.florincoind.getAddressBalance('1HB5XMLmzFVj8ALj6mfBsbifRoD4miY36v', false, function(err, total) {
|
||||||
console.log(total.balance); //Satoshi amount of this address
|
console.log(total.balance); //Satoshi amount of this address
|
||||||
});
|
});
|
||||||
```
|
```
|
||||||
@ -82,7 +82,7 @@ A new service can be created by inheriting from `Node.Service` and implementing
|
|||||||
- `Service.prototype.getPublishEvents()` - Describes which events can be subscribed to for this service, useful to subscribe to events over the included web socket API.
|
- `Service.prototype.getPublishEvents()` - Describes which events can be subscribed to for this service, useful to subscribe to events over the included web socket API.
|
||||||
- `Service.prototype.setupRoutes()` - A service can extend HTTP routes on an express application by implementing this method.
|
- `Service.prototype.setupRoutes()` - A service can extend HTTP routes on an express application by implementing this method.
|
||||||
|
|
||||||
The `package.json` for the service module can either export the `Node.Service` directly, or specify a specific module to load by including `"bitcoreNode": "lib/bitcore-node.js"`.
|
The `package.json` for the service module can either export the `Node.Service` directly, or specify a specific module to load by including `"flocoreNode": "lib/flocore-node.js"`.
|
||||||
|
|
||||||
Please take a look at some of the existing services for implementation specifics.
|
Please take a look at some of the existing services for implementation specifics.
|
||||||
|
|
||||||
|
|||||||
@ -1,13 +1,13 @@
|
|||||||
# Address Service
|
# Address Service
|
||||||
|
|
||||||
The address service provides an address index for the Bitcoin blockchain. Specifically, it builds and maintains the following information about every address ever used on the Bitcoin network:
|
The address service provides an address index for the Florincoin blockchain. Specifically, it builds and maintains the following information about every address ever used on the Florincoin network:
|
||||||
|
|
||||||
- block heights the address appeared in
|
- block heights the address appeared in
|
||||||
- transaction ids and the index in the transaction
|
- transaction ids and the index in the transaction
|
||||||
- whether the address appeared in an input or output
|
- whether the address appeared in an input or output
|
||||||
- the timestamp for the block
|
- the timestamp for the block
|
||||||
|
|
||||||
Additionally, the address index also maintains the unspent transaction output index for the Bitcoin blockchain. Example queries for this type of data is provided by 'getAddressUnspentOutputs', 'getAddressSummary', and 'getAddressHistory'.
|
Additionally, the address index also maintains the unspent transaction output index for the Florincoin blockchain. Example queries for this type of data is provided by 'getAddressUnspentOutputs', 'getAddressSummary', and 'getAddressHistory'.
|
||||||
|
|
||||||
This service is generally used to support other services and is not used externally.
|
This service is generally used to support other services and is not used externally.
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# Block Service
|
# Block Service
|
||||||
|
|
||||||
The block service provides a block index for the Bitcoin blockchain. Specifically, there are two data points this service tracks:
|
The block service provides a block index for the Florincoin blockchain. Specifically, there are two data points this service tracks:
|
||||||
|
|
||||||
- block hash
|
- block hash
|
||||||
- raw block
|
- raw block
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# Db Service
|
# Db Service
|
||||||
|
|
||||||
The db service provides an abstraction over the underlying database used to store the indexes in bitcore-node.
|
The db service provides an abstraction over the underlying database used to store the indexes in flocore-node.
|
||||||
|
|
||||||
## Service Configuration
|
## Service Configuration
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# Fee Service
|
# Fee Service
|
||||||
|
|
||||||
The fee service is a requirement of the insight-api service (not a bitcore-node built-in service). Its primary purpose is to query a bitcoin full node for the most up-to-date miner fees for transactions. A bitcoin full node such as [BTC1](https://github.com/btc1/bitcoin) or [bcoin](https://github.com/bcoin-org/bcoin) with an available RPC interface is required.
|
The fee service is a requirement of the insight-api service (not a flocore-node built-in service). Its primary purpose is to query a florincoin full node for the most up-to-date miner fees for transactions. A florincoin full node such as [BTC1](https://github.com/btc1/florincoin) or [bcoin](https://github.com/bcoin-org/bcoin) with an available RPC interface is required.
|
||||||
|
|
||||||
## Service Configuration
|
## Service Configuration
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# Header Service
|
# Header Service
|
||||||
|
|
||||||
The header service provides a header index for the Bitcoin blockchain. Specifically, it builds and maintains the following information about every bitcoin block header:
|
The header service provides a header index for the Florincoin blockchain. Specifically, it builds and maintains the following information about every florincoin block header:
|
||||||
|
|
||||||
- block hash
|
- block hash
|
||||||
- block height
|
- block height
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# Mempool Service
|
# Mempool Service
|
||||||
|
|
||||||
The mempool service provides a mempool transaction index for the Bitcoin blockchain. Specifically, it maintains a larger index of mempool transactions than a typical full node can manage on its own.
|
The mempool service provides a mempool transaction index for the Florincoin blockchain. Specifically, it maintains a larger index of mempool transactions than a typical full node can manage on its own.
|
||||||
|
|
||||||
- transaction id
|
- transaction id
|
||||||
- transaction
|
- transaction
|
||||||
|
|||||||
@ -1,9 +1,9 @@
|
|||||||
# P2P Service
|
# P2P Service
|
||||||
|
|
||||||
The p2p service provides a peer-to-peer interface for the Bitcoin blockchain. This service abstracts the connection and commnuication interface between the Bitcoin and the rest of bitcore node.
|
The p2p service provides a peer-to-peer interface for the Florincoin blockchain. This service abstracts the connection and commnuication interface between the Florincoin and the rest of flocore node.
|
||||||
|
|
||||||
|
|
||||||
This service also provides the publisher interface on bitcore-node bus architecture. The P2P service will publish header, block and transaction events.
|
This service also provides the publisher interface on flocore-node bus architecture. The P2P service will publish header, block and transaction events.
|
||||||
|
|
||||||
## Service Configuration
|
## Service Configuration
|
||||||
|
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# Timestamp Service
|
# Timestamp Service
|
||||||
|
|
||||||
The timestamp service provides a block timestamp index for the Bitcoin blockchain. The only reason this index needs to exist is to ensure that block timestamps are always strictly greater than all the previous block timestamps. In the native block timestamps, this is not always the case. Without this index, accounting systems that are based on time spans (pretty much all of them), there will be issues accounting for transactions accurately.
|
The timestamp service provides a block timestamp index for the Florincoin blockchain. The only reason this index needs to exist is to ensure that block timestamps are always strictly greater than all the previous block timestamps. In the native block timestamps, this is not always the case. Without this index, accounting systems that are based on time spans (pretty much all of them), there will be issues accounting for transactions accurately.
|
||||||
|
|
||||||
- block timestamp
|
- block timestamp
|
||||||
- block hash
|
- block hash
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
# Transaction Service
|
# Transaction Service
|
||||||
|
|
||||||
The transaction service provides a transaction index for the Bitcoin blockchain. Specifically, it builds and maintains the following information about every transaction on the Bitcoin network:
|
The transaction service provides a transaction index for the Florincoin blockchain. Specifically, it builds and maintains the following information about every transaction on the Florincoin network:
|
||||||
|
|
||||||
- transaction ids and transactions
|
- transaction ids and transactions
|
||||||
- input values for every transaction
|
- input values for every transaction
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
# Web Service
|
# Web Service
|
||||||
The web service creates an express app which can be used by services for setting up web routes for API's, static content, web applications, etc. This allows users to interact with various bitcore node services over one http or https port.
|
The web service creates an express app which can be used by services for setting up web routes for API's, static content, web applications, etc. This allows users to interact with various flocore node services over one http or https port.
|
||||||
|
|
||||||
In order for your service to add routes, it must implement the `setupRoutes()` and `getRoutePrefix()` methods.
|
In order for your service to add routes, it must implement the `setupRoutes()` and `getRoutePrefix()` methods.
|
||||||
|
|
||||||
@ -22,7 +22,7 @@ MyService.prototype.getRoutePrefix = function() {
|
|||||||
```
|
```
|
||||||
|
|
||||||
## Configuring Web Service for HTTPS
|
## Configuring Web Service for HTTPS
|
||||||
You can run the web service over https by editing your bitcore node config, setting https to true and adding httpsOptions:
|
You can run the web service over https by editing your flocore node config, setting https to true and adding httpsOptions:
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,14 +1,14 @@
|
|||||||
# Upgrade Notes
|
# Upgrade Notes
|
||||||
|
|
||||||
## From Bitcore 3.0.0 to 4.0.0
|
## From Flocore 3.0.0 to 4.0.0
|
||||||
|
|
||||||
`bitcore-node@2.1.1` to `bitcore-node@3.0.0`
|
`flocore-node@2.1.1` to `flocore-node@3.0.0`
|
||||||
|
|
||||||
This major upgrade includes changes to indexes, API methods and services. Please review below details before upgrading.
|
This major upgrade includes changes to indexes, API methods and services. Please review below details before upgrading.
|
||||||
|
|
||||||
### Indexes
|
### Indexes
|
||||||
|
|
||||||
Indexes include *more information* and are now also *faster*. Because of this a **reindex will be necessary** when upgrading as the address and database indexes are now a part of bitcoind with three new `bitcoin.conf` options:
|
Indexes include *more information* and are now also *faster*. Because of this a **reindex will be necessary** when upgrading as the address and database indexes are now a part of florincoind with three new `florincoin.conf` options:
|
||||||
- `-addressindex`
|
- `-addressindex`
|
||||||
- `-timestampindex`
|
- `-timestampindex`
|
||||||
- `-spentindex`
|
- `-spentindex`
|
||||||
@ -17,18 +17,18 @@ To start reindexing add `reindex=1` during the **first startup only**.
|
|||||||
|
|
||||||
### Configuration Options
|
### Configuration Options
|
||||||
|
|
||||||
- The `bitcoin.conf` file in will need to be updated to include additional indexes *(see below)*.
|
- The `florincoin.conf` file in will need to be updated to include additional indexes *(see below)*.
|
||||||
- The `datadir` option is now a part of `bitcoind` spawn configuration, and there is a new option to connect to multiple bitcoind processes (Please see [Bitcoin Service Docs](services/bitcoind.md) for more details). The services `db` and `address` are now a part of the `bitcoind` service. Here is how to update `bitcore-node.json` configuration options:
|
- The `datadir` option is now a part of `florincoind` spawn configuration, and there is a new option to connect to multiple florincoind processes (Please see [Florincoin Service Docs](services/florincoind.md) for more details). The services `db` and `address` are now a part of the `florincoind` service. Here is how to update `flocore-node.json` configuration options:
|
||||||
|
|
||||||
**Before**:
|
**Before**:
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
"datadir": "/home/<username>/.bitcoin",
|
"datadir": "/home/<username>/.florincoin",
|
||||||
"network": "livenet",
|
"network": "livenet",
|
||||||
"port": 3001,
|
"port": 3001,
|
||||||
"services": [
|
"services": [
|
||||||
"address",
|
"address",
|
||||||
"bitcoind",
|
"florincoind",
|
||||||
"db",
|
"db",
|
||||||
"web"
|
"web"
|
||||||
]
|
]
|
||||||
@ -41,21 +41,21 @@ To start reindexing add `reindex=1` during the **first startup only**.
|
|||||||
"network": "livenet",
|
"network": "livenet",
|
||||||
"port": 3001,
|
"port": 3001,
|
||||||
"services": [
|
"services": [
|
||||||
"bitcoind",
|
"florincoind",
|
||||||
"web"
|
"web"
|
||||||
],
|
],
|
||||||
"servicesConfig": {
|
"servicesConfig": {
|
||||||
"bitcoind": {
|
"florincoind": {
|
||||||
"spawn": {
|
"spawn": {
|
||||||
"datadir": "/home/<username>/.bitcoin",
|
"datadir": "/home/<username>/.florincoin",
|
||||||
"exec": "/home/<username>/bitcore-node/bin/bitcoind"
|
"exec": "/home/<username>/flocore-node/bin/florincoind"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
It will also be necessary to update `bitcoin.conf` settings, to include these fields:
|
It will also be necessary to update `florincoin.conf` settings, to include these fields:
|
||||||
```
|
```
|
||||||
server=1
|
server=1
|
||||||
whitelist=127.0.0.1
|
whitelist=127.0.0.1
|
||||||
@ -70,8 +70,8 @@ rpcuser=<user>
|
|||||||
rpcpassword=<password>
|
rpcpassword=<password>
|
||||||
```
|
```
|
||||||
|
|
||||||
**Important**: Once changes have been made you'll also need to add the `reindex=1` option **only for the first startup** to regenerate the indexes. Once this is complete you should be able to remove the `bitcore-node.db` directory with the old indexes.
|
**Important**: Once changes have been made you'll also need to add the `reindex=1` option **only for the first startup** to regenerate the indexes. Once this is complete you should be able to remove the `flocore-node.db` directory with the old indexes.
|
||||||
|
|
||||||
### API and Service Changes
|
### API and Service Changes
|
||||||
- Many API methods that were a part of the `db` and `address` services are now a part of the `bitcoind` service. Please see [Bitcoin Service Docs](services/bitcoind.md) for more details.
|
- Many API methods that were a part of the `db` and `address` services are now a part of the `florincoind` service. Please see [Florincoin Service Docs](services/florincoind.md) for more details.
|
||||||
- The `db` and `address` services are deprecated, most of the functionality still exists. Any services that were extending indexes with the `db` service, will need to manage chain state itself, or build the indexes within `bitcoind`.
|
- The `db` and `address` services are deprecated, most of the functionality still exists. Any services that were extending indexes with the `db` service, will need to manage chain state itself, or build the indexes within `florincoind`.
|
||||||
|
|||||||
6
index.js
6
index.js
@ -20,7 +20,7 @@ module.exports.scaffold.defaultConfig = require('./lib/scaffold/default-config')
|
|||||||
module.exports.cli = {};
|
module.exports.cli = {};
|
||||||
module.exports.cli.main = require('./lib/cli/main');
|
module.exports.cli.main = require('./lib/cli/main');
|
||||||
module.exports.cli.daemon = require('./lib/cli/daemon');
|
module.exports.cli.daemon = require('./lib/cli/daemon');
|
||||||
module.exports.cli.bitcore = require('./lib/cli/bitcore');
|
module.exports.cli.flocore = require('./lib/cli/flocore');
|
||||||
module.exports.cli.bitcored = require('./lib/cli/bitcored');
|
module.exports.cli.flocored = require('./lib/cli/flocored');
|
||||||
|
|
||||||
module.exports.lib = require('bitcore-lib');
|
module.exports.lib = require('flocore-lib');
|
||||||
|
|||||||
@ -2,20 +2,20 @@
|
|||||||
|
|
||||||
var program = require('commander');
|
var program = require('commander');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var bitcore = require('..');
|
var flocore = require('..');
|
||||||
|
|
||||||
function main(servicesPath, additionalServices) {
|
function main(servicesPath, additionalServices) {
|
||||||
/* jshint maxstatements: 100 */
|
/* jshint maxstatements: 100 */
|
||||||
|
|
||||||
var version = bitcore.version;
|
var version = flocore.version;
|
||||||
var start = bitcore.scaffold.start;
|
var start = flocore.scaffold.start;
|
||||||
var findConfig = bitcore.scaffold.findConfig;
|
var findConfig = flocore.scaffold.findConfig;
|
||||||
var defaultConfig = bitcore.scaffold.defaultConfig;
|
var defaultConfig = flocore.scaffold.defaultConfig;
|
||||||
|
|
||||||
program
|
program
|
||||||
.version(version)
|
.version(version)
|
||||||
.description('Start the current node')
|
.description('Start the current node')
|
||||||
.option('-c, --config <dir>', 'Specify the directory with Bitcore Node configuration');
|
.option('-c, --config <dir>', 'Specify the directory with Flocore Node configuration');
|
||||||
|
|
||||||
program.parse(process.argv);
|
program.parse(process.argv);
|
||||||
|
|
||||||
|
|||||||
@ -5,10 +5,10 @@ var Liftoff = require('liftoff');
|
|||||||
function main(parentServicesPath, additionalServices) {
|
function main(parentServicesPath, additionalServices) {
|
||||||
|
|
||||||
var liftoff = new Liftoff({
|
var liftoff = new Liftoff({
|
||||||
name: 'bitcore',
|
name: 'flocore',
|
||||||
moduleName: 'bitcore-node',
|
moduleName: 'flocore-node',
|
||||||
configName: 'bitcore-node',
|
configName: 'flocore-node',
|
||||||
processTitle: 'bitcore'
|
processTitle: 'flocore'
|
||||||
}).on('require', function (name) {
|
}).on('require', function (name) {
|
||||||
console.log('Loading:', name);
|
console.log('Loading:', name);
|
||||||
}).on('requireFail', function (name, err) {
|
}).on('requireFail', function (name, err) {
|
||||||
@ -31,6 +31,15 @@ function main(parentServicesPath, additionalServices) {
|
|||||||
node.cli.main(parentServicesPath, additionalServices);
|
node.cli.main(parentServicesPath, additionalServices);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Gracefully Shut Down
|
||||||
|
process.on('SIGTERM', function () {
|
||||||
|
console.log("Shutting down flocore-node")
|
||||||
|
node.stop(function() {
|
||||||
|
console.log("flocore-node successfully stopped!")
|
||||||
|
process.exit(0)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -5,10 +5,10 @@ var Liftoff = require('liftoff');
|
|||||||
function main(parentServicesPath, additionalServices) {
|
function main(parentServicesPath, additionalServices) {
|
||||||
|
|
||||||
var liftoff = new Liftoff({
|
var liftoff = new Liftoff({
|
||||||
name: 'bitcored',
|
name: 'flocored',
|
||||||
moduleName: 'bitcore-node',
|
moduleName: 'flocore-node',
|
||||||
configName: 'bitcore-node',
|
configName: 'flocore-node',
|
||||||
processTitle: 'bitcored'
|
processTitle: 'flocored'
|
||||||
}).on('require', function (name) {
|
}).on('require', function (name) {
|
||||||
console.log('Loading:', name);
|
console.log('Loading:', name);
|
||||||
}).on('requireFail', function (name, err) {
|
}).on('requireFail', function (name, err) {
|
||||||
@ -2,20 +2,20 @@
|
|||||||
|
|
||||||
var program = require('commander');
|
var program = require('commander');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var bitcorenode = require('..');
|
var flocorenode = require('..');
|
||||||
var utils = require('../utils');
|
var utils = require('../utils');
|
||||||
|
|
||||||
function main(servicesPath, additionalServices) {
|
function main(servicesPath, additionalServices) {
|
||||||
/* jshint maxstatements: 100 */
|
/* jshint maxstatements: 100 */
|
||||||
|
|
||||||
var version = bitcorenode.version;
|
var version = flocorenode.version;
|
||||||
var create = bitcorenode.scaffold.create;
|
var create = flocorenode.scaffold.create;
|
||||||
var add = bitcorenode.scaffold.add;
|
var add = flocorenode.scaffold.add;
|
||||||
var start = bitcorenode.scaffold.start;
|
var start = flocorenode.scaffold.start;
|
||||||
var remove = bitcorenode.scaffold.remove;
|
var remove = flocorenode.scaffold.remove;
|
||||||
var callMethod = bitcorenode.scaffold.callMethod;
|
var callMethod = flocorenode.scaffold.callMethod;
|
||||||
var findConfig = bitcorenode.scaffold.findConfig;
|
var findConfig = flocorenode.scaffold.findConfig;
|
||||||
var defaultConfig = bitcorenode.scaffold.defaultConfig;
|
var defaultConfig = flocorenode.scaffold.defaultConfig;
|
||||||
|
|
||||||
program
|
program
|
||||||
.version(version);
|
.version(version);
|
||||||
@ -23,7 +23,7 @@ function main(servicesPath, additionalServices) {
|
|||||||
program
|
program
|
||||||
.command('create <directory>')
|
.command('create <directory>')
|
||||||
.description('Create a new node')
|
.description('Create a new node')
|
||||||
.option('-d, --datadir <dir>', 'Specify the bitcoin database directory')
|
.option('-d, --datadir <dir>', 'Specify the florincoin database directory')
|
||||||
.option('-t, --testnet', 'Enable testnet as the network')
|
.option('-t, --testnet', 'Enable testnet as the network')
|
||||||
.action(function(dirname, cmd){
|
.action(function(dirname, cmd){
|
||||||
if (cmd.datadir) {
|
if (cmd.datadir) {
|
||||||
@ -49,7 +49,7 @@ function main(servicesPath, additionalServices) {
|
|||||||
program
|
program
|
||||||
.command('start')
|
.command('start')
|
||||||
.description('Start the current node')
|
.description('Start the current node')
|
||||||
.option('-c, --config <dir>', 'Specify the directory with Bitcore Node configuration')
|
.option('-c, --config <dir>', 'Specify the directory with Flocore Node configuration')
|
||||||
.action(function(cmd){
|
.action(function(cmd){
|
||||||
if (cmd.config) {
|
if (cmd.config) {
|
||||||
cmd.config = path.resolve(process.cwd(), cmd.config);
|
cmd.config = path.resolve(process.cwd(), cmd.config);
|
||||||
@ -72,7 +72,7 @@ function main(servicesPath, additionalServices) {
|
|||||||
.action(function(services){
|
.action(function(services){
|
||||||
var configInfo = findConfig(process.cwd());
|
var configInfo = findConfig(process.cwd());
|
||||||
if (!configInfo) {
|
if (!configInfo) {
|
||||||
throw new Error('Could not find configuration, see `bitcore-node create --help`');
|
throw new Error('Could not find configuration, see `flocore-node create --help`');
|
||||||
}
|
}
|
||||||
var opts = {
|
var opts = {
|
||||||
path: configInfo.path,
|
path: configInfo.path,
|
||||||
@ -87,8 +87,8 @@ function main(servicesPath, additionalServices) {
|
|||||||
}).on('--help', function() {
|
}).on('--help', function() {
|
||||||
console.log(' Examples:');
|
console.log(' Examples:');
|
||||||
console.log();
|
console.log();
|
||||||
console.log(' $ bitcore-node add wallet-service');
|
console.log(' $ flocore-node add wallet-service');
|
||||||
console.log(' $ bitcore-node add insight-api');
|
console.log(' $ flocore-node add insight-api');
|
||||||
console.log();
|
console.log();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -98,7 +98,7 @@ function main(servicesPath, additionalServices) {
|
|||||||
.action(function(services){
|
.action(function(services){
|
||||||
var configInfo = findConfig(process.cwd());
|
var configInfo = findConfig(process.cwd());
|
||||||
if (!configInfo) {
|
if (!configInfo) {
|
||||||
throw new Error('Could not find configuration, see `bitcore-node create --help`');
|
throw new Error('Could not find configuration, see `flocore-node create --help`');
|
||||||
}
|
}
|
||||||
var opts = {
|
var opts = {
|
||||||
path: configInfo.path,
|
path: configInfo.path,
|
||||||
@ -113,8 +113,8 @@ function main(servicesPath, additionalServices) {
|
|||||||
}).on('--help', function() {
|
}).on('--help', function() {
|
||||||
console.log(' Examples:');
|
console.log(' Examples:');
|
||||||
console.log();
|
console.log();
|
||||||
console.log(' $ bitcore-node remove wallet-service');
|
console.log(' $ flocore-node remove wallet-service');
|
||||||
console.log(' $ bitcore-node remove insight-api');
|
console.log(' $ flocore-node remove insight-api');
|
||||||
console.log();
|
console.log();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -3,9 +3,9 @@
|
|||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
BITCOIN_GENESIS_HASH: {
|
BITCOIN_GENESIS_HASH: {
|
||||||
livenet: '000000000019d6689c085ae165831e934ff763ae46a2a6c172b3f1b60a8ce26f',
|
livenet: '09c7781c9df90708e278c35d38ea5c9041d7ecfcdd1c56ba67274b7cff3e1cea',
|
||||||
regtest: '0f9188f13cb7b2c71f2a335e3a4fc328bf5beb436012afca590b1a11466e2206',
|
regtest: 'ec42fa26ca6dcb1103b59a1d24b161935ea4566f8d5736db8917d5b9a8dee0d7',
|
||||||
testnet: '000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943', //this is testnet3
|
testnet: '9b7bc86236c34b5e3a39367c036b7fe8807a966c22a7a1f0da2a198a27e03731', //this is testnet3
|
||||||
testnet5: '000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943' //this is testnet5
|
testnet5: '000000000933ea01ad0ee984209779baaec3ced90fa3f408719526f8d77f4943' //this is testnet5
|
||||||
},
|
},
|
||||||
DB_PREFIX: new Buffer('ffff', 'hex')
|
DB_PREFIX: new Buffer('ffff', 'hex')
|
||||||
|
|||||||
@ -2,11 +2,11 @@
|
|||||||
|
|
||||||
var createError = require('errno').create;
|
var createError = require('errno').create;
|
||||||
|
|
||||||
var BitcoreNodeError = createError('BitcoreNodeError');
|
var FlocoreNodeError = createError('FlocoreNodeError');
|
||||||
|
|
||||||
var RPCError = createError('RPCError', BitcoreNodeError);
|
var RPCError = createError('RPCError', FlocoreNodeError);
|
||||||
|
|
||||||
module.exports = {
|
module.exports = {
|
||||||
Error: BitcoreNodeError,
|
Error: FlocoreNodeError,
|
||||||
RPCError: RPCError
|
RPCError: RPCError
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var bitcore = require('bitcore-lib');
|
var flocore = require('flocore-lib');
|
||||||
var _ = bitcore.deps._;
|
var _ = flocore.deps._;
|
||||||
var colors = require('colors/safe');
|
var colors = require('colors/safe');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -4,8 +4,8 @@ var util = require('util');
|
|||||||
var EventEmitter = require('events').EventEmitter;
|
var EventEmitter = require('events').EventEmitter;
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var bitcore = require('bitcore-lib');
|
var flocore = require('flocore-lib');
|
||||||
var _ = bitcore.deps._;
|
var _ = flocore.deps._;
|
||||||
var index = require('./');
|
var index = require('./');
|
||||||
var log = index.log;
|
var log = index.log;
|
||||||
var Bus = require('./bus');
|
var Bus = require('./bus');
|
||||||
|
|||||||
@ -4,10 +4,10 @@ var async = require('async');
|
|||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var spawn = require('child_process').spawn;
|
var spawn = require('child_process').spawn;
|
||||||
var bitcore = require('bitcore-lib');
|
var flocore = require('flocore-lib');
|
||||||
var utils = require('../utils');
|
var utils = require('../utils');
|
||||||
var $ = bitcore.util.preconditions;
|
var $ = flocore.util.preconditions;
|
||||||
var _ = bitcore.deps._;
|
var _ = flocore.deps._;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {String} configFilePath - The absolute path to the configuration file
|
* @param {String} configFilePath - The absolute path to the configuration file
|
||||||
@ -62,7 +62,7 @@ function addService(configDir, service, done) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param {String} options.cwd - The current working directory
|
* @param {String} options.cwd - The current working directory
|
||||||
* @param {String} options.dirname - The bitcore-node configuration directory
|
* @param {String} options.dirname - The flocore-node configuration directory
|
||||||
* @param {Array} options.services - An array of strings of service names
|
* @param {Array} options.services - An array of strings of service names
|
||||||
* @param {Function} done - A callback function called when finished
|
* @param {Function} done - A callback function called when finished
|
||||||
*/
|
*/
|
||||||
@ -78,12 +78,12 @@ function add(options, done) {
|
|||||||
var configPath = options.path;
|
var configPath = options.path;
|
||||||
var services = options.services;
|
var services = options.services;
|
||||||
|
|
||||||
var bitcoreConfigPath = path.resolve(configPath, 'bitcore-node.json');
|
var flocoreConfigPath = path.resolve(configPath, 'flocore-node.json');
|
||||||
var packagePath = path.resolve(configPath, 'package.json');
|
var packagePath = path.resolve(configPath, 'package.json');
|
||||||
|
|
||||||
if (!fs.existsSync(bitcoreConfigPath) || !fs.existsSync(packagePath)) {
|
if (!fs.existsSync(flocoreConfigPath) || !fs.existsSync(packagePath)) {
|
||||||
return done(
|
return done(
|
||||||
new Error('Directory does not have a bitcore-node.json and/or package.json file.')
|
new Error('Directory does not have a flocore-node.json and/or package.json file.')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,8 +108,8 @@ function add(options, done) {
|
|||||||
oldPackage = updatedPackage;
|
oldPackage = updatedPackage;
|
||||||
var serviceName = newDependencies[0];
|
var serviceName = newDependencies[0];
|
||||||
|
|
||||||
// add service to bitcore-node.json
|
// add service to flocore-node.json
|
||||||
addConfig(bitcoreConfigPath, serviceName, next);
|
addConfig(flocoreConfigPath, serviceName, next);
|
||||||
});
|
});
|
||||||
}, done
|
}, done
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var spawn = require('child_process').spawn;
|
var spawn = require('child_process').spawn;
|
||||||
var bitcore = require('bitcore-lib');
|
var flocore = require('flocore-lib');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var $ = bitcore.util.preconditions;
|
var $ = flocore.util.preconditions;
|
||||||
var _ = bitcore.deps._;
|
var _ = flocore.deps._;
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var packageFile = require('../../package.json');
|
var packageFile = require('../../package.json');
|
||||||
var mkdirp = require('mkdirp');
|
var mkdirp = require('mkdirp');
|
||||||
@ -14,22 +14,22 @@ var defaultConfig = require('./default-config');
|
|||||||
var version = '^' + packageFile.version;
|
var version = '^' + packageFile.version;
|
||||||
|
|
||||||
var BASE_PACKAGE = {
|
var BASE_PACKAGE = {
|
||||||
description: 'A full Bitcoin node build with Bitcore',
|
description: 'A full Florincoin node build with Flocore',
|
||||||
repository: 'https://github.com/user/project',
|
repository: 'https://github.com/user/project',
|
||||||
license: 'MIT',
|
license: 'MIT',
|
||||||
readme: 'README.md',
|
readme: 'README.md',
|
||||||
dependencies: {
|
dependencies: {
|
||||||
'bitcore-lib': '^' + bitcore.version,
|
'flocore-lib': '^' + flocore.version,
|
||||||
'bitcore-node': version
|
'flocore-node': version
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will create a directory and bitcoin.conf file for Bitcoin.
|
* Will create a directory and florincoin.conf file for Florincoin.
|
||||||
* @param {String} dataDir - The absolute path
|
* @param {String} dataDir - The absolute path
|
||||||
* @param {Function} done - The callback function called when finished
|
* @param {Function} done - The callback function called when finished
|
||||||
*/
|
*/
|
||||||
function createBitcoinDirectory(datadir, done) {
|
function createFlorincoinDirectory(datadir, done) {
|
||||||
mkdirp(datadir, function(err) {
|
mkdirp(datadir, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
@ -42,10 +42,10 @@ function createBitcoinDirectory(datadir, done) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will create a base Bitcore Node configuration directory and files.
|
* Will create a base Flocore Node configuration directory and files.
|
||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
* @param {String} options.network - "testnet" or "livenet"
|
* @param {String} options.network - "testnet" or "livenet"
|
||||||
* @param {String} options.datadir - The bitcoin database directory
|
* @param {String} options.datadir - The florincoin database directory
|
||||||
* @param {String} configDir - The absolute path
|
* @param {String} configDir - The absolute path
|
||||||
* @param {Boolean} isGlobal - If the configuration depends on globally installed node services.
|
* @param {Boolean} isGlobal - If the configuration depends on globally installed node services.
|
||||||
* @param {Function} done - The callback function called when finished
|
* @param {Function} done - The callback function called when finished
|
||||||
@ -61,7 +61,7 @@ function createConfigDirectory(options, configDir, isGlobal, done) {
|
|||||||
var configJSON = JSON.stringify(config, null, 2);
|
var configJSON = JSON.stringify(config, null, 2);
|
||||||
var packageJSON = JSON.stringify(BASE_PACKAGE, null, 2);
|
var packageJSON = JSON.stringify(BASE_PACKAGE, null, 2);
|
||||||
try {
|
try {
|
||||||
fs.writeFileSync(configDir + '/bitcore-node.json', configJSON);
|
fs.writeFileSync(configDir + '/flocore-node.json', configJSON);
|
||||||
if (!isGlobal) {
|
if (!isGlobal) {
|
||||||
fs.writeFileSync(configDir + '/package.json', packageJSON);
|
fs.writeFileSync(configDir + '/package.json', packageJSON);
|
||||||
}
|
}
|
||||||
@ -74,13 +74,13 @@ function createConfigDirectory(options, configDir, isGlobal, done) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will setup a directory with a Bitcore Node directory, configuration file,
|
* Will setup a directory with a Flocore Node directory, configuration file,
|
||||||
* bitcoin configuration, and will install all necessary dependencies.
|
* florincoin configuration, and will install all necessary dependencies.
|
||||||
*
|
*
|
||||||
* @param {Object} options
|
* @param {Object} options
|
||||||
* @param {String} options.cwd - The current working directory
|
* @param {String} options.cwd - The current working directory
|
||||||
* @param {String} options.dirname - The name of the bitcore node configuration directory
|
* @param {String} options.dirname - The name of the flocore node configuration directory
|
||||||
* @param {String} options.datadir - The path to the bitcoin datadir
|
* @param {String} options.datadir - The path to the florincoin datadir
|
||||||
* @param {Function} done - A callback function called when finished
|
* @param {Function} done - A callback function called when finished
|
||||||
*/
|
*/
|
||||||
function create(options, done) {
|
function create(options, done) {
|
||||||
@ -103,7 +103,7 @@ function create(options, done) {
|
|||||||
|
|
||||||
async.series([
|
async.series([
|
||||||
function(next) {
|
function(next) {
|
||||||
// Setup the the bitcore-node directory and configuration
|
// Setup the the flocore-node directory and configuration
|
||||||
if (!fs.existsSync(absConfigDir)) {
|
if (!fs.existsSync(absConfigDir)) {
|
||||||
var createOptions = {
|
var createOptions = {
|
||||||
network: options.network,
|
network: options.network,
|
||||||
@ -115,9 +115,9 @@ function create(options, done) {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
function(next) {
|
function(next) {
|
||||||
// Setup the bitcoin directory and configuration
|
// Setup the florincoin directory and configuration
|
||||||
if (!fs.existsSync(absDataDir)) {
|
if (!fs.existsSync(absDataDir)) {
|
||||||
createBitcoinDirectory(absDataDir, next);
|
createFlorincoinDirectory(absDataDir, next);
|
||||||
} else {
|
} else {
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -10,8 +10,8 @@ function getMajorVersion(versionString) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will return the path and default bitcore-node configuration. It will search for the
|
* Will return the path and default flocore-node configuration. It will search for the
|
||||||
* configuration file in the "~/.bitcore" directory, and if it doesn't exist, it will create one
|
* configuration file in the "~/.flocore" directory, and if it doesn't exist, it will create one
|
||||||
* based on default settings.
|
* based on default settings.
|
||||||
* @param {Object} [options]
|
* @param {Object} [options]
|
||||||
* @param {Array} [options.additionalServices] - An optional array of services.
|
* @param {Array} [options.additionalServices] - An optional array of services.
|
||||||
@ -22,8 +22,8 @@ function getDefaultConfig(options) {
|
|||||||
options = {};
|
options = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
var defaultPath = path.resolve(process.env.HOME, './.bitcore');
|
var defaultPath = path.resolve(process.env.HOME, './.flocore');
|
||||||
var defaultConfigFile = path.resolve(defaultPath, './bitcore-node.json');
|
var defaultConfigFile = path.resolve(defaultPath, './flocore-node.json');
|
||||||
|
|
||||||
if (!fs.existsSync(defaultPath)) {
|
if (!fs.existsSync(defaultPath)) {
|
||||||
mkdirp.sync(defaultPath);
|
mkdirp.sync(defaultPath);
|
||||||
@ -40,11 +40,11 @@ function getDefaultConfig(options) {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log(`The configuration file at '${defaultConfigFile}' is incompatible with this version of Bitcore.`);
|
console.log(`The configuration file at '${defaultConfigFile}' is incompatible with this version of Flocore.`);
|
||||||
|
|
||||||
var now = new Date();
|
var now = new Date();
|
||||||
// bitcore-node.YYYY-MM-DD.UnixTimestamp.json
|
// flocore-node.YYYY-MM-DD.UnixTimestamp.json
|
||||||
var backupFileName = `bitcore-node.${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDate()}.${now.getTime()}.json`;
|
var backupFileName = `flocore-node.${now.getUTCFullYear()}-${now.getUTCMonth()}-${now.getUTCDate()}.${now.getTime()}.json`;
|
||||||
var backupFile = path.resolve(defaultPath, backupFileName);
|
var backupFile = path.resolve(defaultPath, backupFileName);
|
||||||
fs.renameSync(defaultConfigFile, backupFile);
|
fs.renameSync(defaultConfigFile, backupFile);
|
||||||
console.log(`The previous configuration file has been moved to: ${backupFile}.`);
|
console.log(`The previous configuration file has been moved to: ${backupFile}.`);
|
||||||
@ -78,11 +78,11 @@ function getDefaultConfig(options) {
|
|||||||
services: options.additionalServices ? defaultServices.concat(options.additionalServices) : defaultServices,
|
services: options.additionalServices ? defaultServices.concat(options.additionalServices) : defaultServices,
|
||||||
datadir: defaultDataDir,
|
datadir: defaultDataDir,
|
||||||
servicesConfig: {
|
servicesConfig: {
|
||||||
'insight-api': {
|
'flosight-api': {
|
||||||
cwdRequirePath: 'node_modules/insight-api'
|
cwdRequirePath: 'node_modules/flosight-api'
|
||||||
},
|
},
|
||||||
'insight-ui': {
|
'flosight-ui': {
|
||||||
cwdRequirePath: 'node_modules/insight-ui'
|
cwdRequirePath: 'node_modules/flosight-ui'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,21 +1,21 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var bitcore = require('bitcore-lib');
|
var flocore = require('flocore-lib');
|
||||||
var $ = bitcore.util.preconditions;
|
var $ = flocore.util.preconditions;
|
||||||
var _ = bitcore.deps._;
|
var _ = flocore.deps._;
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var utils = require('../utils');
|
var utils = require('../utils');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will return the path and bitcore-node configuration
|
* Will return the path and flocore-node configuration
|
||||||
* @param {String} cwd - The absolute path to the current working directory
|
* @param {String} cwd - The absolute path to the current working directory
|
||||||
*/
|
*/
|
||||||
function findConfig(cwd) {
|
function findConfig(cwd) {
|
||||||
$.checkArgument(_.isString(cwd), 'Argument should be a string');
|
$.checkArgument(_.isString(cwd), 'Argument should be a string');
|
||||||
$.checkArgument(utils.isAbsolutePath(cwd), 'Argument should be an absolute path');
|
$.checkArgument(utils.isAbsolutePath(cwd), 'Argument should be an absolute path');
|
||||||
var directory = String(cwd);
|
var directory = String(cwd);
|
||||||
while (!fs.existsSync(path.resolve(directory, 'bitcore-node.json'))) {
|
while (!fs.existsSync(path.resolve(directory, 'flocore-node.json'))) {
|
||||||
directory = path.resolve(directory, '../');
|
directory = path.resolve(directory, '../');
|
||||||
if (directory === '/') {
|
if (directory === '/') {
|
||||||
return false;
|
return false;
|
||||||
@ -23,7 +23,7 @@ function findConfig(cwd) {
|
|||||||
}
|
}
|
||||||
return {
|
return {
|
||||||
path: directory,
|
path: directory,
|
||||||
config: require(path.resolve(directory, 'bitcore-node.json'))
|
config: require(path.resolve(directory, 'flocore-node.json'))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -4,13 +4,13 @@ var async = require('async');
|
|||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var spawn = require('child_process').spawn;
|
var spawn = require('child_process').spawn;
|
||||||
var bitcore = require('bitcore-lib');
|
var flocore = require('flocore-lib');
|
||||||
var $ = bitcore.util.preconditions;
|
var $ = flocore.util.preconditions;
|
||||||
var _ = bitcore.deps._;
|
var _ = flocore.deps._;
|
||||||
var utils = require('../utils');
|
var utils = require('../utils');
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will remove a service from bitcore-node.json
|
* Will remove a service from flocore-node.json
|
||||||
* @param {String} configFilePath - The absolute path to the configuration file
|
* @param {String} configFilePath - The absolute path to the configuration file
|
||||||
* @param {String} service - The name of the module
|
* @param {String} service - The name of the module
|
||||||
* @param {Function} done
|
* @param {Function} done
|
||||||
@ -82,9 +82,9 @@ function removeService(configDir, service, done) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Will remove the Node.js service and from the bitcore-node configuration.
|
* Will remove the Node.js service and from the flocore-node configuration.
|
||||||
* @param {String} options.cwd - The current working directory
|
* @param {String} options.cwd - The current working directory
|
||||||
* @param {String} options.dirname - The bitcore-node configuration directory
|
* @param {String} options.dirname - The flocore-node configuration directory
|
||||||
* @param {Array} options.services - An array of strings of service names
|
* @param {Array} options.services - An array of strings of service names
|
||||||
* @param {Function} done - A callback function called when finished
|
* @param {Function} done - A callback function called when finished
|
||||||
*/
|
*/
|
||||||
@ -100,12 +100,12 @@ function remove(options, done) {
|
|||||||
var configPath = options.path;
|
var configPath = options.path;
|
||||||
var services = options.services;
|
var services = options.services;
|
||||||
|
|
||||||
var bitcoreConfigPath = path.resolve(configPath, 'bitcore-node.json');
|
var flocoreConfigPath = path.resolve(configPath, 'flocore-node.json');
|
||||||
var packagePath = path.resolve(configPath, 'package.json');
|
var packagePath = path.resolve(configPath, 'package.json');
|
||||||
|
|
||||||
if (!fs.existsSync(bitcoreConfigPath) || !fs.existsSync(packagePath)) {
|
if (!fs.existsSync(flocoreConfigPath) || !fs.existsSync(packagePath)) {
|
||||||
return done(
|
return done(
|
||||||
new Error('Directory does not have a bitcore-node.json and/or package.json file.')
|
new Error('Directory does not have a flocore-node.json and/or package.json file.')
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,8 +117,8 @@ function remove(options, done) {
|
|||||||
if (err) {
|
if (err) {
|
||||||
return next(err);
|
return next(err);
|
||||||
}
|
}
|
||||||
// remove service to bitcore-node.json
|
// remove service to flocore-node.json
|
||||||
removeConfig(bitcoreConfigPath, service, next);
|
removeConfig(flocoreConfigPath, service, next);
|
||||||
});
|
});
|
||||||
}, done
|
}, done
|
||||||
);
|
);
|
||||||
|
|||||||
@ -1,10 +1,10 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
var BitcoreNode = require('../node');
|
var FlocoreNode = require('../node');
|
||||||
var index = require('../');
|
var index = require('../');
|
||||||
var bitcore = require('bitcore-lib');
|
var flocore = require('flocore-lib');
|
||||||
var _ = bitcore.deps._;
|
var _ = flocore.deps._;
|
||||||
var log = index.log;
|
var log = index.log;
|
||||||
var shuttingDown = false;
|
var shuttingDown = false;
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
@ -20,17 +20,17 @@ function start(options) {
|
|||||||
servicesPath = options.path;
|
servicesPath = options.path;
|
||||||
}
|
}
|
||||||
|
|
||||||
fullConfig.path = path.resolve(options.path, './bitcore-node.json');
|
fullConfig.path = path.resolve(options.path, './flocore-node.json');
|
||||||
|
|
||||||
fullConfig.services = start.setupServices(require, servicesPath, options.config);
|
fullConfig.services = start.setupServices(require, servicesPath, options.config);
|
||||||
|
|
||||||
var node = new BitcoreNode(fullConfig);
|
var node = new FlocoreNode(fullConfig);
|
||||||
|
|
||||||
// setup handlers for uncaught exceptions and ctrl+c
|
// setup handlers for uncaught exceptions and ctrl+c
|
||||||
start.registerExitHandlers(process, node);
|
start.registerExitHandlers(process, node);
|
||||||
|
|
||||||
node.on('ready', function() {
|
node.on('ready', function() {
|
||||||
log.info('Bitcore Node ready');
|
log.info('Flocore Node ready');
|
||||||
});
|
});
|
||||||
|
|
||||||
node.on('error', function(err) {
|
node.on('error', function(err) {
|
||||||
@ -113,8 +113,8 @@ function lookInModuleManifest(req, service) {
|
|||||||
try {
|
try {
|
||||||
var servicePackage = req(service.name + '/package.json');
|
var servicePackage = req(service.name + '/package.json');
|
||||||
var serviceModule = service.name;
|
var serviceModule = service.name;
|
||||||
if (servicePackage.bitcoreNode) {
|
if (servicePackage.flocoreNode) {
|
||||||
serviceModule = serviceModule + '/' + servicePackage.bitcoreNode;
|
serviceModule = serviceModule + '/' + servicePackage.flocoreNode;
|
||||||
return req(serviceModule);
|
return req(serviceModule);
|
||||||
}
|
}
|
||||||
} catch(e) {
|
} catch(e) {
|
||||||
@ -139,7 +139,7 @@ function loadModule(req, service) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
//fourth, see if there is directory in our module search path that has a
|
//fourth, see if there is directory in our module search path that has a
|
||||||
//package.json file, if so, then see if there is a bitcoreNode field, if so
|
//package.json file, if so, then see if there is a flocoreNode field, if so
|
||||||
//use this as the path to the service module
|
//use this as the path to the service module
|
||||||
if(!serviceCode) {
|
if(!serviceCode) {
|
||||||
serviceCode = lookInModuleManifest(req, service);
|
serviceCode = lookInModuleManifest(req, service);
|
||||||
@ -149,7 +149,7 @@ function loadModule(req, service) {
|
|||||||
throw new Error('Attempted to load the ' + service.name + ' service from: ' +
|
throw new Error('Attempted to load the ' + service.name + ' service from: ' +
|
||||||
'the requirePath in the services\' config, then "' +
|
'the requirePath in the services\' config, then "' +
|
||||||
process.cwd() + '" then from: "' + __dirname + '/../lib/services' + '" finally from: "' +
|
process.cwd() + '" then from: "' + __dirname + '/../lib/services' + '" finally from: "' +
|
||||||
process.cwd() + '/package.json" - bitcoreNode field. All paths failed to find valid nodeJS code.');
|
process.cwd() + '/package.json" - flocoreNode field. All paths failed to find valid nodeJS code.');
|
||||||
}
|
}
|
||||||
|
|
||||||
service.module = serviceCode;
|
service.module = serviceCode;
|
||||||
@ -160,9 +160,9 @@ function loadModule(req, service) {
|
|||||||
* specified modules, and assemble an array in this format:
|
* specified modules, and assemble an array in this format:
|
||||||
* [
|
* [
|
||||||
* {
|
* {
|
||||||
* name: 'bitcoind',
|
* name: 'florincoind',
|
||||||
* config: {},
|
* config: {},
|
||||||
* module: BitcoinService
|
* module: FlorincoinService
|
||||||
* }
|
* }
|
||||||
* ]
|
* ]
|
||||||
* @param {Function} req - The require function to use
|
* @param {Function} req - The require function to use
|
||||||
@ -201,23 +201,26 @@ function cleanShutdown(_process, node) {
|
|||||||
return _process.exit(1);
|
return _process.exit(1);
|
||||||
}
|
}
|
||||||
log.info('Halted');
|
log.info('Halted');
|
||||||
_process.exit(0);
|
process.exit(0);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function exitHandler(options, _process, node, err) {
|
function exitHandler(options, _process, node, err) {
|
||||||
if (err) {
|
// Handle and log errors other than SIGINT shutdown
|
||||||
|
if (err && err !== "SIGINT") {
|
||||||
log.error('uncaught exception:', err);
|
log.error('uncaught exception:', err);
|
||||||
if(err.stack) {
|
if(err.stack) {
|
||||||
log.error(err.stack);
|
log.error(err.stack);
|
||||||
}
|
}
|
||||||
node.stop(function(err) {
|
if(options.exit)
|
||||||
if(err) {
|
node.stop(function(err) {
|
||||||
log.error('Failed to stop services: ' + err);
|
if(err) {
|
||||||
}
|
log.error('Failed to stop services: ' + err);
|
||||||
_process.exit(-1);
|
}
|
||||||
});
|
_process.exit(-1);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
// Handle SIGINT (Ctrl+C)
|
||||||
if (options.sigint) {
|
if (options.sigint) {
|
||||||
if (!shuttingDown) {
|
if (!shuttingDown) {
|
||||||
shuttingDown = true;
|
shuttingDown = true;
|
||||||
@ -227,7 +230,7 @@ function exitHandler(options, _process, node, err) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function registerExitHandlers(_process, node) {
|
function registerExitHandlers(_process, node) {
|
||||||
_process.on('uncaughtException', exitHandler.bind(null, {exit:true}, _process, node));
|
_process.on('uncaughtException', exitHandler.bind(null, {exit:false}, _process, node));
|
||||||
_process.on('SIGINT', exitHandler.bind(null, {sigint:true}, _process, node));
|
_process.on('SIGINT', exitHandler.bind(null, {sigint:true}, _process, node));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -72,7 +72,7 @@ Service.prototype.start = function(done) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Function to be called when bitcore-node is stopped
|
* Function to be called when flocore-node is stopped
|
||||||
*/
|
*/
|
||||||
Service.prototype.stop = function(done) {
|
Service.prototype.stop = function(done) {
|
||||||
setImmediate(done);
|
setImmediate(done);
|
||||||
|
|||||||
@ -2,11 +2,13 @@
|
|||||||
|
|
||||||
function Encoding(servicePrefix) {
|
function Encoding(servicePrefix) {
|
||||||
this.servicePrefix = servicePrefix;
|
this.servicePrefix = servicePrefix;
|
||||||
|
this.addressIndex = new Buffer('00', 'hex');
|
||||||
|
this.utxoIndex = new Buffer('01', 'hex');
|
||||||
|
this.addressCache = new Buffer('fe', 'hex');
|
||||||
}
|
}
|
||||||
|
|
||||||
Encoding.prototype.encodeAddressIndexKey = function(address, height, txid, index, input, timestamp) {
|
Encoding.prototype.encodeAddressIndexKey = function(address, height, txid, index, input, timestamp) {
|
||||||
var prefix = new Buffer('00', 'hex');
|
var buffers = [this.servicePrefix, this.addressIndex];
|
||||||
var buffers = [this.servicePrefix, prefix];
|
|
||||||
|
|
||||||
var addressSizeBuffer = new Buffer(1);
|
var addressSizeBuffer = new Buffer(1);
|
||||||
addressSizeBuffer.writeUInt8(address.length);
|
addressSizeBuffer.writeUInt8(address.length);
|
||||||
@ -58,8 +60,7 @@ Encoding.prototype.decodeAddressIndexKey = function(buffer) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
Encoding.prototype.encodeUtxoIndexKey = function(address, txid, outputIndex) {
|
Encoding.prototype.encodeUtxoIndexKey = function(address, txid, outputIndex) {
|
||||||
var prefix = new Buffer('01', 'hex');
|
var buffers = [this.servicePrefix, this.utxoIndex];
|
||||||
var buffers = [this.servicePrefix, prefix];
|
|
||||||
|
|
||||||
var addressSizeBuffer = new Buffer(1);
|
var addressSizeBuffer = new Buffer(1);
|
||||||
addressSizeBuffer.writeUInt8(address.length);
|
addressSizeBuffer.writeUInt8(address.length);
|
||||||
@ -114,5 +115,53 @@ Encoding.prototype.decodeUtxoIndexValue = function(buffer) {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
Encoding.prototype.encodeAddressCacheKey = function(address) {
|
||||||
|
return Buffer.concat([this.servicePrefix, this.addressCache, new Buffer(address, 'utf8')]);
|
||||||
|
}
|
||||||
|
|
||||||
|
Encoding.prototype.decodeAddressCacheKey = function(buffer) {
|
||||||
|
return buffer.slice(3).toString('utf8');
|
||||||
|
}
|
||||||
|
|
||||||
|
Encoding.prototype.encodeAddressCacheValue = function(lastTx, lastBlock, balance, received, sent, txApperances) {
|
||||||
|
|
||||||
|
var buffer = [];
|
||||||
|
|
||||||
|
var balanceBuffer = new Buffer(8);
|
||||||
|
balanceBuffer.writeBigUInt64BE(BigInt(balance));
|
||||||
|
buffer.push(balanceBuffer);
|
||||||
|
|
||||||
|
var receivedBuffer = new Buffer(8);
|
||||||
|
receivedBuffer.writeBigUInt64BE(BigInt(received));
|
||||||
|
buffer.push(receivedBuffer);
|
||||||
|
|
||||||
|
var sentBuffer = new Buffer(8);
|
||||||
|
sentBuffer.writeBigUInt64BE(BigInt(sent));
|
||||||
|
buffer.push(sentBuffer);
|
||||||
|
|
||||||
|
var txApperancesBuffer = new Buffer(4);
|
||||||
|
txApperancesBuffer.writeUInt32BE(txApperances);
|
||||||
|
buffer.push(txApperancesBuffer);
|
||||||
|
|
||||||
|
var txidBuffer = new Buffer(lastTx, 'hex');
|
||||||
|
buffer.push(txidBuffer);
|
||||||
|
|
||||||
|
var blkBuffer = new Buffer(lastBlock, 'hex');
|
||||||
|
buffer.push(blkBuffer);
|
||||||
|
|
||||||
|
return Buffer.concat(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
Encoding.prototype.decodeAddressCacheValue = function(buffer) {
|
||||||
|
|
||||||
|
var balance = parseInt(buffer.readBigUInt64BE(0));
|
||||||
|
var received = parseInt(buffer.readBigUInt64BE(8));
|
||||||
|
var sent = parseInt(buffer.readBigUInt64BE(16));
|
||||||
|
var txApperances = buffer.readUInt32BE(24);
|
||||||
|
var lastTx = buffer.slice(28, 60).toString('hex'); //28 + 32 (tx hash buffer length) = 60
|
||||||
|
var lastBlock = buffer.slice(60).toString('hex');
|
||||||
|
return { lastTx, lastBlock, balance, received, sent, txApperances };
|
||||||
|
}
|
||||||
|
|
||||||
module.exports = Encoding;
|
module.exports = Encoding;
|
||||||
|
|
||||||
|
|||||||
@ -5,14 +5,27 @@ var inherits = require('util').inherits;
|
|||||||
var async = require('async');
|
var async = require('async');
|
||||||
var index = require('../../');
|
var index = require('../../');
|
||||||
var log = index.log;
|
var log = index.log;
|
||||||
var bitcore = require('bitcore-lib');
|
var flocore = require('flocore-lib');
|
||||||
var Unit = bitcore.Unit;
|
var Unit = flocore.Unit;
|
||||||
var _ = bitcore.deps._;
|
var _ = flocore.deps._;
|
||||||
var lodash = require('lodash');
|
var lodash = require('lodash');
|
||||||
var Encoding = require('./encoding');
|
var Encoding = require('./encoding');
|
||||||
var Transform = require('stream').Transform;
|
var Transform = require('stream').Transform;
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var utils = require('../../utils');
|
var utils = require('../../utils');
|
||||||
|
var LRU = require('lru-cache');
|
||||||
|
var XXHash = require('xxhash');
|
||||||
|
|
||||||
|
const MAX_TX_QUERY_LIMIT_HISTORY = 1000;
|
||||||
|
const MAX_TX_QUERY_LIMIT_UTXO = 1000;
|
||||||
|
const MAX_TX_QUERY_LIMIT_SUMMARY = 500;
|
||||||
|
|
||||||
|
// See rationale about this cache at function getTxList(next)
|
||||||
|
const TXID_LIST_CACHE_ITEMS = 250; // nr of items (this translates to: consecutive
|
||||||
|
// clients downloading their tx history)
|
||||||
|
const TXID_LIST_CACHE_EXPIRATION = 1000 * 30; // ms
|
||||||
|
const TXID_LIST_CACHE_MIN = 100; // Min items to cache
|
||||||
|
const TXID_LIST_CACHE_SEED = 0x3233DE; // Min items to cache
|
||||||
|
|
||||||
var AddressService = function(options) {
|
var AddressService = function(options) {
|
||||||
|
|
||||||
@ -24,6 +37,11 @@ var AddressService = function(options) {
|
|||||||
this._network = this.node.network;
|
this._network = this.node.network;
|
||||||
this._db = this.node.services.db;
|
this._db = this.node.services.db;
|
||||||
this._mempool = this.node.services.mempool;
|
this._mempool = this.node.services.mempool;
|
||||||
|
this._txIdListCache = new LRU({
|
||||||
|
max: TXID_LIST_CACHE_ITEMS,
|
||||||
|
maxAge: TXID_LIST_CACHE_EXPIRATION
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
if (this._network === 'livenet') {
|
if (this._network === 'livenet') {
|
||||||
this._network = 'main';
|
this._network = 'main';
|
||||||
@ -49,8 +67,12 @@ AddressService.dependencies = [
|
|||||||
// for example if the query /api/addrs/txs?from=0&to=5&noAsm=1&noScriptSig=1&noSpent=1, and the addresses passed
|
// for example if the query /api/addrs/txs?from=0&to=5&noAsm=1&noScriptSig=1&noSpent=1, and the addresses passed
|
||||||
// in are [addr1, addr2, addr3], then if addr3 has tx1 at height 10, addr2 has tx2 at height 9 and tx1 has no txs,
|
// in are [addr1, addr2, addr3], then if addr3 has tx1 at height 10, addr2 has tx2 at height 9 and tx1 has no txs,
|
||||||
// then I would pass back [tx1, tx2] in that order
|
// then I would pass back [tx1, tx2] in that order
|
||||||
AddressService.prototype.getAddressHistory = function(addresses, options, callback) {
|
//
|
||||||
|
// Instead of passing addresses, with from>0, options.cacheKey can be used to define the address set.
|
||||||
|
//(old one: non-optimized for large data)
|
||||||
|
AddressService.prototype.__getAddressHistory = function(addresses, options, callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
var cacheUsed = false;
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
options.from = options.from || 0;
|
options.from = options.from || 0;
|
||||||
@ -65,31 +87,75 @@ AddressService.prototype.getAddressHistory = function(addresses, options, callba
|
|||||||
addresses = [addresses];
|
addresses = [addresses];
|
||||||
}
|
}
|
||||||
|
|
||||||
async.eachLimit(addresses, 4, function(address, next) {
|
|
||||||
|
|
||||||
self._getAddressTxidHistory(address, options, next);
|
function getTxList(next) {
|
||||||
|
|
||||||
}, function(err) {
|
|
||||||
|
|
||||||
|
function hashAddresses(addresses) {
|
||||||
|
|
||||||
|
// Given there are only TXID_LIST_CACHE_ITEMS ~ 250 items cached at the sametime
|
||||||
|
// a 32 bits hash is secure enough
|
||||||
|
|
||||||
|
return XXHash.hash(Buffer.from(addresses.join('')), TXID_LIST_CACHE_SEED);
|
||||||
|
};
|
||||||
|
|
||||||
|
var calculatedCacheKey;
|
||||||
|
|
||||||
|
// We use the cache ONLY on from > 0 queries.
|
||||||
|
//
|
||||||
|
// Rationale: The a full history is downloaded, the client do
|
||||||
|
// from =0, to=x
|
||||||
|
// then from =x+1 to=y
|
||||||
|
// then [...]
|
||||||
|
// The objective of this cache is to speed up the from>0 queries, and also
|
||||||
|
// "freeze" the txid list during download.
|
||||||
|
//
|
||||||
|
if (options.from >0 ) {
|
||||||
|
|
||||||
|
let cacheKey = options.cacheKey;
|
||||||
|
if (!cacheKey) {
|
||||||
|
calculatedCacheKey = hashAddresses(addresses);
|
||||||
|
cacheKey = calculatedCacheKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
var txIdList = self._txIdListCache.get(cacheKey);
|
||||||
|
if (txIdList) {
|
||||||
|
options.txIdList = txIdList;
|
||||||
|
cacheUsed = true;
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the list from the db
|
||||||
|
async.eachLimit(addresses, 4, function(address, next) {
|
||||||
|
self._getAddressTxidHistory(address, options, next);
|
||||||
|
}, function(err) {
|
||||||
|
if (err) return next(err);
|
||||||
|
|
||||||
|
var list = lodash.uniqBy(options.txIdList, function(x) {
|
||||||
|
return x.txid + x.height;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
options.txIdList = lodash.orderBy(list,['height','txid'], ['desc','asc']);
|
||||||
|
|
||||||
|
if (list.length > TXID_LIST_CACHE_MIN) {
|
||||||
|
calculatedCacheKey = calculatedCacheKey || hashAddresses(addresses);
|
||||||
|
|
||||||
|
self._txIdListCache.set(calculatedCacheKey, options.txIdList);
|
||||||
|
}
|
||||||
|
|
||||||
|
return next();
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
getTxList(function(err) {
|
||||||
if(err) {
|
if(err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
var unique = {};
|
|
||||||
var list = [];
|
|
||||||
|
|
||||||
for (let i = 0; i < options.txIdList.length; i++) {
|
|
||||||
unique[options.txIdList[i].txid + options.txIdList[i].height] = options.txIdList[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var prop in unique) {
|
|
||||||
list.push(unique[prop]);
|
|
||||||
}
|
|
||||||
|
|
||||||
options.txIdList = list.sort(function(a, b) {
|
|
||||||
return b.height - a.height;
|
|
||||||
});
|
|
||||||
|
|
||||||
self._getAddressTxHistory(options, function(err, txList) {
|
self._getAddressTxHistory(options, function(err, txList) {
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
@ -98,18 +164,117 @@ AddressService.prototype.getAddressHistory = function(addresses, options, callba
|
|||||||
|
|
||||||
var results = {
|
var results = {
|
||||||
totalCount: options.txIdList.length || 0,
|
totalCount: options.txIdList.length || 0,
|
||||||
items: txList
|
items: txList,
|
||||||
};
|
};
|
||||||
|
|
||||||
callback(null, results);
|
// cacheUsed is returned for testing
|
||||||
|
callback(null, results, cacheUsed);
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
AddressService.prototype.getAddressHistory = function(addresses, options, streamer, callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
|
//options.from = options.from || 0; //Deprecated, use `after` and `before` option
|
||||||
|
//options.to = options.to || 0xffffffff; //Deprecated, use `after` and `before` option
|
||||||
|
|
||||||
|
if(!_.isFunction(callback)){ //if only 3 args, then streamer is callback
|
||||||
|
callback = streamer;
|
||||||
|
streamer = () => null; //NULL fn
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_.isUndefined(options.queryMempool)) {
|
||||||
|
options.queryMempool = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_.isUndefined(options.mempoolOnly)) {
|
||||||
|
options.mempoolOnly = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_.isUndefined(options.reverse)) {
|
||||||
|
options.reverse = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var old_support = false;
|
||||||
|
//Quick support for `from` and `to` options (DEPRECATED! Not recommeded to use)
|
||||||
|
if(!_.isUndefined(options.from) || !_.isUndefined(options.to)) {
|
||||||
|
old_support = true;
|
||||||
|
options.from = options.from || 0;
|
||||||
|
options.to = options.to || 0xffffffff; //Max value of to will actually be MAX_TX_QUERY_LIMIT_HISTORY
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_.isString(addresses)) {
|
||||||
|
addresses = [addresses];
|
||||||
|
}
|
||||||
|
|
||||||
|
var results = {
|
||||||
|
totalCount: 0,
|
||||||
|
items: [],
|
||||||
|
}
|
||||||
|
|
||||||
|
async.eachLimit(addresses, 4, function(address, next) {
|
||||||
|
|
||||||
|
var addr_options = Object.assign({}, options), addr_count = 0;
|
||||||
|
|
||||||
|
self._streamAddressSummary(address, addr_options, function(err, tx){
|
||||||
|
|
||||||
|
if(err)
|
||||||
|
return log.error(err);
|
||||||
|
|
||||||
|
addr_count++;
|
||||||
|
|
||||||
|
if(!results.items.some(x => x.txid() === tx.txid())) {//add only if tx not already in array
|
||||||
|
if(!options.reverse)
|
||||||
|
results.items.unshift(tx); //using unshift, so that recent tx (low) are at front
|
||||||
|
else
|
||||||
|
results.items.push(tx);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if(results.items.length > MAX_TX_QUERY_LIMIT_HISTORY) { //remove items from array when overflown
|
||||||
|
results.items.sort((a, b) => (b.__height || 0xffffffff) - (a.__height || 0xffffffff) || b.txid().localeCompare(a.txid()));
|
||||||
|
let del_count = results.items.length - MAX_TX_QUERY_LIMIT_HISTORY;
|
||||||
|
let start_index = (old_support || options.reverse) ? MAX_TX_QUERY_LIMIT_HISTORY : 0;
|
||||||
|
results.items.splice(start_index, del_count);
|
||||||
|
|
||||||
|
results.incomplete = true;
|
||||||
|
|
||||||
|
if(!old_support && addr_count >= MAX_TX_QUERY_LIMIT_HISTORY)
|
||||||
|
addr_options.flag_stop = true; //limit has reached, stop quering db for more tx
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
streamer(null, tx);
|
||||||
|
|
||||||
|
}, next);
|
||||||
|
|
||||||
|
}, function(err) {
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
//sort items in desc block-height, then asc txid (if same height)
|
||||||
|
results.items.sort((a, b) => (b.__height || 0xffffffff) - (a.__height || 0xffffffff) || b.txid().localeCompare(a.txid()));
|
||||||
|
results.totalCount = results.items.length ;
|
||||||
|
|
||||||
|
//Quick support for `from` and `to` options (DEPRECATED! Not recommeded to use)
|
||||||
|
if(old_support) {
|
||||||
|
results.items = results.items.slice(options.from, options.to);
|
||||||
|
}
|
||||||
|
|
||||||
|
callback(null, results);
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
// this is basically the same as _getAddressHistory apart from the summary
|
// this is basically the same as _getAddressHistory apart from the summary
|
||||||
AddressService.prototype.getAddressSummary = function(address, options, callback) {
|
//(old one: non-optimized for large data)
|
||||||
|
AddressService.prototype.__getAddressSummary = function(address, options, callback) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
@ -135,7 +300,7 @@ AddressService.prototype.getAddressSummary = function(address, options, callback
|
|||||||
txApperances: 0,
|
txApperances: 0,
|
||||||
};
|
};
|
||||||
|
|
||||||
self.getAddressHistory(address, options, function(err, results) {
|
self.__getAddressHistory(address, options, function(err, results) { //old fn
|
||||||
|
|
||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
@ -153,6 +318,229 @@ AddressService.prototype.getAddressSummary = function(address, options, callback
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
AddressService.prototype.getAddressSummary = function(address, options, streamer, callback) {
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
|
//options.from = options.from || 0; //Deprecated, use `after` and `before` option
|
||||||
|
//options.to = options.to || 0xffffffff; //Deprecated, use `after` and `before` option
|
||||||
|
|
||||||
|
if (_.isUndefined(options.queryMempool)) {
|
||||||
|
options.queryMempool = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!_.isFunction(callback)){ //if only 3 args, then streamer is callback
|
||||||
|
callback = streamer;
|
||||||
|
streamer = () => null; //NULL fn
|
||||||
|
}
|
||||||
|
|
||||||
|
var count = 0;
|
||||||
|
|
||||||
|
var result = {
|
||||||
|
addrStr: address,
|
||||||
|
balance: 0,
|
||||||
|
balanceSat: 0,
|
||||||
|
totalReceived: 0,
|
||||||
|
totalReceivedSat: 0,
|
||||||
|
totalSent: 0,
|
||||||
|
totalSentSat: 0,
|
||||||
|
unconfirmedBalance: 0,
|
||||||
|
unconfirmedBalanceSat: 0,
|
||||||
|
unconfirmedTxApperances: 0,
|
||||||
|
txApperances: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
var useCache = _.isUndefined(options.after) && _.isUndefined(options.before);
|
||||||
|
var lastTx, lastBlock;
|
||||||
|
|
||||||
|
self._loadCache(address, result, useCache, function(err, lastCachedTx) {
|
||||||
|
if(err)
|
||||||
|
log.error(err);
|
||||||
|
|
||||||
|
if(!_.isUndefined(lastCachedTx))
|
||||||
|
options.after = lastCachedTx;
|
||||||
|
|
||||||
|
self._streamAddressSummary(address, options, function(err, tx) {
|
||||||
|
|
||||||
|
if(err)
|
||||||
|
return log.error(err);
|
||||||
|
|
||||||
|
if(tx) {
|
||||||
|
count++;
|
||||||
|
self._aggregateAddressSummaryResult(tx, address, result, options);
|
||||||
|
|
||||||
|
if(tx.confirmations) {
|
||||||
|
lastTx = tx.txid();
|
||||||
|
lastBlock = tx.blockhash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(count >= MAX_TX_QUERY_LIMIT_SUMMARY) {//stop quering db when limit reached
|
||||||
|
options.flag_stop = true;
|
||||||
|
result.incomplete = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
streamer(null, tx);
|
||||||
|
|
||||||
|
}, function(err) {
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
result.balanceSat = parseInt(result.balanceSat.toFixed());
|
||||||
|
result.totalReceivedSat = parseInt(result.totalReceivedSat.toFixed());
|
||||||
|
result.totalSentSat = parseInt(result.totalSentSat.toFixed());
|
||||||
|
result.txApperances = parseInt(result.txApperances.toFixed());
|
||||||
|
result.unconfirmedBalanceSat = parseInt(result.unconfirmedBalanceSat.toFixed());
|
||||||
|
result.unconfirmedTxApperances = parseInt(result.unconfirmedTxApperances.toFixed());
|
||||||
|
|
||||||
|
result.balance = Unit.fromSatoshis(result.balanceSat).toBTC();
|
||||||
|
result.totalReceived = Unit.fromSatoshis(result.totalReceivedSat).toBTC();
|
||||||
|
result.totalSent = Unit.fromSatoshis(result.totalSentSat).toBTC();
|
||||||
|
result.unconfirmedBalance = Unit.fromSatoshis(result.unconfirmedBalanceSat).toBTC();
|
||||||
|
|
||||||
|
result.lastItem = lastTx;
|
||||||
|
|
||||||
|
callback(null, result);
|
||||||
|
|
||||||
|
//store in cache if needed
|
||||||
|
if(useCache) {
|
||||||
|
if(result.incomplete) //full summary needs to be calculated in background
|
||||||
|
self._cacheSummaryInBackground(address, lastTx, lastBlock, result);
|
||||||
|
else if (!_.isUndefined(lastCachedTx) && !_.isUndefined(lastTx)
|
||||||
|
&& lastTx != lastCachedTx && !self._cacheInstance.has(address)) //update cache if needed
|
||||||
|
self._storeCache(address, lastTx, lastBlock, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AddressService.prototype._cacheInstance = new Set();
|
||||||
|
AddressService.prototype._cacheSummaryInBackground = function(address, lastTx, lastBlock, result){
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
if(self._cacheInstance.has(address))
|
||||||
|
return;
|
||||||
|
|
||||||
|
self._cacheInstance.add(address);
|
||||||
|
|
||||||
|
const cache = {
|
||||||
|
balanceSat: result.balanceSat,
|
||||||
|
totalReceivedSat: result.totalReceivedSat,
|
||||||
|
totalSentSat: result.totalSentSat,
|
||||||
|
txApperances: result.txApperances,
|
||||||
|
unconfirmedBalanceSat: 0, //unconfirmed (mempool) values should not be cached
|
||||||
|
unconfirmedTxApperances: 0
|
||||||
|
};
|
||||||
|
const options = { queryMempool: false, after: lastTx, noTxList: true };
|
||||||
|
|
||||||
|
self._streamAddressSummary(address, options, function(err, tx) {
|
||||||
|
|
||||||
|
if(err)
|
||||||
|
return log.error(err);
|
||||||
|
|
||||||
|
if(tx) {
|
||||||
|
self._aggregateAddressSummaryResult(tx, address, cache, options);
|
||||||
|
if(tx.confirmations){
|
||||||
|
lastTx = tx.txid();
|
||||||
|
lastBlock = tx.blockhash;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}, function(err) {
|
||||||
|
|
||||||
|
if (err)
|
||||||
|
return log.error(err);
|
||||||
|
|
||||||
|
cache.balanceSat = parseInt(cache.balanceSat.toFixed());
|
||||||
|
cache.totalReceivedSat = parseInt(cache.totalReceivedSat.toFixed());
|
||||||
|
cache.totalSentSat = parseInt(cache.totalSentSat.toFixed());
|
||||||
|
cache.txApperances = parseInt(cache.txApperances.toFixed());
|
||||||
|
|
||||||
|
if(!_.isUndefined(lastTx))
|
||||||
|
self._storeCache(address, lastTx, lastBlock, cache);
|
||||||
|
|
||||||
|
self._cacheInstance.delete(address); //remove from running instance
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AddressService.prototype._storeCache = function(address, lastCacheTx, lastCacheBlock, result, callback) {
|
||||||
|
const self = this;
|
||||||
|
var key = self._encoding.encodeAddressCacheKey(address);
|
||||||
|
var value = self._encoding.encodeAddressCacheValue(lastCacheTx, lastCacheBlock, result.balanceSat, result.totalReceivedSat, result.totalSentSat, result.txApperances)
|
||||||
|
|
||||||
|
if(!_.isFunction(callback)) //if callback is not passed, call a empty function
|
||||||
|
callback = () => null;
|
||||||
|
|
||||||
|
self._db.put(key, value, callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
AddressService.prototype._loadCache = function(address, result, useCache, callback) {
|
||||||
|
const self = this;
|
||||||
|
|
||||||
|
if(!useCache) //skip if useCache is false (cases like 'after' and/or 'before' parameter is used by client)
|
||||||
|
return callback();
|
||||||
|
|
||||||
|
var key = self._encoding.encodeAddressCacheKey(address);
|
||||||
|
self._db.get(key, function(err, value) {
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
if (!value) {
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
var addressCache = self._encoding.decodeAddressCacheValue(value);
|
||||||
|
|
||||||
|
var lastCacheTx = addressCache.lastTx, lastCacheBlock = addressCache.lastBlock
|
||||||
|
|
||||||
|
self._block.getBlock(lastCacheBlock, function(err, block) {
|
||||||
|
|
||||||
|
if(err) {
|
||||||
|
return callback(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!block) { //block not found, probably removed in reorg.
|
||||||
|
//delete the existing cache and recalc values freshly
|
||||||
|
self._deleteCache(address, function() {
|
||||||
|
callback();
|
||||||
|
});
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
//values are in satoshis
|
||||||
|
result.balanceSat = addressCache.balance;
|
||||||
|
result.totalReceivedSat = addressCache.received;
|
||||||
|
result.totalSentSat = addressCache.sent;
|
||||||
|
result.txApperances = addressCache.txApperances;
|
||||||
|
|
||||||
|
callback(null, lastCacheTx);
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
AddressService.prototype._deleteCache = function(address, callback) {
|
||||||
|
const self = this;
|
||||||
|
var key = self._encoding.encodeAddressCacheKey(address);
|
||||||
|
|
||||||
|
if(!_.isFunction(callback)) //if callback is not passed, call a empty function
|
||||||
|
callback = () => null;
|
||||||
|
|
||||||
|
self._db.del(key, callback);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
AddressService.prototype._setOutputResults = function(tx, address, result) {
|
AddressService.prototype._setOutputResults = function(tx, address, result) {
|
||||||
|
|
||||||
for(var j = 0; j < tx.outputs.length; j++) {
|
for(var j = 0; j < tx.outputs.length; j++) {
|
||||||
@ -200,7 +588,6 @@ AddressService.prototype._getAddressSummaryResult = function(txs, address, resul
|
|||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
for(var i = 0; i < txs.length; i++) {
|
for(var i = 0; i < txs.length; i++) {
|
||||||
|
|
||||||
var tx = txs[i];
|
var tx = txs[i];
|
||||||
|
|
||||||
self._setOutputResults(tx, address, result);
|
self._setOutputResults(tx, address, result);
|
||||||
@ -218,6 +605,110 @@ AddressService.prototype._getAddressSummaryResult = function(txs, address, resul
|
|||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
AddressService.prototype._getOccurrenceCount = function(tx, address) {
|
||||||
|
let count = 0;
|
||||||
|
|
||||||
|
for(var i = 0; i < tx.inputs.length; i++) {
|
||||||
|
|
||||||
|
var input = tx.inputs[i];
|
||||||
|
|
||||||
|
if(utils.getAddress(input, this._network) === address)
|
||||||
|
count++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var j = 0; j < tx.outputs.length; j++) {
|
||||||
|
|
||||||
|
var output = tx.outputs[j];
|
||||||
|
|
||||||
|
if(utils.getAddress(output, this._network) === address)
|
||||||
|
count++;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return count;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
AddressService.prototype._getOutputResults = function(tx, address) {
|
||||||
|
|
||||||
|
let value = 0;
|
||||||
|
|
||||||
|
for(var j = 0; j < tx.outputs.length; j++) {
|
||||||
|
|
||||||
|
var output = tx.outputs[j];
|
||||||
|
|
||||||
|
if (utils.getAddress(output, this._network) === address)
|
||||||
|
value += output.value;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
AddressService.prototype._getInputResults = function(tx, address) {
|
||||||
|
|
||||||
|
let value = 0;
|
||||||
|
|
||||||
|
for(var i = 0; i < tx.inputs.length; i++) {
|
||||||
|
|
||||||
|
var input = tx.inputs[i];
|
||||||
|
|
||||||
|
if (utils.getAddress(input, this._network) === address)
|
||||||
|
value += tx.__inputValues[i];
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
AddressService.prototype._aggregateAddressSummaryResult = function (tx, address, result, options) {
|
||||||
|
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
let output_val = self._getOutputResults(tx, address);
|
||||||
|
let input_val = self._getInputResults(tx, address);
|
||||||
|
|
||||||
|
//aggregate the result
|
||||||
|
|
||||||
|
if(tx.confirmations) {
|
||||||
|
|
||||||
|
result.txApperances++;
|
||||||
|
|
||||||
|
result.totalReceivedSat += output_val;
|
||||||
|
result.balanceSat += output_val;
|
||||||
|
|
||||||
|
result.totalSentSat += input_val;
|
||||||
|
result.balanceSat -= input_val;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
result.unconfirmedTxApperances++;
|
||||||
|
result.unconfirmedBalanceSat += output_val;
|
||||||
|
result.unconfirmedBalanceSat -= input_val;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!options.noTxList) {
|
||||||
|
|
||||||
|
if (!result.transactions) {
|
||||||
|
result.transactions = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
let txid = tx.txid();
|
||||||
|
if(!result.transactions.includes(txid)) { //push txid only if its not in the array
|
||||||
|
|
||||||
|
result.transactions.unshift(txid); //using unshift, so that recent tx (low confirmation) are at front
|
||||||
|
|
||||||
|
if(result.transactions.length > MAX_TX_QUERY_LIMIT_SUMMARY)
|
||||||
|
result.transactions.pop(); //pop the oldest tx in list (when list limit is maxed out)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
AddressService.prototype.getAddressUnspentOutputs = function(address, options, callback) {
|
AddressService.prototype.getAddressUnspentOutputs = function(address, options, callback) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
@ -300,6 +791,11 @@ AddressService.prototype.getAddressUnspentOutputs = function(address, options, c
|
|||||||
|
|
||||||
utxoStream.on('data', function(data) {
|
utxoStream.on('data', function(data) {
|
||||||
|
|
||||||
|
if(results.length >= MAX_TX_QUERY_LIMIT_UTXO) { //Max array limit reached, end response
|
||||||
|
utxoStream.emit('end');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
var key = self._encoding.decodeUtxoIndexKey(data.key);
|
var key = self._encoding.decodeUtxoIndexKey(data.key);
|
||||||
var value = self._encoding.decodeUtxoIndexValue(data.value);
|
var value = self._encoding.decodeUtxoIndexValue(data.value);
|
||||||
|
|
||||||
@ -375,19 +871,21 @@ AddressService.prototype.stop = function(callback) {
|
|||||||
|
|
||||||
AddressService.prototype._getTxidStream = function(address, options) {
|
AddressService.prototype._getTxidStream = function(address, options) {
|
||||||
|
|
||||||
var start = this._encoding.encodeAddressIndexKey(address);
|
var criteria = {};
|
||||||
var end = Buffer.concat([
|
|
||||||
start.slice(0, address.length + 4),
|
|
||||||
options.endHeightBuf,
|
|
||||||
new Buffer(new Array(83).join('f'), 'hex')
|
|
||||||
]);
|
|
||||||
|
|
||||||
var criteria = {
|
if(options.after)
|
||||||
gte: start,
|
criteria.gt = this._encoding.encodeAddressIndexKey(address, options.start, options.after, 0xffffffff, 1, 0xffffffff); //0xffffffff is for getting after the txid
|
||||||
lte: end,
|
else
|
||||||
reverse: true // txids stream from low confirmations to high confirmations
|
criteria.gte = this._encoding.encodeAddressIndexKey(address, options.start);
|
||||||
};
|
|
||||||
|
|
||||||
|
if(options.before)
|
||||||
|
criteria.lt = this._encoding.encodeAddressIndexKey(address, options.end, options.before); //get before the txid
|
||||||
|
else
|
||||||
|
criteria.lte = this._encoding.encodeAddressIndexKey(address, options.end, Array(65).join('f'), 0xffffffff, 1, 0xffffffff);
|
||||||
|
|
||||||
|
//reverse option can be used explictly when latest tx are required
|
||||||
|
if(options.reverse)
|
||||||
|
criteria.reverse = true;
|
||||||
// txid stream
|
// txid stream
|
||||||
var txidStream = this._db.createKeyStream(criteria);
|
var txidStream = this._db.createKeyStream(criteria);
|
||||||
|
|
||||||
@ -398,6 +896,7 @@ AddressService.prototype._getTxidStream = function(address, options) {
|
|||||||
return txidStream;
|
return txidStream;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//(used by old fn)
|
||||||
AddressService.prototype._getAddressTxHistory = function(options, callback) {
|
AddressService.prototype._getAddressTxHistory = function(options, callback) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
@ -420,14 +919,14 @@ AddressService.prototype._getAddressTxHistory = function(options, callback) {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
self._transaction.getTransaction(id.txid, next);
|
self._transaction.getDetailedTransaction(id.txid, options, next);
|
||||||
|
|
||||||
}, callback);
|
}, callback);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//(used by old fn)
|
||||||
AddressService.prototype._getAddressTxidHistory = function(address, options, callback) {
|
AddressService.prototype._getAddressTxidHistory = function(address, options, callback) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
options = options || {};
|
options = options || {};
|
||||||
@ -436,9 +935,6 @@ AddressService.prototype._getAddressTxidHistory = function(address, options, cal
|
|||||||
|
|
||||||
var results = [];
|
var results = [];
|
||||||
|
|
||||||
options.endHeightBuf = new Buffer(4);
|
|
||||||
options.endHeightBuf.writeUInt32BE(options.end);
|
|
||||||
|
|
||||||
if (_.isUndefined(options.queryMempool)) {
|
if (_.isUndefined(options.queryMempool)) {
|
||||||
options.queryMempool = true;
|
options.queryMempool = true;
|
||||||
}
|
}
|
||||||
@ -487,7 +983,15 @@ AddressService.prototype._getAddressTxidHistory = function(address, options, cal
|
|||||||
|
|
||||||
txIdTransformStream._transform = function(chunk, enc, callback) {
|
txIdTransformStream._transform = function(chunk, enc, callback) {
|
||||||
var txInfo = self._encoding.decodeAddressIndexKey(chunk);
|
var txInfo = self._encoding.decodeAddressIndexKey(chunk);
|
||||||
results.push({ txid: txInfo.txid, height: txInfo.height });
|
|
||||||
|
if(results.length >= MAX_TX_QUERY_LIMIT_HISTORY) { //Max array limit reached, end response
|
||||||
|
txIdTransformStream.emit('end');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!results.some(r => r.txid == txInfo.txid)) //add txid to array only if its not already there
|
||||||
|
results.push({ txid: txInfo.txid, height: txInfo.height });
|
||||||
|
|
||||||
callback();
|
callback();
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -499,6 +1003,225 @@ AddressService.prototype._getAddressTxidHistory = function(address, options, cal
|
|||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
AddressService.prototype._streamAddressSummary = function(address, options, streamer, callback) {
|
||||||
|
var self = this;
|
||||||
|
|
||||||
|
options = options || {};
|
||||||
|
options.start = options.start || 0;
|
||||||
|
options.end = options.end || 0xffffffff;
|
||||||
|
|
||||||
|
//options.from = options.from || 0; //Deprecated, use `after` and `before` option
|
||||||
|
//options.to = options.to || 0xffffffff; //Deprecated, use `after` and `before` option
|
||||||
|
|
||||||
|
if (_.isUndefined(options.queryMempool)) {
|
||||||
|
options.queryMempool = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_.isUndefined(options.mempoolOnly)) {
|
||||||
|
options.mempoolOnly = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_.isUndefined(options.reverse)) {
|
||||||
|
options.reverse = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//declare the queue to process tx data
|
||||||
|
var tmpTxList = {}; //store processed txid temporarily to ignore duplication
|
||||||
|
|
||||||
|
var q = async.queue(function(id, cb) {
|
||||||
|
|
||||||
|
//duplication finding
|
||||||
|
if(id.txid in tmpTxList){
|
||||||
|
|
||||||
|
tmpTxList[id.txid][0]++;
|
||||||
|
|
||||||
|
if(tmpTxList[id.txid][1] !== null && tmpTxList[id.txid][0] >= tmpTxList[id.txid][1]) //all duplications are found for this txid
|
||||||
|
delete tmpTxList[id.txid];
|
||||||
|
|
||||||
|
return cb();
|
||||||
|
|
||||||
|
} else tmpTxList[id.txid] = [1, null];
|
||||||
|
|
||||||
|
if (id.height === 0xffffffff) {
|
||||||
|
|
||||||
|
return self._mempool.getMempoolTransaction(id.txid, function(err, tx) {
|
||||||
|
|
||||||
|
if (err || !tx) {
|
||||||
|
return cb(err || new Error('Address Service: could not find tx: ' + id.txid));
|
||||||
|
}
|
||||||
|
self._transaction.setTxMetaInfo(tx, options, cb);
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
self._transaction.getDetailedTransaction(id.txid, options, cb);
|
||||||
|
|
||||||
|
}, 1);
|
||||||
|
|
||||||
|
//q.pause(); //pause and wait until queue is set (not needed)
|
||||||
|
|
||||||
|
function chunkCallback(err, tx){
|
||||||
|
|
||||||
|
if(q.killed || (!err && !tx)) //no error or tx data (duplicate calls will have empty tx value)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if(tx){
|
||||||
|
let txid = tx.txid();
|
||||||
|
tmpTxList[txid][1] = self._getOccurrenceCount(tx, address);
|
||||||
|
|
||||||
|
if(tmpTxList[txid][0] >= tmpTxList[txid][1]) //all duplications are found for this txid
|
||||||
|
delete tmpTxList[txid];
|
||||||
|
}
|
||||||
|
|
||||||
|
streamer(err, tx);
|
||||||
|
|
||||||
|
if((err || options.flag_stop) && !q.killed){
|
||||||
|
|
||||||
|
q.kill();
|
||||||
|
q.killed = true;
|
||||||
|
|
||||||
|
return callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const waterfall_array = [];
|
||||||
|
|
||||||
|
waterfall_array.push(
|
||||||
|
//Find start height if `after` option is passed
|
||||||
|
function parse_after_id(next){
|
||||||
|
|
||||||
|
if(_.isUndefined(options.after)) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
self._transaction.getTransaction(options.after, options, function(err, tx) {
|
||||||
|
|
||||||
|
if(tx && tx.confirmations && tx.height >= options.start) {
|
||||||
|
|
||||||
|
options.start = tx.height;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
delete options.after;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
waterfall_array.push(
|
||||||
|
//Find end height if `before` option is passed
|
||||||
|
function parse_before_id(next){
|
||||||
|
|
||||||
|
if(_.isUndefined(options.before)) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
self._transaction.getTransaction(options.before, options, function(err, tx) {
|
||||||
|
|
||||||
|
if(tx && tx.confirmations && tx.height <= options.end) {
|
||||||
|
|
||||||
|
options.end = tx.height;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
delete options.before;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
next();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
// stream the confirmed txids out of the address index
|
||||||
|
function query_confirmed_txids(next) {
|
||||||
|
|
||||||
|
if (options.mempoolOnly) { //Option to query from mempool only (ie, unconfirmed txs only)
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
var txIdTransformStream = new Transform({ objectMode: true });
|
||||||
|
|
||||||
|
txIdTransformStream._flush = function(cb) {
|
||||||
|
txIdTransformStream.emit('end');
|
||||||
|
cb();
|
||||||
|
};
|
||||||
|
|
||||||
|
txIdTransformStream.on('error', function(err) {
|
||||||
|
log.error('Address Service: txstream err: ' + err);
|
||||||
|
txIdTransformStream.unpipe();
|
||||||
|
});
|
||||||
|
|
||||||
|
txIdTransformStream.on('end', function() {
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
txIdTransformStream._transform = function(chunk, enc, cb) {
|
||||||
|
|
||||||
|
if(options.flag_stop)//stop data query
|
||||||
|
return txIdTransformStream.unpipe();
|
||||||
|
|
||||||
|
var txInfo = self._encoding.decodeAddressIndexKey(chunk);
|
||||||
|
q.push({ txid: txInfo.txid, height: txInfo.height }, chunkCallback);
|
||||||
|
|
||||||
|
cb();
|
||||||
|
};
|
||||||
|
|
||||||
|
var txidStream = self._getTxidStream(address, options);
|
||||||
|
txidStream.pipe(txIdTransformStream);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// query the mempool for relevant txs for this address
|
||||||
|
function query_mempool_txids(next) {
|
||||||
|
|
||||||
|
if (!options.queryMempool || !_.isUndefined(options.before)) { //if queryMempool=false or options.before is given a valid value, then do not query mempool
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
self._mempool.getTxidsByAddress(address, 'both', function(err, mempoolTxids) {
|
||||||
|
|
||||||
|
if (mempoolTxids.length <= 0) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
mempoolTxids.map(id => q.push(id, chunkCallback));
|
||||||
|
next();
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if(options.reverse){ //when queried txs in reverse key order, mempool first then confirmed
|
||||||
|
waterfall_array.push(query_mempool_txids);
|
||||||
|
waterfall_array.push(query_confirmed_txids);
|
||||||
|
} else { //when queried tx in key order, confirmed tx 1st, then mempool
|
||||||
|
waterfall_array.push(query_confirmed_txids);
|
||||||
|
waterfall_array.push(query_mempool_txids);
|
||||||
|
}
|
||||||
|
|
||||||
|
waterfall_array.push(
|
||||||
|
//wait for queue to complete
|
||||||
|
function end_fall(next) {
|
||||||
|
|
||||||
|
if(!q.started || q.idle()) //No tx in query (or) already finished querying
|
||||||
|
return next();
|
||||||
|
|
||||||
|
else
|
||||||
|
q.drain = () => next();
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
async.waterfall(waterfall_array, callback);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
AddressService.prototype._removeBlock = function(block, callback) {
|
AddressService.prototype._removeBlock = function(block, callback) {
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
@ -566,7 +1289,7 @@ AddressService.prototype._removeInput = function(input, tx, block, index, callba
|
|||||||
}
|
}
|
||||||
|
|
||||||
address.network = self._network;
|
address.network = self._network;
|
||||||
address = address.toString();
|
address = address.toString(self._network);
|
||||||
|
|
||||||
assert(block && block.__ts && block.__height, 'Missing block or block values.');
|
assert(block && block.__ts && block.__height, 'Missing block or block values.');
|
||||||
|
|
||||||
@ -610,7 +1333,7 @@ AddressService.prototype._removeOutput = function(output, tx, block, index, call
|
|||||||
}
|
}
|
||||||
|
|
||||||
address.network = self._network;
|
address.network = self._network;
|
||||||
address = address.toString();
|
address = address.toString(self._network);
|
||||||
|
|
||||||
assert(block && block.__ts && block.__height, 'Missing block or block values.');
|
assert(block && block.__ts && block.__height, 'Missing block or block values.');
|
||||||
|
|
||||||
@ -679,7 +1402,7 @@ AddressService.prototype._processInput = function(tx, input, index, opts) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
address.network = this._network;
|
address.network = this._network;
|
||||||
address = address.toString();
|
address = address.toString(this._network);
|
||||||
|
|
||||||
var txid = tx.txid();
|
var txid = tx.txid();
|
||||||
var timestamp = this._timestamp.getTimestampSync(opts.block.rhash());
|
var timestamp = this._timestamp.getTimestampSync(opts.block.rhash());
|
||||||
@ -717,7 +1440,7 @@ AddressService.prototype._processOutput = function(tx, output, index, opts) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
address.network = this._network;
|
address.network = this._network;
|
||||||
address = address.toString();
|
address = address.toString(this._network);
|
||||||
|
|
||||||
var txid = tx.txid();
|
var txid = tx.txid();
|
||||||
var timestamp = this._timestamp.getTimestampSync(opts.block.rhash());
|
var timestamp = this._timestamp.getTimestampSync(opts.block.rhash());
|
||||||
|
|||||||
@ -242,7 +242,7 @@ ProcessSerial.prototype._write = function(block, enc, callback) {
|
|||||||
|
|
||||||
self.block.once('concurrentaddblock', function() {
|
self.block.once('concurrentaddblock', function() {
|
||||||
if(!check()) {
|
if(!check()) {
|
||||||
var err = new Error('Concurrent block ' + self.block.concurrentTip.__height + ' is less than ' + block.__height);
|
var err = 'Concurrent block ' + self.block.concurrentTip.__height + ' is less than ' + block.__height;
|
||||||
return self.emit('error', err);
|
return self.emit('error', err);
|
||||||
}
|
}
|
||||||
self._process(block, callback);
|
self._process(block, callback);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var Block = require('bcoin').block;
|
var Block = require('fcoin').Block;
|
||||||
// stores -- block header as key, block itself as value (optionally)
|
// stores -- block header as key, block itself as value (optionally)
|
||||||
|
|
||||||
function Encoding(servicePrefix) {
|
function Encoding(servicePrefix) {
|
||||||
|
|||||||
@ -9,10 +9,12 @@ var log = index.log;
|
|||||||
var utils = require('../../utils');
|
var utils = require('../../utils');
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var constants = require('../../constants');
|
var constants = require('../../constants');
|
||||||
var bcoin = require('bcoin');
|
var bcoin = require('fcoin');
|
||||||
var _ = require('lodash');
|
var _ = require('lodash');
|
||||||
var LRU = require('lru-cache');
|
var LRU = require('lru-cache');
|
||||||
|
|
||||||
|
const MAX_IGNORED_BLOCK = 16; //Maximum ignored block allowed before trigging sync again
|
||||||
|
|
||||||
var BlockService = function(options) {
|
var BlockService = function(options) {
|
||||||
|
|
||||||
BaseService.call(this, options);
|
BaseService.call(this, options);
|
||||||
@ -36,6 +38,7 @@ var BlockService = function(options) {
|
|||||||
this._recentBlockHashes = new LRU(this._recentBlockHashesCount);
|
this._recentBlockHashes = new LRU(this._recentBlockHashesCount);
|
||||||
this._readAheadBlockCount = options.readAheadBlockCount || 2; // this is the number of blocks to direct the p2p service to read aheead
|
this._readAheadBlockCount = options.readAheadBlockCount || 2; // this is the number of blocks to direct the p2p service to read aheead
|
||||||
this._pauseSync = options.pause;
|
this._pauseSync = options.pause;
|
||||||
|
this._reorgToBlock = options.reorgToBlock;
|
||||||
};
|
};
|
||||||
|
|
||||||
inherits(BlockService, BaseService);
|
inherits(BlockService, BaseService);
|
||||||
@ -90,8 +93,8 @@ BlockService.prototype.getInfo = function(callback) {
|
|||||||
errors: '',
|
errors: '',
|
||||||
network: self.node.network,
|
network: self.node.network,
|
||||||
relayFee: 0,
|
relayFee: 0,
|
||||||
version: 'bitcore-1.1.2',
|
version: 'flocore-5.0.0',
|
||||||
protocolversion: 700001,
|
protocolversion: 70015,
|
||||||
difficulty: self._header.getCurrentDifficulty()
|
difficulty: self._header.getCurrentDifficulty()
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -161,7 +164,7 @@ BlockService.prototype.getBlockOverview = function(hash, callback) {
|
|||||||
prevHash: header.prevHash,
|
prevHash: header.prevHash,
|
||||||
nextHash: header.nextHash,
|
nextHash: header.nextHash,
|
||||||
merkleRoot: header.merkleRoot,
|
merkleRoot: header.merkleRoot,
|
||||||
time: block.ts,
|
time: block.time,
|
||||||
medianTime: null,
|
medianTime: null,
|
||||||
nonce: header.nonce,
|
nonce: header.nonce,
|
||||||
bits: header.bits,
|
bits: header.bits,
|
||||||
@ -201,7 +204,7 @@ BlockService.prototype._checkTip = function(callback) {
|
|||||||
|
|
||||||
header = header || self._header.getLastHeader();
|
header = header || self._header.getLastHeader();
|
||||||
|
|
||||||
if (header.hash === self._tip.hash) {
|
if (header.hash === self._tip.hash && !self._reorgToBlock) {
|
||||||
log.info('Block Service: saved tip is good to go.');
|
log.info('Block Service: saved tip is good to go.');
|
||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
@ -230,8 +233,8 @@ BlockService.prototype._resetTip = function(callback) {
|
|||||||
self._header.getAllHeaders(function(err, headers) {
|
self._header.getAllHeaders(function(err, headers) {
|
||||||
|
|
||||||
if (err || !headers) {
|
if (err || !headers) {
|
||||||
return callback(err || new Error('headers required'));
|
log.error(err || 'headers required'); return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
log.info('Block Service: retrieved all the headers for lookups.');
|
log.info('Block Service: retrieved all the headers for lookups.');
|
||||||
|
|
||||||
@ -251,13 +254,18 @@ BlockService.prototype._resetTip = function(callback) {
|
|||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!_block) {
|
if (!_block) {
|
||||||
log.debug('Block Service: block: ' + header.hash + ' was not found, proceeding to older blocks.');
|
log.debug('Block Service: block: ' + header.hash + ' was not found, proceeding to older blocks.');
|
||||||
}
|
}
|
||||||
|
|
||||||
block = _block;
|
block = _block;
|
||||||
header = headers.getIndex(--height);
|
header = headers.getIndex(--height);
|
||||||
assert(header, 'Header not found for reset.');
|
|
||||||
|
// FLOSight Error Correction from RanchiMall 17th May 2021. removed the unhandled assert and replaced by looging of error
|
||||||
|
if (header == false) {
|
||||||
|
log.error('Header not found for reset.');
|
||||||
|
}
|
||||||
|
// assert(header, 'Header not found for reset.');
|
||||||
|
|
||||||
if (!block) {
|
if (!block) {
|
||||||
log.debug('Block Service: trying block: ' + header.hash);
|
log.debug('Block Service: trying block: ' + header.hash);
|
||||||
@ -270,8 +278,8 @@ BlockService.prototype._resetTip = function(callback) {
|
|||||||
}, function(err) {
|
}, function(err) {
|
||||||
|
|
||||||
if (err || !block) {
|
if (err || !block) {
|
||||||
return callback(err ||
|
log.error(err ||
|
||||||
new Error('Block Service: none of the blocks from the headers match what is already indexed in the block service.'));
|
'Block Service: none of the blocks from the headers match what is already indexed in the block service.'); return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
self._setTip({ hash: block.rhash(), height: height + 1 }, callback);
|
self._setTip({ hash: block.rhash(), height: height + 1 }, callback);
|
||||||
@ -369,8 +377,8 @@ BlockService.prototype._loadRecentBlockHashes = function(callback) {
|
|||||||
self.getBlock(hash, function(err, block) {
|
self.getBlock(hash, function(err, block) {
|
||||||
|
|
||||||
if (err || !block) {
|
if (err || !block) {
|
||||||
return callback(err || new Error('Block Service: attempted to retrieve block: ' + hash +
|
log.error(err || 'Block Service: attempted to retrieve block: ' + hash +
|
||||||
' but was not in the index.'));
|
' but was not in the index.'); return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
var prevHash = bcoin.util.revHex(block.prevBlock);
|
var prevHash = bcoin.util.revHex(block.prevBlock);
|
||||||
@ -386,7 +394,11 @@ BlockService.prototype._loadRecentBlockHashes = function(callback) {
|
|||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(self._recentBlockHashes.length === times, 'Block Service: did not load enough recent block hashes from the index.');
|
// FLOSight Error Correction from RanchiMall 17th May 2021. removed the unhandled assert and replaced by looging of error
|
||||||
|
if (self._recentBlockHashes.length != times) {
|
||||||
|
log.error('Block Service: did not load enough recent block hashes from the index.');
|
||||||
|
}
|
||||||
|
//assert(self._recentBlockHashes.length === times, 'Block Service: did not load enough recent block hashes from the index.');
|
||||||
log.info('Block Service: loaded: ' + self._recentBlockHashes.length + ' hashes from the index.');
|
log.info('Block Service: loaded: ' + self._recentBlockHashes.length + ' hashes from the index.');
|
||||||
callback();
|
callback();
|
||||||
|
|
||||||
@ -405,7 +417,7 @@ BlockService.prototype._getTimeSinceLastBlock = function(callback) {
|
|||||||
self._header.getBlockHeader(Math.max(self._tip.height - 1, 0), function(err, header) {
|
self._header.getBlockHeader(Math.max(self._tip.height - 1, 0), function(err, header) {
|
||||||
|
|
||||||
if(err || !header) {
|
if(err || !header) {
|
||||||
return callback(err || new Error('Block Service: we should have a header in order to get time since last block.'));
|
log.error(err || 'Block Service: we should have a header in order to get time since last block.'); return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
async.map([ self._tip.hash, header.hash ], function(hash, next) {
|
async.map([ self._tip.hash, header.hash ], function(hash, next) {
|
||||||
@ -627,6 +639,7 @@ BlockService.prototype._startBlockSubscription = function() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
this._subscribedBlock = true;
|
this._subscribedBlock = true;
|
||||||
|
this._ignoredBlockCount = 0; //SZ: reset the ignored count to 0 when subscription starts
|
||||||
|
|
||||||
log.info('Block Service: starting p2p block subscription.');
|
log.info('Block Service: starting p2p block subscription.');
|
||||||
this._bus.on('p2p/block', this._queueBlock.bind(this));
|
this._bus.on('p2p/block', this._queueBlock.bind(this));
|
||||||
@ -652,6 +665,15 @@ BlockService.prototype._findLatestValidBlockHeader = function(callback) {
|
|||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
if (self._reorgToBlock) {
|
||||||
|
return self._header.getBlockHeader(self._reorgToBlock, function(err, header) {
|
||||||
|
if (err || !header) {
|
||||||
|
log.error(err || 'Block Service: header not found to reorg to.'); return callback(err);
|
||||||
|
}
|
||||||
|
callback(null, header);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
var blockServiceHash = self._tip.hash;
|
var blockServiceHash = self._tip.hash;
|
||||||
var blockServiceHeight = self._tip.height;
|
var blockServiceHeight = self._tip.height;
|
||||||
var iterCount = 0;
|
var iterCount = 0;
|
||||||
@ -701,12 +723,19 @@ BlockService.prototype._findLatestValidBlockHeader = function(callback) {
|
|||||||
// any of our recent block hashes in its indexes.
|
// any of our recent block hashes in its indexes.
|
||||||
// if some joker mines a block using an orphan block as its prev block, then the effect of this will be
|
// if some joker mines a block using an orphan block as its prev block, then the effect of this will be
|
||||||
// us detecting a reorg, but not actually reorging anything
|
// us detecting a reorg, but not actually reorging anything
|
||||||
assert(header, 'Block Service: we could not locate any of our recent block hashes in the header service ' +
|
// FLOSight Error Correction from RanchiMall 17th May 2021. removed the unhandled assert and replaced by looging of error
|
||||||
'index. Perhaps our header service sync\'ed to the wrong chain?');
|
if (!_.isUndefined(header)) {
|
||||||
|
if (header == false) {
|
||||||
assert(header.height <= self._tip.height, 'Block Service: we found a common ancestor header whose ' +
|
log.error('Block Service: we could not locate any of our recent block hashes in the header service ' + 'index. Perhaps our header service sync\'ed to the wrong chain?');
|
||||||
'height was greater than our current tip. This should be impossible.');
|
}
|
||||||
|
}
|
||||||
|
// assert(header, 'Block Service: we could not locate any of our recent block hashes in the header service ' + 'index. Perhaps our header service sync\'ed to the wrong chain?');
|
||||||
|
if (!_.isUndefined(header.height)) {
|
||||||
|
if (header.height > self._tip.height) {
|
||||||
|
log.error('Block Service: we found a common ancestor header whose ' + 'height was greater than our current tip. This should be impossible.');
|
||||||
|
}
|
||||||
|
// assert(header.height <= self._tip.height, 'Block Service: we found a common ancestor header whose ' + 'height was greater than our current tip. This should be impossible.');
|
||||||
|
}
|
||||||
callback(null, header);
|
callback(null, header);
|
||||||
|
|
||||||
});
|
});
|
||||||
@ -729,13 +758,13 @@ BlockService.prototype._findBlocksToRemove = function(commonHeader, callback) {
|
|||||||
self._getBlock(hash, function(err, block) {
|
self._getBlock(hash, function(err, block) {
|
||||||
|
|
||||||
if (err || !block) {
|
if (err || !block) {
|
||||||
return next(err || new Error('Block Service: block not found in index.'));
|
return next(err || 'Block Service: block not found in index.');
|
||||||
}
|
}
|
||||||
|
|
||||||
self._timestamp.getTimestamp(block.rhash(), function(err, timestamp) {
|
self._timestamp.getTimestamp(block.rhash(), function(err, timestamp) {
|
||||||
|
|
||||||
if (err || !timestamp) {
|
if (err || !timestamp) {
|
||||||
return callback(err || new Error('timestamp missing from reorg.'));
|
log.error(err || 'timestamp missing from reorg.'); return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
block.__height = height;
|
block.__height = height;
|
||||||
@ -805,8 +834,10 @@ BlockService.prototype._handleReorg = function(callback) {
|
|||||||
|
|
||||||
blocksToRemove = _blocksToRemove;
|
blocksToRemove = _blocksToRemove;
|
||||||
|
|
||||||
assert(blocksToRemove.length >= 1 && blocksToRemove.length <= self._recentBlockHashes.length,
|
// FLOSight Error Correction from RanchiMall 17th May 2021. removed the unhandled assert and replaced by looging of error
|
||||||
'Block Service: the number of blocks to remove looks to be incorrect.');
|
if (blocksToRemove.length < 1 || blocksToRemove.length > self._recentBlockHashes.length) {
|
||||||
|
log.error('Block Service: the number of blocks to remove looks to be incorrect.'); }
|
||||||
|
// assert(blocksToRemove.length >= 1 && blocksToRemove.length <= self._recentBlockHashes.length, 'Block Service: the number of blocks to remove looks to be incorrect.');
|
||||||
|
|
||||||
log.warn('Block Service: chain reorganization detected, current height/hash: ' + self._tip.height + '/' +
|
log.warn('Block Service: chain reorganization detected, current height/hash: ' + self._tip.height + '/' +
|
||||||
self._tip.hash + ' common ancestor hash: ' + commonAncestorHeader.hash + ' at height: ' + commonAncestorHeader.height +
|
self._tip.hash + ' common ancestor hash: ' + commonAncestorHeader.hash + ' at height: ' + commonAncestorHeader.height +
|
||||||
@ -910,6 +941,15 @@ BlockService.prototype._processBlock = function(block, callback) {
|
|||||||
return self._saveBlock(block, callback);
|
return self._saveBlock(block, callback);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//SZ: count the ignored blocks. if many blocks ignored, trigger sync process
|
||||||
|
if(self._ignoredBlockCount < MAX_IGNORED_BLOCK)
|
||||||
|
self._ignoredBlockCount++;
|
||||||
|
else {
|
||||||
|
self._ignoredBlockCount = 0;
|
||||||
|
self._removeAllSubscriptions();
|
||||||
|
self._startSync();
|
||||||
|
}
|
||||||
|
|
||||||
// reorg -- in this case, we will not handle the reorg right away
|
// reorg -- in this case, we will not handle the reorg right away
|
||||||
// instead, we will skip the block and wait for the eventual call to
|
// instead, we will skip the block and wait for the eventual call to
|
||||||
// "onHeaders" function. When the header service calls this function,
|
// "onHeaders" function. When the header service calls this function,
|
||||||
@ -925,6 +965,7 @@ BlockService.prototype._saveBlock = function(block, callback) {
|
|||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
block.__height = self._tip.height + 1;
|
block.__height = self._tip.height + 1;
|
||||||
|
self._ignoredBlockCount = 0; //SZ: a block is saved, reset the ignored count
|
||||||
|
|
||||||
var services = self.node.services;
|
var services = self.node.services;
|
||||||
|
|
||||||
@ -972,7 +1013,9 @@ BlockService.prototype._saveBlock = function(block, callback) {
|
|||||||
BlockService.prototype._handleError = function(err) {
|
BlockService.prototype._handleError = function(err) {
|
||||||
if (!this.node.stopping) {
|
if (!this.node.stopping) {
|
||||||
log.error('Block Service: handle error ' + err);
|
log.error('Block Service: handle error ' + err);
|
||||||
return this.node.stop();
|
//FLO Crash Error Resolution by RanchiMall 10th May 2021
|
||||||
|
//return this.node.stop();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -1067,7 +1110,7 @@ BlockService.prototype._startSync = function() {
|
|||||||
this.on('next block', this._sync.bind(this));
|
this.on('next block', this._sync.bind(this));
|
||||||
this.on('synced', this._onSynced.bind(this));
|
this.on('synced', this._onSynced.bind(this));
|
||||||
clearInterval(this._reportInterval);
|
clearInterval(this._reportInterval);
|
||||||
this._reportingInterval = setInterval(this._logProgress.bind(this), 5000);
|
this._reportInterval = setInterval(this._logProgress.bind(this), 5000);
|
||||||
return this._sync();
|
return this._sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1116,7 +1159,7 @@ BlockService.prototype._sync = function() {
|
|||||||
|
|
||||||
self._getBlocksTimer.unref();
|
self._getBlocksTimer.unref();
|
||||||
|
|
||||||
// TODO; research how different bitcoin implementation handle block
|
// TODO; research how different florincoin implementation handle block
|
||||||
// locator objects. If you pass a block locator object that has one
|
// locator objects. If you pass a block locator object that has one
|
||||||
// block hash and that block hash is not on the main chain, then will
|
// block hash and that block hash is not on the main chain, then will
|
||||||
// the peer send an inv for block 1 or no inv at all?
|
// the peer send an inv for block 1 or no inv at all?
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
var bitcore = require('bitcore-lib');
|
var flocore = require('flocore-lib');
|
||||||
var BufferUtil = bitcore.util.buffer;
|
var BufferUtil = flocore.util.buffer;
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
|
|
||||||
function Reorg(node, block) {
|
function Reorg(node, block) {
|
||||||
@ -315,7 +315,7 @@ Reorg.prototype.findCommonAncestorAndNewHashes = function(oldTipHash, newTipHash
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(!mainPosition && !forkPosition) {
|
if(!mainPosition && !forkPosition) {
|
||||||
return next(new Error('Unknown common ancestor'));
|
return next('Unknown common ancestor');
|
||||||
}
|
}
|
||||||
|
|
||||||
next();
|
next();
|
||||||
|
|||||||
@ -3,6 +3,7 @@
|
|||||||
var util = require('util');
|
var util = require('util');
|
||||||
var fs = require('fs');
|
var fs = require('fs');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
|
var _ = require('lodash');
|
||||||
var levelup = require('levelup');
|
var levelup = require('levelup');
|
||||||
var leveldown = require('leveldown');
|
var leveldown = require('leveldown');
|
||||||
var mkdirp = require('mkdirp');
|
var mkdirp = require('mkdirp');
|
||||||
@ -49,18 +50,19 @@ DB.dependencies = [];
|
|||||||
DB.prototype._onError = function(err) {
|
DB.prototype._onError = function(err) {
|
||||||
if (!this._stopping) {
|
if (!this._stopping) {
|
||||||
log.error('Db Service: error: ' + err);
|
log.error('Db Service: error: ' + err);
|
||||||
this.node.stop();
|
//FLO Crash Error Resolution by RanchiMall 10th May 2021
|
||||||
|
//this.node.stop();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
DB.prototype._setDataPath = function() {
|
DB.prototype._setDataPath = function() {
|
||||||
assert(fs.existsSync(this.node.datadir), 'Node is expected to have a "datadir" property');
|
assert(fs.existsSync(this.node.datadir), 'Node is expected to have a "datadir" property');
|
||||||
if (this.node.network === 'livenet' || this.node.network === 'mainnet') {
|
if (this.node.network === 'livenet' || this.node.network === 'mainnet') {
|
||||||
this.dataPath = this.node.datadir + '/bitcorenode.db';
|
this.dataPath = this.node.datadir + '/flocorenode.db';
|
||||||
} else if (this.node.network === 'regtest') {
|
} else if (this.node.network === 'regtest') {
|
||||||
this.dataPath = this.node.datadir + '/regtest/bitcorenode.db';
|
this.dataPath = this.node.datadir + '/regtest/flocorenode.db';
|
||||||
} else if (this.node.network === 'testnet') {
|
} else if (this.node.network === 'testnet') {
|
||||||
this.dataPath = this.node.datadir + '/testnet/bitcorenode.db';
|
this.dataPath = this.node.datadir + '/testnet/flocorenode.db';
|
||||||
} else {
|
} else {
|
||||||
throw new Error('Unknown network: ' + this.network);
|
throw new Error('Unknown network: ' + this.network);
|
||||||
}
|
}
|
||||||
@ -94,7 +96,7 @@ DB.prototype.get = function(key, options, callback) {
|
|||||||
var cb = callback;
|
var cb = callback;
|
||||||
var opts = options;
|
var opts = options;
|
||||||
|
|
||||||
if (typeof callback !== 'function') {
|
if (!_.isFunction(callback)) {
|
||||||
cb = options;
|
cb = options;
|
||||||
opts = {};
|
opts = {};
|
||||||
}
|
}
|
||||||
@ -117,7 +119,9 @@ DB.prototype.get = function(key, options, callback) {
|
|||||||
|
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
cb(new Error('Shutdown sequence underway, not able to complete the query'));
|
// FLOSight Error Correction from RanchiMall 20th May 2021.
|
||||||
|
//cb(new Error('Shutdown sequence underway, not able to complete the query'));
|
||||||
|
log.error('Shutdown sequence underway, not able to complete the query');
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
@ -128,17 +132,40 @@ DB.prototype.put = function(key, value, callback) {
|
|||||||
callback();
|
callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(Buffer.isBuffer(key), 'key NOT a buffer as expected.');
|
// FLOSight Error Correction from RanchiMall 20th May 2021. removed the unhandled assert and replaced by looging of error
|
||||||
|
if (Buffer.isBuffer(key) == false) {
|
||||||
|
log.error('key NOT a buffer as expected.');
|
||||||
|
}
|
||||||
|
// assert(Buffer.isBuffer(key), 'key NOT a buffer as expected.');
|
||||||
|
|
||||||
if (value) {
|
if (value) {
|
||||||
|
|
||||||
assert(Buffer.isBuffer(value), 'value exists but NOT a buffer as expected.');
|
// FLOSight Error Correction from RanchiMall 20th May 2021. removed the unhandled assert and replaced by looging of error
|
||||||
|
if (Buffer.isBuffer(value) == false) {
|
||||||
|
log.error('value exists but NOT a buffer as expected.');
|
||||||
|
}
|
||||||
|
//assert(Buffer.isBuffer(value), 'value exists but NOT a buffer as expected.');
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this._store.put(key, value, callback);
|
this._store.put(key, value, callback);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
DB.prototype.del = function(key, callback) {
|
||||||
|
|
||||||
|
if (this._stopping) {
|
||||||
|
callback();
|
||||||
|
}
|
||||||
|
|
||||||
|
// FLOSight Error Correction from RanchiMall 20th May 2021. removed the unhandled assert and replaced by looging of error
|
||||||
|
if (Buffer.isBuffer(key) == false) {
|
||||||
|
log.error('key NOT a buffer as expected.');
|
||||||
|
}
|
||||||
|
// assert(Buffer.isBuffer(key), 'key NOT a buffer as expected.');
|
||||||
|
|
||||||
|
this._store.del(key, callback);
|
||||||
|
}
|
||||||
|
|
||||||
DB.prototype.batch = function(ops, callback) {
|
DB.prototype.batch = function(ops, callback) {
|
||||||
|
|
||||||
if (this._stopping) {
|
if (this._stopping) {
|
||||||
@ -147,11 +174,18 @@ DB.prototype.batch = function(ops, callback) {
|
|||||||
|
|
||||||
for(var i = 0; i < ops.length; i++) {
|
for(var i = 0; i < ops.length; i++) {
|
||||||
|
|
||||||
assert(Buffer.isBuffer(ops[i].key), 'key NOT a buffer as expected.');
|
// FLOSight Error Correction from RanchiMall 20th May 2021. removed the unhandled assert and replaced by looging of error
|
||||||
|
if (Buffer.isBuffer(ops[i].key) == false) {
|
||||||
|
log.error('key NOT a buffer as expected.');
|
||||||
|
}
|
||||||
|
//assert(Buffer.isBuffer(ops[i].key), 'key NOT a buffer as expected.');
|
||||||
|
|
||||||
if (ops[i].value) {
|
if (ops[i].value) {
|
||||||
|
// FLOSight Error Correction from RanchiMall 20th May 2021. removed the unhandled assert and replaced by looging of error
|
||||||
assert(Buffer.isBuffer(ops[i].value), 'value exists but NOT a buffer as expected.');
|
if (Buffer.isBuffer(ops[i].value) == false) {
|
||||||
|
log.error('value exists but NOT a buffer as expected.');
|
||||||
|
}
|
||||||
|
//assert(Buffer.isBuffer(ops[i].value), 'value exists but NOT a buffer as expected.');
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -2,18 +2,18 @@
|
|||||||
|
|
||||||
var BaseService = require('../../service');
|
var BaseService = require('../../service');
|
||||||
var inherits = require('util').inherits;
|
var inherits = require('util').inherits;
|
||||||
var BitcoreRPC = require('bitcoind-rpc');
|
var FlocoreRPC = require('florincoind-rpc');
|
||||||
|
|
||||||
var FeeService = function(options) {
|
var FeeService = function(options) {
|
||||||
this._config = options.rpc || {
|
this._config = options.rpc || {
|
||||||
user: 'bitcoin',
|
user: 'florincoin',
|
||||||
pass: 'local321',
|
pass: 'local321',
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
protocol: 'http',
|
protocol: 'http',
|
||||||
port: 8332
|
port: 7312
|
||||||
};
|
};
|
||||||
BaseService.call(this, options);
|
BaseService.call(this, options);
|
||||||
this._client = new BitcoreRPC(this._config);
|
this._client = new FlocoreRPC(this._config);
|
||||||
};
|
};
|
||||||
|
|
||||||
inherits(FeeService, BaseService);
|
inherits(FeeService, BaseService);
|
||||||
|
|||||||
@ -7,11 +7,14 @@ var index = require('../../');
|
|||||||
var log = index.log;
|
var log = index.log;
|
||||||
var utils = require('../../utils');
|
var utils = require('../../utils');
|
||||||
var async = require('async');
|
var async = require('async');
|
||||||
var BN = require('bn.js');
|
// var BN = require('bn.js');
|
||||||
var consensus = require('bcoin').consensus;
|
var BN = require('bcrypto/lib/bn.js')
|
||||||
|
var consensus = require('fcoin').consensus;
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var constants = require('../../constants');
|
var constants = require('../../constants');
|
||||||
var bcoin = require('bcoin');
|
var bcoin = require('fcoin');
|
||||||
|
|
||||||
|
const SYNC_CHECK_INTERVAL = 1000 * 60 * 15; //15 mins
|
||||||
|
|
||||||
var HeaderService = function(options) {
|
var HeaderService = function(options) {
|
||||||
|
|
||||||
@ -39,7 +42,7 @@ inherits(HeaderService, BaseService);
|
|||||||
HeaderService.dependencies = [ 'p2p', 'db' ];
|
HeaderService.dependencies = [ 'p2p', 'db' ];
|
||||||
|
|
||||||
HeaderService.MAX_CHAINWORK = new BN(1).ushln(256);
|
HeaderService.MAX_CHAINWORK = new BN(1).ushln(256);
|
||||||
HeaderService.STARTING_CHAINWORK = '0000000000000000000000000000000000000000000000000000000100010001';
|
HeaderService.STARTING_CHAINWORK = '0000000000000000000000000000000000000000000000000000000000100010';
|
||||||
|
|
||||||
HeaderService.prototype.subscribe = function(name, emitter) {
|
HeaderService.prototype.subscribe = function(name, emitter) {
|
||||||
this.subscriptions[name].push(emitter);
|
this.subscriptions[name].push(emitter);
|
||||||
@ -144,7 +147,11 @@ HeaderService.prototype._adjustTipBackToCheckpoint = function() {
|
|||||||
|
|
||||||
HeaderService.prototype._setGenesisBlock = function(callback) {
|
HeaderService.prototype._setGenesisBlock = function(callback) {
|
||||||
|
|
||||||
assert(this._tip.hash === this.GENESIS_HASH, 'Expected tip hash to be genesis hash, but it was not.');
|
// FLOSight Error Correction from RanchiMall 17th May 2021. removed the unhandled assert and replaced by looging of error
|
||||||
|
if (this._tip.hash != this.GENESIS_HASH) {
|
||||||
|
log.error('Expected tip hash to be genesis hash, but it was not.');
|
||||||
|
}
|
||||||
|
// assert(this._tip.hash === this.GENESIS_HASH, 'Expected tip hash to be genesis hash, but it was not.');
|
||||||
|
|
||||||
var genesisHeader = {
|
var genesisHeader = {
|
||||||
hash: this.GENESIS_HASH,
|
hash: this.GENESIS_HASH,
|
||||||
@ -152,10 +159,10 @@ HeaderService.prototype._setGenesisBlock = function(callback) {
|
|||||||
chainwork: HeaderService.STARTING_CHAINWORK,
|
chainwork: HeaderService.STARTING_CHAINWORK,
|
||||||
version: 1,
|
version: 1,
|
||||||
prevHash: new Array(65).join('0'),
|
prevHash: new Array(65).join('0'),
|
||||||
timestamp: 1231006505,
|
timestamp: 1371488396,
|
||||||
nonce: 2083236893,
|
nonce: 1000112548,
|
||||||
bits: 0x1d00ffff,
|
bits: 0x1e0ffff0,
|
||||||
merkleRoot: '4a5e1e4baab89f3a32518a88c31bc87f618f76673e2cc77ab2127b7afdeda33b'
|
merkleRoot: '730f0c8ddc5a592d5512566890e2a73e45feaa6748b24b849d1c29a7ab2b2300'
|
||||||
};
|
};
|
||||||
|
|
||||||
this._lastHeader = genesisHeader;
|
this._lastHeader = genesisHeader;
|
||||||
@ -334,7 +341,7 @@ HeaderService.prototype._persistHeader = function(block, callback) {
|
|||||||
HeaderService.prototype._formatHeader = function(block) {
|
HeaderService.prototype._formatHeader = function(block) {
|
||||||
|
|
||||||
var header = block.toHeaders().toJSON();
|
var header = block.toHeaders().toJSON();
|
||||||
header.timestamp = header.ts;
|
header.timestamp = header.time;
|
||||||
header.prevHash = header.prevBlock;
|
header.prevHash = header.prevBlock;
|
||||||
return header;
|
return header;
|
||||||
|
|
||||||
@ -411,7 +418,10 @@ HeaderService.prototype._getDBOpForLastHeader = function(nextHeader) {
|
|||||||
this._lastHeader.nextHash = nextHeader.hash;
|
this._lastHeader.nextHash = nextHeader.hash;
|
||||||
var keyHash = this._encoding.encodeHeaderHashKey(this._lastHeader.hash);
|
var keyHash = this._encoding.encodeHeaderHashKey(this._lastHeader.hash);
|
||||||
|
|
||||||
assert(this._lastHeader.height >= 0, 'Trying to save a header with incorrect height.');
|
// FLOSight Error Correction from RanchiMall 17th May 2021. removed the unhandled assert and replaced by looging of error
|
||||||
|
if (this._lastHeader.height < 0) { log.error('Trying to save a header with incorrect height.');
|
||||||
|
}
|
||||||
|
// assert(this._lastHeader.height >= 0, 'Trying to save a header with incorrect height.');
|
||||||
|
|
||||||
var keyHeight = this._encoding.encodeHeaderHeightKey(this._lastHeader.height);
|
var keyHeight = this._encoding.encodeHeaderHeightKey(this._lastHeader.height);
|
||||||
var value = this._encoding.encodeHeaderValue(this._lastHeader);
|
var value = this._encoding.encodeHeaderValue(this._lastHeader);
|
||||||
@ -466,8 +476,11 @@ HeaderService.prototype._onHeaders = function(headers) {
|
|||||||
|
|
||||||
var header = transformedHeaders[i];
|
var header = transformedHeaders[i];
|
||||||
|
|
||||||
assert(self._lastHeader.hash === header.prevHash, 'headers not in order: ' + self._lastHeader.hash +
|
// FLOSight Error Correction from RanchiMall 17th May 2021. removed the unhandled assert and replaced by looging of error
|
||||||
' -and- ' + header.prevHash + ' Last header at height: ' + self._lastHeader.height);
|
if (self._lastHeader.hash != header.prevHash) {
|
||||||
|
log.error('headers not in order: ' + self._lastHeader.hash + ' -and- ' + header.prevHash + ' Last header at height: ' + self._lastHeader.height);
|
||||||
|
}
|
||||||
|
// assert(self._lastHeader.hash === header.prevHash, 'headers not in order: ' + self._lastHeader.hash + ' -and- ' + header.prevHash + ' Last header at height: ' + self._lastHeader.height);
|
||||||
|
|
||||||
var ops = self._onHeader(header);
|
var ops = self._onHeader(header);
|
||||||
|
|
||||||
@ -484,8 +497,9 @@ HeaderService.prototype._onHeaders = function(headers) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
HeaderService.prototype._handleError = function(err) {
|
HeaderService.prototype._handleError = function(err) {
|
||||||
log.error('Header Service: ' + err);
|
log.error('Error in Header Service: ' + err);
|
||||||
this.node.stop();
|
return;
|
||||||
|
//this.node.stop();
|
||||||
};
|
};
|
||||||
|
|
||||||
HeaderService.prototype._saveHeaders = function(dbOps, callback) {
|
HeaderService.prototype._saveHeaders = function(dbOps, callback) {
|
||||||
@ -510,6 +524,7 @@ HeaderService.prototype._saveHeaders = function(dbOps, callback) {
|
|||||||
HeaderService.prototype._onHeadersSave = function(callback) {
|
HeaderService.prototype._onHeadersSave = function(callback) {
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
|
self._syncUnresponsive = false; //SZ: got response from peer
|
||||||
self._logProgress();
|
self._logProgress();
|
||||||
|
|
||||||
if (!self._syncComplete()) {
|
if (!self._syncComplete()) {
|
||||||
@ -517,6 +532,12 @@ HeaderService.prototype._onHeadersSave = function(callback) {
|
|||||||
return callback();
|
return callback();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//SZ: clear the interval check as sync is completed
|
||||||
|
if(self._syncCheckInterval){
|
||||||
|
clearInterval(self._syncCheckInterval);
|
||||||
|
self._syncCheckInterval = null;
|
||||||
|
}
|
||||||
|
|
||||||
self._endHeaderSubscription(); // we don't need headers any more
|
self._endHeaderSubscription(); // we don't need headers any more
|
||||||
self._startBlockSubscription(); // we need new blocks coming tu us aynchronuously
|
self._startBlockSubscription(); // we need new blocks coming tu us aynchronuously
|
||||||
|
|
||||||
@ -573,12 +594,15 @@ HeaderService.prototype._startBlockSubscription = function() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
HeaderService.prototype._syncComplete = function() {
|
HeaderService.prototype._syncComplete = function() {
|
||||||
|
// Check if we have reached the last block that we can download.
|
||||||
|
var bestHeight = Math.max(this._bestHeight, this._lastHeader.height);
|
||||||
|
|
||||||
// we always ask for the max number of headers, which is 2000.
|
var syncComplete = bestHeight === this._tip.height
|
||||||
// so any response with < 2000 means we have reached the end of the headers list.
|
|
||||||
// we could make an extra call if the number of total headers is multiple of 2000.
|
|
||||||
return this._lastHeaderCount < 2000;
|
|
||||||
|
|
||||||
|
if (syncComplete && this._initialSync)
|
||||||
|
log.info("Header Service: Sync Complete!")
|
||||||
|
|
||||||
|
return syncComplete
|
||||||
};
|
};
|
||||||
|
|
||||||
HeaderService.prototype._setBestHeader = function() {
|
HeaderService.prototype._setBestHeader = function() {
|
||||||
@ -593,7 +617,7 @@ HeaderService.prototype._getHeader = function(height, hash, callback) {
|
|||||||
/*jshint -W018 */
|
/*jshint -W018 */
|
||||||
if (!hash && !(height >= 0)) {
|
if (!hash && !(height >= 0)) {
|
||||||
/*jshint +W018 */
|
/*jshint +W018 */
|
||||||
return callback(new Error('invalid arguments'));
|
return callback('invalid arguments');
|
||||||
}
|
}
|
||||||
|
|
||||||
if (height === self._lastHeader.height || hash === self._lastHeader.hash) {
|
if (height === self._lastHeader.height || hash === self._lastHeader.hash) {
|
||||||
@ -692,6 +716,23 @@ HeaderService.prototype._startSync = function() {
|
|||||||
// common case
|
// common case
|
||||||
if (numNeeded > 0) {
|
if (numNeeded > 0) {
|
||||||
log.info('Header Service: Gathering: ' + numNeeded + ' ' + 'header(s) from the peer-to-peer network.');
|
log.info('Header Service: Gathering: ' + numNeeded + ' ' + 'header(s) from the peer-to-peer network.');
|
||||||
|
|
||||||
|
//SZ: Adding interval check for sync with peer is responsive or not
|
||||||
|
//(only if fcoin is started by flocore)
|
||||||
|
if(self._p2p._bcoin){
|
||||||
|
self._syncUnresponsive = true;
|
||||||
|
self._syncCheckInterval = setInterval(() => {
|
||||||
|
//check the best height
|
||||||
|
if(self._bestHeight < self._p2p._bcoin._bcoin.pool.chain.height)
|
||||||
|
self._bestHeight = self._p2p._bcoin._bcoin.pool.chain.height;
|
||||||
|
//call sync again if unresponsive
|
||||||
|
if(self._syncUnresponsive)
|
||||||
|
self._sync();
|
||||||
|
else //reset unresponsive as true
|
||||||
|
self._syncUnresponsive = true;
|
||||||
|
}, SYNC_CHECK_INTERVAL);
|
||||||
|
}
|
||||||
|
|
||||||
return self._sync();
|
return self._sync();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -773,9 +814,11 @@ HeaderService.prototype._findReorgConditionInNewPeer = function(callback) {
|
|||||||
|
|
||||||
// nothing matched...
|
// nothing matched...
|
||||||
// at this point, we should wonder if we are connected to the wrong network
|
// at this point, we should wonder if we are connected to the wrong network
|
||||||
assert(true, 'We tried to find a common header between current set of headers ' +
|
// FLOSight Error Correction from RanchiMall 17th May 2021. removed the unhandled assert and replaced by looging of error
|
||||||
'and the new peer\'s set of headers, but there were none. This should be impossible ' +
|
if (false) {
|
||||||
' if the new peer is using the same genesis block.');
|
log.error('We tried to find a common header between current set of headers ' + 'and the new peer\'s set of headers, but there were none. This should be impossible ' + ' if the new peer is using the same genesis block.');
|
||||||
|
}
|
||||||
|
// assert(true, 'We tried to find a common header between current set of headers ' +'and the new peer\'s set of headers, but there were none. This should be impossible ' +' if the new peer is using the same genesis block.');
|
||||||
});
|
});
|
||||||
|
|
||||||
self._getP2PHeaders(self.GENESIS_HASH);
|
self._getP2PHeaders(self.GENESIS_HASH);
|
||||||
@ -876,7 +919,11 @@ HeaderService.prototype._sync = function() {
|
|||||||
|
|
||||||
HeaderService.prototype.getEndHash = function(tip, blockCount, callback) {
|
HeaderService.prototype.getEndHash = function(tip, blockCount, callback) {
|
||||||
|
|
||||||
assert(blockCount >= 1, 'Header Service: block count to getEndHash must be at least 1.');
|
// FLOSight Error Correction from RanchiMall 17th May 2021. removed the unhandled assert and replaced by looging of error
|
||||||
|
if (blockCount < 1) {
|
||||||
|
log.error('Header Service: block count to getEndHash must be at least 1.');
|
||||||
|
}
|
||||||
|
// assert(blockCount >= 1, 'Header Service: block count to getEndHash must be at least 1.');
|
||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
@ -887,7 +934,7 @@ HeaderService.prototype.getEndHash = function(tip, blockCount, callback) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (numResultsNeeded <= 0) {
|
if (numResultsNeeded <= 0) {
|
||||||
return callback(new Error('Header Service: block service is mis-aligned '));
|
return callback('Header Service: block service is mis-aligned ');
|
||||||
}
|
}
|
||||||
|
|
||||||
var startingHeight = tip.height + 1;
|
var startingHeight = tip.height + 1;
|
||||||
@ -917,8 +964,11 @@ HeaderService.prototype.getEndHash = function(tip, blockCount, callback) {
|
|||||||
if (streamErr) {
|
if (streamErr) {
|
||||||
return streamErr;
|
return streamErr;
|
||||||
}
|
}
|
||||||
|
// FLOSight Error Correction from RanchiMall 17th May 2021. removed the unhandled assert and replaced by looging of error
|
||||||
assert(results.length === numResultsNeeded, 'getEndHash returned incorrect number of results.');
|
if (results.length != numResultsNeeded) {
|
||||||
|
log.error('getEndHash returned incorrect number of results.');
|
||||||
|
}
|
||||||
|
// assert(results.length === numResultsNeeded, 'getEndHash returned incorrect number of results.');
|
||||||
|
|
||||||
var index = numResultsNeeded - 1;
|
var index = numResultsNeeded - 1;
|
||||||
var endHash = index <= 0 || !results[index] ? 0 : results[index];
|
var endHash = index <= 0 || !results[index] ? 0 : results[index];
|
||||||
@ -936,7 +986,11 @@ HeaderService.prototype.getEndHash = function(tip, blockCount, callback) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
HeaderService.prototype.getLastHeader = function() {
|
HeaderService.prototype.getLastHeader = function() {
|
||||||
assert(this._lastHeader, 'Last header should be populated.');
|
// FLOSight Error Correction from RanchiMall 17th May 2021. removed the unhandled assert and replaced by looging of error
|
||||||
|
if (this._lastHeader == false) {
|
||||||
|
log.error('Last header should be populated.'); return;
|
||||||
|
}
|
||||||
|
// assert(this._lastHeader, 'Last header should be populated.');
|
||||||
return this._lastHeader;
|
return this._lastHeader;
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -949,7 +1003,7 @@ HeaderService.prototype._adjustHeadersForCheckPointTip = function(callback) {
|
|||||||
var start = self._encoding.encodeHeaderHeightKey(self._tip.height);
|
var start = self._encoding.encodeHeaderHeightKey(self._tip.height);
|
||||||
var end = self._encoding.encodeHeaderHeightKey(0xffffffff);
|
var end = self._encoding.encodeHeaderHeightKey(0xffffffff);
|
||||||
|
|
||||||
log.info('Getting last header synced at height: ' + self._tip.height);
|
log.info('Header Service: Getting last header synced at height: ' + self._tip.height);
|
||||||
|
|
||||||
var criteria = {
|
var criteria = {
|
||||||
gte: start,
|
gte: start,
|
||||||
@ -969,6 +1023,7 @@ HeaderService.prototype._adjustHeadersForCheckPointTip = function(callback) {
|
|||||||
// any records with a height greater than our current tip height can be scheduled for removal
|
// any records with a height greater than our current tip height can be scheduled for removal
|
||||||
// because they will be replaced shortly
|
// because they will be replaced shortly
|
||||||
// and for every height record, we must also remove its hash record
|
// and for every height record, we must also remove its hash record
|
||||||
|
|
||||||
if (header.height > self._tip.height) {
|
if (header.height > self._tip.height) {
|
||||||
removalOps.push({
|
removalOps.push({
|
||||||
type: 'del',
|
type: 'del',
|
||||||
@ -993,7 +1048,13 @@ HeaderService.prototype._adjustHeadersForCheckPointTip = function(callback) {
|
|||||||
return streamErr;
|
return streamErr;
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(self._lastHeader, 'The last synced header was not in the database.');
|
|
||||||
|
// FLOSight Error Correction from RanchiMall 17th May 2021. removed the unhandled assert and replaced by looging of error
|
||||||
|
if (self._lastHeader == false) {
|
||||||
|
log.error('The last synced header was not in the database.');
|
||||||
|
}
|
||||||
|
//assert(self._lastHeader, 'The last synced header was not in the database.');
|
||||||
|
|
||||||
self._tip.hash = self._lastHeader.hash;
|
self._tip.hash = self._lastHeader.hash;
|
||||||
self._tip.height = self._lastHeader.height;
|
self._tip.height = self._lastHeader.height;
|
||||||
self._db.batch(removalOps, callback);
|
self._db.batch(removalOps, callback);
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var tx = require('bcoin').tx;
|
var tx = require('fcoin').TX;
|
||||||
|
|
||||||
function Encoding(servicePrefix) {
|
function Encoding(servicePrefix) {
|
||||||
this.servicePrefix = servicePrefix;
|
this.servicePrefix = servicePrefix;
|
||||||
|
|||||||
@ -247,7 +247,7 @@ MempoolService.prototype._onTransaction = function(tx) {
|
|||||||
self._db.batch(ops, function(err) {
|
self._db.batch(ops, function(err) {
|
||||||
if(err) {
|
if(err) {
|
||||||
log.error(err);
|
log.error(err);
|
||||||
self.node.stop();
|
// self.node.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < self._subscriptions.transaction.length; i++) {
|
for (var i = 0; i < self._subscriptions.transaction.length; i++) {
|
||||||
|
|||||||
@ -2,47 +2,51 @@
|
|||||||
|
|
||||||
var index = require('../../');
|
var index = require('../../');
|
||||||
var log = index.log;
|
var log = index.log;
|
||||||
var bcoin = require('bcoin');
|
var bcoin = require('fcoin');
|
||||||
|
// var bzmq = require('bzmq');
|
||||||
|
|
||||||
var Bcoin = function(options) {
|
var Bcoin = function(options) {
|
||||||
this._config = this._getConfig(options);
|
this._config = this._getConfig(options);
|
||||||
};
|
};
|
||||||
|
|
||||||
Bcoin.prototype.start = function(callback) {
|
Bcoin.prototype.start = function(callback) {
|
||||||
var self = this;
|
this._bcoin = new bcoin.FullNode(this._config);
|
||||||
self._bcoin = bcoin.fullnode(self._config);
|
|
||||||
|
|
||||||
log.info('Starting Bcoin full node...');
|
log.info('Starting fcoin FullNode...');
|
||||||
|
|
||||||
self._bcoin.open().then(function() {
|
this._bcoin.open().then(() => {
|
||||||
self._bcoin.connect().then(function() {
|
this._bcoin.connect().then(() => {
|
||||||
log.info('Waiting for Bcoin to sync');
|
this._bcoin.startSync();
|
||||||
self._bcoin.startSync();
|
|
||||||
// this will instruct the p2p service to start trying to connect to bcoin right away
|
|
||||||
callback();
|
callback();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
Bcoin.prototype.stop = function() {
|
Bcoin.prototype.stop = function(callback) {
|
||||||
this._bcoin.stopSync();
|
this._bcoin.close().then(() => {
|
||||||
this._bcoin.disconnect();
|
log.info("fcoin shutdown")
|
||||||
this._bcoin.close();
|
callback()
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// --- privates
|
// --- privates
|
||||||
|
|
||||||
Bcoin.prototype._getConfig = function(options) {
|
Bcoin.prototype._getConfig = function(options) {
|
||||||
var config = {
|
var config = {
|
||||||
db: 'leveldb',
|
|
||||||
checkpoints: true,
|
|
||||||
network: options.network || 'main',
|
network: options.network || 'main',
|
||||||
listen: true,
|
port: options.port,
|
||||||
|
|
||||||
|
logFile: true,
|
||||||
logConsole: true,
|
logConsole: true,
|
||||||
logLevel: 'info',
|
logLevel: 'info',
|
||||||
port: options.port,
|
|
||||||
persistent: true,
|
// indexTx: true,
|
||||||
workers: true
|
// indexAddress: true,
|
||||||
|
|
||||||
|
checkpoints: true,
|
||||||
|
memory: false,
|
||||||
|
workers: true,
|
||||||
|
listen: true
|
||||||
};
|
};
|
||||||
if (options.prefix) {
|
if (options.prefix) {
|
||||||
config.prefix = options.prefix;
|
config.prefix = options.prefix;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var p2p = require('bitcore-p2p');
|
var p2p = require('flocore-p2p');
|
||||||
var LRU = require('lru-cache');
|
var LRU = require('lru-cache');
|
||||||
var util = require('util');
|
var util = require('util');
|
||||||
var index = require('../../');
|
var index = require('../../');
|
||||||
@ -8,8 +8,10 @@ var log = index.log;
|
|||||||
var BaseService = require('../../service');
|
var BaseService = require('../../service');
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var Bcoin = require('./bcoin');
|
var Bcoin = require('./bcoin');
|
||||||
var BcoinTx = require('bcoin').tx;
|
var BcoinBlock = require('fcoin').Block;
|
||||||
var Networks = require('bitcore-lib').Networks;
|
var BcoinTx = require('fcoin').TX;
|
||||||
|
var Networks = require('flocore-lib').Networks;
|
||||||
|
var BitcoreRPC = require('bitcoind-rpc');
|
||||||
var LRU = require('lru-cache');
|
var LRU = require('lru-cache');
|
||||||
|
|
||||||
var P2P = function(options) {
|
var P2P = function(options) {
|
||||||
@ -21,6 +23,7 @@ var P2P = function(options) {
|
|||||||
BaseService.call(this, options);
|
BaseService.call(this, options);
|
||||||
this._options = options;
|
this._options = options;
|
||||||
|
|
||||||
|
this._initRPC(options);
|
||||||
this._initP2P();
|
this._initP2P();
|
||||||
this._initPubSub();
|
this._initPubSub();
|
||||||
this._bcoin = null;
|
this._bcoin = null;
|
||||||
@ -87,7 +90,7 @@ P2P.prototype.getP2PBlock = function(opts, callback) {
|
|||||||
|
|
||||||
self.once(opts.blockHash, callback);
|
self.once(opts.blockHash, callback);
|
||||||
|
|
||||||
peer.sendMessage(self.messages.GetBlocks(blockFilter));
|
peer.sendMessage(self.messages.GetBlocks(blockFilter, { Block: BcoinBlock }));
|
||||||
};
|
};
|
||||||
|
|
||||||
P2P.prototype.getHeaders = function(filter) {
|
P2P.prototype.getHeaders = function(filter) {
|
||||||
@ -128,26 +131,7 @@ P2P.prototype.getPublishEvents = function() {
|
|||||||
|
|
||||||
|
|
||||||
P2P.prototype.sendTransaction = function(tx, callback) {
|
P2P.prototype.sendTransaction = function(tx, callback) {
|
||||||
var peer = this._getPeer();
|
return this._client.sendRawTransaction(tx, callback);
|
||||||
|
|
||||||
var bcoinTx;
|
|
||||||
try {
|
|
||||||
bcoinTx = BcoinTx.fromRaw(tx, 'hex');
|
|
||||||
} catch(e) {
|
|
||||||
return callback(e);
|
|
||||||
}
|
|
||||||
|
|
||||||
log.info('P2P Service: sending transaction: ' + bcoinTx.txid());
|
|
||||||
|
|
||||||
this._outgoingTxs.set(bcoinTx.txid(), bcoinTx);
|
|
||||||
var inv = p2p.Inventory.forTransaction(bcoinTx.txid());
|
|
||||||
var txMessage = this.messages.Inventory([inv]);
|
|
||||||
|
|
||||||
peer.sendMessage(txMessage);
|
|
||||||
|
|
||||||
this._onPeerTx(peer, { transaction: bcoinTx });
|
|
||||||
|
|
||||||
return callback(null, bcoinTx.txid());
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
@ -269,6 +253,23 @@ P2P.prototype._initCache = function() {
|
|||||||
this._inv = LRU(1000);
|
this._inv = LRU(1000);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
P2P.prototype._initRPC = function (options) {
|
||||||
|
var port = 7313
|
||||||
|
|
||||||
|
if (this.node.network === 'testnet')
|
||||||
|
port = 17313
|
||||||
|
|
||||||
|
this._config = options.rpc || {
|
||||||
|
user: 'flocore',
|
||||||
|
pass: 'flocorepassw123',
|
||||||
|
host: 'localhost',
|
||||||
|
protocol: 'http',
|
||||||
|
port: port
|
||||||
|
};
|
||||||
|
|
||||||
|
this._client = new BitcoreRPC(this._config);
|
||||||
|
}
|
||||||
|
|
||||||
P2P.prototype._initP2P = function() {
|
P2P.prototype._initP2P = function() {
|
||||||
this._maxPeers = this._options.maxPeers || 60;
|
this._maxPeers = this._options.maxPeers || 60;
|
||||||
this._minPeers = this._options.minPeers || 0;
|
this._minPeers = this._options.minPeers || 0;
|
||||||
@ -277,7 +278,7 @@ P2P.prototype._initP2P = function() {
|
|||||||
if (this.node.network === 'regtest') {
|
if (this.node.network === 'regtest') {
|
||||||
Networks.enableRegtest();
|
Networks.enableRegtest();
|
||||||
}
|
}
|
||||||
this.messages = new p2p.Messages({ network: Networks.get(this.node.network), Transaction: BcoinTx });
|
this.messages = new p2p.Messages({ network: Networks.get(this.node.network), Transaction: BcoinTx, Block: BcoinBlock });
|
||||||
this._peerHeights = [];
|
this._peerHeights = [];
|
||||||
this._peers = [];
|
this._peers = [];
|
||||||
this._peerIndex = 0;
|
this._peerIndex = 0;
|
||||||
@ -363,7 +364,8 @@ P2P.prototype._matchNetwork = function(network) {
|
|||||||
log.error('Configured network: "' + this.node.network +
|
log.error('Configured network: "' + this.node.network +
|
||||||
'" does not match our peer\'s reported network: "' +
|
'" does not match our peer\'s reported network: "' +
|
||||||
network.name + '".');
|
network.name + '".');
|
||||||
return this.node.stop();
|
// return this.node.stop();
|
||||||
|
return ;
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.node.network === network.name ? network.name : network.alias;
|
return this.node.network === network.name ? network.name : network.alias;
|
||||||
@ -423,7 +425,12 @@ P2P.prototype._setListeners = function() {
|
|||||||
|
|
||||||
P2P.prototype._setResourceFilter = function(filter) {
|
P2P.prototype._setResourceFilter = function(filter) {
|
||||||
|
|
||||||
assert(filter && filter.startHash, 'A "startHash" field is required to retrieve headers or blocks');
|
// FLOSight Error Correction from RanchiMall 17th May 2021. removed the unhandled assert and replaced by looging of error
|
||||||
|
if (filter == false || filter.startHash == false) {
|
||||||
|
log.error('A "startHash" field is required to retrieve headers or blocks');
|
||||||
|
}
|
||||||
|
// assert(filter && filter.startHash, 'A "startHash" field is required to retrieve headers or blocks');
|
||||||
|
|
||||||
if (!filter.endHash) {
|
if (!filter.endHash) {
|
||||||
filter.endHash = 0;
|
filter.endHash = 0;
|
||||||
}
|
}
|
||||||
@ -432,29 +439,26 @@ P2P.prototype._setResourceFilter = function(filter) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
P2P.prototype._startBcoin = function(callback) {
|
P2P.prototype._startBcoin = function(callback) {
|
||||||
|
|
||||||
var self = this;
|
|
||||||
|
|
||||||
var network;
|
var network;
|
||||||
var port;
|
var port;
|
||||||
if (['livenet', 'live', 'main', 'mainnet'].indexOf(this.node.network) !== -1) {
|
if (['livenet', 'live', 'main', 'mainnet'].indexOf(this.node.network) !== -1) {
|
||||||
network = 'main';
|
network = 'main';
|
||||||
port = this._configPeers[0].port || 8333;
|
port = this._configPeers[0].port || 7312;
|
||||||
} else if (this.node.network !== 'regtest') {
|
} else if (this.node.network !== 'regtest') {
|
||||||
network = 'testnet';
|
network = 'testnet';
|
||||||
port = this._configPeers[0].port || 18333;
|
port = this._configPeers[0].port || 17312;
|
||||||
} else {
|
} else {
|
||||||
network = this.node.network;
|
network = this.node.network;
|
||||||
port = this._configPeers[0].port || 48444;
|
port = this._configPeers[0].port || 17412;
|
||||||
}
|
}
|
||||||
|
|
||||||
self._bcoin = new Bcoin({
|
this._bcoin = new Bcoin({
|
||||||
network: network,
|
network: network,
|
||||||
prefix: self.node.datadir,
|
prefix: this.node.datadir,
|
||||||
port: port
|
port: port
|
||||||
});
|
});
|
||||||
|
|
||||||
self._bcoin.start(callback);
|
this._bcoin.start(callback);
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -91,7 +91,7 @@ TimestampService.prototype.onBlock = function(block, callback) {
|
|||||||
|
|
||||||
var operations = [];
|
var operations = [];
|
||||||
|
|
||||||
var ts = block.ts;
|
var ts = block.time;
|
||||||
var hash = block.rhash();
|
var hash = block.rhash();
|
||||||
|
|
||||||
if (ts <= this._lastBlockTimestamp) {
|
if (ts <= this._lastBlockTimestamp) {
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var Tx = require('bcoin').tx;
|
var Tx = require('fcoin').TX;
|
||||||
|
|
||||||
function Encoding(servicePrefix) {
|
function Encoding(servicePrefix) {
|
||||||
this.servicePrefix = servicePrefix;
|
this.servicePrefix = servicePrefix;
|
||||||
|
|||||||
@ -7,6 +7,7 @@ var _ = require('lodash');
|
|||||||
var async = require('async');
|
var async = require('async');
|
||||||
var assert = require('assert');
|
var assert = require('assert');
|
||||||
var LRU = require('lru-cache');
|
var LRU = require('lru-cache');
|
||||||
|
var log = require('../../index').log;
|
||||||
|
|
||||||
function TransactionService(options) {
|
function TransactionService(options) {
|
||||||
BaseService.call(this, options);
|
BaseService.call(this, options);
|
||||||
@ -16,7 +17,6 @@ function TransactionService(options) {
|
|||||||
this._header = this.node.services.header;
|
this._header = this.node.services.header;
|
||||||
this._p2p = this.node.services.p2p;
|
this._p2p = this.node.services.p2p;
|
||||||
this._timestamp = this.node.services.timestamp;
|
this._timestamp = this.node.services.timestamp;
|
||||||
this._address = this.node.services.address;
|
|
||||||
this._network = this.node.network;
|
this._network = this.node.network;
|
||||||
|
|
||||||
if (this._network === 'livenet') {
|
if (this._network === 'livenet') {
|
||||||
@ -114,7 +114,7 @@ TransactionService.prototype.getTransaction = function(txid, options, callback)
|
|||||||
|
|
||||||
var self = this;
|
var self = this;
|
||||||
|
|
||||||
if (typeof callback !== 'function') {
|
if (!_.isFunction(callback)) {
|
||||||
callback = options;
|
callback = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -131,7 +131,7 @@ TransactionService.prototype.getTransaction = function(txid, options, callback)
|
|||||||
if (err) {
|
if (err) {
|
||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
if (tx) {
|
if (tx && tx.confirmations >= 6) {
|
||||||
self._cacheTx.set(txid, tx);
|
self._cacheTx.set(txid, tx);
|
||||||
}
|
}
|
||||||
callback(err, tx);
|
callback(err, tx);
|
||||||
@ -185,8 +185,11 @@ TransactionService.prototype.setTxMetaInfo = function(tx, options, callback) {
|
|||||||
|
|
||||||
var inputSatoshis = 0;
|
var inputSatoshis = 0;
|
||||||
|
|
||||||
assert(tx.__inputValues.length === tx.inputs.length,
|
// FLOSight Error Correction from RanchiMall 17th May 2021. removed the unhandled assert and replaced by looging of error
|
||||||
'Transaction Service: input values length is not the same as the number of inputs.');
|
if (tx.__inputValues.length != tx.inputs.length) {
|
||||||
|
log.error('Transaction Service: input values length is not the same as the number of inputs.');
|
||||||
|
}
|
||||||
|
// assert(tx.__inputValues.length === tx.inputs.length, 'Transaction Service: input values length is not the same as the number of inputs.');
|
||||||
|
|
||||||
tx.__inputValues.forEach(function(val) {
|
tx.__inputValues.forEach(function(val) {
|
||||||
|
|
||||||
@ -309,13 +312,18 @@ TransactionService.prototype._getInputValues = function(tx, options, callback) {
|
|||||||
// if not in mempool or tx index, we just don't have it, yet?
|
// if not in mempool or tx index, we just don't have it, yet?
|
||||||
function(txid, tx, next) {
|
function(txid, tx, next) {
|
||||||
if (!tx) {
|
if (!tx) {
|
||||||
return next(new Error('Transaction Service: prev transacion: (' + input.prevout.txid() + ') for tx: ' +
|
return next(log.error('Transaction Service: prev transacion: (' + input.prevout.txid() + ') for tx: ' +
|
||||||
_tx.txid() + ' at input index: ' + outputIndex + ' is missing from the index or not in the memory pool. It could be' +
|
_tx.txid() + ' at input index: ' + outputIndex + ' is missing from the index or not in the memory pool. It could be' +
|
||||||
' that the parent tx has not yet been relayed to us, but will be relayed in the near future.'));
|
' that the parent tx has not yet been relayed to us, but will be relayed in the near future.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
var output = tx.outputs[outputIndex];
|
var output = tx.outputs[outputIndex];
|
||||||
|
|
||||||
assert(output, 'Expected an output, but did not get one for tx: ' + tx.txid() + ' outputIndex: ' + outputIndex);
|
// FLOSight Error Correction from RanchiMall 17th May 2021. removed the unhandled assert and replaced by looging of error
|
||||||
|
if (output == false) {
|
||||||
|
log.error('Expected an output, but did not get one for tx: ' + tx.txid() + ' outputIndex: ' + outputIndex);
|
||||||
|
}
|
||||||
|
// assert(output, 'Expected an output, but did not get one for tx: ' + tx.txid() + ' outputIndex: ' + outputIndex);
|
||||||
|
|
||||||
next(null, output.value);
|
next(null, output.value);
|
||||||
}
|
}
|
||||||
@ -375,7 +383,11 @@ TransactionService.prototype.onBlock = function(block, callback) {
|
|||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(block.txs.length === operations.length, 'It seems we are not indexing the correct number of transactions.');
|
// FLOSight Error Correction from RanchiMall 17th May 2021. removed the unhandled assert and replaced by looging of error
|
||||||
|
if (block.txs.length != operations.length) {
|
||||||
|
log.error('It seems we are not indexing the correct number of transactions.');
|
||||||
|
}
|
||||||
|
// assert(block.txs.length === operations.length, 'It seems we are not indexing the correct number of transactions.');
|
||||||
|
|
||||||
callback(null, _.flattenDeep(operations));
|
callback(null, _.flattenDeep(operations));
|
||||||
});
|
});
|
||||||
@ -477,19 +489,30 @@ TransactionService.prototype._processTransaction = function(tx, opts, callback)
|
|||||||
return callback(err);
|
return callback(err);
|
||||||
}
|
}
|
||||||
|
|
||||||
assert(inputValues && inputValues.length === tx.inputs.length,
|
// FLOSight Error Correction from RanchiMall 17th May 2021. removed the unhandled assert and replaced by looging of error
|
||||||
'Input values missing from tx.');
|
if (inputValues == false || inputValues.length != tx.inputs.length) {
|
||||||
|
log.error('Input values missing from tx.');
|
||||||
|
}
|
||||||
|
// assert(inputValues && inputValues.length === tx.inputs.length, 'Input values missing from tx.');
|
||||||
|
|
||||||
// inputValues
|
// inputValues
|
||||||
tx.__inputValues = inputValues;
|
tx.__inputValues = inputValues;
|
||||||
|
|
||||||
// timestamp
|
// timestamp
|
||||||
tx.__timestamp = self._getBlockTimestamp(opts.block.rhash());
|
tx.__timestamp = self._getBlockTimestamp(opts.block.rhash());
|
||||||
assert(tx.__timestamp, 'Timestamp is required when saving a transaction.');
|
// FLOSight Error Correction from RanchiMall 17th May 2021. removed the unhandled assert and replaced by looging of error
|
||||||
|
if (tx.__timestamp == false) {
|
||||||
|
log.error('Timestamp is required when saving a transaction.');
|
||||||
|
}
|
||||||
|
// assert(tx.__timestamp, 'Timestamp is required when saving a transaction.');
|
||||||
|
|
||||||
// height
|
// height
|
||||||
tx.__height = opts.block.__height;
|
tx.__height = opts.block.__height;
|
||||||
assert(tx.__height, 'Block height is required when saving a trasnaction.');
|
// FLOSight Error Correction from RanchiMall 17th May 2021. removed the unhandled assert and replaced by looging of error
|
||||||
|
if (tx.__height == false) {
|
||||||
|
log.error('Block height is required when saving a trasnaction.');
|
||||||
|
}
|
||||||
|
//assert(tx.__height, 'Block height is required when saving a trasnaction.');
|
||||||
|
|
||||||
// block hash
|
// block hash
|
||||||
tx.__blockhash = opts.block.rhash();
|
tx.__blockhash = opts.block.rhash();
|
||||||
|
|||||||
@ -4,13 +4,14 @@ var fs = require('fs');
|
|||||||
var http = require('http');
|
var http = require('http');
|
||||||
var https = require('https');
|
var https = require('https');
|
||||||
var express = require('express');
|
var express = require('express');
|
||||||
|
var express_ws = require('express-ws');
|
||||||
var bodyParser = require('body-parser');
|
var bodyParser = require('body-parser');
|
||||||
var socketio = require('socket.io');
|
var socketio = require('socket.io');
|
||||||
var inherits = require('util').inherits;
|
var inherits = require('util').inherits;
|
||||||
|
|
||||||
var BaseService = require('../../service');
|
var BaseService = require('../../service');
|
||||||
var bitcore = require('bitcore-lib');
|
var flocore = require('flocore-lib');
|
||||||
var _ = bitcore.deps._;
|
var _ = flocore.deps._;
|
||||||
var index = require('../../');
|
var index = require('../../');
|
||||||
var log = index.log;
|
var log = index.log;
|
||||||
|
|
||||||
@ -105,7 +106,7 @@ WebService.prototype.setupAllRoutes = function() {
|
|||||||
|
|
||||||
if(service.getRoutePrefix && service.setupRoutes) {
|
if(service.getRoutePrefix && service.setupRoutes) {
|
||||||
this.app.use('/' + this.node.services[key].getRoutePrefix(), subApp);
|
this.app.use('/' + this.node.services[key].getRoutePrefix(), subApp);
|
||||||
this.node.services[key].setupRoutes(subApp, express);
|
this.node.services[key].setupRoutes(subApp, express, express_ws);
|
||||||
} else {
|
} else {
|
||||||
log.debug('No routes defined for: ' + key);
|
log.debug('No routes defined for: ' + key);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -175,7 +175,7 @@ utils.getAddress = function(item, network) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
address.network = network;
|
address.network = network;
|
||||||
return address.toString();
|
return address.toString(network);
|
||||||
};
|
};
|
||||||
|
|
||||||
module.exports = utils;
|
module.exports = utils;
|
||||||
|
|||||||
6033
package-lock.json
generated
6033
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
45
package.json
45
package.json
@ -1,54 +1,59 @@
|
|||||||
{
|
{
|
||||||
"name": "bitcore-node",
|
"name": "flocore-node",
|
||||||
"description": "Full node with extended capabilities using Bitcore and Bitcoin Core",
|
"description": "Full node with extended capabilities using Flocore and Florincoin Core",
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": ">=8.0.0"
|
"node": ">=8.0.0"
|
||||||
},
|
},
|
||||||
"author": "BitPay <dev@bitpay.com>",
|
"author": "BitPay <dev@bitpay.com>",
|
||||||
"version": "5.0.0-beta.40",
|
"version": "5.0.9-beta-rm",
|
||||||
"main": "./index.js",
|
"main": "./index.js",
|
||||||
"repository": "git://github.com/bitpay/bitcore-node.git",
|
"repository": "git://github.com/ranchimall/flocore-node.git",
|
||||||
"homepage": "https://github.com/bitpay/bitcore-node",
|
"homepage": "https://github.com/ranchimall/flocore-node",
|
||||||
"bugs": {
|
"bugs": {
|
||||||
"url": "https://github.com/bitpay/bitcore-node/issues"
|
"url": "https://github.com/ranchimall/flocore-node/issues"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"bitcore-node": "./bin/bitcore-node"
|
"flocore-node": "./bin/flocore-node"
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "NODE_ENV=test mocha -R spec --recursive test"
|
"test": "NODE_ENV=test mocha -R spec --recursive test"
|
||||||
},
|
},
|
||||||
"tags": [
|
"tags": [
|
||||||
"bitcoin",
|
"florincoin",
|
||||||
"bitcoind",
|
"florincoind",
|
||||||
"bcoin",
|
"bcoin",
|
||||||
"bitcoin full node",
|
"florincoin full node",
|
||||||
"bitcoin index",
|
"florincoin index",
|
||||||
"block explorer",
|
"block explorer",
|
||||||
"wallet backend"
|
"wallet backend"
|
||||||
],
|
],
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"async": "^2.5.0",
|
"async": "^2.5.0",
|
||||||
"bcoin": "bitpay/bcoin#v1.0.0-beta.14+cash",
|
"bitcoind-rpc": "^0.7.2",
|
||||||
"bitcoind-rpc": "^0.6.0",
|
|
||||||
"bitcore-lib": "5.0.0-beta.1",
|
|
||||||
"bitcore-p2p": "5.0.0-beta.1",
|
|
||||||
"bn.js": "^4.11.8",
|
"bn.js": "^4.11.8",
|
||||||
"body-parser": "^1.13.3",
|
"body-parser": "^1.13.3",
|
||||||
"colors": "^1.1.2",
|
"colors": "^1.1.2",
|
||||||
"commander": "^2.8.1",
|
"commander": "^2.8.1",
|
||||||
"errno": "^0.1.4",
|
"errno": "^0.1.4",
|
||||||
"express": "^4.13.3",
|
"express": "^4.13.3",
|
||||||
|
"express-ws": "^5.0.2",
|
||||||
|
"fcoin": "^1.1.4",
|
||||||
|
"flocore-lib": "^0.15.2",
|
||||||
|
"flocore-message": "^1.0.7",
|
||||||
|
"flocore-p2p": "^5.0.0-beta.8",
|
||||||
|
"florincoind-rpc": "0.7.1",
|
||||||
|
"flosight-api": "github:ranchimall/flosight-api",
|
||||||
|
"flosight-ui": "github:ranchimall/flosight-ui",
|
||||||
"leveldown": "^2.0.0",
|
"leveldown": "^2.0.0",
|
||||||
"levelup": "^2.0.0",
|
"levelup": "^2.0.0",
|
||||||
"liftoff": "^2.2.0",
|
"liftoff": "^2.2.0",
|
||||||
"lodash": "^4.17.4",
|
"lodash": "^4.17.12",
|
||||||
"lru-cache": "^4.0.2",
|
"lru-cache": "^4.1.1",
|
||||||
"memwatch-next": "^0.3.0",
|
|
||||||
"mkdirp": "0.5.0",
|
"mkdirp": "0.5.0",
|
||||||
"path-is-absolute": "^1.0.0",
|
"path-is-absolute": "^1.0.0",
|
||||||
"socket.io": "^1.4.5",
|
"socket.io": "^1.4.5",
|
||||||
"socket.io-client": "^1.4.5"
|
"socket.io-client": "^1.4.5",
|
||||||
|
"xxhash": "^0.2.4"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"chai": "^3.5.0",
|
"chai": "^3.5.0",
|
||||||
@ -56,7 +61,7 @@
|
|||||||
"istanbul": "^0.4.3",
|
"istanbul": "^0.4.3",
|
||||||
"jshint": "^2.9.2",
|
"jshint": "^2.9.2",
|
||||||
"jshint-stylish": "^2.1.0",
|
"jshint-stylish": "^2.1.0",
|
||||||
"mocha": "3.2.0",
|
"mocha": "^6.2.0",
|
||||||
"proxyquire": "^1.3.1",
|
"proxyquire": "^1.3.1",
|
||||||
"rimraf": "^2.4.2",
|
"rimraf": "^2.4.2",
|
||||||
"sinon": "^1.15.4"
|
"sinon": "^1.15.4"
|
||||||
|
|||||||
@ -3,10 +3,10 @@
|
|||||||
var should = require('chai').should();
|
var should = require('chai').should();
|
||||||
|
|
||||||
describe('Index Exports', function() {
|
describe('Index Exports', function() {
|
||||||
it('will export bitcore-lib', function() {
|
it('will export flocore-lib', function() {
|
||||||
var bitcore = require('../');
|
var flocore = require('../');
|
||||||
should.exist(bitcore.lib);
|
should.exist(flocore.lib);
|
||||||
should.exist(bitcore.lib.Transaction);
|
should.exist(flocore.lib.Transaction);
|
||||||
should.exist(bitcore.lib.Block);
|
should.exist(flocore.lib.Block);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@ -25,7 +25,7 @@ describe('#add', function() {
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
fs.writeFile(
|
fs.writeFile(
|
||||||
testDir + '/s0/s1/bitcore-node.json',
|
testDir + '/s0/s1/flocore-node.json',
|
||||||
JSON.stringify(startConfig),
|
JSON.stringify(startConfig),
|
||||||
function(err) {
|
function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
@ -90,12 +90,12 @@ describe('#add', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('will update bitcore-node.json services', function(done) {
|
it('will update flocore-node.json services', function(done) {
|
||||||
var callCount = 0;
|
var callCount = 0;
|
||||||
var oldPackage = {
|
var oldPackage = {
|
||||||
dependencies: {
|
dependencies: {
|
||||||
'bitcore-lib': '^v0.13.7',
|
'flocore-lib': '^v0.13.7',
|
||||||
'bitcore-node': '^v0.2.0'
|
'flocore-node': '^v0.2.0'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
var spawn = sinon.stub().returns({
|
var spawn = sinon.stub().returns({
|
||||||
@ -130,7 +130,7 @@ describe('#add', function() {
|
|||||||
services: ['a', 'b', 'c']
|
services: ['a', 'b', 'c']
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
var configPath = path.resolve(testDir, 's0/s1/bitcore-node.json');
|
var configPath = path.resolve(testDir, 's0/s1/flocore-node.json');
|
||||||
var config = JSON.parse(fs.readFileSync(configPath));
|
var config = JSON.parse(fs.readFileSync(configPath));
|
||||||
config.services.should.deep.equal(['a','b','c']);
|
config.services.should.deep.equal(['a','b','c']);
|
||||||
done();
|
done();
|
||||||
|
|||||||
@ -33,7 +33,7 @@ describe('#create', function() {
|
|||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
mkdirp(testDir + '/.bitcoin', function(err) {
|
mkdirp(testDir + '/.florincoin', function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
@ -64,14 +64,14 @@ describe('#create', function() {
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
|
|
||||||
var configPath = testDir + '/mynode/bitcore-node.json';
|
var configPath = testDir + '/mynode/flocore-node.json';
|
||||||
var packagePath = testDir + '/mynode/package.json';
|
var packagePath = testDir + '/mynode/package.json';
|
||||||
|
|
||||||
should.equal(fs.existsSync(configPath), true);
|
should.equal(fs.existsSync(configPath), true);
|
||||||
should.equal(fs.existsSync(packagePath), true);
|
should.equal(fs.existsSync(packagePath), true);
|
||||||
|
|
||||||
var config = JSON.parse(fs.readFileSync(configPath));
|
var config = JSON.parse(fs.readFileSync(configPath));
|
||||||
config.services.should.deep.equal(['bitcoind', 'db', 'address', 'web']);
|
config.services.should.deep.equal(['florincoind', 'db', 'address', 'web']);
|
||||||
config.datadir.should.equal('./data');
|
config.datadir.should.equal('./data');
|
||||||
config.network.should.equal('livenet');
|
config.network.should.equal('livenet');
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ describe('#create', function() {
|
|||||||
dirname: 'mynode3',
|
dirname: 'mynode3',
|
||||||
name: 'My Node 3',
|
name: 'My Node 3',
|
||||||
isGlobal: true,
|
isGlobal: true,
|
||||||
datadir: '../.bitcoin'
|
datadir: '../.florincoin'
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
throw err;
|
throw err;
|
||||||
@ -139,7 +139,7 @@ describe('#create', function() {
|
|||||||
dirname: 'mynode4',
|
dirname: 'mynode4',
|
||||||
name: 'My Node 4',
|
name: 'My Node 4',
|
||||||
isGlobal: false,
|
isGlobal: false,
|
||||||
datadir: '../.bitcoin'
|
datadir: '../.florincoin'
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
should.exist(err);
|
should.exist(err);
|
||||||
err.message.should.equal('There was an error installing dependencies.');
|
err.message.should.equal('There was an error installing dependencies.');
|
||||||
|
|||||||
@ -25,7 +25,7 @@ describe('#remove', function() {
|
|||||||
throw err;
|
throw err;
|
||||||
}
|
}
|
||||||
fs.writeFile(
|
fs.writeFile(
|
||||||
testDir + '/s0/s1/bitcore-node.json',
|
testDir + '/s0/s1/flocore-node.json',
|
||||||
JSON.stringify(startConfig),
|
JSON.stringify(startConfig),
|
||||||
function(err) {
|
function(err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
@ -64,7 +64,7 @@ describe('#remove', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('will update bitcore-node.json services', function(done) {
|
it('will update flocore-node.json services', function(done) {
|
||||||
var spawn = sinon.stub().returns({
|
var spawn = sinon.stub().returns({
|
||||||
stdout: {
|
stdout: {
|
||||||
on: sinon.stub()
|
on: sinon.stub()
|
||||||
@ -92,7 +92,7 @@ describe('#remove', function() {
|
|||||||
services: ['b']
|
services: ['b']
|
||||||
}, function(err) {
|
}, function(err) {
|
||||||
should.not.exist(err);
|
should.not.exist(err);
|
||||||
var configPath = path.resolve(testDir, 's0/s1/bitcore-node.json');
|
var configPath = path.resolve(testDir, 's0/s1/flocore-node.json');
|
||||||
var config = JSON.parse(fs.readFileSync(configPath));
|
var config = JSON.parse(fs.readFileSync(configPath));
|
||||||
config.services.should.deep.equal(['a', 'c']);
|
config.services.should.deep.equal(['a', 'c']);
|
||||||
done();
|
done();
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
'use strict';
|
'use strict';
|
||||||
|
|
||||||
var bitcore = require('bitcore-lib');
|
var flocore = require('flocore-lib');
|
||||||
var should = require('chai').should();
|
var should = require('chai').should();
|
||||||
var Encoding = require('../../../lib/services/address/encoding');
|
var Encoding = require('../../../lib/services/address/encoding');
|
||||||
|
|
||||||
@ -38,7 +38,7 @@ describe('Address service encoding', function() {
|
|||||||
new Buffer(txid, 'hex'),
|
new Buffer(txid, 'hex'),
|
||||||
new Buffer('00000005', 'hex')]);
|
new Buffer('00000005', 'hex')]);
|
||||||
var txHex = '0100000001cc3ffe0638792c8b39328bb490caaefe2cf418f2ce0144956e0c22515f29724d010000006a473044022030ce9fa68d1a32abf0cd4adecf90fb998375b64fe887c6987278452b068ae74c022036a7d00d1c8af19e298e04f14294c807ebda51a20389ad751b4ff3c032cf8990012103acfcb348abb526526a9f63214639d79183871311c05b2eebc727adfdd016514fffffffff02f6ae7d04000000001976a9144455183e407ee4d3423858c8a3275918aedcd18e88aca99b9b08010000001976a9140beceae2c29bfde08d2b6d80b33067451c5887be88ac00000000';
|
var txHex = '0100000001cc3ffe0638792c8b39328bb490caaefe2cf418f2ce0144956e0c22515f29724d010000006a473044022030ce9fa68d1a32abf0cd4adecf90fb998375b64fe887c6987278452b068ae74c022036a7d00d1c8af19e298e04f14294c807ebda51a20389ad751b4ff3c032cf8990012103acfcb348abb526526a9f63214639d79183871311c05b2eebc727adfdd016514fffffffff02f6ae7d04000000001976a9144455183e407ee4d3423858c8a3275918aedcd18e88aca99b9b08010000001976a9140beceae2c29bfde08d2b6d80b33067451c5887be88ac00000000';
|
||||||
var tx = new bitcore.Transaction(txHex);
|
var tx = new flocore.Transaction(txHex);
|
||||||
var sats = tx.outputs[0].satoshis;
|
var sats = tx.outputs[0].satoshis;
|
||||||
var satsBuf = new Buffer(8);
|
var satsBuf = new Buffer(8);
|
||||||
satsBuf.writeDoubleBE(sats);
|
satsBuf.writeDoubleBE(sats);
|
||||||
|
|||||||
@ -8,6 +8,7 @@ var Encoding = require('../../../lib/services/address/encoding');
|
|||||||
var Readable = require('stream').Readable;
|
var Readable = require('stream').Readable;
|
||||||
var EventEmitter = require('events').EventEmitter;
|
var EventEmitter = require('events').EventEmitter;
|
||||||
var bcoin = require('bcoin');
|
var bcoin = require('bcoin');
|
||||||
|
var lodash = require('lodash');
|
||||||
|
|
||||||
describe('Address Service', function() {
|
describe('Address Service', function() {
|
||||||
|
|
||||||
@ -54,7 +55,7 @@ describe('Address Service', function() {
|
|||||||
|
|
||||||
describe('#getAddressHistory', function() {
|
describe('#getAddressHistory', function() {
|
||||||
|
|
||||||
it('should get the address history', function(done) {
|
it('should get the address history (null case)', function(done) {
|
||||||
|
|
||||||
sandbox.stub(addressService, '_getAddressTxidHistory').callsArgWith(2, null, null);
|
sandbox.stub(addressService, '_getAddressTxidHistory').callsArgWith(2, null, null);
|
||||||
sandbox.stub(addressService, '_getAddressTxHistory').callsArgWith(1, null, []);
|
sandbox.stub(addressService, '_getAddressTxHistory').callsArgWith(1, null, []);
|
||||||
@ -74,6 +75,222 @@ describe('Address Service', function() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should get the sorted address history', function(done) {
|
||||||
|
|
||||||
|
var old_getAddressTxidHistory = addressService._getAddressTxidHistory;
|
||||||
|
addressService._getAddressTxidHistory = function(addr, options, cb) {
|
||||||
|
options.txIdList = [
|
||||||
|
{
|
||||||
|
txid: "d",
|
||||||
|
height: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
txid: "c",
|
||||||
|
height: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
txid: "a",
|
||||||
|
height: 101,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
txid: "b",
|
||||||
|
height: 100,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return cb();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var old_getAddressTxHistory = addressService._getAddressTxHistory;
|
||||||
|
addressService._getAddressTxHistory = function(options, cb) {
|
||||||
|
return cb(null, options.txIdList);
|
||||||
|
};
|
||||||
|
|
||||||
|
addressService.getAddressHistory(['a', 'b', 'c'], { from: 12, to: 14 }, function(err, res) {
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(res.totalCount).equal(4);
|
||||||
|
expect(lodash.map(res.items,'txid')).to.be.deep.equal(['a','b','c','d']);
|
||||||
|
|
||||||
|
addressService._getAddressTxidHistory = old_getAddressTxHistory;
|
||||||
|
addressService._getAddressTxHistory = old_getAddressTxHistory;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should remove duplicated items in history', function(done) {
|
||||||
|
|
||||||
|
var old_getAddressTxidHistory = addressService._getAddressTxidHistory;
|
||||||
|
addressService._getAddressTxidHistory = function(addr, options, cb) {
|
||||||
|
options.txIdList = [
|
||||||
|
{
|
||||||
|
txid: "b",
|
||||||
|
height: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
txid: "b",
|
||||||
|
height: 10,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
txid: "d",
|
||||||
|
height: 101,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
txid: "c",
|
||||||
|
height: 100,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
txid: "d",
|
||||||
|
height: 101,
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return cb();
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
var old_getAddressTxHistory = addressService._getAddressTxHistory;
|
||||||
|
addressService._getAddressTxHistory = function(options, cb) {
|
||||||
|
return cb(null, options.txIdList);
|
||||||
|
};
|
||||||
|
|
||||||
|
addressService.getAddressHistory(['a', 'b', 'c'], { from: 12, to: 14 }, function(err, res) {
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
|
||||||
|
expect(res.totalCount).equal(3);
|
||||||
|
expect(lodash.map(res.items,'txid')).to.be.deep.equal(['d','c','b']);
|
||||||
|
|
||||||
|
addressService._getAddressTxidHistory = old_getAddressTxHistory;
|
||||||
|
addressService._getAddressTxHistory = old_getAddressTxHistory;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe('TxIdList cache', function() {
|
||||||
|
var list, old_getAddressTxidHistory, old_getAddressTxHistory;
|
||||||
|
|
||||||
|
beforeEach(function(done){
|
||||||
|
this.clock = sinon.useFakeTimers();
|
||||||
|
list = [];
|
||||||
|
for(let i=1000; i>0; i--) {
|
||||||
|
list.push({
|
||||||
|
txid: "txid" + i,
|
||||||
|
height: 1000 + i,
|
||||||
|
|
||||||
|
});
|
||||||
|
};
|
||||||
|
old_getAddressTxidHistory = addressService._getAddressTxidHistory;
|
||||||
|
// Note that this stub DOES NOT respect options.from/to as the real function
|
||||||
|
addressService._getAddressTxidHistory = function(addr, options, cb) {
|
||||||
|
options.txIdList = lodash.clone(list);
|
||||||
|
return cb();
|
||||||
|
};
|
||||||
|
old_getAddressTxHistory = addressService._getAddressTxHistory;
|
||||||
|
|
||||||
|
addressService._getAddressTxHistory = function(options, cb) {
|
||||||
|
return cb(null, options.txIdList);
|
||||||
|
};
|
||||||
|
|
||||||
|
addressService.getAddressHistory(['a', 'b', 'c'], { from: 0, to: 10 }, function(err, res, cacheUsed) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
expect(res.totalCount).equal(1000);
|
||||||
|
expect(res.items,'txid').to.be.deep.equal(list);
|
||||||
|
expect(cacheUsed).equal(false);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach(function(done){
|
||||||
|
this.clock.restore();
|
||||||
|
|
||||||
|
addressService._getAddressTxidHistory = old_getAddressTxHistory;
|
||||||
|
addressService._getAddressTxHistory = old_getAddressTxHistory;
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should not cache the address txlist history when from =0 ', function(done) {
|
||||||
|
addressService.getAddressHistory(['a', 'b', 'c'], { from: 0, to: 10 }, function(err, res, cacheUsed) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
expect(res.totalCount).equal(1000);
|
||||||
|
expect(res.items,'txid').to.be.deep.equal(list);
|
||||||
|
expect(cacheUsed).equal(false);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cache the address txlist history', function(done) {
|
||||||
|
addressService.getAddressHistory(['a', 'b', 'c'], { from: 1, to: 10 }, function(err, res, cacheUsed) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
expect(cacheUsed).equal(true);
|
||||||
|
expect(res.totalCount).equal(1000);
|
||||||
|
expect(res.items,'txid').to.be.deep.equal(list);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
it('should retrive cached list using cachekey', function(done) {
|
||||||
|
addressService.getAddressHistory([], { from: 1, to: 10, cacheKey: 977282097 }, function(err, res, cacheUsed) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
expect(cacheUsed).equal(true);
|
||||||
|
expect(res.totalCount).equal(1000);
|
||||||
|
expect(res.items,'txid').to.be.deep.equal(list);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
it('should expire cache', function(done) {
|
||||||
|
this.clock.tick(35*1000);
|
||||||
|
addressService.getAddressHistory(['a', 'b', 'c'], { from: 1, to: 10 }, function(err, res, cacheUsed) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
expect(cacheUsed).equal(false);
|
||||||
|
expect(res.totalCount).equal(1000);
|
||||||
|
expect(res.items,'txid').to.be.deep.equal(list);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cache using the address as key', function(done) {
|
||||||
|
addressService.getAddressHistory(['a', 'b', 'c', 'd'], { from: 1, to: 10 }, function(err, res, cacheUsed) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
expect(cacheUsed).equal(false);
|
||||||
|
expect(res.totalCount).equal(1000);
|
||||||
|
expect(res.items,'txid').to.be.deep.equal(list);
|
||||||
|
addressService.getAddressHistory(['a', 'b', 'c', 'd'], { from: 1, to: 10 }, function(err, res, cacheUsed) {
|
||||||
|
if (err) {
|
||||||
|
return done(err);
|
||||||
|
}
|
||||||
|
expect(cacheUsed).equal(true);
|
||||||
|
expect(res.totalCount).equal(1000);
|
||||||
|
expect(res.items,'txid').to.be.deep.equal(list);
|
||||||
|
done();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#_getAddressTxidHistory', function() {
|
describe('#_getAddressTxidHistory', function() {
|
||||||
|
|||||||
@ -77,7 +77,7 @@ describe('DB', function() {
|
|||||||
|
|
||||||
it('should set the data path', function() {
|
it('should set the data path', function() {
|
||||||
dbService._setDataPath();
|
dbService._setDataPath();
|
||||||
dbService.dataPath.should.equal('/tmp/regtest/bitcorenode.db');
|
dbService.dataPath.should.equal('/tmp/regtest/flocorenode.db');
|
||||||
});
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -13,7 +13,7 @@ describe('#Fee Service', function() {
|
|||||||
sandbox = sinon.sandbox.create();
|
sandbox = sinon.sandbox.create();
|
||||||
feeService = new FeeService({
|
feeService = new FeeService({
|
||||||
rpc: {
|
rpc: {
|
||||||
user: 'bitcoin',
|
user: 'florincoin',
|
||||||
pass: 'local321',
|
pass: 'local321',
|
||||||
host: 'localhost',
|
host: 'localhost',
|
||||||
protocol: 'http',
|
protocol: 'http',
|
||||||
|
|||||||
@ -7,7 +7,7 @@ var assert = chai.assert;
|
|||||||
var expect = chai.expect;
|
var expect = chai.expect;
|
||||||
var Encoding = require('../../../lib/services/header/encoding');
|
var Encoding = require('../../../lib/services/header/encoding');
|
||||||
var utils = require('../../../lib/utils');
|
var utils = require('../../../lib/utils');
|
||||||
var Block = require('bitcore-lib').Block;
|
var Block = require('flocore-lib').Block;
|
||||||
var BN = require('bn.js');
|
var BN = require('bn.js');
|
||||||
var Emitter = require('events').EventEmitter;
|
var Emitter = require('events').EventEmitter;
|
||||||
var bcoin = require('bcoin');
|
var bcoin = require('bcoin');
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user