Compare commits

...

343 Commits

Author SHA1 Message Date
Dave Collins
40cdacde23 Prepare for release 0.7.0. 2014-02-20 15:51:40 -06:00
Dave Collins
264c89099f Make non-canoncial data pushes non-standard.
This commit makes use of the new btcscript.HasCanonicalPushes to enforce
canonical data pushes for transactions that are considered standard.

A canonical data push is one where the fewest number of bytes possible to
encode the size of the data being pushed is used.  This includes using the
small integer opcodes for single byte data that can be represented
directly.
2014-02-20 01:25:29 -06:00
Owain G. Ainsworth
cb1f3cf48c Rate limit free-to-relay transactions.
Closes #40

ok @davecgh
2014-02-19 22:39:02 +00:00
Dave Collins
28929ff429 Increase block prio size and max standard tx size.
This commit increases the block priority size to 50000 and the max
standard tx size to 100k.  This matches relatively recent changes in the
reference implementation.  The max block size was also increased to
750000, but since btcd does not currently create blocks, there is no
constant for it.  That constant will likely be added as a part of the
getwork implementation since it requires block creation.

Closes #71.
2014-02-19 12:28:13 -06:00
Dave Collins
8ac86f1053 Allow notifications to work for all address types.
Previously the websocket notifications for addresses were limited to
pay-to-pubkey-hash only.  This commit removes that restriction so
all btcutil.Address types are supported.  This includes pay-to-pubkey,
pay-to-pubkey-hash, and pay-to-script-hash.
2014-02-19 09:47:17 -06:00
Dave Collins
7162a11995 Remove tracking requests from websocket client too.
When a spent notification and address notification is removed, the
tracking entry in the client which is used to track what to remove on
shutdown needs to be removed as well.
2014-02-19 09:14:11 -06:00
Dave Collins
7d35bc9460 Add --rpcmaxwebsockets option with default of 25.
This commit adds a new configuration option, --rpcmaxwebsockets, to limit the
number of max RPC websocket clients that are served concurrently.
2014-02-19 00:53:14 -06:00
Dave Collins
54203d7db0 Rework and improve websocket notification system.
This commit refactors the entire websocket client code to resolve several
issues with the previous implementation.  Note that this commit does not
change the public API for websockets.  It only consists of internal
improvements.

The following is the major issues which have been addressed:
- A slow websocket client could impede notifications to all clients
- Long-running operations such as rescans would block all other requests
  until it had completed
- The above two points taken together could lead to apparant hangs since
  the client doing the rescan would eventually run out of channel buffer
  and block the entire group of clients until the rescan completed
- Disconnecting a websocket during certain operations could lead to a hang
- Stopping the rpc server with operations under way could lead to a hang
- There were no limits to the number of websocket clients that could
  connect

The following is a summary of the major changes:

- The websocket code has been split into two entities: a
  connection/notification manager and a websocket client
- The new connection/notification manager acts as the entry point from
  the rest of the subsystems to feed data which potentially needs to
  notify clients
- Each websocket client now has its own instance of the new websocket
  client type which controls its own lifecycle
- The data flow has been completely redesigned to closely resemble the
  peer data flow
- Each websocket now has its own long-lived goroutines for input, output,
  and queuing of notifications
- Notifications use the new notification queue goroutine along with
  queueing to ensure they dont't block on stalled or slow peers
- There is a new infrastructure for asynchronously executing long-running
  commands such as a rescan while still allowing the faster operations to
  continue to be serviced by the same client
- Since long-running operations now run asynchronously, they have been
  limited to one at a time
- Added a limit of 10 websocket clients.  This is hard coded for now, but
  will be made configurable in the future

Taken together these changes make the code far easier to reason about and
update as well solve the aforementioned issues.

Further optimizations to improve performance are possible in regards to
the way the connection/notification manager works, however this commit
already contains a ton of changes, so they are being left for another
time.
2014-02-19 00:53:05 -06:00
Dave Collins
97e0149dc3 Include IP address in RPC auth failure log message. 2014-02-19 00:30:18 -06:00
Dave Collins
9a15453806 Make room for longer options in doc.go.
Also, fix a few missing equals signs while here.
2014-02-18 21:00:06 -06:00
Dave Collins
a293212581 Add --rpcmaxclients option with default of 10.
This commit adds a new configuration option, --rpcmaxclients, to limit the
number of max standard RPC clients that are served concurrently.  Note
that this value does not apply to websocket connections.  A future commit
will add support for limiting those separately.

Closes #68.
2014-02-18 20:46:41 -06:00
Dave Collins
66e93f5163 Switch over to new btcjson.GetRawMempoolResult.
Rather than using a type specifically in btcd for the getrawmempool, this
commit, along with a recent commit to btcjson, changes the code over to
use the type from btcjson.  This is more consistent with other RPC results
and provides a few extra benefits such as the ability for btcjson to
automatically unmarshal the results into a concrete type with proper field
types as opposed to a generic interface.
2014-02-16 14:00:40 -06:00
Dave Collins
41da7ae606 Switch over to new btcjson.GetPeerInfoResult.
Rather than using a type specifically in btcd for the getpeerinfo, this
commit, along with a recent commit to btcjson, changes the code over to
use the type from btcjson.  This is more consistent with other RPC results
and provides a few extra benefits such as the ability for btcjson to
automatically unmarshal the results into a concrete type with proper field
types as opposed to a generic interface.
2014-02-16 13:45:48 -06:00
Dave Collins
8ebbee1f05 Always include syncnode field in getpeerinfo RPC.
Recent commits to the reference implementation have changed the syncnode
field to be present in the getpeerinfo RPC even when it is false.  This
commit changes btcd to match.
2014-02-16 12:41:35 -06:00
Dave Collins
3a195b9100 Remove duplicate PEER prefix from peer logging.
These were left over from the switch to the logging subsystems which
include the prefix as a part of the subsystem.
2014-02-14 12:45:33 -06:00
Francis Lam
9306270a84 Fixed wsContext locking problems in NewBlockNotifyCheckTxIn
The wsContext was being locked twice when NewBlockNotifyCheckTxIn is
called.  Fixed by changing handlers to assume lock is acquired and
renamed methods to not be exported.
2014-02-13 16:13:58 -05:00
David Hill
12242ee589 Add a requirements section to the README 2014-02-13 12:39:17 -05:00
Dave Collins
e5a1c6e5ac Use mutexes for byte counts to fix i386/arm panic.
This commit changes the server byte counters over to use a mutex instead
of the atomic package.  The atomic.AddUint64 function requires the struct
fields to be 64-bit aligned on 32-bit platforms.  The byte counts are
fields in the server struct and are not 64-bit aligned.  While it would be
possible to arrange the fields to be aligned through various means, it
would make the code too fragile for my tastes.  I prefer code that doesn't
depend on platform specific alignment.

Fixes #96.
2014-02-13 09:58:19 -06:00
Dave Collins
f9922c7305 Add --logdir option to specify logging directory.
This commit adds a new option, --logdir, which works in the same fashion
as the --datadir option.  Consequently, the logging directory is name
"namespaced" by the network as well.  This resolves the issue where two
btcd instances running (one for mainnet and one for testnet) would
overwrite each other's log files by default.

It also provides the user with a method to change the logging location to
non-default locations if they prefer.  For example, it enables multiple
btcd instances on the same network to specify unique logging directories
(even though running multiple btcd instances on the same network is not
the most sane configuration).

Closes #95.
2014-02-12 15:56:05 -06:00
Owain G. Ainsworth
e20a3e9f2c Copy netaddresses in AddressCache instead of referencing them.
While harmless, this prevents the race detector complaining when these
are then used. Closes #94
2014-02-12 15:33:52 +00:00
Owain G. Ainsworth
33082445c5 Add signmessage support to btcctl 2014-02-12 15:30:05 +00:00
Francis Lam
4431fd9c0d Added checking for closed connections in websocket handler helpers
Since the websocket handlers run in their own separate goroutines, it's
possible that they execute after the websocket connection has been
closed and cleaned up.  This commit add the necessary checks to ensure
stale data isn't added to notification lists and that requests to closed
connections are ignored.

This closes #92.
2014-02-11 22:39:11 -05:00
Dave Collins
44e3a44a9c Remove help addenda for getpeerinfo.
This was no longer accurate since btcd now implements all getpeerinfo
fields.
2014-02-11 20:45:15 -06:00
Dave Collins
db87b9e85f Update btcctl getwork for latest btcjson changes. 2014-02-11 16:47:25 -06:00
Francis Lam
1d60e5dba5 Fixed data races in rpcwebsocket Notify functions
Access to connections map and associated notification maps in rpcServer
need to be protected with s.ws.Lock to prevent race with add/remove new
clients.

This closes #88.
2014-02-10 10:34:26 -05:00
Francis Lam
354cc38d2d Added comments for new AddAllNewTxRequest method 2014-02-08 18:01:27 -05:00
Francis Lam
b89e93e52f Added notifyallnewtxs custom websocket command
Changed mempool.MaybeAcceptTransaction to accept an additional parameter
to differentiate betwee new transactions and those added from
disconnected blocks.

Added new fields to requestContexts to indicate which clients want to
receive all new transaction notifications.

Added NotifyForNewTx to rpcServer to deliver approriate transaction
notification.
2014-02-08 17:15:17 -05:00
David Hill
642c834ada move Connection header so it is applied to failed authentication
attempts as well.

ok @davecgh
2014-02-07 20:59:20 -05:00
Dave Collins
476000193f Implement getnetworkhashps RPC.
Closes #87.
2014-02-07 16:32:11 -06:00
Dave Collins
02e594f826 Add getnetworkhashps to btcctl. 2014-02-05 15:33:32 -06:00
Dave Collins
bc6ff038e3 Fix a race in the amgr found by the race detector. 2014-02-05 12:37:35 -06:00
Dave Collins
6f063e0c1b Update byte counts unconditionally.
Rather than updating the byte counts in the error path and after the error
path, do it unconditionally before checking the error.
2014-02-05 11:50:40 -06:00
Dave Collins
c51df0ca3c Don't assign result from atomic.AddUint64. 2014-02-05 11:44:07 -06:00
Owain G. Ainsworth
75b59ef6e4 Add gettransaction to btcctl. 2014-02-05 17:39:25 +00:00
Dave Collins
1716136f62 Return milliseconds in getnettotals RPC.
This was returning microsecond instead of milliseconds.
2014-02-05 11:26:03 -06:00
Dave Collins
a39f4a0698 Correct total byte counters for server.
Previously the getnettotals was just looping through all of the currently
connected peers to sum the byte counts and returning that.  However, the
intention of the getnettotals RPC is to get all bytes since the server was
started, so this logic was not correct.

This commit modifies the code to keep an atomic counter on the server for
bytes read/written and has each peer update the server counters as well as
the per-peer counters.
2014-02-05 11:15:41 -06:00
Dave Collins
5d70935b04 Convert getnettotals TimeMillis field to UTC.
This matches the reference implementation.
2014-02-05 09:54:16 -06:00
Dave Collins
6f5f582c42 Implement getnettotals RPC.
Also, change the display handler for getnettotals in btcctl to the JSON
display handler for better display.

Closes #84.
2014-02-05 09:39:03 -06:00
Dave Collins
f8c843e2e3 Rename bytesRead/Written to bytesReceived/Sent.
This makes it a little more clear the variables reprsent bytes sent across
the network as opposed to from disk.
2014-02-04 22:37:13 -06:00
Dave Collins
7cfef69f23 Prepare for release 0.6.0. 2014-02-04 17:38:24 -06:00
Dave Collins
bcb5a21b37 Add comment to ErrBadAuth to make go lint happy. 2014-02-04 15:37:34 -06:00
Dave Collins
238d942a69 Make go vet happy. 2014-02-04 15:34:44 -06:00
David Hill
c1df0bfce6 get rid of redundant &handlerData 2014-02-04 15:49:31 -05:00
David Hill
9f044fb946 use %d for int32 2014-02-04 13:55:23 -05:00
Josh Rickmar
979357c5f1 Add sendtoaddress support to btcctl. 2014-02-04 13:18:58 -05:00
Dave Collins
038c8fb278 Add wiki link to README.md.
T#	util/loadheaders/
2014-02-04 11:06:39 -06:00
Dave Collins
71c35ec521 Update README.md with recent information. 2014-02-04 11:03:35 -06:00
Dave Collins
cdbe387545 Add warning on invalid msg type in block manager. 2014-02-04 09:47:12 -06:00
Dave Collins
d949072d6d Change new get sync peer bits to a query channel.
Rather than using a dedicated channel for the sync peer request and reply,
use a single query channel that accepts a query type as well as a reply
channel.  This will allow other queries to be added in the future without
the various queries being racy.
2014-02-04 09:47:07 -06:00
Dave Collins
aab3a6643c Add getnettotals to btcctl. 2014-02-04 01:27:00 -06:00
Dave Collins
ba5e457c38 Finish getpeerinfo RPC syncnode field.
This commit adds code to get the current sync peer from the block manager
for use in the getpeerinfo RPC.
2014-02-04 00:27:10 -06:00
Dave Collins
591b0f431d Switch over to new btcwire Read/WriteMessageN.
This commit adds byte counters to each peer using the new btcwire
ReadMessageN and WriteMessageN functions to obtain the number of bytes
read and written, respectively.  It also returns those byte counters via
the PeerInfo struct which is used to populate the RPC getpeerinfo reply.

Closes #83.
2014-02-04 00:00:13 -06:00
Dave Collins
2a7d725a09 Move websocket endpoint to /ws.
This commit moves the connection endpoint for websockets to /ws instead of
/wallet. First, the former is more standard, and second the latter
presumes how the websocket is to be used.

Closes #80.
2014-02-03 10:46:03 -06:00
Dave Collins
5ec951f6a7 Rework and improve headers-first mode.
This commit improves how the headers-first mode works in several ways.

The previous headers-first code was an initial implementation that did not
have all of the bells and whistles and a few less than ideal
characteristics.  This commit improves the heaers-first code to resolve
the issues discussed next.

- The previous code only used headers-first mode when starting out from
  block height 0 rather than allowing it to work starting at any height
  before the final checkpoint.  This means if you stopped the chain
  download at any point before the final checkpoint and restarted, it
  would not resume and you therefore would not have the benefit of the
  faster processing offered by headers-first mode.
- Previously all headers (even those after the final checkpoint) were
  downloaded and only the final checkpoint was verified.  This resulted in
  the following issues:
  - As the block chain grew, increasingly larger numbers of headers were
    downloaded and kept in memory
  - If the node the node serving up the headers was serving an invalid
    chain, it wouldn't be detected until downloading a large number of
    headers
  - When an invalid checkpoint was detected, no action was taken to
    recover which meant the chain download would essentially be stalled
- The headers were kept in memory even though they didn't need to be as
  merely keeping track of the hashes and heights is enough to provde they
  properly link together and checkpoints match
- There was no logging when headers were being downloaded so it could
  appear like nothing was happening
- Duplicate requests for the same headers weren't being filtered which
  meant is was possible to inadvertently download the same headers twice
  only to throw them away.

This commit resolves these issues with the following changes:

- The current height is now examined at startup and prior each sync peer
  selection to allow it to resume headers-first mode starting from the
  known height to the next checkpoint
- All checkpoints are now verified and the headers are only downloaded
  from the current known block height up to the next checkpoint.  This has
  several desirable properties:
  - The amount of memory required is bounded by the maximum distance
    between to checkpoints rather than the entire length of the chain
  - A node serving up an invalid chain is detected very quickly and with
    little work
  - When an invalid checkpoint is detected, the headers are simply
    discarded and the peer is disconnected for serving an invalid chain
  - When the sync peer disconnets, all current headers are thrown away
    and, due to the new aforementioned resume code, when a new sync peer
    is selected, headers-first mode will continue from the last known good
    block
- In addition to reduced memory usage from only keeping information about
  headers between two checkpoints, the only information now kept in memory
  about the headers is the hash and height rather than the entire header
- There is now logging information about what is happening with headers
- Duplicate header requests are now filtered
2014-02-01 16:33:00 -06:00
Dave Collins
c6d865f3b5 Cleanup and slightly optimize the progress logging.
Previously the logging function which reports on progress was called for
every block, regardless of whether it was an orphan or not.  This could be
confusing since it could show a different number of blocks processed as
compared to the old versus new heights reported (orphans do not add to the
block height since they aren't extending the main chain).  Further, the
database had to be consulted for the latest block since the block we just
processed might not be the latest one if it was an orphan.  This is quite
a bit more time conusming than it should've been for progress reporting.

This commit modifies that to only include non-orphan blocks.  As a result,
the latest height shown will match the number of blocks processed (even
when there are orphans) and the additional block lookup from the database
is avoided.
2014-01-30 12:40:37 -06:00
David Hill
79ff9b76ee add verifymessage to btcctl 2014-01-30 13:26:15 -05:00
David Hill
bd0c799e2f add validateaddress to btcctl 2014-01-30 13:14:28 -05:00
David Hill
9c2abd1fa1 add listreceivedbyaccount and listreceivedbyaddress to btcctl 2014-01-30 13:10:43 -05:00
David Hill
1a64f33fca add listunspent to btcctl 2014-01-30 12:59:26 -05:00
David Hill
ed4ffc44a1 add listlockunspent to btcctl 2014-01-30 12:31:56 -05:00
David Hill
0951ffa1c7 add listaddressgroupings to btcctl 2014-01-30 12:29:21 -05:00
David Hill
ea689489d3 add gettxoutsetinfo to btcctl 2014-01-30 12:23:38 -05:00
David Hill
46709bda08 add getrawchangeaddress to btcctl 2014-01-30 12:19:04 -05:00
David Hill
8749cde081 add getreceivedbyaccount and getreceivedbyaddress to btcctl 2014-01-30 12:08:50 -05:00
David Hill
2b9f5b8932 in getrawmempool verbose mode, initialize depends to an empty array.
this matches bitcoind.

from jrick
ok davec
2014-01-29 22:22:07 -05:00
David Hill
b532860477 sync with btcjson changes to getrawtransaction.
ok oga@
2014-01-29 21:21:58 -05:00
Owain G. Ainsworth
82fca37eae implement the getinfo rpc command.
This contains some wallet inforamtion, but bitcoind if wallet is
disabled returns just the non wallet information. we do the same.
2014-01-29 18:31:33 +00:00
Owain G. Ainsworth
042d9206a1 change some more code over to using newer btcdb apis. 2014-01-29 18:31:33 +00:00
Owain G. Ainsworth
8d930ceed1 convert getdifficulty to getBlockHeaderBySha to save some cycles. 2014-01-29 18:31:33 +00:00
Dave Collins
970c0cdb30 Misc cleanup.
This commit contains various code cleanup such as comment fixes and
function ordering consistency.
2014-01-28 18:57:07 -06:00
David Hill
112525bd7a cleanup sendfrom and sendmany 2014-01-27 21:31:33 -05:00
David Hill
6c9a206709 add keypoolrefill to btcctl 2014-01-27 21:11:59 -05:00
David Hill
34849a181c add listaccounts to btcctl 2014-01-27 21:05:27 -05:00
Owain G. Ainsworth
5cef5bc05c add support for listsinceblock to btcctl 2014-01-27 23:22:09 +00:00
David Hill
2697a0a9ea add settxfee to btcctl 2014-01-27 16:31:48 -05:00
David Hill
c0d6180685 add sendmany to btcctl 2014-01-27 15:57:44 -05:00
David Hill
b354015426 add sendfrom to btcctl 2014-01-27 15:19:39 -05:00
Dave Collins
72afc787e6 Move getinfo RPC method to askwallet list.
The getinfo RPC method requires access to information only available in
the wallet.  Therefore, it has been moved to the list of methods which
return an error information the caller to send the request to the wallet
instead.
2014-01-27 13:52:59 -06:00
David Hill
a5e5903caf add walletlock to btcctl 2014-01-27 13:44:31 -05:00
Josh Rickmar
36e5aa8e92 Add walletpassphrasechange support to btcctl. 2014-01-27 13:39:01 -05:00
David Hill
ffe767c679 add getaddressesbyaccount to btcctl 2014-01-27 13:32:36 -05:00
David Hill
82552e4465 add walletpassphrase to btcctl 2014-01-27 12:49:54 -05:00
David Hill
221352586b add getnewaddress to btcctl 2014-01-27 12:35:20 -05:00
Dave Collins
b9a641ab79 Move getnewaddress RPC method to askwallet list.
The getnewaddress RPC method deals with wallet-related functionality and
therefore has been moved to the list of methods which return an error
information the caller to send the request to the wallet instead.
2014-01-27 10:58:24 -06:00
Dave Collins
0bf4e0e097 Move getblocktemplate/getwork to askwallet list.
Both of these RPC methods require access to information ony available in
the wallet.  Therefore they have been moved to the list of methods which
return an error information the caller to send the request to the wallet
instead.
2014-01-25 22:58:38 -06:00
Dave Collins
d58af1c3cf Make peer state iterator funcs receivers.
Rather than having the iterator functions a separate entities that access
the state to iterate, just expose the iterators as receivers on the state
itself.  This is more consistent with the style used throughout the code
and the other receivers on the state such as Count, OutboundCount, etc.
2014-01-25 22:51:28 -06:00
Dave Collins
fea4109eb2 Add getaddednodeinfo support to btcctl. 2014-01-25 22:50:34 -06:00
Dave Collins
dcef4128b8 Add support for getaddednodeinfo RPC command.
This commit adds full support for the getaddednodeinfo RPC command
including DNS lookups which abide by proxy/onion/tor rules when the DNS
flag is specified.  Note that it returns an array of strings when the DNS
flag is not set which is different than the current version of bitcoind
which is bugged and scheduled to be fixed per issue 3581 on the bitcoind
issue tracker.
2014-01-25 22:50:32 -06:00
David Hill
5736dc05ae add getwork support to btcctl 2014-01-23 15:57:15 -05:00
David Hill
109ca258af sort commands and fix typos 2014-01-23 14:21:41 -05:00
David Hill
136aa95446 add getblocktemplate support to btcctl. 2014-01-23 13:44:28 -05:00
Dave Collins
5859deea7e Improve RPC authentication failure responses.
This commit improves how the legacy RPC server responds to authentication
failures so things like web browsers can react better.  The following
changes have been made:

First, authentication failures were only printing the 401 error response
in the body instead of setting the http status code.  This means the
response had a 200 OK header with a body of 401 Unauthorized.  Therefore
the client would think everything was ok, but see the response as
malformed JSON.

Second, the spec for 401 Unauthorized responses state they must include a
WWW-Authenticate header to instruct the client how to authenticate.
Without this, browsers won't prompt the user for credentials.
2014-01-23 11:27:48 -06:00
Josh Rickmar
8f43dc758e Fix build. 2014-01-22 21:21:05 -05:00
Dave Collins
0d40bf901d Implement alternative auth for websockets.
The previous websocket code required HTTP auth headers to be sent in order
to use the websocket.  While this makes sense for most applications, some
use cases such as javascript-based websockets from browsers do no have the
ability to send headers.

This commit modifies the authentication logic to allow an alternative
authentication mechanism when HTTP auth headers can't be sent.  In
particular, it introduces a new JSON-RPC command named authenticate which
accepts the username and passphrase for the RPC server.  The command is
only required if the connetion has not already been authenticated via HTTP
auth headers and it must be the first command to be received.  Sending any
other command will immediately disconnect the websocket.

ok from @owainga and @jrick.

This closes #77.
2014-01-22 17:40:14 -06:00
Owain G. Ainsworth
9cb5190ac2 add support for the ping rpc command.
And the pingtime and pingwait fields of getpeerinfo.
2014-01-22 16:21:08 +00:00
Owain G. Ainsworth
1487a352da add basic support for the help rpc command. 2014-01-22 16:20:38 +00:00
Josh Rickmar
d3e4bcdcf5 Fix sendrawtransaction for websockets.
This fixes two issues: first, the sendrawtransaction handler had an
extra character in the key in the websocket handler map, preventing
the handler from never running.  Second, a nil pointer dereference was
removed from the handler.

This change fixes the minedtx notifications for btcwallet, since the
websocket-handler now runs instead of falling back to the legacy RPC
handler.
2014-01-20 18:27:27 -05:00
Dave Collins
f12ca20372 Enable memdb support.
This commit adds the btcdb memdb backend as a supported database type.
Note that users will NOT want to run in this mode because, being memory
only, it obviously does not persist the database when shutdown.

It is being added for testing purposes to help prevent constant abuse to
developer's hard drive when churning the block database multiple times a
day.
2014-01-19 20:44:06 -06:00
Dave Collins
f309e899f3 Make use of the new btcdb functions.
This commit switches the handleGetHeadersMsg function to make use of the
new FetchBlockHeightBySha and FetchBlockHeaderBySha functions in btcdb.

Also, while here, nuke the header copy which is no longer required due to
the recent btcwire changes.
2014-01-19 03:05:02 -06:00
Dave Collins
33bb455365 Update for recent btcwire API changes. 2014-01-18 21:15:09 -06:00
Dave Collins
7ad6e235ad Reduce the initial idle timeout to 30 seconds.
This commit reduces the initial idle timeout before version negotiation
has happened on a new peer to 30 seconds.  Previously it could take 5
minutes due to the general idle timeout.
2014-01-18 01:44:34 -06:00
Dave Collins
8c7d44c8dc Add authentication deadline to RPC server cnxns.
Previously it was possible to open a connection to the RPC server, never
authenticate, and idle forever.

This is work toward #68.
2014-01-18 00:08:39 -06:00
Dave Collins
0fbd962f8a Use our own websocket.Server instance.
The websocket.Server used the by websocket.Handler type automatically adds
a handshake function which prevents connections when the Origin header is
not set.  Not all clients send this information and we already require
authentication headers as the auth mechanism anyways.
2014-01-17 16:30:29 -06:00
Dave Collins
737e69594b Don't spend time on txout ntfns when no listeners. 2014-01-17 16:30:01 -06:00
Dave Collins
9474ef29d7 Use strings in websocket send/receive.
The websocket package assumes binary blobs if []byte is used.  Since we're
using JSON-RPC for all the websocket communications, it should be text
based.

This commit changes the websocket Send/Receive and associated channels to
strings accordingly.
2014-01-17 15:48:00 -06:00
Dave Collins
75554fab09 Fix merge conflict. 2014-01-17 15:45:23 -06:00
Dave Collins
f089853d4d Move RPC websocket init code to rpcwebsocket.go. 2014-01-17 15:44:36 -06:00
Josh Rickmar
20e56d6eda Ask for block(dis)connected updates.
This removes the last notification that was being sent unsolicited.
Since it is no longer needed, the code to duplicate notifications to
all clients has been removed.
2014-01-17 16:38:16 -05:00
David Hill
871481ce1b Implement submitblock.
Closes #61.
2014-01-15 15:07:43 -06:00
Dave Collins
e3122c1b1d Remove extra parnethesis in previous. 2014-01-15 10:19:51 -06:00
Dave Collins
fcdddd499f Use a more specific license adjective in README.md.
This was proposed by @apotheon.
2014-01-15 10:17:22 -06:00
Josh Rickmar
bd98836a2b Fix several bugs in the RPC server shutdown.
The RPC server was performing some of the shutdown logic in the wrong
order, that is, logging the the server has shut down, waiting for all
server goroutines to finish, and then closing a channel to notify
server goroutines to stop.  These three items have been reversed to
fix a hang where goroutines currently being waited on had not shut
down because they did not receive the notification.

While here, the server waitgroup was incremented for a goroutine that
was running without it, another select statement was added to stop a
duplicate close (which never occured last commit when I added the
select statements), and the "stopping rescan" logging was moved to
debug to make the ^C shutdown logging nicer.
2014-01-14 22:53:07 -05:00
Josh Rickmar
d8227c2751 Only close websocket disconnected chan if still open.
A channel cannot be closed multiple times, so use a select statement
to only close the channel if it can not be read from.
2014-01-14 21:47:27 -05:00
Josh Rickmar
86600a0356 Remove punctuation from end of logging for consistency. 2014-01-14 16:41:32 -05:00
Dave Collins
477b733ed9 Make Go 1.2 requirement more noticeable.
It was pointed out in #76 that if you arrived to the Update section
of the README without seeing the Installation section, the requirement for
Go 1.2 is easy to miss.  This commit builds the requirement in the
Installation section and adds it to the Updating section as well to
hopefully make it more noticable.
2014-01-14 15:28:02 -06:00
Josh Rickmar
051a9013ce Stop rescans when a websocket client disconnects.
Fixes #66.
2014-01-14 15:59:31 -05:00
Josh Rickmar
6abad1d8ac Change websocket handlers to return results.
This is the first step to fixing #66.
2014-01-14 13:15:22 -05:00
Dave Collins
6222b1d8cc Rework and Improve addblock utility.
The addblock utility was originally written as a quick debug tool during
initial development to populate blocks into the database.  However, now
that it has been designated as the standard way to import bootstrap.dat
(and indeed block data files in general), it was lacking a few features
such as properly checking against the chain rules and known good
checkpoints.

This commit reworks and improves the utility in several ways:

- Imported blocks are now checked against the chain rules including
  checkpoints to ensure they match the known good chain
- The utility now properly shuts down after processing all blocks
- Attempting to import orphan blocks (blocks which build off a block you
  don't yet have in the database) returns an error
- Blocks that are already known are now skipped instead of causing an
  error which means you can stop and restart the import mid-way through
  without issues or start it after you've already downloaded a
  portion of the chain
- The block height is no longer assumed to start at 0 which means input
  files that start later in the chain will work properly so long as you
  already have the chain at least up to the point of the block just before
  the first one in the input file
- Improved error handling and reporting
- How often the progress display is shown is now configurable
- Statistics about how many blocks were processed, imported, and already
  known are now displayed after the input file has been fully processed

This resolves comments made in #60.
2014-01-14 01:18:52 -06:00
Dave Collins
d8ec5bd33c Prepare for release 0.5.0. 2014-01-13 17:47:21 -06:00
Dave Collins
8b479794ab Add 0.5.0 deps to deps.txt. 2014-01-13 17:45:15 -06:00
Dave Collins
c99a227df2 Fix a couple of comment typos. 2014-01-10 22:32:05 -06:00
Josh Rickmar
427fb3cd94 gencerts: Print error lowercased for consistency. 2014-01-10 17:57:14 -05:00
Josh Rickmar
aec17304a0 Add standalone gencerts utility to create RPC TLS certificates. 2014-01-10 16:59:54 -05:00
Josh Rickmar
035f8f82b7 Switch to btcutil for certificate generation. 2014-01-10 15:41:57 -05:00
David Hill
3a59e4d064 Set the Connection header to close 2014-01-10 10:57:05 -05:00
Dave Collins
dacf9d77c5 Comment the onion/proxy dial/lookup configuration. 2014-01-10 01:52:15 -06:00
Dave Collins
4f8c2a3aaf Move missing config file warn just before return.
The warning about a missing config file should only be shown after all
other configuration has succeeded so it's not shown when there are invalid
options specified.  Also add a comment about it where its intended
placement is for the future.
2014-01-10 01:34:50 -06:00
Dave Collins
d33e9b4165 Unexport and comment btcDial and btcLookup func.
These functions are at the package level and only apply within btcd, so
unexport them to be consistent.
2014-01-10 01:31:20 -06:00
Josh Rickmar
cd3084afcd Rework the btcwallet connection.
This changes the protocol between btcd and btcwallet to follow
JSON-RPC specifications sending notifications as requests with an
empty ID.

The notification request context handling has been greatly cleaned up
now that IDs no longer need to be saved when sending notifications.
2014-01-09 14:03:00 -05:00
Dale Rahn
a5cc3196b4 Clear fetchheaders fields if in that mode and syncpeer detaches. 2014-01-09 13:22:42 -05:00
Dave Collins
f93c8a4af1 Don't display btcctl getconnectioncount as float. 2014-01-09 12:17:26 -06:00
Dave Collins
af0750dd76 Don't display getblockccount from btcctl as float. 2014-01-09 12:15:46 -06:00
Dave Collins
b3f63cf35e Fix typo in fetch blocks log message. 2014-01-09 12:13:14 -06:00
Marco Peereboom
371dfdd76a Kill last sqlite3 bits.
This was driving me insane when building with -a -v and it slowed down
the process dramatically.

ok davec
2014-01-09 10:26:17 -06:00
Dave Collins
3946d84887 Make use of the new size hint functions in btcwire.
This commit changes a couple of sections which deal with large lists of
inventory vectors to use the new size hint functions recently added to
btcwire.  This allows a bit more efficiency since the size of the list is
known up front and we can therefore avoid dynamically growing the backing
array several times.  This also helps avoid a Go bug that leaks memory on
appends and GC churn.
2014-01-08 17:46:59 -06:00
Dave Collins
462bc5a031 Update for recent btcscript API changes.
This commit changes all code which deals with extracting addresses from
scripts to use the btcscript API ExtractPkScriptAddrs which in turn makes
use of the new btcutil.Address interface.

This provides much cleaner code for dealing with arbitrary script
destinations which is extensible without having to churn the APIs if new
destination types are added.
2014-01-08 11:22:29 -06:00
Marco Peereboom
96ded50f6b Dont write pprof info in addblock.
dont write pprof info in addblock  (ok drahn); and make progress a little
more silent
2014-01-07 16:06:29 -06:00
Marco Peereboom
d00ccc0d3c Make limits into a pkg.
This is so that we can use the in other utils.

debated with davec
2014-01-07 16:06:12 -06:00
Owain G. Ainsworth
dd7c910e86 Enable use of a different proxy for .onion addresses.
This implements --onion (and --onionuser/--onionpass) that enable a
different proxy to be used to connect to .onion addresses. If no main
proxy is supplied then no proxy will be used for non-onion addresses.

Additionally we add --noonion that blocks connection attempts to .onion
addresses entirely (and avoids using tor for proxy dns lookups).

the --tor option has been supersceded and thus removed.

Closes #47
2014-01-07 19:18:09 +00:00
Dave Collins
34657d43d9 Add the useragent to the new valid peer message.
This commit modifies the new valid peer message to display the useragent.
Previously this information was only available by setting the PEER
subsystem debuglevel to debug or lower.

This was prompted by #64.
2014-01-07 11:16:15 -06:00
David Hill
47c92c0e62 fix typo 2014-01-06 15:57:37 -05:00
Josh Rickmar
8b5277e73a Add sendrawtransaction support to btcctl. 2014-01-06 11:50:02 -05:00
David Hill
ae347a6f67 Add new DNS seed seed.bitcoinstats.com 2014-01-04 23:20:06 -05:00
Dave Collins
8a73f9b245 Correct p2sh field of decodescript RPC result.
The p2sh field in the result should be the encoded address which includes
the network (human-readable address) instead of just the raw hash.
2014-01-04 12:44:14 -06:00
Dave Collins
92eee5cb96 Populate p2sh field in decodescript RPC result. 2014-01-04 11:55:09 -06:00
Dave Collins
4cb1500a0b Add decodescript support to btcctl. 2014-01-03 23:30:08 -06:00
Dave Collins
47a78ea5c2 Add support for decodescript RPC command. 2014-01-03 23:29:24 -06:00
Dave Collins
a8ff7fecb4 Add createrawtransaction support to btcctl. 2014-01-03 13:45:12 -06:00
Dave Collins
b6b2fd15b3 Add support for createrawtransaction RPC command. 2014-01-03 12:43:13 -06:00
Josh Rickmar
405eca4a44 Remove usage of deprecated address encode/decode API. 2014-01-03 13:22:28 -05:00
Francis Lam
082ad7caf2 Updated createVoutList to support scripthash/multisig outputs
Used updates in btcscript/btcutil to support decoding scripthash and
multisig outputs for display in getrawtransactions/decoderawtransaction
2014-01-02 13:48:50 -06:00
Dave Collins
aeec39c1ff Add 2014 to copyright dates. 2014-01-01 10:16:15 -06:00
Dave Collins
30802fdd52 Rename RPC handlers map to rpcHandlers.
The name handlers for a package level is a bit too generic and could
easily cause a name collision.  Even though the compiler would catch it,
use something a bit more descriptive.
2013-12-31 15:53:19 -06:00
Dave Collins
835cee229a Move RPC handler maps near the top of file.
Since the command to handler mappings are the most often modified and
referenced code in rpcserver.go and rpcwebsocket.go, move them near the
top of their respective files.
2013-12-31 14:48:50 -06:00
Dave Collins
9b166b3876 Remove wallet notifications chan from std commands.
This commit cleans up the standard RPC command hanlding a bit by removing
the websocket specific notification channel from the handlers.  This was
previously required because the sendrawtransaction, when called from a
websocket enabled connection, needs to add a notification for when the
transaction is mined.

This commit modifies that to instead implement a websocket extended
version of sendrawtransaction which invokes the standard handler and adds
the notification.  In addition, the main send was modified to first look
if the command has a websocket specific handler first, and then falls back
to standard commands, rather than the previous approach of first checking
for a standard command and falling through to websocket commands.  This
essentially allows websockets connections to extend commands with the same
name with additional functionality such as what was done in this commit.
2013-12-31 14:42:15 -06:00
Dave Collins
5ad6d543d6 Move RPC websocket code to its own file.
The rpcserver.go file is starting to get a bit unwieldy.  This commit
moves the separable websocket specific bits into a separate file named
rpcwebsocket.go.
2013-12-31 13:19:07 -06:00
Josh Rickmar
426db5ae21 Add getaccountaddress support to btcctl. 2013-12-31 13:11:01 -05:00
Dave Collins
f2a2744bec Correct error handling from the previous commit. 2013-12-30 20:27:36 -06:00
Francis Lam
28d08f8b16 Small changes to createVinList/createVoutList
Added error checking for script disassembley

Changed vout to handle errors in processing the way bitcoind does: the
type displayed is "nonstandard" when the calculated type is nonstandard
or nulltype and also when there is an error getting the address.

Still doesn't properly support multisig addresses, but now it should
return "nonstandard" since since address lookup fails for those cases.
2013-12-30 20:06:13 -05:00
Dave Collins
eb624acfd4 Add support for decoderawtransaction RPC command.
Since the decoderawtransaction result makes use of the same vin and vout
lists, this commit also factors the logic for those out into separate
functions.
2013-12-30 18:33:13 -06:00
Josh Rickmar
bf7c5fa062 Fix a typo. 2013-12-30 16:32:25 -05:00
Josh Rickmar
13868e76ec Add getaccount support to btcctl. 2013-12-30 16:31:47 -05:00
Dave Collins
d17a97b485 Correct verifychain RPC call return.
The return value should be a boolean for compatibility.
2013-12-30 10:59:14 -06:00
Dave Collins
3bfeabb47a Improve btcctl verifychain usage.
Also, add a comment for makeVerifyChain while here.
2013-12-30 10:35:33 -06:00
Josh Rickmar
bd29b12d31 Notify wallets when mempool txs pay to a wallet address. 2013-12-30 09:33:36 -05:00
Dave Collins
ca14702e5d Correct getpeerinfo JSON marshalling for omitempty.
The fields of the PeerInfo should not have been marked omit as the only
ones that should be omitted to for compatibility are the SyncNode and
BanScore fields.
2013-12-27 19:25:20 -06:00
Dave Collins
3faa256f75 Correct the getpeerinfo RPC services field.
In order to match the Satohsi client, the return is supposed to be an
8-digit string representation of the services instead of the actual
services numeric value.
2013-12-27 17:35:51 -06:00
Dave Collins
674ef590bb No need to cast something is already a uint32. 2013-12-27 13:20:13 -06:00
Dave Collins
f0cc672d23 Update for btcjson sequence number type change. 2013-12-27 12:38:51 -06:00
Dave Collins
ca0e38e58b Update for recent btcjson getrawtransaction change.
The ScriptSig field of the Vin type for TxRawResult is now a pointer in
btcjson so it can be properly omitted.  This commit updates the code to
create the new ScriptSig object as needed.
2013-12-27 12:24:03 -06:00
Dave Collins
e0fab228a4 Correct getrawtransaction RPC handling and cleanup.
The getrawtransaction RPC call should return a hex-encoded string of the
transaction when verbose is false instead of a TxRawResult object with the
Hex field set to be compatible with the Sathoshi client.  This commit,
along with a recent commit to btcjson corrects this.

Also, while here, do a bit of cleanup, finish a TODO to check for an
invalid hash, and optimize the handling of non-verbose slightly.
2013-12-26 23:30:57 -06:00
Dave Collins
af3609d861 Factor out common message to hex in RPC server. 2013-12-26 22:53:44 -06:00
Dave Collins
8477ef569a Update btcctl getblock for recent changes.
This commit allows btcctl to accept the optional verbosity parameters on
getblock.
2013-12-26 11:19:32 -06:00
Dave Collins
67b5c2fb7e Correct getblock RPC handling and optimize.
The getblock RPC call should return a hex-encoded string of the block when
verbose is false instead of a BlockResult object with a Hex field set to
be compatible with the Sathoshi client.  This commit, along with a recent
commit to btcjson corrects this.

Also, while here, move code which only applies to verbose mode after the
call which handles the non-verbose logic.  This saves a few cycles since
the non-verbose logic doesn't need the extra information.
2013-12-26 11:13:31 -06:00
Dave Collins
ca4cf29e49 Add a new function to btcctl for JSON display.
This commit adds a new function to btcctl that shows the results as
properly indented JSON instead of relying on spew and changes all of the
commands that used spew to the new function.  The output of btcctl
should be more user-facing than developer-facing.
2013-12-25 23:18:16 -06:00
Dave Collins
23ff70d682 Add gethashespersec command to btcctl. 2013-12-25 19:13:14 -06:00
Dave Collins
195ada0979 Add newline to previous print change. 2013-12-25 17:47:56 -06:00
Dave Collins
6be128a843 Display float values with btcctl with %f.
This commit modifies btcctl to show float values with %f instead of the
default %v.  This means the values will show similar to 1180923195.260000
instead of 1.18092319526e+09 (scientific notation).
2013-12-25 17:47:12 -06:00
Dave Collins
dc200d002e Correct getrawmempool verbose fee field.
The fee field of the getrawmempool RPC JSON response should be in Bitcoins
instead of Satoshi.  This commit corrects that issue.

Also, add a couple of comments and fix a comment typo while here.
2013-12-25 12:33:12 -06:00
Dave Collins
edf8f2f224 Use curHeight in mempool transaction add.
Since there is already a variable for the current block height in addition
to the next block height, use the existing curHeight variable instead
doing nextBlockHeight-1 in mempool add.
2013-12-25 12:15:59 -06:00
Dave Collins
4c184ead54 Use consistent wrapping for externalip config opt. 2013-12-24 17:01:32 -06:00
Dave Collins
17a9b41bef Cleanup peer.go.
This commit does some housekeeping on peer.go to make the code more
consistent, correct a few comments, and add new comments to explain the
peer data flow.  A couple of examples are variables not using the standard
Go style (camelCase) and comments that don't match the style of other
comments.
2013-12-24 14:05:28 -06:00
Owain G. Ainsworth
d3a7f15a87 Alter the way peers queue outbound messages somewhat.
Instead of one thread that queues and writes, we move to a two queue
model. The queueHandler muxes all the sources of outgoung packets and
drips them to the actual sender. This is done so that a large send
doesnt' allow the channels to fillup and cause blockmanager and server
to block, which delays other peers.

Most messages we handle as is. However, for getdata we do some manual
limiting and pipelining, we queue up three and then we load the next
into memory, not sending it until the otherp ackets have been sent. We
may want to change this later to queue the packet *then* wait so that we
don't completely drain the pipe.

A few misc tweaks to avoid deadlocking by ensuring the all channels will
always drain. mostly this relates to ensuring that we know no more data
will be coming before we drain the channel, and not queueing after we
are marked to disconnect.

Discussed heavily with drahn@ and davec@.
2013-12-23 23:51:52 +00:00
Dave Collins
cc9aadf041 Don't use headers first when checkpoints disabled.
Headers first relies on having valid checkpoints, so if checkpoints are
disabled, it needs to be disabled as well.
2013-12-20 14:01:25 -06:00
Owain G. Ainsworth
2c81f61616 Also drain the inv channel as well as the message channel on peer quit.
Shold prevent a deadlock where we exit but server/blockmaanger is
waiting on our inv channel.

Closes #62
2013-12-17 15:51:37 +00:00
Owain G. Ainsworth
d72255bce3 gofmt 2013-12-17 14:02:35 +00:00
Owain G. Ainsworth
8310661c29 Parse out ports from externalip strings if present.
If not, continue to use the default. Should fix a problem reported by
Justus Ranvier on github.

Closes #38 (again)
2013-12-16 20:48:23 +00:00
Owain G. Ainsworth
7b304515d6 fix getrawtransaction verbose to be bool. 2013-12-16 18:14:51 +00:00
Owain G. Ainsworth
8aaad1e97b Add support for verbose in getrawmempool.
Closes #55.
2013-12-16 18:14:50 +00:00
Josh Rickmar
e4fa45ff08 Better logging for rescans. 2013-12-16 09:09:28 -05:00
Dave Collins
011025dc0d Fix regtest mode with new headers-first approach.
The regression test does not work properly with the new headers-first
download approach, so force the old inv-based block download for
regression test mode.
2013-12-12 18:10:06 -06:00
Dale Rahn
7b406dcb0f Implement a fast path for the Initial Block Download.
It is not necessary to do all of the transaction validation on
blocks if they have been confirmed to be in the block chain leading
up to the final checkpoint in a given blockschain.

This algorithm fetches block headers from the peer, then once it has
established the full blockchain connection, it requests blocks.
Any blocks before the final checkpoint pass true for fastAdd on
btcchain operation, which causes it to do less valiation on the block.
2013-12-12 17:24:05 -05:00
Dave Collins
c34ab6a95e Remove sqlite3 import from addblock.
The sqlite3 db backend is deprecated, so this is no longer needed.  Also,
since sqlite3 is a cgo binding it doesn't cross compile nicely.
2013-12-12 13:37:46 -06:00
Dave Collins
bbc3c1cf7e Prepare for release 0.4.0. 2013-12-12 13:27:06 -06:00
Owain G. Ainsworth
95563691cd go fmt. 2013-12-11 00:41:03 +00:00
Owain G. Ainsworth
7df65008be Run interrupt handlers in lifo order. 2013-12-10 22:40:26 +00:00
Owain G. Ainsworth
2a554c43b0 Shutdown server fully on ctrl-c
since we don't wait for peers, this largely just waits for the server procs
themselves to die. Unless the entire server is wedged (which is what kill -9 is
for) this should always shut down fairly swiftly.

This should mean we sync addrmanager and disestablish upnp correctly on
interrupt.

Discussed with davec.
2013-12-10 22:40:17 +00:00
Owain G. Ainsworth
f8e88df237 Add basic support for UPnP.
This code borrows and fixes up a chunk of code to handle upnp from
Taipei-Torrent (https://github.com/jackpal/Taipei-Torrent), under
current versions of go none of the xml parsing was working correctly.
This fixes that and also refactors the SOAP code to be a little nicer by
stripping off the soap containers. It is still rather rough but seems to
correctly redirect ports and advertise the correct address.

Upnp is not run by default. --upnp will enable it, but it will still not
run if we are not listening or if --externalip is in use.

Closes #51
2013-12-10 22:39:54 +00:00
Owain G. Ainsworth
1145fb57ed handle .onion addresses in deserialising addrmanager.
Use the generic function that already handles this.
2013-12-10 19:39:48 +00:00
Owain G. Ainsworth
8968f7dd74 Add support for --externalip.
Closes #38
2013-12-10 19:39:47 +00:00
Owain G. Ainsworth
d2d899d157 pushVersionMessage: fix you/me address generation
The you address is the one we already set up fo the user, so either waht
we connected to (this will work with tor, etc), or the ip the user
connect to us from otherwise. We must however check to see if it is the address
of the proxy and strip it.

The me addesss, we use the same address selection for local addresses as
always

This should mean that we pass our tor address out in the version message
and thus the peers should add us to their addressmanager.
2013-12-10 19:39:47 +00:00
Owain G. Ainsworth
f93203b91e Initial basic support for selection of external ip address.
This implements only the bare bones of external ip address selection
using very similar algorithms and selection methods to bitcoind. Every
address we bind to, and if we bind to the wildcard, every listening
address is recorded, and one for the appropriate address type of the
peer is selected.

Support for fetching addresses via upnp, external services, or via the
command line are not yet implemented.

Closes #35
2013-12-10 19:39:47 +00:00
Owain G. Ainsworth
1e836d26f4 Handle tor and dns-name addresses.
Perform the requisite processing on .onion addresses to turn them into the tor
reserved ipv6 region (the same as bitcoind and onioncat). Furthermore,
when printing an ip address, reverse the conversion so we print it
nicely.  base32 as standard is uppercase, but tor and bitcoind seem to
use lowercase so we  first must for we force .onion addrs to uppercase
(and to lowercase on the reverse).

As a side effect we now should handle dns names on the command line (via tor if
required) and add them to the addressmanger as necessary.
2013-12-10 19:39:46 +00:00
Owain G. Ainsworth
d2dd40aae2 fix tor range. add :: to make sure it is parsed as v6. 2013-12-10 19:39:46 +00:00
Owain G. Ainsworth
a3d783e9e8 set up p.na before preparing our reply version message.
Means we can use that instead of generating another (next commit).
2013-12-10 19:39:46 +00:00
Dave Collins
c8e88d383e Correct issue with pushing address messages.
The code to send an address messages in batches was previously clearing
all addresses from the existing message after queueing it to be sent.
Since the message is a pointer, this means it was removing the addresses
from the same message which might not have already been sent yet (from
another goroutine) which led to a race.

This commit modifies the code to create a new address message for each
batch as intended.

Fixes #58.
2013-12-10 09:13:16 -06:00
David Hill
9e44506fdc Have TravisCI build against both latest release and tip 2013-12-08 22:11:54 -05:00
Dave Collins
f1c807231d Add TravisCI build status badge to README.md. 2013-12-08 20:44:06 -06:00
Dave Collins
6995910df5 Add support for TravisCI. 2013-12-08 20:35:56 -06:00
Dave Collins
7654eb1eb5 Cast SatoshiPerBitcoin const for createTxRawResult. 2013-12-08 20:21:23 -06:00
Francis Lam
22b61f634a Updated createTxRawResult to use btcutil.SatoshiPerBitcoin 2013-12-08 18:47:39 -05:00
Francis Lam
dd10de9e8b Fix handleGetBlock/handleGetRawTransaction to return btcjson.Error
Wrap *.BtcEncode errors into btcjson.Error when failing to encode wire
bytes to buffer
2013-12-08 18:43:47 -05:00
Francis Lam
762fc2c11c Fixed up GetRawTransaction and updated GetBlock to handle verbose
Updated handleGetRawTransaction to populate all the fields required to
match bitcoind.  It still doesn't handle MULTISIG addresses correctly.

Changed handleGetBlock to implement new optional verbose (default true)
flag and also added a verboseTx flag to return TxRawDefault instead of
Txid.  When verbose=false, GetBlock returns hex-encoded wire bytes for
the block.
2013-12-08 14:57:14 -05:00
Josh Rickmar
305be0c29f Correctly set vout for getrawtransaction.
The vout field (as part of the getrawtransaction JSON reply) should be
set to the input's previous outpoint's index, not the current input
index.

Found by flam and reported on IRC. Thanks!
2013-12-06 17:34:18 -05:00
Josh Rickmar
bbb10dc387 Make authentication check time-constant. 2013-12-05 13:21:51 -05:00
Dave Collins
786409d06e Update README.md now that Go 1.2 has been released. 2013-12-02 09:49:43 -06:00
Josh Rickmar
f22164b261 Search each txout for payment to a wallet address.
This switches a break to a continue if a txout does not include a
pay-to-pubkey-hash script type.  btcwallet only supports
pay-to-pubkey-hash at the moment, and this fixes an issue where a tx
may have an different type of output, as well as pay-to-pubkey-hash,
which may be ignored by the wallet notification code.

Found by dhill.
2013-12-02 10:35:25 -05:00
Josh Rickmar
aea23ddff3 Send nil when rescan is finished.
I previously fixed the duplicate send (before seeing GH issue #54),
but forgot that btcwallet expects a nil reply when rescan has
finished.  This adds the final reply back, but replies with nil.

Fixes #54.
2013-12-02 10:24:22 -05:00
Josh Rickmar
41ecc9f835 Do not duplicate sending final rescan tx. 2013-12-02 10:11:00 -05:00
David Hill
9e57a6c5be add --notls option to disable connecting with TLS
ok oga@
2013-11-26 20:13:31 -05:00
David Hill
295cc873f4 add getbalance 2013-11-26 17:05:33 -05:00
Josh Rickmar
de9176b94f Fix listtransactions optarg indexes. 2013-11-26 16:21:19 -05:00
Josh Rickmar
d1570c5d87 Include more data in tx-to-wallet-address notifications.
This updates the replies for rescan and tx notifications with
additional information that is needed for wallet to properly support
the listtransactions command.

While here, drastically improve rescan performance by not looking up
every sha in rescan's block range.
2013-11-26 15:07:01 -05:00
Dave Collins
dfbb9446c4 Ensure Win service bits only compile on Windows. 2013-11-25 20:58:18 -06:00
Dave Collins
45732c99fb Allow btcd to run as a Windows service.
This commit modifies btcd to run cleanly as a Windows service.  btcd is
intended to be a long running process that stays synchronized with the
bitcoin block chain and provides chain services to multiple users.  It
follows that a service is the best option on Windows for this
functionality.

A few key points are:

- Supports graceful shutdown via the service stop/shutdown commands
- Integrates cleanly with the Windows event log
- Adds a new /s flag that can be used to install/remove/start/stop the
  service

One outstanding issue is that the application data directory is currently
user specific which means, by default, if you start btcd as a user, the
same data won't be used as when it's running as a service.  This needs to
be resovled.  The most likely approach will be to put all data into the
common appdata directory Windows provides, but it will require some
additional work to deal with permissions properly as user processes can't
write there by default.

Closes #42.
2013-11-25 18:36:11 -06:00
Dave Collins
766aae5a72 Add rolling log file.
This commit modifies the logging to also log all output to a rolling log
file in the btcd home directory under the logs folder.  It uses a maximum
size of 10MB per log file and a max rotation size of 3.  This means the
log files will not exceed 30 megabytes.
2013-11-25 13:40:53 -06:00
Josh Rickmar
7e21226ca6 Update for new btcws.TxMinedNtfn information. 2013-11-25 12:54:23 -05:00
Dave Collins
8e3ede441b Update for recent go-flags API changes. 2013-11-24 12:33:36 -06:00
Josh Rickmar
6f2b96b7e2 Fix help message for listtransactions. 2013-11-22 15:18:23 -05:00
Josh Rickmar
d00ca48475 Print listtransactions replies with go-spew. 2013-11-22 14:20:31 -05:00
Owain G. Ainsworth
f843c141ae in Valid() ensure that na.IP is set. Found by drahn. 2013-11-22 18:48:36 +00:00
Dave Collins
a9bf28af4d Allow the overall log level to be dynamically set.
The code was previously only changing the logging level if it wasn't the
default which is accurate for setting the log level once at startup time,
but it needs to set it unconditionally to allow dynamic updates.
2013-11-22 12:46:44 -06:00
Josh Rickmar
ef47455b05 Add listtransactions support to btcctl.
This is currently using the generic reply formater, and will likely be
switched out for a custom one later.
2013-11-22 13:13:39 -05:00
Dave Collins
daa5310e2f Add support for debuglevel RPC command.
Also include the supported subsystems in the error message if an invalid
subsystem is specified.

Closes #15.
2013-11-22 10:47:29 -06:00
John C. Vernaleo
e930dc55f0 Change a variable name to match btcjson. 2013-11-22 08:50:15 -05:00
Dave Collins
07c656c8b5 Update doc.go with new debug level usage. 2013-11-21 20:01:11 -06:00
Dave Collins
93c1f7d31b Update utilities to use new btclog as well. 2013-11-21 19:03:55 -06:00
Dave Collins
48ab97c271 Allow per-subsystem log levels to be specified.
Now that each subsystem is capable of having its own log level, modify the
-d/--debuglevel flag to allow them to be specified.

Closes #48.
2013-11-21 17:41:45 -06:00
Dave Collins
eb8688df79 Convert btcd to use new btclog package.
Also, make every subsystem within btcd use its own logger instance so each
subsystem can have its own level specified independent of the others.

This is work towards #48.
2013-11-21 17:41:21 -06:00
Owain G. Ainsworth
41d2d36643 Only update p.na on reciept of version message for inbound peers.
Outbound we already have the exact same thing set up, and this should
quieten the race detector. Please note that this does *not* cause
problems with the service flags being wrong. Since by this point we have
already done every thing that would use the service flags from p.na in
addrmanager, and now p.Services is correct..
2013-11-21 19:19:17 +00:00
Marco Peereboom
694fccefa8 typo 2013-11-20 15:55:36 -06:00
John C. Vernaleo
3f37e881dc Remove workaround for certs on go1.1.2.
btcd now requires go1.2.  A note to that effect is in README.md.
2013-11-20 15:34:37 -05:00
Josh Rickmar
0bee8478a9 Fix optional arg handling for importprivkey. 2013-11-20 11:12:00 -05:00
Josh Rickmar
4a290162ee Add dumpprivkey to btcctl. 2013-11-19 22:36:25 -05:00
David Hill
0301690499 Create the home directory if it doesn't exist. 2013-11-19 22:21:50 -05:00
Dave Collins
9643cb6d23 Make golint happy with btcctl. 2013-11-19 20:35:49 -06:00
Dave Collins
b1ed5f75ca Add version to btcctl.
This commit adds version information to btcctl.  The plan is to keep
it in lock step with the btcd version.  It also updates the release script
so the version file is updated automatically with the btcd one.
2013-11-19 20:07:31 -06:00
Dave Collins
9c26f6c4c5 Update doc.go with recent Winows path change. 2013-11-19 18:40:56 -06:00
Dave Collins
0cc756337e Update command line options in doc.go. 2013-11-19 18:16:29 -06:00
Dave Collins
f5a7dcdcbf Default RPC server listener to localhost. 2013-11-19 17:33:36 -06:00
Josh Rickmar
5d13288174 Check auth header for websocket connections. 2013-11-19 18:28:08 -05:00
Dave Collins
9edf7d44fa Ensure generated cert contains 127.0.0.1. 2013-11-19 16:35:00 -06:00
Dave Collins
d18c34a628 Mask the password from btcctl usage. 2013-11-19 16:25:18 -06:00
Dave Collins
02e6d47590 Return error on dir create failure from loadConfig.
Rather than simply exiting on a home directory creation error, return the
error so it exits via the normal path.
2013-11-19 15:56:21 -06:00
David Hill
deb19c2fa3 Bring MarshalECPrivateKey local so < go1.2 works.
Closes #45.
2013-11-19 15:53:31 -06:00
David Hill
35eea401e1 Create the home directory if it doesn't exist. 2013-11-19 16:40:20 -05:00
Dave Collins
c2bec24f51 Improve btcctl config.
This commit improves the configuration for btcctl in several ways:

- Add the ability to specify a config file
- Add a default entry to the rpc cert to point to the location
  it will likely be in the btcd home directory
- Move the config bits into a separate file for easier maintenance
2013-11-19 14:53:22 -06:00
Dave Collins
04a2f66d64 Allow go-flags to generate help entry in btcctl. 2013-11-19 13:44:52 -06:00
Dave Collins
c3fab78e2c Use consistent case in TLS logging. 2013-11-19 11:42:56 -06:00
Dave Collins
5cd4c8265c Add subsystem to RPC cert generation log messages. 2013-11-19 11:41:31 -06:00
Dave Collins
9a853fdf90 Use single cert/key for RPC by default.
Also, while here, set the default RPC cert/key in the initial config
struct so the default will show when displaying the usage.
2013-11-19 11:38:46 -06:00
David Hill
6fcc1c9d1b Add localhost to DNSNames in generated certificate. 2013-11-19 12:02:40 -05:00
Dave Collins
e433439308 Make RPC section of sample-btcd.conf consistent. 2013-11-19 11:01:23 -06:00
Dave Collins
22600c7c67 Correct a couple of usage inconsistencies.
- Remove periods from --rpccert/--rpckey since none of the other options use
  them
- Make it a little more clear that the --listen and --rpclisten options can be
  used multiple times by changing the summary to start with "Add an ..."
  which also matches the --addpeer style
2013-11-19 10:39:38 -06:00
Owain G. Ainsworth
5d3fcc660e Handle tor addresses in addrmanager.
Both GroupKey and Tor() needed amending for tor addresses, filling in some
TODOs.

Closes #36.
Closes #37
2013-11-19 14:51:41 +00:00
Owain G. Ainsworth
75e577c82e RPC TLS Support.
All rpc sockets now listen using TLS by default, and this can not be
turned off. The keys (defauling to the datadirectory) may be provided by
--rpccert and --rpckey. If the keys do not exist we will generate a new
self-signed keypair with some sane defaults (hostname and all current
interface addresses).

Additionally add tls capability to btcctl so that it can still be used.
The certificate to use for verify can be provided on the commandline or
verification can be turned off (this leaves you susceptible to MITM
attacks)

Initial code from dhill (rpc tls support) and jrick (key generation),
cleanup, debugging and polishing from me.
2013-11-19 14:50:31 +00:00
Owain G. Ainsworth
5da5dfe1c4 Add --rpclisten that behaves frighteningly similar to --listen.
Except it works for the rpcserver instead of the main server.

Closes #34
2013-11-19 14:48:58 +00:00
Josh Rickmar
5ec4aaff09 Add importprivkey support to btcctl. 2013-11-18 14:51:31 -05:00
Dave Collins
5bf879dcfc Minor cleanup of some documentation and comments. 2013-11-18 10:38:24 -06:00
Dave Collins
37d3d83ed3 Improve mempool handling.
- Lock the mempool when removing transactions during a notification as
  intended
- When generating the inventory vectors to serve on a mempool request,
  recheck the memory pool for each hash since it's possible another thread
  could have removed an entry after the initial query for available
  hashes
- When a block is connected, remove any transactions which are now double
  spends as a result of the newly connected transactions
2013-11-15 16:23:27 -06:00
David Hill
7b7d4e8555 fix typo - now addresses show up in getrawtransaction 2013-11-15 16:12:08 -05:00
Dave Collins
6b8c10d1fb Don't load default config file in regtest mode.
The regression test mode is special and therefore most likely will not
want to use the same settings that are in the configuration file.  The -C
option can still be used to specify a config file in regression test mode
if desired.
2013-11-15 14:43:36 -06:00
Dave Collins
527a08eb14 Convert chain RuleError to TxRuleError in mempool.
When a transaction is being checked for acceptance into the transation
memory pool, it makes use of a chain function to ensure the invariant rules
for what transactions are allowed into a block are not violated.  That
function returns a btcchain.RuleError if rules are violated.  However,
since this code path is tailored to free-standing transactions, the error
needs to be converted to a TxRuleError so the caller can properly detect
the transaction as a rejected transaction instead of treating it like an
real error.
2013-11-15 11:59:32 -06:00
Dave Collins
166f8c9ae5 Don't relay resurrected transactions.
This commit modifies the transaction memory pool handling so that it does
not relay resurrected transactions.  The other peers on the network will
also be reorganizing to the same block, so they already know about them.
2013-11-15 09:52:47 -06:00
Dave Collins
9fb17c3a6d Improve usage display a little.
This commit makes use of the new default-mask go-flags option in
conjunction with delaying the usage display until after the config file is
parsed.  This has a couple of nice properties such as showing the actual
values that will be used as loaded from the specific config file instead
of the defaults specified in btcd itself, and also allows any config file
parsing errors to be shown prior to displaying the usage.
2013-11-14 20:38:27 -06:00
Dave Collins
50388bcf66 Add more mempool standard checks.
This commit adds a few more checks to restrict what transactions are
allowed into the transaction memory pool and therefore are candidates
to be mined and relayed.

In particular, the following changes were made to what is considered
standard:

- nulldata scripts are now supported and considered standard
- multi-signature transaction are now checked to ensure they only have a
  max of 3 pubkeys and the number of signatures doesn't exceed the number
  of pubkeys
- the number of inputs to a signature script must now match the expected
  number of inputs for the script type (includes support for additional
  pay-to-script-hash inputs)
- the number of inputs pushed onto the stack by a redeeming sig script
  must match the number of inputs consumed by the referenced pk script
- there can now only be a max of one nulldata output per transaction
2013-11-14 17:40:12 -06:00
Dave Collins
e3eca752da Allow verbose param on btcctl getrawtransaction. 2013-11-14 12:19:20 -06:00
Dave Collins
c3a3fbcabf Don't bother parsing listeners if disabled. 2013-11-13 21:16:49 -06:00
Dave Collins
3902a71bee Minor cleanup. 2013-11-13 21:12:41 -06:00
Dave Collins
ac375df71f Use the passed addresses in parseListeners.
Also, check the return error on parseListeners to catch invalid IP
addresses.
2013-11-13 21:02:34 -06:00
Dave Collins
50484c5841 Update sample config file with recent changes.
This commit updates the sample config file to add the new listen option
and update the semantics regarding the combination of --proxy and --tor
flags.
2013-11-13 20:20:32 -06:00
Dave Collins
7b86bec825 Comment new parseLiteners function. 2013-11-13 19:43:36 -06:00
Owain G. Ainsworth
6116a6cb02 Support --listen.
This allows the provision of address/port pairs to be listened on instead
of just providing the port. e.g.:
btcd --listen 1.2.3.4:4321 --listen 127.0.0.01 --listen [::1]:5432

When --proxy and --connect are used, we disable listening *unless* any --listen
arguments have been provided, when we will listen on those addresses as
requested.

Initial code by davec, integration by myself.

Closes #33

allow listens to fail, but warn. error if all failed

fmt
2013-11-14 01:15:47 +00:00
Dave Collins
3108b94401 Add 0.3.3 deps to deps.txt. 2013-11-13 10:53:57 -06:00
Dave Collins
58fdcec6e2 Prepare for release 0.3.3. 2013-11-13 10:52:14 -06:00
Owain G. Ainsworth
31a97d5c09 look up tx in mempool first for getrawtransaction
Closes #26
2013-11-12 22:57:27 +00:00
Owain G. Ainsworth
bb276b53aa Add support for the verifychain command.
So far we only do level 0 and level 1 checks (precense and basic
sanity). The checks done at higher levels in bitcoind are closely
coupled with their database layout.

arguably Closes #13
2013-11-12 22:57:26 +00:00
Josh Rickmar
31f27cffd5 Safely remove elements from list.Lists. 2013-11-12 16:24:32 -05:00
Josh Rickmar
afc520634f Process all tx notifications, then notify new block.
This change allows wallet to record all transactions in a block before
receving the new block notification, and then process them all
together when the blockconnected notification arrives.
2013-11-12 14:50:33 -05:00
Josh Rickmar
77e1af792b Fix mutex handling for removing minedtx requests.
Previously, RemoveMinedTxRequest was being run from a caller which
held a reader lock for the websocket request contexts.  When
RemoveMinedTxRequest tried to grab a writer lock, it would block.
This change creates a new function, removeMinedTxRequest, that does
not grab any locks, and the caller (NotifyBlockConnected) grabs a
writer lock instead of a reader lock.
2013-11-11 14:23:11 -05:00
Josh Rickmar
8b5413a4ac Always release ws context reader lock.
Previously, on a blockconnected notification, the websocket context
reader lock was not always being given up properly.  This change
defers the unlock so it will always happen.

This fixes an issue where wallet will stop responding (due to not
being able to complete its handshake) on reconnect.
2013-11-11 12:54:49 -05:00
Dave Collins
dcf2994905 Add some missed imports in last commit. 2013-11-11 11:52:59 -06:00
Dave Collins
89eae6f590 Update utilities to use new btcutil.AppDataDir.
Closes #30.
2013-11-11 10:58:39 -06:00
Dave Collins
72c186f9a9 Migrate to new app data directories.
This commit makes use of the new btcutil.AppDataDir function which chooses
appropriate data directories for each supported operating system.  It also
adds code to the upgrade path to properly migrate existing data from the
old to new locations.

This is part of work toward issue #30.
2013-11-11 10:58:38 -06:00
Josh Rickmar
41838b83b0 Use btcws package for wallet notifications. 2013-11-08 12:44:00 -05:00
Dave Collins
af311078b4 Correct btcctl getblockhash.
Also run gofmt while here.
2013-11-07 17:07:26 -06:00
Owain G. Ainsworth
d81a2c9067 Check for 0 shas before dereferencing list 2013-11-07 22:26:43 +00:00
Josh Rickmar
ced24946a4 Rework JSON handlers to take a btcjson.Cmd.
This change reworks where the command parsing occurs to be done before
handlers are checked.  Before, the websocket extension handler called
the standard handler with the same message, and if it was unhandled,
would unmarshal it a second time for checking extension handlers.
2013-11-07 13:53:41 -05:00
Josh Rickmar
d403863e2b Don't marshal a function for getbestblockhash replies. 2013-11-07 13:53:22 -05:00
Josh Rickmar
b97a2145d8 Try to match bitcoind sendrawtransaction RPC errors. 2013-11-07 10:34:55 -05:00
Owain G. Ainsworth
c1a1e6b6b2 keep track of connected groups as we gain and lose outbound peers.
Instead of recalculating every time we go to look for more peers. Should
save a few cycles.
2013-11-06 19:08:06 +00:00
Owain G. Ainsworth
6949a4f940 Have a list per class of peer instead of just one.
persistentpeers and outbound(nonpersistent) peers get their own lists,
so iterating over them can be much simpler (and quicker when you have
125 peer of which 8 are outbound).
2013-11-06 19:08:06 +00:00
Owain G. Ainsworth
4d80750afe Move all local data to peerhandler into a peerState structure
Pass peerstate around instead of indivdual bits.
2013-11-06 18:52:58 +00:00
Owain G. Ainsworth
d8c5222474 Rework the way we send notifications over the websocket
Redo the datastructures we search so that we only do one lookup per txin and
txout instead of doing a loop per wallet connection.

Don't send spent data on tx notifications, this can be worked out in wallet and
it is expensiveish to calculate. However we DO check upon getting a notification
request if the output is already spent, and in which case we send an immediate
notification to force a rescan.

MinedTxNotfications are handled separately to the connected block messages
largely to enable this to scale rather better.

Tested by jrick (who found one bug i had introduced, thanks!)

Additionally (accidentally squashed in):

Add handlers for all known commands.

We have handlers for all wallet-requiring commands that will return a suitable
error.

Unimplemented commands temporarily return an error stating so.
2013-11-06 18:47:06 +00:00
Josh Rickmar
8c2b9ae06e Begin using btcws.
This change adds support for the unmarshaling custom commands sent by
btcwallet and supported in the btcws package.
2013-11-06 11:20:36 -05:00
John C. Vernaleo
b72f0c6474 Make peerinfo output match bitcoind a bit better.
Add long option for username in btcctl.
2013-11-04 16:25:13 -05:00
Josh Rickmar
1f087adf15 Separate ws command handlers into separate funcs. 2013-11-04 14:15:05 -05:00
Josh Rickmar
53e1c2d6bd Stringify the address hash for txRequests map.
This change allows map lookups using address hashes (which are
returned as []byte) instead of either copying the hash into an array,
or doing a bytes.Equal().

A stupid range over a map until the right key is found was also just
changed to a single map lookup.
2013-11-04 14:11:39 -05:00
Josh Rickmar
2511fee152 Complete rescan extension.
This adds to the initial rescan implementation, but switches it to
rescan based on a group of addresses, rather than just one.  Due to
how expensive database lookups are during a rescan, wallets should
take advantage of this to rescan once for all needed addresses for all
accounts.
2013-11-01 09:35:52 -04:00
Dave Collins
5a46de5103 Add support for min required fees in tx mempool.
This commit adds checks to the memory pool to restrict allowed
transactions based on minimum required fees.
2013-10-31 12:24:28 -05:00
Dave Collins
a4794798d4 Make use of new btcwire SerializeSize API.
This commit changes the various cases that were serializing transactions
into a buffer and taking the length to use the new faster SerializeSize
API.  It also completes a TODO since the serialized size of a transaction
output is now available.
2013-10-31 00:28:37 -05:00
Dave Collins
470ef8c58e Use correct calls to tx.Sha() in RPC server. 2013-10-30 18:40:55 -05:00
Dave Collins
70094fcee8 Minor cleanup.
Fix a couple of comments and call tx.Sha directly in a couple of places.
2013-10-30 14:13:29 -05:00
Dave Collins
b866e9f14c Improve RPC server log of rejected transactions.
Rather than showing all errors from ProcessTransaction as an error, check
if the error is a TxRuleError meaning the transaction was rejected as
opposed to something actually going wrong and log it accordingly.
2013-10-30 14:11:45 -05:00
Dave Collins
e76fada2d2 Optimize NotifyNewTxListener.
Looking up transactions from the database is an expensive operation.
This commit modifies the NotifyNewTxListener code to simply iterate the
transactions in the block instead of looking them up from the db.

Currently the wallet code needs a spent flag which ultimately shouldn't be
required.  For now, the spent data is simply created on the fly which is
still significantly faster than doing database transaction lookups.

Closes #24.
2013-10-30 10:54:03 -05:00
Josh Rickmar
d7d2385fb0 Return JSON id with errors when possible.
This change unbreaks the case where an unknown command is sent to the
RPC server.  Instead of replying back with a nil JSON id, if the
initial unmarshal was successful (and thus, the message was valid
JSON-RPC), the unmarshaled id will be used in the error reply.
2013-10-29 23:23:22 -04:00
Dave Collins
9442d96929 Convert errors in RPC server to new btcjson consts. 2013-10-29 19:42:21 -05:00
Owain G. Ainsworth
231efa35f5 Provide useragent in getpeerinfo now we know it. 2013-10-29 23:00:23 +00:00
David Hill
4f25d45e77 Grab the remote peer's user agent. 2013-10-29 21:45:45 +00:00
Owain G. Ainsworth
59e2b204a3 Add doc comments. 2013-10-29 20:30:16 +00:00
Owain G. Ainsworth
da2901a4fd Add support for the addnode command
Currently this acts on the list of all peers, and not just the list of
permanent peers. this will be changed shortly.
2013-10-29 20:30:07 +00:00
Owain G. Ainsworth
b1f14732b1 Implement getpeerinfo and getconnectedcount
We have a channel for queries and commands in server, where we pass in
args and the channel to reply from, let rpcserver use these interfaces
to provide the requistie information.

So far not all of the informaation is 100% correct, the syncpeer
information needs to be fetched from blockmanager, the subversion isn't
recorded and the number of bytes sent and recieved needs to be obtained
from btcwire. The rest should be correct.
2013-10-29 17:05:12 +00:00
Owain G. Ainsworth
d647eea2b7 update statistics for the time we last send/recieved a message. 2013-10-29 17:02:04 +00:00
Owain G. Ainsworth
d26b8b2d43 Set peer.timeConnected when we are actualy connected. 2013-10-29 17:02:04 +00:00
Owain G. Ainsworth
c242d75866 Split the massive switch statement in rpcserver into a map of handlers.
Cleans up a the code a lot and removes the need to be careful about
shadowing err.

Much rejoicing from jcv, jrick and davec.
2013-10-29 17:00:25 +00:00
Owain G. Ainsworth
b96cb4317a Alphabetise command handlers. 2013-10-29 17:00:25 +00:00
Owain G. Ainsworth
975640c6b3 Switch rpcserver.go over to using the jsoncmd apis. 2013-10-29 16:59:19 +00:00
Owain G. Ainsworth
3c405563bd Add missing return; avoids panic. 2013-10-29 16:42:26 +00:00
Owain G. Ainsworth
9f96e59392 Rough and ready cut over of btcctl to use the btcjson command api. 2013-10-29 16:42:11 +00:00
Dave Collins
08fc3050a3 Convert to use new btcutil.Tx and btcchain APIs.
This commit updates btcd to work with the new btcchain APIs which now
accept btcutil.Tx instead of raw btcwire.MsgTx.  It also modifies the
transaction memory pool to store btcutil.Tx.

This is part of the ongoing transaction hash optimization effort noted in
conformal/btcd#25.
2013-10-28 15:47:24 -05:00
Dave Collins
7d8bb5ab4c Add --cpuprofile option.
This commit provides a new --cpuprofile flag that can be used to specify a
file path to write CPU profile data into.  The resulting profile can then be
consumed by the 'go tool pprof' command.
2013-10-26 16:13:50 -05:00
Dave Collins
1aa65e6863 Correct and improve SIGINT (Ctrl+C) handling.
Since the main SIGINT handler is running as a goroutine, the main
goroutine must be kept active long enough for it to finish or it will be
nuked when the main goroutine exits.  This commit makes that happen by
slightly modifying how it waits for shutdown.

Rather than having the main goroutine only wait for the server to
shutdown, there is now a shutdown channel that is used to signal the main
goroutine to shutdown for all cases such as a graceful shutdown, a
scheduled shutdown, an RPC stop, or a SIGINT.

While here, also add a few prints to indicate a SIGINT was received and
the shutdown progress.
2013-10-26 16:10:04 -05:00
Dave Collins
36bd4b8994 Revert "Enable memdb support."
This reverts commit 1fadf619c7.
2013-10-26 01:58:50 -05:00
Dave Collins
7b01074f5e Add simple benchmark for MruInventoryMap handling. 2013-10-26 01:57:00 -05:00
Dave Collins
261e61f8ee Vastly optimize the MRU inventory handling.
Profiling showed the MRU inventory handling was taking 5% of the total
block handling time.  Upon inspection this is because the original
implementation was extremely inefficient using iteration to find the
oldest node for eviction.

This commit reworks it to use a map and list in order to achieve close to
O(1) performance for lookups, insertion, and eviction.

The following benchmark results show the difference:

Before: BenchmarkMruInventoryList          100     1069168 ns/op
After:  BenchmarkMruInventoryList     10000000         152 ns/op

Closes #21
2013-10-26 01:39:53 -05:00
Dave Collins
1fadf619c7 Enable memdb support. 2013-10-25 21:41:17 -05:00
Dave Collins
24a5645fce Update for recent btcchain tx script val change.
The ValidateTransactionScripts was recently changed to accept script flags
which pass through to the script engine in order to control its validation
behavior.  This commit modifies the transaction memory pool script
validation code for this change and additionally adds the new flag to
perform canonical signtaure checking.
2013-10-25 15:36:31 -04:00
Josh Rickmar
618b885e9e Notify wallets of mined transactions.
This change allows btcwallet to keep a pool of transactions that have
not yet been mined into a block, notifying wallet when transactions
are mined, as well as introducing a new way to send the
btcd:blockconnected notification with wallet-specific information as
part of the same notification.  When a transaction is sent using the
RPC call 'sendrawtransaction', a notification request will be
automatically registered with the connected wallet (if using
websockets) to notify the wallet when the transaction first appears in
a block.

To perform this notification, and to avoid requiring wallets from
waiting for seperate mined tx notifications (and resend after a
timeout) or from sending an additional tx mined request for every tx
in the pool after each new block, the blockconnected notification is
now created seperately for each wallet.  If the notified wallet has
sent a transaction, an additional JSON field "minedtxs" will include
an array of transaction IDs that the wallet has created and which are
included in the new block.

This new unique blockconnected notification can also be used for
additional notifications that may happen each new block in the future,
and to cut down on existing notification handlers in btcwallet, such
as for transactions to a watched address.
2013-10-23 18:13:03 -04:00
Dave Collins
2bb8c5d5cc Bring back default redirect for profiling.
Now that the RPC server uses its own mux, bring back the code which does
the automatic 303 redirect for the root of the profile server.
2013-10-23 10:48:17 -05:00
Dave Collins
5a4772bdc6 Change the RPC server to use it's own Muxer.
Rather than relying on the http package's DefaultServeMux for the RPC
server, create a unique mux specifically for the RPC server.  This ensures
things, such as the http profiling handlers, do not commingle.
2013-10-23 10:45:43 -05:00
Dave Collins
8970d4bf99 Revert "Add default redirect for profiling when enabled."
This reverts commit cc6cfdad9e.
2013-10-23 10:25:09 -05:00
Dave Collins
cc6cfdad9e Add default redirect for profiling when enabled.
This commit adds a simple 303 redirect for the root of profile server so
launching it without a path automatically goes to the debug profiling
page.
2013-10-23 09:39:02 -05:00
Dave Collins
7846b2f4e2 Update README.md TODO to remove 32-bit MSI.
32-bit MSIs will now be available for all future releases.
2013-10-22 17:05:43 -05:00
42 changed files with 8428 additions and 2453 deletions

4
.travis.yml Normal file
View File

@@ -0,0 +1,4 @@
language: go
go:
- release
- tip

303
CHANGES
View File

@@ -3,6 +3,309 @@ User visible changes for btcd
A full-node bitcoin implementation written in Go
============================================================================
Changes in 0.7.0 (Thu Feb 20 2014)
- Fix an issue when parsing scripts which contain a multi-signature script
which require zero signatures such as testnet block
000000001881dccfeda317393c261f76d09e399e15e27d280e5368420f442632
(https://github.com/conformal/btcscript/issues/7)
- Add check to ensure all transactions accepted to mempool only contain
canonical data pushes (https://github.com/conformal/btcscript/issues/6)
- Fix an issue causing excessive memory consumption
- Significantly rework and improve the websocket notification system:
- Each client is now independent so slow clients no longer limit the
speed of other connected clients
- Potentially long-running operations such as rescans are now run in
their own handler and rate-limited to one operation at a time without
preventing simultaneous requests from the same client for the faster
requests or notifications
- A couple of scenarios which could cause shutdown to hang have been
resolved
- Update notifynewtx notifications to support all address types instead
of only pay-to-pubkey-hash
- Provide a --rpcmaxwebsockets option to allow limiting the number of
concurrent websocket clients
- Add a new websocket command notifyallnewtxs to request notifications
(https://github.com/conformal/btcd/issues/86) (thanks @flammit)
- Improve btcctl utility in the following ways:
- Add getnetworkhashps command
- Add gettransaction command (wallet-specific)
- Add signmessage command (wallet-specific)
- Update getwork command to accept
- Continue cleanup and work on implementing the RPC API:
- Implement getnettotals command
(https://github.com/conformal/btcd/issues/84)
- Implement networkhashps command
(https://github.com/conformal/btcd/issues/87)
- Update getpeerinfo to always include syncnode field even when false
- Remove help addenda for getpeerinfo now that it supports all fields
- Close standard RPC connections on auth failure
- Provide a --rpcmaxclients option to allow limiting the number of
concurrent RPC clients (https://github.com/conformal/btcd/issues/68)
- Include IP address in RPC auth failure log messages
- Resolve a rather harmless data races found by the race detector
(https://github.com/conformal/btcd/issues/94)
- Increase block priority size and max standard transaction size to 50k
and 100k, respectively (https://github.com/conformal/btcd/issues/71)
- Add rate limiting of free transactions to the memory pool to prevent
penny flooding (https://github.com/conformal/btcd/issues/40)
- Provide a --logdir option (https://github.com/conformal/btcd/issues/95)
- Change the default log file path to include the network
- Add a new ScriptBuilder interface to btcscript to support creation of
custom scripts (https://github.com/conformal/btcscript/issues/5)
- General code cleanup
Changes in 0.6.0 (Tue Feb 04 2014)
- Fix an issue when parsing scripts which contain invalid signatures that
caused a chain fork on block
0000000000000001e4241fd0b3469a713f41c5682605451c05d3033288fb2244
- Correct an issue which could lead to an error in removeBlockNode
(https://github.com/conformal/btcchain/issues/4)
- Improve addblock utility as follows:
- Check imported blocks against all chain rules and checkpoints
- Skip blocks which are already known so you can stop and restart the
import or start the import after you have already downloaded a portion
of the chain
- Correct an issue where the utility did not shutdown cleanly after
processing all blocks
- Add error on attempt to import orphan blocks
- Improve error handling and reporting
- Display statistics after input file has been fully processed
- Rework, optimize, and improve headers-first mode:
- Resuming the chain sync from any point before the final checkpoint
will now use headers-first mode
(https://github.com/conformal/btcd/issues/69)
- Verify all checkpoints as opposed to only the final one
- Reduce and bound memory usage
- Rollback to the last known good point when a header does not match a
checkpoint
- Log information about what is happening with headers
- Improve btcctl utility in the following ways:
- Add getaddednodeinfo command
- Add getnettotals command
- Add getblocktemplate command (wallet-specific)
- Add getwork command (wallet-specific)
- Add getnewaddress command (wallet-specific)
- Add walletpassphrasechange command (wallet-specific)
- Add walletlock command (wallet-specific)
- Add sendfrom command (wallet-specific)
- Add sendmany command (wallet-specific)
- Add settxfee command (wallet-specific)
- Add listsinceblock command (wallet-specific)
- Add listaccounts command (wallet-specific)
- Add keypoolrefill command (wallet-specific)
- Add getreceivedbyaccount command (wallet-specific)
- Add getrawchangeaddress command (wallet-specific)
- Add gettxoutsetinfo command (wallet-specific)
- Add listaddressgroupings command (wallet-specific)
- Add listlockunspent command (wallet-specific)
- Add listlock command (wallet-specific)
- Add listreceivedbyaccount command (wallet-specific)
- Add validateaddress command (wallet-specific)
- Add verifymessage command (wallet-specific)
- Add sendtoaddress command (wallet-specific)
- Continue cleanup and work on implementing the RPC API:
- Implement submitblock command
(https://github.com/conformal/btcd/issues/61)
- Implement help command
- Implement ping command
- Implement getaddednodeinfo command
(https://github.com/conformal/btcd/issues/78)
- Implement getinfo command
- Update getpeerinfo to support bytesrecv and bytessent
(https://github.com/conformal/btcd/issues/83)
- Improve and correct several RPC server and websocket areas:
- Change the connection endpoint for websockets from /wallet to /ws
(https://github.com/conformal/btcd/issues/80)
- Implement an alternative authentication for websockets so clients
such as javascript from browsers that don't support setting HTTP
headers can authenticate (https://github.com/conformal/btcd/issues/77)
- Add an authentication deadline for RPC connections
(https://github.com/conformal/btcd/issues/68)
- Use standard authentication failure responses for RPC connections
- Make automatically generated certificate more standard so it works
from client such as node.js and Firefox
- Correct some minor issues which could prevent the RPC server from
shutting down in an orderly fashion
- Make all websocket notifications require registration
- Change the data sent over websockets to text since it is JSON-RPC
- Allow connections that do not have an Origin header set
- Expose and track the number of bytes read and written per peer
(https://github.com/conformal/btcwire/issues/6)
- Correct an issue with sendrawtransaction when invoked via websockets
which prevented a minedtx notification from being added
- Rescan operations issued from remote wallets are no stopped when
the wallet disconnects mid-operation
(https://github.com/conformal/btcd/issues/66)
- Several optimizations related to fetching block information from the
database
- General code cleanup
Changes in 0.5.0 (Mon Jan 13 2014)
- Optimize initial block download by introducing a new mode which
downloads the block headers first (up to the final checkpoint)
- Improve peer handling to remove the potential for slow peers to cause
sluggishness amongst all peers
(https://github.com/conformal/btcd/issues/63)
- Fix an issue where the initial block sync could stall when the sync peer
disconnects (https://github.com/conformal/btcd/issues/62)
- Correct an issue where --externalip was doing a DNS lookup on the full
host:port instead of just the host portion
(https://github.com/conformal/btcd/issues/38)
- Fix an issue which could lead to a panic on chain switches
(https://github.com/conformal/btcd/issues/70)
- Improve btcctl utility in the following ways:
- Show getdifficulty output as floating point to 6 digits of precision
- Show all JSON object replies formatted as standard JSON
- Allow btcctl getblock to accept optional params
- Add getaccount command (wallet-specific)
- Add getaccountaddress command (wallet-specific)
- Add sendrawtransaction command
- Continue cleanup and work on implementing RPC API calls
- Update getrawmempool to support new optional verbose flag
- Update getrawtransaction to match the reference client
- Update getblock to support new optional verbose flag
- Update raw transactions to fully match the reference client including
support for all transaction types and address types
- Correct getrawmempool fee field to return BTC instead of Satoshi
- Correct getpeerinfo service flag to return 8 digit string so it
matches the reference client
- Correct verifychain to return a boolean
- Implement decoderawtransaction command
- Implement createrawtransaction command
- Implement decodescript command
- Implement gethashespersec command
- Allow RPC handler overrides when invoked via a websocket versus
legacy connection
- Add new DNS seed for peer discovery
- Display user agent on new valid peer log message
(https://github.com/conformal/btcd/issues/64)
- Notify wallet when new transactions that pay to registered addresses
show up in the mempool before being mined into a block
- Support a tor-specific proxy in addition to a normal proxy
(https://github.com/conformal/btcd/issues/47)
- Remove deprecated sqlite3 imports from utilities
- Remove leftover profile write from addblock utility
- Quite a bit of code cleanup and refactoring to improve maintainability
Changes in 0.4.0 (Thu Dec 12 2013)
- Allow listen interfaces to be specified via --listen instead of only the
port (https://github.com/conformal/btcd/issues/33)
- Allow listen interfaces for the RPC server to be specified via
--rpclisten instead of only the port
(https://github.com/conformal/btcd/issues/34)
- Only disable listening when --connect or --proxy are used when no
--listen interface are specified
(https://github.com/conformal/btcd/issues/10)
- Add several new standard transaction checks to transaction memory pool:
- Support nulldata scripts as standard
- Only allow a max of one nulldata output per transaction
- Enforce a maximum of 3 public keys in multi-signature transactions
- The number of signatures in multi-signature transactions must not
exceed the number of public keys
- The number of inputs to a signature script must match the expected
number of inputs for the script type
- The number of inputs pushed onto the stack by a redeeming signature
script must match the number of inputs consumed by the referenced
public key script
- When a block is connected, remove any transactions from the memory pool
which are now double spends as a result of the newly connected
transactions
- Don't relay transactions resurrected during a chain switch since
other peers will also be switching chains and therefore already know
about them
- Cleanup a few cases where rejected transactions showed as an error
rather than as a rejected transaction
- Ignore the default configuration file when --regtest (regression test
mode) is specified
- Implement TLS support for RPC including automatic certificate generation
- Support HTTP authentication headers for web sockets
- Update address manager to recognize and properly work with Tor
addresses (https://github.com/conformal/btcd/issues/36) and
(https://github.com/conformal/btcd/issues/37)
- Improve btcctl utility in the following ways:
- Add the ability to specify a configuration file
- Add a default entry for the RPC cert to point to the location
it will likely be in the btcd home directory
- Implement --version flag
- Provide a --notls option to support non-TLS configurations
- Fix a couple of minor races found by the Go race detector
- Improve logging
- Allow logging level to be specified on a per subsystem basis
(https://github.com/conformal/btcd/issues/48)
- Allow logging levels to be dynamically changed via RPC
(https://github.com/conformal/btcd/issues/15)
- Implement a rolling log file with a max of 10MB per file and a
rotation size of 3 which results in a max logging size of 30 MB
- Correct a minor issue with the rescanning websocket call
(https://github.com/conformal/btcd/issues/54)
- Fix a race with pushing address messages that could lead to a panic
(https://github.com/conformal/btcd/issues/58)
- Improve which external IP address is reported to peers based on which
interface they are connected through
(https://github.com/conformal/btcd/issues/35)
- Add --externalip option to allow an external IP address to be specified
for cases such as tor hidden services or advanced network configurations
(https://github.com/conformal/btcd/issues/38)
- Add --upnp option to support automatic port mapping via UPnP
(https://github.com/conformal/btcd/issues/51)
- Update Ctrl+C interrupt handler to properly sync address manager and
remove the UPnP port mapping (if needed)
- Continue cleanup and work on implementing RPC API calls
- Add importprivkey (import private key) command to btcctl
- Update getrawtransaction to provide addresses properly, support
new verbose param, and match the reference implementation with the
exception of MULTISIG (thanks @flammit)
- Update getblock with new verbose flag (thanks @flammit)
- Add listtransactions command to btcctl
- Add getbalance command to btcctl
- Add basic support for btcd to run as a native Windows service
(https://github.com/conformal/btcd/issues/42)
- Package addblock utility with Windows MSIs
- Add support for TravisCI (continuous build integration)
- Cleanup some documentation and usage
- Several other minor bug fixes and general code cleanup
Changes in 0.3.3 (Wed Nov 13 2013)
- Significantly improve initial block chain download speed
(https://github.com/conformal/btcd/issues/20)
- Add a new checkpoint at block height 267300
- Optimize most recently used inventory handling
(https://github.com/conformal/btcd/issues/21)
- Optimize duplicate transaction input check
(https://github.com/conformal/btcchain/issues/2)
- Optimize transaction hashing
(https://github.com/conformal/btcd/issues/25)
- Rework and optimize wallet listener notifications
(https://github.com/conformal/btcd/issues/22)
- Optimize serialization and deserialization
(https://github.com/conformal/btcd/issues/27)
- Add support for minimum transaction fee to memory pool acceptance
(https://github.com/conformal/btcd/issues/29)
- Improve leveldb database performance by removing explicit GC call
- Fix an issue where Ctrl+C was not always finishing orderly database
shutdown
- Fix an issue in the script handling for OP_CHECKSIG
- Impose max limits on all variable length protocol entries to prevent
abuse from malicious peers
- Enforce DER signatures for transactions allowed into the memory pool
- Separate the debug profile http server from the RPC server
- Rework of the RPC code to improve performance and make the code cleaner
- The getrawtransaction RPC call now properly checks the memory pool
before consulting the db (https://github.com/conformal/btcd/issues/26)
- Add support for the following RPC calls: getpeerinfo, getconnectedcount,
addnode, verifychain
(https://github.com/conformal/btcd/issues/13)
(https://github.com/conformal/btcd/issues/17)
- Implement rescan websocket extension to allow wallet rescans
- Use correct paths for application data storage for all supported
operating systems (https://github.com/conformal/btcd/issues/30)
- Add a default redirect to the http profiling page when accessing the
http profile server
- Add a new --cpuprofile option which can be used to generate CPU
profiling data on platforms that support it
- Several other minor performance optimizations
- Other minor bug fixes and general code cleanup
Changes in 0.3.2 (Tue Oct 22 2013)
- Fix an issue that could cause the download of the block chain to stall
(https://github.com/conformal/btcd/issues/12)

View File

@@ -1,4 +1,4 @@
Copyright (c) 2013 Conformal Systems LLC.
Copyright (c) 2013-2014 Conformal Systems LLC.
Permission to use, copy, modify, and distribute this software for any
purpose with or without fee is hereby granted, provided that the above

View File

@@ -1,6 +1,9 @@
btcd
====
[![Build Status](https://travis-ci.org/conformal/btcd.png?branch=master)]
(https://travis-ci.org/conformal/btcd)
btcd is an alternative full node bitcoin implementation written in Go (golang).
This project is currently under active development and is in an Alpha state.
@@ -22,8 +25,14 @@ One key difference between btcd and bitcoind is that btcd does *NOT* include
wallet functionality and this was a very intentional design decision. See the
blog entry [here](https://blog.conformal.com/btcd-not-your-moms-bitcoin-daemon)
for more details. This means you can't actually make or receive payments
directly with btcd. That functionality will be provided by the forthcoming
btcwallet and btcgui.
directly with btcd. That functionality is provided by the
[btcwallet](https://github.com/conformal/btcwallet) and
[btcgui](https://github.com/conformal/btcgui) projects which are both under
active development.
## Requirements
[Go](http://golang.org) 1.2 or newer.
## Installation
@@ -76,19 +85,14 @@ $ ./btcd
To subscribe to a given list, send email to list+subscribe@opensource.conformal.com
## TODO
## Issue Tracker
The following is a brief overview of the next things we have planned to work on
for btcd. Note this does not include the separate btcwallet and btcgui which
are currently under heavy development:
The [integrated github issue tracker](https://github.com/conformal/btcd/issues)
is used for this project.
- Documentation
- Code cleanup
- Add remaining missing RPC calls
- Add option to allow btcd run as a daemon/service
- Complete several TODO items in the code
- Offer 32-bit MSI as well as the 64-bit one
- Offer cross-compiled binaries for popular OSes (Fedora, Ubuntu, FreeBSD, OpenBSD)
## Documentation
The documentation is a work-in-progress. It uses the [github wiki](https://github.com/conformal/btcd/wiki) facility.
## GPG Verification Key
@@ -112,4 +116,4 @@ signature perform the following:
## License
btcd is licensed under the liberal ISC License.
btcd is licensed under the [copyfree](http://copyfree.org) ISC License.

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2013 Conformal Systems LLC.
// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
@@ -8,6 +8,7 @@ import (
"bytes"
"container/list"
crand "crypto/rand" // for seeding
"encoding/base32"
"encoding/binary"
"encoding/json"
"fmt"
@@ -19,6 +20,7 @@ import (
"os"
"path/filepath"
"strconv"
"strings"
"sync"
"sync/atomic"
"time"
@@ -138,7 +140,11 @@ func (a *AddrManager) updateAddress(netAddr, srcAddr *btcwire.NetAddress) {
return
}
} else {
ka = &knownAddress{na: netAddr, srcAddr: srcAddr}
// Make a copy of the net address to avoid races since it is
// updated elsewhere in the addrmanager code and would otherwise
// change the actual netaddress on the peer.
netAddrCopy := *netAddr
ka = &knownAddress{na: &netAddrCopy, srcAddr: srcAddr}
a.addrIndex[addr] = ka
a.nNew++
// XXX time penalty?
@@ -153,7 +159,7 @@ func (a *AddrManager) updateAddress(netAddr, srcAddr *btcwire.NetAddress) {
// Enforce max addresses.
if len(a.addrNew[bucket]) > newBucketSize {
log.Tracef("AMGR: new bucket is full, expiring old ")
amgrLog.Tracef("new bucket is full, expiring old ")
a.expireNew(bucket)
}
@@ -161,7 +167,7 @@ func (a *AddrManager) updateAddress(netAddr, srcAddr *btcwire.NetAddress) {
ka.refs++
a.addrNew[bucket][addr] = ka
log.Tracef("AMGR: Added new address %s for a total of %d addresses",
amgrLog.Tracef("Added new address %s for a total of %d addresses",
addr, a.nTried+a.nNew)
}
@@ -259,7 +265,7 @@ func (a *AddrManager) expireNew(bucket int) {
var oldest *knownAddress
for k, v := range a.addrNew[bucket] {
if bad(v) {
log.Tracef("AMGR: expiring bad address %v", k)
amgrLog.Tracef("expiring bad address %v", k)
delete(a.addrNew[bucket], k)
v.refs--
if v.refs == 0 {
@@ -277,7 +283,7 @@ func (a *AddrManager) expireNew(bucket int) {
if oldest != nil {
key := NetAddressKey(oldest.na)
log.Tracef("AMGR: expiring oldest address %v", key)
amgrLog.Tracef("expiring oldest address %v", key)
delete(a.addrNew[bucket], key)
oldest.refs--
@@ -320,18 +326,20 @@ type knownAddress struct {
// AddrManager provides a concurrency safe address manager for caching potential
// peers on the bitcoin network.
type AddrManager struct {
mtx sync.Mutex
rand *rand.Rand
key [32]byte
addrIndex map[string]*knownAddress // address key to ka for all addrs.
addrNew [newBucketCount]map[string]*knownAddress
addrTried [triedBucketCount]*list.List
started int32
shutdown int32
wg sync.WaitGroup
quit chan bool
nTried int
nNew int
mtx sync.Mutex
rand *rand.Rand
key [32]byte
addrIndex map[string]*knownAddress // address key to ka for all addrs.
addrNew [newBucketCount]map[string]*knownAddress
addrTried [triedBucketCount]*list.List
started int32
shutdown int32
wg sync.WaitGroup
quit chan bool
nTried int
nNew int
lamtx sync.Mutex
localAddresses map[string]*localAddress
}
func (a *AddrManager) getNewBucket(netAddr, srcAddr *btcwire.NetAddress) int {
@@ -393,7 +401,7 @@ out:
dumpAddressTicker.Stop()
a.savePeers()
a.wg.Done()
log.Trace("AMGR: Address handler done")
amgrLog.Trace("Address handler done")
}
type serialisedKnownAddress struct {
@@ -465,7 +473,7 @@ func (a *AddrManager) savePeers() {
w, err := os.Create(filePath)
if err != nil {
log.Error("Error opening file: ", filePath, err)
amgrLog.Error("Error opening file: ", filePath, err)
return
}
enc := json.NewEncoder(w)
@@ -485,19 +493,17 @@ func (a *AddrManager) loadPeers() {
err := a.deserialisePeers(filePath)
if err != nil {
log.Errorf("AMGR: Failed to parse %s: %v", filePath,
err)
amgrLog.Errorf("Failed to parse %s: %v", filePath, err)
// if it is invalid we nuke the old one unconditionally.
err = os.Remove(filePath)
if err != nil {
log.Warn("Failed to remove corrupt peers "+
amgrLog.Warn("Failed to remove corrupt peers "+
"file: ", err)
}
a.reset()
return
}
log.Infof("AMGR: Loaded %d addresses from '%s'", a.nNew+a.nTried,
filePath)
amgrLog.Infof("Loaded %d addresses from '%s'", a.nNew+a.nTried, filePath)
}
func (a *AddrManager) deserialisePeers(filePath string) error {
@@ -593,14 +599,12 @@ func deserialiseNetAddress(addr string) (*btcwire.NetAddress, error) {
if err != nil {
return nil, err
}
ip := net.ParseIP(host)
port, err := strconv.ParseUint(portStr, 10, 16)
if err != nil {
return nil, err
}
na := btcwire.NewNetAddressIPPort(ip, uint16(port),
btcwire.SFNodeNetwork)
return na, nil
return hostToNetAddress(host, uint16(port), btcwire.SFNodeNetwork)
}
// Start begins the core address handler which manages a pool of known
@@ -611,7 +615,7 @@ func (a *AddrManager) Start() {
return
}
log.Trace("AMGR: Starting address manager")
amgrLog.Trace("Starting address manager")
a.wg.Add(1)
@@ -625,12 +629,12 @@ func (a *AddrManager) Start() {
// Stop gracefully shuts down the address manager by stopping the main handler.
func (a *AddrManager) Stop() error {
if atomic.AddInt32(&a.shutdown, 1) != 1 {
log.Warnf("AMGR: Address manager is already in the process of " +
amgrLog.Warnf("Address manager is already in the process of " +
"shutting down")
return nil
}
log.Infof("AMGR: Address manager shutting down")
amgrLog.Infof("Address manager shutting down")
close(a.quit)
a.wg.Wait()
return nil
@@ -660,7 +664,7 @@ func (a *AddrManager) AddAddressByIP(addrIP string) {
// Split IP and port
addr, portStr, err := net.SplitHostPort(addrIP)
if err != nil {
log.Warnf("AMGR: AddADddressByIP given bullshit adddress"+
amgrLog.Warnf("AddADddressByIP given bullshit adddress"+
"(%s): %v", err)
return
}
@@ -669,12 +673,12 @@ func (a *AddrManager) AddAddressByIP(addrIP string) {
na.Timestamp = time.Now()
na.IP = net.ParseIP(addr)
if na.IP == nil {
log.Error("AMGR: Invalid ip address:", addr)
amgrLog.Error("Invalid ip address:", addr)
return
}
port, err := strconv.ParseUint(portStr, 10, 0)
if err != nil {
log.Error("AMGR: Invalid port: ", portStr, err)
amgrLog.Error("Invalid port: ", portStr, err)
return
}
na.Port = uint16(port)
@@ -710,7 +714,8 @@ func (a *AddrManager) AddressCache() []*btcwire.NetAddress {
i := 0
// Iteration order is undefined here, but we randomise it anyway.
for _, v := range a.addrIndex {
allAddr[i] = v.na
copyNa := *v.na
allAddr[i] = &copyNa
i++
}
// Fisher-Yates shuffle the array
@@ -748,18 +753,63 @@ func (a *AddrManager) reset() {
// Use Start to begin processing asynchronous address updates.
func NewAddrManager() *AddrManager {
am := AddrManager{
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
quit: make(chan bool),
rand: rand.New(rand.NewSource(time.Now().UnixNano())),
quit: make(chan bool),
localAddresses: make(map[string]*localAddress),
}
am.reset()
return &am
}
// hostToNetAddress returns a netaddress given a host address. If the address is
// a tor .onion address this will be taken care of. else if the host is not an
// IP address it will be resolved (via tor if required).
func hostToNetAddress(host string, port uint16, services btcwire.ServiceFlag) (*btcwire.NetAddress, error) {
// tor address is 16 char base32 + ".onion"
var ip net.IP
if len(host) == 22 && host[16:] == ".onion" {
// go base32 encoding uses capitals (as does the rfc
// but tor and bitcoind tend to user lowercase, so we switch
// case here.
data, err := base32.StdEncoding.DecodeString(
strings.ToUpper(host[:16]))
if err != nil {
return nil, err
}
prefix := []byte{0xfd, 0x87, 0xd8, 0x7e, 0xeb, 0x43}
ip = net.IP(append(prefix, data...))
} else if ip = net.ParseIP(host); ip == nil {
ips, err := btcdLookup(host)
if err != nil {
return nil, err
}
if len(ips) == 0 {
return nil, fmt.Errorf("No addresses found for %s", host)
}
ip = ips[0]
}
return btcwire.NewNetAddressIPPort(ip, port, services), nil
}
// ipString returns a string for the ip from the provided NetAddress. If the
// ip is in the range used for tor addresses then it will be transformed into
// the relavent .onion address.
func ipString(na *btcwire.NetAddress) string {
if Tor(na) {
// We know now that na.IP is long enogh.
base32 := base32.StdEncoding.EncodeToString(na.IP[6:])
return strings.ToLower(base32) + ".onion"
} else {
return na.IP.String()
}
}
// NetAddressKey returns a string key in the form of ip:port for IPv4 addresses
// or [ip]:port for IPv6 addresses.
func NetAddressKey(na *btcwire.NetAddress) string {
port := strconv.FormatUint(uint64(na.Port), 10)
addr := net.JoinHostPort(na.IP.String(), port)
addr := net.JoinHostPort(ipString(na), port)
return addr
}
@@ -809,8 +859,8 @@ func (a *AddrManager) GetAddress(class string, newBias int) *knownAddress {
ka := e.Value.(*knownAddress)
randval := a.rand.Intn(large)
if float64(randval) < (factor * chance(ka) * float64(large)) {
log.Tracef("AMGR: Selected %v from tried "+
"bucket", NetAddressKey(ka.na))
amgrLog.Tracef("Selected %v from tried bucket",
NetAddressKey(ka.na))
return ka
}
factor *= 1.2
@@ -837,7 +887,7 @@ func (a *AddrManager) GetAddress(class string, newBias int) *knownAddress {
}
randval := a.rand.Intn(large)
if float64(randval) < (factor * chance(ka) * float64(large)) {
log.Tracef("AMGR: Selected %v from new bucket",
amgrLog.Tracef("Selected %v from new bucket",
NetAddressKey(ka.na))
return ka
}
@@ -972,7 +1022,7 @@ func (a *AddrManager) Good(addr *btcwire.NetAddress) {
a.nNew++
rmkey := NetAddressKey(rmka.na)
log.Tracef("AMGR: replacing %s with %s in tried", rmkey, addrKey)
amgrLog.Tracef("Replacing %s with %s in tried", rmkey, addrKey)
// We made sure there is space here just above.
a.addrNew[newBucket][rmkey] = rmka
@@ -1063,17 +1113,19 @@ func RFC6145(na *btcwire.NetAddress) bool {
return rfc6145.Contains(na.IP)
}
var onioncatrange = net.IPNet{IP: net.ParseIP("FD87:d87e:eb43::"),
Mask: net.CIDRMask(48, 128)}
func Tor(na *btcwire.NetAddress) bool {
// bitcoind encodes a .onion address as a 16 byte number by decoding the
// address prior to the .onion (i.e. the key hash) base32 into a ten
// byte number. it then stores the first 6 bytes of the address as
// 0xfD, 0x87, 0xD8, 0x7e, 0xeb, 0x43
// making the format
// this is the same range used by onioncat, part of the
// RFC4193 Unique local IPv6 range.
// In summary the format is:
// { magic 6 bytes, 10 bytes base32 decode of key hash }
// Since we use btcwire.NetAddress to represent and address we may
// well have to emulate this.
// XXX fillmein
return false
return onioncatrange.Contains(na.IP)
}
var zero4 = net.IPNet{IP: net.ParseIP("0.0.0.0"),
@@ -1092,7 +1144,7 @@ func Valid(na *btcwire.NetAddress) bool {
// invalid protocol addresses from earlier versions of bitcoind (before
// 0.2.9), however, since protocol versions before 70001 are
// disconnected by the bitcoin network now we have elided it.
return !(na.IP.IsUnspecified() || RFC3849(na) ||
return na.IP != nil && !(na.IP.IsUnspecified() || RFC3849(na) ||
na.IP.Equal(net.IPv4bcast))
}
@@ -1100,8 +1152,9 @@ func Valid(na *btcwire.NetAddress) bool {
// not. This is true as long as the address is valid and is not in any reserved
// ranges.
func Routable(na *btcwire.NetAddress) bool {
// TODO(oga) bitcoind doesn't include RFC3849 here, but should we?
return Valid(na) && !(RFC1918(na) || RFC3927(na) || RFC4862(na) ||
RFC4193(na) || Tor(na) || RFC4843(na) || Local(na))
(RFC4193(na) && !Tor(na)) || RFC4843(na) || Local(na))
}
// GroupKey returns a string representing the network group an address
@@ -1140,9 +1193,9 @@ func GroupKey(na *btcwire.NetAddress) string {
}
return (&net.IPNet{IP: ip, Mask: net.CIDRMask(16, 32)}).String()
}
// XXX tor?
if Tor(na) {
panic("oga should have implemented me")
// group is keyed off the first 4 bits of the actual onion key.
return fmt.Sprintf("tor:%d", na.IP[6]&((1<<4)-1))
}
// OK, so now we know ourselves to be a IPv6 address.
@@ -1159,3 +1212,142 @@ func GroupKey(na *btcwire.NetAddress) string {
return (&net.IPNet{IP: na.IP, Mask: net.CIDRMask(bits, 128)}).String()
}
// addressPrio is an enum type used to describe the heirarchy of local address
// discovery methods.
type addressPrio int
const (
InterfacePrio addressPrio = iota // address of local interface.
BoundPrio // Address explicitly bound to.
UpnpPrio // External IP discovered from UPnP
HttpPrio // Obtained from internet service.
ManualPrio // provided by --externalip.
)
type localAddress struct {
na *btcwire.NetAddress
score addressPrio
}
// addLocalAddress adds na to the list of known local addresses to advertise
// with the given priority.
func (a *AddrManager) addLocalAddress(na *btcwire.NetAddress,
priority addressPrio) {
// sanity check.
if !Routable(na) {
amgrLog.Debugf("rejecting address %s:%d due to routability",
na.IP, na.Port)
return
}
amgrLog.Debugf("adding address %s:%d",
na.IP, na.Port)
a.lamtx.Lock()
defer a.lamtx.Unlock()
key := NetAddressKey(na)
la, ok := a.localAddresses[key]
if !ok || la.score < priority {
if ok {
la.score = priority + 1
} else {
a.localAddresses[key] = &localAddress{
na: na,
score: priority,
}
}
}
}
// getReachabilityFrom returns the relative reachability of na from fromna.
func getReachabilityFrom(na, fromna *btcwire.NetAddress) int {
const (
Unreachable = 0
Default = iota
Teredo
Ipv6_weak
Ipv4
Ipv6_strong
Private
)
if !Routable(fromna) {
return Unreachable
} else if Tor(fromna) {
if Tor(na) {
return Private
} else if Routable(na) && na.IP.To4() != nil {
return Ipv4
} else {
return Default
}
} else if RFC4380(fromna) {
if !Routable(na) {
return Default
} else if RFC4380(na) {
return Teredo
} else if na.IP.To4() != nil {
return Ipv4
} else { // ipv6
return Ipv6_weak
}
} else if fromna.IP.To4() != nil {
if Routable(na) && na.IP.To4() != nil {
return Ipv4
}
return Default
} else /* ipv6 */ {
var tunnelled bool
// Is our v6 is tunnelled?
if RFC3964(na) || RFC6052(na) || RFC6145(na) {
tunnelled = true
}
if !Routable(na) {
return Default
} else if RFC4380(na) {
return Teredo
} else if na.IP.To4() != nil {
return Ipv4
} else if tunnelled {
// only prioritise ipv6 if we aren't tunnelling it.
return Ipv6_weak
}
return Ipv6_strong
}
}
// getBestLocalAddress returns the most appropriate local address that we know
// of to be contacted by rna
func (a *AddrManager) getBestLocalAddress(rna *btcwire.NetAddress) *btcwire.NetAddress {
a.lamtx.Lock()
defer a.lamtx.Unlock()
bestreach := 0
var bestscore addressPrio
var bestna *btcwire.NetAddress
for _, la := range a.localAddresses {
reach := getReachabilityFrom(la.na, rna)
if reach > bestreach ||
(reach == bestreach && la.score > bestscore) {
bestreach = reach
bestscore = la.score
bestna = la.na
}
}
if bestna != nil {
amgrLog.Debugf("Suggesting address %s:%d for %s:%d",
bestna.IP, bestna.Port, rna.IP, rna.Port)
} else {
amgrLog.Debugf("No worthy address for %s:%d",
rna.IP, rna.Port)
// Send something unroutable if nothing suitable.
bestna = &btcwire.NetAddress{
Timestamp: time.Now(),
Services: 0,
IP: net.IP([]byte{0, 0, 0, 0}),
Port: 0,
}
}
return bestna
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2013 Conformal Systems LLC.
// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2013 Conformal Systems LLC.
// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
@@ -6,7 +6,6 @@ package main
import (
"container/list"
"fmt"
"github.com/conformal/btcchain"
"github.com/conformal/btcdb"
"github.com/conformal/btcutil"
@@ -22,6 +21,11 @@ import (
const (
chanBufferSize = 50
// minInFlightBlocks is the minimum number of blocks that should be
// in the request queue for headers-first mode before requesting
// more.
minInFlightBlocks = 10
// blockDbNamePrefix is the prefix for the block database name. The
// database type is appended to this value to form the full block
// database name.
@@ -47,6 +51,13 @@ type invMsg struct {
peer *peer
}
// blockMsg packages a bitcoin block message and the peer it came from together
// so the block handler has access to that information.
type headersMsg struct {
headers *btcwire.MsgHeaders
peer *peer
}
// donePeerMsg signifies a newly disconnected peer to the block handler.
type donePeerMsg struct {
peer *peer
@@ -55,10 +66,23 @@ type donePeerMsg struct {
// txMsg packages a bitcoin tx message and the peer it came from together
// so the block handler has access to that information.
type txMsg struct {
tx *btcwire.MsgTx
tx *btcutil.Tx
peer *peer
}
// getSyncPeerMsg is a message type to be sent across the query channel for
// retrieving the current sync peer.
type getSyncPeerMsg struct {
reply chan *peer
}
// headerNode is used as a node in a list of headers that are linked together
// between checkpoints.
type headerNode struct {
height int64
sha *btcwire.ShaHash
}
// blockManager provides a concurrency safe block manager for handling all
// incoming blocks.
type blockManager struct {
@@ -75,8 +99,59 @@ type blockManager struct {
processingReqs bool
syncPeer *peer
msgChan chan interface{}
query chan interface{}
wg sync.WaitGroup
quit chan bool
// The following fields are used for headers-first mode.
headersFirstMode bool
headerList *list.List
startHeader *list.Element
nextCheckpoint *btcchain.Checkpoint
}
// resetHeaderState sets the headers-first mode state to values appropriate for
// syncing from a new peer.
func (b *blockManager) resetHeaderState(newestHash *btcwire.ShaHash, newestHeight int64) {
b.headersFirstMode = false
b.headerList.Init()
b.startHeader = nil
// When there is a next checkpoint, add an entry for the latest known
// block into the header pool. This allows the next downloaded header
// to prove it links to the chain properly.
if b.nextCheckpoint != nil {
node := headerNode{height: newestHeight, sha: newestHash}
b.headerList.PushBack(&node)
}
}
// findNextHeaderCheckpoint returns the next checkpoint after the passed height.
// It returns nil when there is not one either because the height is already
// later than the final checkpoint or some other reason such as disabled
// checkpoints.
func (b *blockManager) findNextHeaderCheckpoint(height int64) *btcchain.Checkpoint {
checkpoints := b.blockChain.Checkpoints()
if checkpoints == nil {
return nil
}
// There is no next checkpoint if the height is already after the final
// checkpoint.
finalCheckpoint := &checkpoints[len(checkpoints)-1]
if height >= finalCheckpoint.Height {
return nil
}
// Find the next checkpoint.
nextCheckpoint := finalCheckpoint
for i := len(checkpoints) - 2; i >= 0; i-- {
if height >= checkpoints[i].Height {
break
}
nextCheckpoint = &checkpoints[i]
}
return nextCheckpoint
}
// startSync will choose the best peer among the available candidate peers to
@@ -92,7 +167,7 @@ func (b *blockManager) startSync(peers *list.List) {
// Find the height of the current known best block.
_, height, err := b.server.db.NewestSha()
if err != nil {
log.Errorf("BMGR: %v", err)
bmgrLog.Errorf("%v", err)
return
}
@@ -122,17 +197,45 @@ func (b *blockManager) startSync(peers *list.List) {
if bestPeer != nil {
locator, err := b.blockChain.LatestBlockLocator()
if err != nil {
log.Errorf("BMGR: Failed to get block locator for the "+
bmgrLog.Errorf("Failed to get block locator for the "+
"latest block: %v", err)
return
}
log.Infof("BMGR: Syncing to block height %d from peer %v",
bmgrLog.Infof("Syncing to block height %d from peer %v",
bestPeer.lastBlock, bestPeer.addr)
bestPeer.PushGetBlocksMsg(locator, &zeroHash)
// When the current height is less than a known checkpoint we
// can use block headers to learn about which blocks comprise
// the chain up to the checkpoint and perform less validation
// for them. This is possible since each header contains the
// hash of the previous header and a merkle root. Therefore if
// we validate all of the received headers link together
// properly and the checkpoint hashes match, we can be sure the
// hashes for the blocks in between are accurate. Further, once
// the full blocks are downloaded, the merkle root is computed
// and compared against the value in the header which proves the
// full block hasn't been tampered with.
//
// Once we have passed the final checkpoint, or checkpoints are
// disabled, use standard inv messages learn about the blocks
// and fully validate them. Finally, regression test mode does
// not support the headers-first approach so do normal block
// downloads when in regression test mode.
if b.nextCheckpoint != nil && height < b.nextCheckpoint.Height &&
!cfg.RegressionTest && !cfg.DisableCheckpoints {
bestPeer.PushGetHeadersMsg(locator, b.nextCheckpoint.Hash)
b.headersFirstMode = true
bmgrLog.Infof("Downloading headers for blocks %d to "+
"%d from peer %s", height+1,
b.nextCheckpoint.Height, bestPeer.addr)
} else {
bestPeer.PushGetBlocksMsg(locator, &zeroHash)
}
b.syncPeer = bestPeer
} else {
log.Warnf("BMGR: No sync peer candidates available")
bmgrLog.Warnf("No sync peer candidates available")
}
}
@@ -173,7 +276,7 @@ func (b *blockManager) handleNewPeerMsg(peers *list.List, p *peer) {
return
}
log.Infof("BMGR: New valid peer %s", p)
bmgrLog.Infof("New valid peer %s (%s)", p, p.userAgent)
// Ignore the peer if it's not a sync candidate.
if !b.isSyncCandidate(p) {
@@ -200,7 +303,7 @@ func (b *blockManager) handleDonePeerMsg(peers *list.List, p *peer) {
}
}
log.Infof("BMGR: Lost peer %s", p)
bmgrLog.Infof("Lost peer %s", p)
// Remove requested transactions from the global map so that they will
// be fetched from elsewhere next time we get an inv.
@@ -217,9 +320,22 @@ func (b *blockManager) handleDonePeerMsg(peers *list.List, p *peer) {
}
// Attempt to find a new peer to sync from if the quitting peer is the
// sync peer.
// sync peer. Also, reset the headers-first state if in headers-first
// mode so
if b.syncPeer != nil && b.syncPeer == p {
b.syncPeer = nil
if b.headersFirstMode {
// This really shouldn't fail. We have a fairly
// unrecoverable database issue if it does.
newestHash, height, err := b.server.db.NewestSha()
if err != nil {
bmgrLog.Warnf("Unable to obtain latest "+
"block information from the database: "+
"%v", err)
return
}
b.resetHeaderState(newestHash, height)
}
b.startSync(peers)
}
}
@@ -227,9 +343,9 @@ func (b *blockManager) handleDonePeerMsg(peers *list.List, p *peer) {
// logBlockHeight logs a new block height as an information message to show
// progress to the user. In order to prevent spam, it limits logging to one
// message every 10 seconds with duration and totals included.
func (b *blockManager) logBlockHeight(numTx, height int64, latestHash *btcwire.ShaHash) {
func (b *blockManager) logBlockHeight(block *btcutil.Block) {
b.receivedLogBlocks++
b.receivedLogTx += numTx
b.receivedLogTx += int64(len(block.MsgBlock().Transactions))
now := time.Now()
duration := now.Sub(b.lastBlockLogTime)
@@ -237,17 +353,10 @@ func (b *blockManager) logBlockHeight(numTx, height int64, latestHash *btcwire.S
return
}
// Truncated the duration to 10s of milliseconds.
// Truncate the duration to 10s of milliseconds.
durationMillis := int64(duration / time.Millisecond)
tDuration := 10 * time.Millisecond * time.Duration(durationMillis/10)
// Attempt to get the timestamp of the latest block.
blockTimeStr := ""
block, err := b.server.db.FetchBlockBySha(latestHash)
if err == nil {
blockTimeStr = fmt.Sprintf(", %s", block.MsgBlock().Header.Timestamp)
}
// Log information about new block height.
blockStr := "blocks"
if b.receivedLogBlocks == 1 {
@@ -257,9 +366,9 @@ func (b *blockManager) logBlockHeight(numTx, height int64, latestHash *btcwire.S
if b.receivedLogTx == 1 {
txStr = "transaction"
}
log.Infof("BMGR: Processed %d %s in the last %s (%d %s, height %d%s)",
bmgrLog.Infof("Processed %d %s in the last %s (%d %s, height %d, %s)",
b.receivedLogBlocks, blockStr, tDuration, b.receivedLogTx,
txStr, height, blockTimeStr)
txStr, block.Height(), block.MsgBlock().Header.Timestamp)
b.receivedLogBlocks = 0
b.receivedLogTx = 0
@@ -269,12 +378,12 @@ func (b *blockManager) logBlockHeight(numTx, height int64, latestHash *btcwire.S
// handleTxMsg handles transaction messages from all peers.
func (b *blockManager) handleTxMsg(tmsg *txMsg) {
// Keep track of which peer the tx was sent from.
txHash, _ := tmsg.tx.TxSha()
txHash := tmsg.tx.Sha()
// If we didn't ask for this block then the peer is misbehaving.
if _, ok := tmsg.peer.requestedTxns[txHash]; !ok {
log.Warnf("BMGR: Got unrequested transaction %v from %s -- "+
"disconnecting", &txHash, tmsg.peer.addr)
// If we didn't ask for this transaction then the peer is misbehaving.
if _, ok := tmsg.peer.requestedTxns[*txHash]; !ok {
bmgrLog.Warnf("Got unrequested transaction %v from %s -- "+
"disconnecting", txHash, tmsg.peer.addr)
tmsg.peer.Disconnect()
return
}
@@ -287,8 +396,8 @@ func (b *blockManager) handleTxMsg(tmsg *txMsg) {
// already knows about it and as such we shouldn't have any more
// instances of trying to fetch it, or we failed to insert and thus
// we'll retry next time we get an inv.
delete(tmsg.peer.requestedTxns, txHash)
delete(b.requestedTxns, txHash)
delete(tmsg.peer.requestedTxns, *txHash)
delete(b.requestedTxns, *txHash)
if err != nil {
// When the error is a rule error, it means the transaction was
@@ -296,9 +405,9 @@ func (b *blockManager) handleTxMsg(tmsg *txMsg) {
// so log it as such. Otherwise, something really did go wrong,
// so log it as an actual error.
if _, ok := err.(TxRuleError); ok {
log.Debugf("Rejected transaction %v: %v", txHash, err)
bmgrLog.Debugf("Rejected transaction %v: %v", txHash, err)
} else {
log.Errorf("Failed to process transaction %v: %v", txHash, err)
bmgrLog.Errorf("Failed to process transaction %v: %v", txHash, err)
}
return
}
@@ -331,11 +440,8 @@ func (b *blockManager) current() bool {
// handleBlockMsg handles block messages from all peers.
func (b *blockManager) handleBlockMsg(bmsg *blockMsg) {
// Keep track of which peer the block was sent from so the notification
// handler can request the parent blocks from the appropriate peer.
blockSha, _ := bmsg.block.Sha()
// If we didn't ask for this block then the peer is misbehaving.
blockSha, _ := bmsg.block.Sha()
if _, ok := bmsg.peer.requestedBlocks[*blockSha]; !ok {
// The regression test intentionally sends some blocks twice
// to test duplicate block insertion fails. Don't disconnect
@@ -343,24 +449,50 @@ func (b *blockManager) handleBlockMsg(bmsg *blockMsg) {
// mode in this case so the chain code is actually fed the
// duplicate blocks.
if !cfg.RegressionTest {
log.Warnf("BMGR: Got unrequested block %v from %s -- "+
bmgrLog.Warnf("Got unrequested block %v from %s -- "+
"disconnecting", blockSha, bmsg.peer.addr)
bmsg.peer.Disconnect()
return
}
}
// Keep track of which peer the block was sent from so the notification
// handler can request the parent blocks from the appropriate peer.
b.blockPeer[*blockSha] = bmsg.peer
// Process the block to include validation, best chain selection, orphan
// handling, etc.
err := b.blockChain.ProcessBlock(bmsg.block)
// When in headers-first mode, if the block matches the hash of the
// first header in the list of headers that are being fetched, it's
// eligible for less validation since the headers have already been
// verified to link together and are valid up to the next checkpoint.
// Also, remove the list entry for all blocks except the checkpoint
// since it is needed to verify the next round of headers links
// properly.
isCheckpointBlock := false
fastAdd := false
if b.headersFirstMode {
firstNodeEl := b.headerList.Front()
if firstNodeEl != nil {
firstNode := firstNodeEl.Value.(*headerNode)
if blockSha.IsEqual(firstNode.sha) {
fastAdd = true
if firstNode.sha.IsEqual(b.nextCheckpoint.Hash) {
isCheckpointBlock = true
} else {
b.headerList.Remove(firstNodeEl)
}
}
}
}
// Remove block from request maps. Either chain knows about it and such
// we shouldn't have any more instances of trying to fetch it, or we
// failed to insert and thus we'll retry next time we get an inv.
// Remove block from request maps. Either chain will know about it and
// so we shouldn't have any more instances of trying to fetch it, or we
// will fail the insert and thus we'll retry next time we get an inv.
delete(bmsg.peer.requestedBlocks, *blockSha)
delete(b.requestedBlocks, *blockSha)
// Process the block to include validation, best chain selection, orphan
// handling, etc.
err := b.blockChain.ProcessBlock(bmsg.block, fastAdd)
if err != nil {
delete(b.blockPeer, *blockSha)
@@ -369,45 +501,233 @@ func (b *blockManager) handleBlockMsg(bmsg *blockMsg) {
// it as such. Otherwise, something really did go wrong, so log
// it as an actual error.
if _, ok := err.(btcchain.RuleError); ok {
log.Infof("BMGR: Rejected block %v: %v", blockSha, err)
bmgrLog.Infof("Rejected block %v: %v", blockSha, err)
} else {
log.Errorf("BMGR: Failed to process block %v: %v", blockSha, err)
bmgrLog.Errorf("Failed to process block %v: %v", blockSha, err)
}
return
}
// Don't keep track of the peer that sent the block any longer if it's
// not an orphan.
// When the block is not an orphan, don't keep track of the peer that
// sent it any longer and log information about it.
if !b.blockChain.IsKnownOrphan(blockSha) {
delete(b.blockPeer, *blockSha)
b.logBlockHeight(bmsg.block)
}
// Log info about the new block height.
latestHash, height, err := b.server.db.NewestSha()
if err != nil {
log.Warnf("BMGR: Failed to obtain latest sha - %v", err)
return
}
b.logBlockHeight(int64(len(bmsg.block.MsgBlock().Transactions)), height,
latestHash)
// Sync the db to disk.
b.server.db.Sync()
// Nothing more to do if we aren't in headers-first mode.
if !b.headersFirstMode {
return
}
// This is headers-first mode, so if the block is not a checkpoint
// request more blocks using the header list when the request queue is
// getting short.
if !isCheckpointBlock {
if b.startHeader != nil &&
len(bmsg.peer.requestedBlocks) < minInFlightBlocks {
b.fetchHeaderBlocks()
}
return
}
// This is headers-first mode and the block is a checkpoint. When
// there is a next checkpoint, get the next round of headers by asking
// for headers starting from the block after this one up to the next
// checkpoint.
prevHeight := b.nextCheckpoint.Height
prevHash := b.nextCheckpoint.Hash
b.nextCheckpoint = b.findNextHeaderCheckpoint(prevHeight)
if b.nextCheckpoint != nil {
locator := btcchain.BlockLocator([]*btcwire.ShaHash{prevHash})
err := bmsg.peer.PushGetHeadersMsg(locator, b.nextCheckpoint.Hash)
if err != nil {
bmgrLog.Warnf("Failed to send getheaders message to "+
"peer %s: %v", bmsg.peer.addr, err)
return
}
bmgrLog.Infof("Downloading headers for blocks %d to %d from "+
"peer %s", prevHeight+1, b.nextCheckpoint.Height,
b.syncPeer.addr)
return
}
// This is headers-first mode, the block is a checkpoint, and there are
// no more checkpoints, so switch to normal mode by requesting blocks
// from the block after this one up to the end of the chain (zero hash).
b.headersFirstMode = false
b.headerList.Init()
bmgrLog.Infof("Reached the final checkpoint -- switching to normal mode")
locator := btcchain.BlockLocator([]*btcwire.ShaHash{blockSha})
err = bmsg.peer.PushGetBlocksMsg(locator, &zeroHash)
if err != nil {
bmgrLog.Warnf("Failed to send getblocks message to peer %s: %v",
bmsg.peer.addr, err)
return
}
}
// fetchHeaderBlocks creates and sends a request to the syncPeer for the next
// list of blocks to be downloaded based on the current list of headers.
func (b *blockManager) fetchHeaderBlocks() {
// Nothing to do if there is not start header.
if b.startHeader == nil {
bmgrLog.Warnf("fetchHeaderBlocks called with no start header")
return
}
// Build up a getdata request for the list of blocks the headers
// describe. The size hint will be limited to btcwire.MaxInvPerMsg by
// the function, so no need to double check it here.
gdmsg := btcwire.NewMsgGetDataSizeHint(uint(b.headerList.Len()))
numRequested := 0
for e := b.startHeader; e != nil; e = e.Next() {
node, ok := e.Value.(*headerNode)
if !ok {
bmgrLog.Warn("Header list node type is not a headerNode")
continue
}
iv := btcwire.NewInvVect(btcwire.InvTypeBlock, node.sha)
if !b.haveInventory(iv) {
b.requestedBlocks[*node.sha] = true
b.syncPeer.requestedBlocks[*node.sha] = true
gdmsg.AddInvVect(iv)
numRequested++
}
b.startHeader = e.Next()
if numRequested >= btcwire.MaxInvPerMsg {
break
}
}
if len(gdmsg.InvList) > 0 {
b.syncPeer.QueueMessage(gdmsg, nil)
}
}
// handleHeadersMsghandles headers messages from all peers.
func (b *blockManager) handleHeadersMsg(hmsg *headersMsg) {
// The remote peer is misbehaving if we didn't request headers.
msg := hmsg.headers
numHeaders := len(msg.Headers)
if !b.headersFirstMode {
bmgrLog.Warnf("Got %d unrequested headers from %s -- "+
"disconnecting", numHeaders, hmsg.peer.addr)
hmsg.peer.Disconnect()
return
}
// Nothing to do for an empty headers message.
if numHeaders == 0 {
return
}
// Process all of the received headers ensuring each one connects to the
// previous and that checkpoints match.
receivedCheckpoint := false
var finalHash *btcwire.ShaHash
for _, blockHeader := range msg.Headers {
blockHash, err := blockHeader.BlockSha()
if err != nil {
bmgrLog.Warnf("Failed to compute hash of header "+
"received from peer %s -- disconnecting",
hmsg.peer.addr)
hmsg.peer.Disconnect()
return
}
finalHash = &blockHash
// Ensure there is a previous header to compare against.
prevNodeEl := b.headerList.Back()
if prevNodeEl == nil {
bmgrLog.Warnf("Header list does not contain a previous" +
"element as expected -- disconnecting peer")
hmsg.peer.Disconnect()
return
}
// Ensure the header properly connects to the previous one and
// add it to the list of headers.
node := headerNode{sha: &blockHash}
prevNode := prevNodeEl.Value.(*headerNode)
if prevNode.sha.IsEqual(&blockHeader.PrevBlock) {
node.height = prevNode.height + 1
e := b.headerList.PushBack(&node)
if b.startHeader == nil {
b.startHeader = e
}
} else {
bmgrLog.Warnf("Received block header that does not"+
"properly connect to the chain from peer %s "+
"-- disconnecting", hmsg.peer.addr)
hmsg.peer.Disconnect()
return
}
// Verify the header at the next checkpoint height matches.
if node.height == b.nextCheckpoint.Height {
if node.sha.IsEqual(b.nextCheckpoint.Hash) {
receivedCheckpoint = true
bmgrLog.Infof("Verified downloaded block "+
"header against checkpoint at height "+
"%d/hash %s", node.height, node.sha)
} else {
bmgrLog.Warnf("Block header at height %d/hash "+
"%s from peer %s does NOT match "+
"expected checkpoint hash of %s -- "+
"disconnecting", node.height,
node.sha, hmsg.peer.addr,
b.nextCheckpoint.Hash)
hmsg.peer.Disconnect()
return
}
break
}
}
// When this header is a checkpoint, switch to fetching the blocks for
// all of the headers since the last checkpoint.
if receivedCheckpoint {
// Since the first entry of the list is always the final block
// that is already in the database and is only used to ensure
// the next header links properly, it must be removed before
// fetching the blocks.
b.headerList.Remove(b.headerList.Front())
bmgrLog.Infof("Received %v block headers: Fetching blocks",
b.headerList.Len())
b.lastBlockLogTime = time.Now()
b.fetchHeaderBlocks()
return
}
// This header is not a checkpoint, so request the next batch of
// headers starting from the latest known header and ending with the
// next checkpoint.
locator := btcchain.BlockLocator([]*btcwire.ShaHash{finalHash})
err := hmsg.peer.PushGetHeadersMsg(locator, b.nextCheckpoint.Hash)
if err != nil {
bmgrLog.Warnf("Failed to send getheaders message to "+
"peer %s: %v", hmsg.peer.addr, err)
return
}
}
// haveInventory returns whether or not the inventory represented by the passed
// inventory vector is known. This includes checking all of the various places
// inventory can be when it is in different states such as blocks that are part
// of the main chain, on a side chain, in the orphan pool, and transactions that
// in the memory pool (either the main pool or orphan pool).
// are in the memory pool (either the main pool or orphan pool).
func (b *blockManager) haveInventory(invVect *btcwire.InvVect) bool {
switch invVect.Type {
case btcwire.InvVect_Block:
case btcwire.InvTypeBlock:
// Ask chain if the block is known to it in any form (main
// chain, side chain, or orphan).
return b.blockChain.HaveBlock(&invVect.Hash)
case btcwire.InvVect_Tx:
case btcwire.InvTypeTx:
// Ask the transaction memory pool if the transaction is known
// to it in any form (main pool or orphan).
if b.server.txMemPool.HaveTransaction(&invVect.Hash) {
@@ -457,7 +777,12 @@ func (b *blockManager) handleInvMsg(imsg *invMsg) {
// Add the inventory to the cache of known inventory
// for the peer.
imsg.peer.addKnownInventory(iv)
imsg.peer.AddKnownInventory(iv)
// Ignore inventory when we're in headers-first mode.
if b.headersFirstMode {
continue
}
// Request the inventory if we don't already have it.
if !b.haveInventory(iv) {
@@ -484,7 +809,7 @@ func (b *blockManager) handleInvMsg(imsg *invMsg) {
orphanRoot := chain.GetOrphanRoot(&iv.Hash)
locator, err := chain.LatestBlockLocator()
if err != nil {
log.Errorf("PEER: Failed to get block "+
bmgrLog.Errorf("PEER: Failed to get block "+
"locator for the latest block: "+
"%v", err)
continue
@@ -517,7 +842,7 @@ func (b *blockManager) handleInvMsg(imsg *invMsg) {
imsg.peer.requestQueue.Remove(e)
switch iv.Type {
case btcwire.InvVect_Block:
case btcwire.InvTypeBlock:
// Request the block if there is not already a pending
// request.
if _, exists := b.requestedBlocks[iv.Hash]; !exists {
@@ -527,7 +852,7 @@ func (b *blockManager) handleInvMsg(imsg *invMsg) {
numRequested++
}
case btcwire.InvVect_Tx:
case btcwire.InvTypeTx:
// Request the transaction if there is not already a
// pending request.
if _, exists := b.requestedTxns[iv.Hash]; !exists {
@@ -574,11 +899,26 @@ out:
case *invMsg:
b.handleInvMsg(msg)
case *headersMsg:
b.handleHeadersMsg(msg)
case *donePeerMsg:
b.handleDonePeerMsg(candidatePeers, msg.peer)
default:
// bitch and whine.
bmgrLog.Warnf("Invalid message type in block "+
"handler: %T", msg)
}
// Queries used for atomically retrieving internal state.
case qmsg := <-b.query:
switch msg := qmsg.(type) {
case getSyncPeerMsg:
msg.reply <- b.syncPeer
default:
bmgrLog.Warnf("Invalid query type in block "+
"handler query: %T", msg)
}
case <-b.quit:
@@ -586,7 +926,7 @@ out:
}
}
b.wg.Done()
log.Trace("BMGR: Block handler done")
bmgrLog.Trace("Block handler done")
}
// handleNotifyMsg handles notifications from btcchain. It does things such
@@ -601,14 +941,14 @@ func (b *blockManager) handleNotifyMsg(notification *btcchain.Notification) {
orphanRoot := b.blockChain.GetOrphanRoot(orphanHash)
locator, err := b.blockChain.LatestBlockLocator()
if err != nil {
log.Errorf("BMGR: Failed to get block locator "+
bmgrLog.Errorf("Failed to get block locator "+
"for the latest block: %v", err)
break
}
peer.PushGetBlocksMsg(locator, orphanRoot)
delete(b.blockPeer, *orphanRoot)
} else {
log.Warnf("Notification for orphan %v with no peer",
bmgrLog.Warnf("Notification for orphan %v with no peer",
orphanHash)
}
@@ -624,7 +964,7 @@ func (b *blockManager) handleNotifyMsg(notification *btcchain.Notification) {
block, ok := notification.Data.(*btcutil.Block)
if !ok {
log.Warnf("BMGR: Chain accepted notification is not a block.")
bmgrLog.Warnf("Chain accepted notification is not a block.")
break
}
@@ -640,45 +980,52 @@ func (b *blockManager) handleNotifyMsg(notification *btcchain.Notification) {
case btcchain.NTBlockConnected:
block, ok := notification.Data.(*btcutil.Block)
if !ok {
log.Warnf("BMGR: Chain connected notification is not a block.")
bmgrLog.Warnf("Chain connected notification is not a block.")
break
}
// Remove all of the transactions (except the coinbase) in the
// connected block from the transaction pool.
for _, tx := range block.MsgBlock().Transactions[1:] {
b.server.txMemPool.removeTransaction(tx)
// connected block from the transaction pool. Also, remove any
// transactions which are now double spends as a result of these
// new transactions. Note that removing a transaction from
// pool also removes any transactions which depend on it,
// recursively.
for _, tx := range block.Transactions()[1:] {
b.server.txMemPool.RemoveTransaction(tx)
b.server.txMemPool.RemoveDoubleSpends(tx)
}
// Notify frontends
if r := b.server.rpcServer; r != nil {
go r.NotifyBlockConnected(block)
go r.NotifyNewTxListeners(b.server.db, block)
go func() {
r.ntfnMgr.NotifyBlockTXs(block)
r.ntfnMgr.NotifyBlockConnected(block)
}()
}
// A block has been disconnected from the main block chain.
case btcchain.NTBlockDisconnected:
block, ok := notification.Data.(*btcutil.Block)
if !ok {
log.Warnf("BMGR: Chain disconnected notification is not a block.")
bmgrLog.Warnf("Chain disconnected notification is not a block.")
break
}
// Reinsert all of the transactions (except the coinbase) into
// the transaction pool.
for _, tx := range block.MsgBlock().Transactions[1:] {
err := b.server.txMemPool.ProcessTransaction(tx)
for _, tx := range block.Transactions()[1:] {
err := b.server.txMemPool.MaybeAcceptTransaction(tx, nil, false)
if err != nil {
// Remove the transaction and all transactions
// that depend on it if it wasn't accepted into
// the transaction pool.
b.server.txMemPool.removeTransaction(tx)
b.server.txMemPool.RemoveTransaction(tx)
}
}
// Notify frontends
if r := b.server.rpcServer; r != nil {
go r.NotifyBlockDisconnected(block)
go r.ntfnMgr.NotifyBlockDisconnected(block)
}
}
}
@@ -695,7 +1042,7 @@ func (b *blockManager) NewPeer(p *peer) {
// QueueTx adds the passed transaction message and peer to the block handling
// queue.
func (b *blockManager) QueueTx(tx *btcwire.MsgTx, p *peer) {
func (b *blockManager) QueueTx(tx *btcutil.Tx, p *peer) {
// Don't accept more transactions if we're shutting down.
if atomic.LoadInt32(&b.shutdown) != 0 {
p.txProcessed <- false
@@ -727,6 +1074,18 @@ func (b *blockManager) QueueInv(inv *btcwire.MsgInv, p *peer) {
b.msgChan <- &invMsg{inv: inv, peer: p}
}
// QueueHeaders adds the passed headers message and peer to the block handling
// queue.
func (b *blockManager) QueueHeaders(headers *btcwire.MsgHeaders, p *peer) {
// No channel handling here because peers do not need to block on
// headers messages.
if atomic.LoadInt32(&b.shutdown) != 0 {
return
}
b.msgChan <- &headersMsg{headers: headers, peer: p}
}
// DonePeer informs the blockmanager that a peer has disconnected.
func (b *blockManager) DonePeer(p *peer) {
// Ignore if we are shutting down.
@@ -744,7 +1103,7 @@ func (b *blockManager) Start() {
return
}
log.Trace("BMGR: Starting block manager")
bmgrLog.Trace("Starting block manager")
b.wg.Add(1)
go b.blockHandler()
}
@@ -753,20 +1112,32 @@ func (b *blockManager) Start() {
// handlers and waiting for them to finish.
func (b *blockManager) Stop() error {
if atomic.AddInt32(&b.shutdown, 1) != 1 {
log.Warnf("BMGR: Block manager is already in the process of " +
bmgrLog.Warnf("Block manager is already in the process of " +
"shutting down")
return nil
}
log.Infof("BMGR: Block manager shutting down")
bmgrLog.Infof("Block manager shutting down")
close(b.quit)
b.wg.Wait()
return nil
}
// SyncPeer returns the current sync peer.
func (b *blockManager) SyncPeer() *peer {
reply := make(chan *peer)
b.query <- getSyncPeerMsg{reply: reply}
return <-reply
}
// newBlockManager returns a new bitcoin block manager.
// Use Start to begin processing asynchronous block and inv updates.
func newBlockManager(s *server) (*blockManager, error) {
newestHash, height, err := s.db.NewestSha()
if err != nil {
return nil, err
}
bm := blockManager{
server: s,
blockPeer: make(map[btcwire.ShaHash]*peer),
@@ -774,21 +1145,29 @@ func newBlockManager(s *server) (*blockManager, error) {
requestedBlocks: make(map[btcwire.ShaHash]bool),
lastBlockLogTime: time.Now(),
msgChan: make(chan interface{}, cfg.MaxPeers*3),
query: make(chan interface{}),
headerList: list.New(),
quit: make(chan bool),
}
bm.blockChain = btcchain.New(s.db, s.btcnet, bm.handleNotifyMsg)
bm.blockChain.DisableCheckpoints(cfg.DisableCheckpoints)
if cfg.DisableCheckpoints {
log.Info("BMGR: Checkpoints are disabled")
if !cfg.DisableCheckpoints {
// Initialize the next checkpoint based on the current height.
bm.nextCheckpoint = bm.findNextHeaderCheckpoint(height)
if bm.nextCheckpoint != nil {
bm.resetHeaderState(newestHash, height)
}
} else {
bmgrLog.Info("Checkpoints are disabled")
}
log.Infof("BMGR: Generating initial block node index. This may " +
bmgrLog.Infof("Generating initial block node index. This may " +
"take a while...")
err := bm.blockChain.GenerateInitialIndex()
err = bm.blockChain.GenerateInitialIndex()
if err != nil {
return nil, err
}
log.Infof("BMGR: Block index generation complete")
bmgrLog.Infof("Block index generation complete")
return &bm, nil
}
@@ -804,7 +1183,7 @@ func removeRegressionDB(dbPath string) error {
// Remove the old regression test database if it already exists.
fi, err := os.Stat(dbPath)
if err == nil {
log.Infof("BMGR: Removing regression test database from '%s'", dbPath)
btcdLog.Infof("Removing regression test database from '%s'", dbPath)
if fi.IsDir() {
err := os.RemoveAll(dbPath)
if err != nil {
@@ -856,7 +1235,7 @@ func warnMultipeDBs() {
// Warn if there are extra databases.
if len(duplicateDbPaths) > 0 {
selectedDbPath := blockDbPath(cfg.DbType)
log.Warnf("WARNING: There are multiple block chain databases "+
btcdLog.Warnf("WARNING: There are multiple block chain databases "+
"using different database types.\nYou probably don't "+
"want to waste disk space by having more than one.\n"+
"Your current database is located at [%v].\nThe "+
@@ -865,8 +1244,24 @@ func warnMultipeDBs() {
}
}
// loadBlockDB opens the block database and returns a handle to it.
func loadBlockDB() (btcdb.Db, error) {
// setupBlockDB loads (or creates when needed) the block database taking into
// account the selected database backend. It also contains additional logic
// such warning the user if there are multiple databases which consume space on
// the file system and ensuring the regression test database is clean when in
// regression test mode.
func setupBlockDB() (btcdb.Db, error) {
// The memdb backend does not have a file path associated with it, so
// handle it uniquely. We also don't want to worry about the multiple
// database type warnings when running with the memory database.
if cfg.DbType == "memdb" {
btcdLog.Infof("Creating block database in memory.")
db, err := btcdb.CreateDB(cfg.DbType)
if err != nil {
return nil, err
}
return db, nil
}
warnMultipeDBs()
// The database name is based on the database type.
@@ -876,11 +1271,11 @@ func loadBlockDB() (btcdb.Db, error) {
// each run, so remove it now if it already exists.
removeRegressionDB(dbPath)
log.Infof("BMGR: Loading block database from '%s'", dbPath)
btcdLog.Infof("Loading block database from '%s'", dbPath)
db, err := btcdb.OpenDB(cfg.DbType, dbPath)
if err != nil {
// Return the error if it's not because the database doesn't
// exist.
// Return the error if it's not because the database
// doesn't exist.
if err != btcdb.DbDoesNotExist {
return nil, err
}
@@ -896,6 +1291,16 @@ func loadBlockDB() (btcdb.Db, error) {
}
}
return db, nil
}
// loadBlockDB opens the block database and returns a handle to it.
func loadBlockDB() (btcdb.Db, error) {
db, err := setupBlockDB()
if err != nil {
return nil, err
}
// Get the latest block height from the database.
_, height, err := db.NewestSha()
if err != nil {
@@ -912,11 +1317,11 @@ func loadBlockDB() (btcdb.Db, error) {
db.Close()
return nil, err
}
log.Infof("BMGR: Inserted genesis block %v",
btcdLog.Infof("Inserted genesis block %v",
activeNetParams.genesisHash)
height = 0
}
log.Infof("BMGR: Block database loaded with block height %d", height)
btcdLog.Infof("Block database loaded with block height %d", height)
return db, nil
}

115
btcd.go
View File

@@ -1,86 +1,125 @@
// Copyright (c) 2013 Conformal Systems LLC.
// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"github.com/conformal/btcd/limits"
"net"
"net/http"
_ "net/http/pprof"
"os"
"runtime"
"runtime/pprof"
)
var (
cfg *config
cfg *config
shutdownChannel = make(chan bool)
)
// btcdMain is the real main function for btcd. It is necessary to work around
// the fact that deferred functions do not run when os.Exit() is called.
func btcdMain() error {
// Initialize logging and setup deferred flushing to ensure all
// outstanding messages are written on shutdown.
loggers := setLogLevel(defaultLogLevel)
defer func() {
for _, logger := range loggers {
logger.Flush()
}
}()
// winServiceMain is only invoked on Windows. It detects when btcd is running
// as a service and reacts accordingly.
var winServiceMain func() (bool, error)
// Load configuration and parse command line.
// btcdMain is the real main function for btcd. It is necessary to work around
// the fact that deferred functions do not run when os.Exit() is called. The
// optional serverChan parameter is mainly used by the service code to be
// notified with the server once it is setup so it can gracefully stop it when
// requested from the service control manager.
func btcdMain(serverChan chan<- *server) error {
// Load configuration and parse command line. This function also
// initializes logging and configures it accordingly.
tcfg, _, err := loadConfig()
if err != nil {
return err
}
cfg = tcfg
// Change the logging level if needed.
if cfg.DebugLevel != defaultLogLevel {
loggers = setLogLevel(cfg.DebugLevel)
}
defer backendLog.Flush()
// Show version at startup.
log.Infof("Version %s", version())
btcdLog.Infof("Version %s", version())
// See if we want to enable profiling.
// Enable http profiling server if requested.
if cfg.Profile != "" {
go func() {
listenAddr := net.JoinHostPort("", cfg.Profile)
log.Infof("Profile server listening on %s", listenAddr)
log.Errorf("%v", http.ListenAndServe(listenAddr, nil))
btcdLog.Infof("Profile server listening on %s", listenAddr)
profileRedirect := http.RedirectHandler("/debug/pprof",
http.StatusSeeOther)
http.Handle("/", profileRedirect)
btcdLog.Errorf("%v", http.ListenAndServe(listenAddr, nil))
}()
}
// Write cpu profile if requested.
if cfg.CpuProfile != "" {
f, err := os.Create(cfg.CpuProfile)
if err != nil {
btcdLog.Errorf("Unable to create cpu profile: %v", err)
return err
}
pprof.StartCPUProfile(f)
defer f.Close()
defer pprof.StopCPUProfile()
}
// Perform upgrades to btcd as new versions require it.
if err := doUpgrades(); err != nil {
log.Errorf("%v", err)
btcdLog.Errorf("%v", err)
return err
}
// Load the block database.
db, err := loadBlockDB()
if err != nil {
log.Errorf("%v", err)
btcdLog.Errorf("%v", err)
return err
}
defer db.Close()
// Ensure the database is sync'd and closed on Ctrl+C.
addInterruptHandler(func() {
btcdLog.Infof("Gracefully shutting down the database...")
db.RollbackClose()
})
// Create server and start it.
listenAddr := net.JoinHostPort("", cfg.Port)
server, err := newServer(listenAddr, db, activeNetParams.btcnet)
server, err := newServer(cfg.Listeners, db, activeNetParams.btcnet)
if err != nil {
log.Errorf("Unable to start server on %v: %v", listenAddr, err)
// TODO(oga) this logging could do with some beautifying.
btcdLog.Errorf("Unable to start server on %v: %v",
cfg.Listeners, err)
return err
}
addInterruptHandler(func() {
btcdLog.Infof("Gracefully shutting down the server...")
server.Stop()
server.WaitForShutdown()
})
server.Start()
if serverChan != nil {
serverChan <- server
}
server.WaitForShutdown()
// Monitor for graceful server shutdown and signal the main goroutine
// when done. This is done in a separate goroutine rather than waiting
// directly so the main goroutine can be signaled for shutdown by either
// a graceful shutdown or from the main interrupt handler. This is
// necessary since the main goroutine must be kept running long enough
// for the interrupt handler goroutine to finish.
go func() {
server.WaitForShutdown()
srvrLog.Infof("Server shutdown complete")
shutdownChannel <- true
}()
// Wait for shutdown signal from either a graceful server stop or from
// the interrupt handler.
<-shutdownChannel
btcdLog.Info("Shutdown complete")
return nil
}
@@ -89,12 +128,26 @@ func main() {
runtime.GOMAXPROCS(runtime.NumCPU())
// Up some limits.
if err := setLimits(); err != nil {
if err := limits.SetLimits(); err != nil {
os.Exit(1)
}
// Call serviceMain on Windows to handle running as a service. When
// the return isService flag is true, exit now since we ran as a
// service. Otherwise, just fall through to normal operation.
if runtime.GOOS == "windows" {
isService, err := winServiceMain()
if err != nil {
fmt.Println(err)
os.Exit(1)
}
if isService {
os.Exit(0)
}
}
// Work around defer not working after os.Exit()
if err := btcdMain(); err != nil {
if err := btcdMain(nil); err != nil {
os.Exit(1)
}
}

471
config.go
View File

@@ -1,39 +1,60 @@
// Copyright (c) 2013 Conformal Systems LLC.
// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package main
import (
"errors"
"fmt"
"github.com/conformal/btcdb"
_ "github.com/conformal/btcdb/ldb"
_ "github.com/conformal/btcdb/memdb"
"github.com/conformal/btcutil"
"github.com/conformal/btcwire"
"github.com/conformal/go-flags"
"github.com/conformal/go-socks"
"net"
"os"
"path/filepath"
"runtime"
"sort"
"strconv"
"strings"
"time"
)
const (
defaultConfigFilename = "btcd.conf"
defaultLogLevel = "info"
defaultBtcnet = btcwire.MainNet
defaultMaxPeers = 125
defaultBanDuration = time.Hour * 24
defaultVerifyEnabled = false
defaultDbType = "leveldb"
defaultConfigFilename = "btcd.conf"
defaultDataDirname = "data"
defaultLogLevel = "info"
defaultLogDirname = "logs"
defaultLogFilename = "btcd.log"
defaultBtcnet = btcwire.MainNet
defaultMaxPeers = 125
defaultBanDuration = time.Hour * 24
defaultMaxRPCClients = 10
defaultMaxRPCWebsockets = 25
defaultVerifyEnabled = false
defaultDbType = "leveldb"
defaultFreeTxRelayLimit = 15.0
)
var (
defaultConfigFile = filepath.Join(btcdHomeDir(), defaultConfigFilename)
defaultDataDir = filepath.Join(btcdHomeDir(), "data")
knownDbTypes = btcdb.SupportedDBs()
btcdHomeDir = btcutil.AppDataDir("btcd", false)
defaultConfigFile = filepath.Join(btcdHomeDir, defaultConfigFilename)
defaultDataDir = filepath.Join(btcdHomeDir, defaultDataDirname)
defaultListener = net.JoinHostPort("", netParams(defaultBtcnet).listenPort)
knownDbTypes = btcdb.SupportedDBs()
defaultRPCKeyFile = filepath.Join(btcdHomeDir, "rpc.key")
defaultRPCCertFile = filepath.Join(btcdHomeDir, "rpc.cert")
defaultLogDir = filepath.Join(btcdHomeDir, defaultLogDirname)
)
// runServiceCommand is only set to a real function on Windows. It is used
// to parse and execute service commands specified via the -s flag.
var runServiceCommand func(string) error
// config defines the configuration options for btcd.
//
// See loadConfig for details on the configuration load process.
@@ -41,45 +62,49 @@ type config struct {
ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"`
ConfigFile string `short:"C" long:"configfile" description:"Path to configuration file"`
DataDir string `short:"b" long:"datadir" description:"Directory to store data"`
LogDir string `long:"logdir" description:"Directory to log output."`
AddPeers []string `short:"a" long:"addpeer" description:"Add a peer to connect with at startup"`
ConnectPeers []string `long:"connect" description:"Connect only to the specified peers at startup"`
DisableListen bool `long:"nolisten" description:"Disable listening for incoming connections -- NOTE: Listening is automatically disabled if the --connect option is used or if the --proxy option is used without the --tor option"`
Port string `short:"p" long:"port" description:"Listen for connections on this port (default: 8333, testnet: 18333)"`
DisableListen bool `long:"nolisten" description:"Disable listening for incoming connections -- NOTE: Listening is automatically disabled if the --connect or --proxy options are used without also specifying listen interfaces via --listen"`
Listeners []string `long:"listen" description:"Add an interface/port to listen for connections (default all interfaces port: 8333, testnet: 18333)"`
MaxPeers int `long:"maxpeers" description:"Max number of inbound and outbound peers"`
BanDuration time.Duration `long:"banduration" description:"How long to ban misbehaving peers. Valid time units are {s, m, h}. Minimum 1 second"`
RPCUser string `short:"u" long:"rpcuser" description:"Username for RPC connections"`
RPCPass string `short:"P" long:"rpcpass" description:"Password for RPC connections"`
RPCPort string `short:"r" long:"rpcport" description:"Listen for JSON/RPC messages on this port"`
RPCPass string `short:"P" long:"rpcpass" default-mask:"-" description:"Password for RPC connections"`
RPCListeners []string `long:"rpclisten" description:"Add an interface/port to listen for RPC connections (default port: 8334, testnet: 18334)"`
RPCCert string `long:"rpccert" description:"File containing the certificate file"`
RPCKey string `long:"rpckey" description:"File containing the certificate key"`
RPCMaxClients int `long:"rpcmaxclients" description:"Max number of RPC clients for standard connections"`
RPCMaxWebsockets int `long:"rpcmaxwebsockets" description:"Max number of RPC websocket connections"`
DisableRPC bool `long:"norpc" description:"Disable built-in RPC server -- NOTE: The RPC server is disabled by default if no rpcuser/rpcpass is specified"`
DisableDNSSeed bool `long:"nodnsseed" description:"Disable DNS seeding for peers"`
ExternalIPs []string `long:"externalip" description:"Add an ip to the list of local addresses we claim to listen on to peers"`
Proxy string `long:"proxy" description:"Connect via SOCKS5 proxy (eg. 127.0.0.1:9050)"`
ProxyUser string `long:"proxyuser" description:"Username for proxy server"`
ProxyPass string `long:"proxypass" description:"Password for proxy server"`
UseTor bool `long:"tor" description:"Specifies the proxy server used is a Tor node"`
ProxyPass string `long:"proxypass" default-mask:"-" description:"Password for proxy server"`
OnionProxy string `long:"onion" description:"Connect to tor hidden services via SOCKS5 proxy (eg. 127.0.0.1:9050)"`
OnionProxyUser string `long:"onionuser" description:"Username for onion proxy server"`
OnionProxyPass string `long:"onionpass" default-mask:"-" description:"Password for onion proxy server"`
NoOnion bool `long:"noonion" description:"Disable connecting to tor hidden services"`
TestNet3 bool `long:"testnet" description:"Use the test network"`
RegressionTest bool `long:"regtest" description:"Use the regression test network"`
DisableCheckpoints bool `long:"nocheckpoints" description:"Disable built-in checkpoints. Don't do this unless you know what you're doing."`
DbType string `long:"dbtype" description:"Database backend to use for the Block Chain"`
Profile string `long:"profile" description:"Enable HTTP profiling on given port -- NOTE port must be between 1024 and 65536"`
DebugLevel string `short:"d" long:"debuglevel" description:"Logging level {trace, debug, info, warn, error, critical}"`
CpuProfile string `long:"cpuprofile" description:"Write CPU profile to the specified file"`
DebugLevel string `short:"d" long:"debuglevel" description:"Logging level for all subsystems {trace, debug, info, warn, error, critical} -- You may also specify <subsystem>=<level>,<subsystem2>=<level>,... to set the log level for individual subsystems -- Use show to list available subsystems"`
Upnp bool `long:"upnp" description:"Use UPnP to map our listening port outside of NAT"`
FreeTxRelayLimit float64 `long:"limitfreerelay" description:"Limit relay of transactions with no transaction fee to the given amount in thousands of bytes per minute"`
onionlookup func(string) ([]net.IP, error)
lookup func(string) ([]net.IP, error)
oniondial func(string, string) (net.Conn, error)
dial func(string, string) (net.Conn, error)
}
// btcdHomeDir returns an OS appropriate home directory for btcd.
func btcdHomeDir() string {
// Search for Windows APPDATA first. This won't exist on POSIX OSes.
appData := os.Getenv("APPDATA")
if appData != "" {
return filepath.Join(appData, "btcd")
}
// Fall back to standard HOME directory that works for most POSIX OSes.
home := os.Getenv("HOME")
if home != "" {
return filepath.Join(home, ".btcd")
}
// In the worst case, use the current directory.
return "."
// serviceOptions defines the configuration options for btcd as a service on
// Windows.
type serviceOptions struct {
ServiceCommand string `short:"s" long:"service" description:"Service command {install, remove, start, stop}"`
}
// cleanAndExpandPath expands environement variables and leading ~ in the
@@ -87,7 +112,7 @@ func btcdHomeDir() string {
func cleanAndExpandPath(path string) string {
// Expand initial ~ to OS specific home directory.
if strings.HasPrefix(path, "~") {
homeDir := filepath.Dir(btcdHomeDir())
homeDir := filepath.Dir(btcdHomeDir)
path = strings.Replace(path, "~", homeDir, 1)
}
@@ -115,6 +140,71 @@ func validLogLevel(logLevel string) bool {
return false
}
// supportedSubsystems returns a sorted slice of the supported subsystems for
// logging purposes.
func supportedSubsystems() []string {
// Convert the subsystemLoggers map keys to a slice.
subsystems := make([]string, 0, len(subsystemLoggers))
for subsysID := range subsystemLoggers {
subsystems = append(subsystems, subsysID)
}
// Sort the subsytems for stable display.
sort.Strings(subsystems)
return subsystems
}
// parseAndSetDebugLevels attempts to parse the specified debug level and set
// the levels accordingly. An appropriate error is returned if anything is
// invalid.
func parseAndSetDebugLevels(debugLevel string) error {
// When the specified string doesn't have any delimters, treat it as
// the log level for all subsystems.
if !strings.Contains(debugLevel, ",") && !strings.Contains(debugLevel, "=") {
// Validate debug log level.
if !validLogLevel(debugLevel) {
str := "The specified debug level [%v] is invalid"
return fmt.Errorf(str, debugLevel)
}
// Change the logging level for all subsystems.
setLogLevels(debugLevel)
return nil
}
// Split the specified string into subsystem/level pairs while detecting
// issues and update the log levels accordingly.
for _, logLevelPair := range strings.Split(debugLevel, ",") {
if !strings.Contains(logLevelPair, "=") {
str := "The specified debug level contains an invalid " +
"subsystem/level pair [%v]"
return fmt.Errorf(str, logLevelPair)
}
// Extract the specified subsystem and log level.
fields := strings.Split(logLevelPair, "=")
subsysID, logLevel := fields[0], fields[1]
// Validate subsystem.
if _, exists := subsystemLoggers[subsysID]; !exists {
str := "The specified subsystem [%v] is invalid -- " +
"supported subsytems %v"
return fmt.Errorf(str, subsysID, supportedSubsystems())
}
// Validate log level.
if !validLogLevel(logLevel) {
str := "The specified debug level [%v] is invalid"
return fmt.Errorf(str, logLevel)
}
setLogLevel(subsysID, logLevel)
}
return nil
}
// validDbType returns whether or not dbType is a supported database type.
func validDbType(dbType string) bool {
for _, knownType := range knownDbTypes {
@@ -126,16 +216,6 @@ func validDbType(dbType string) bool {
return false
}
// normalizePeerAddress returns addr with the default peer port appended if
// there is not already a port specified.
func normalizePeerAddress(addr string) string {
_, _, err := net.SplitHostPort(addr)
if err != nil {
return net.JoinHostPort(addr, activeNetParams.peerPort)
}
return addr
}
// removeDuplicateAddresses returns a new slice with all duplicate entries in
// addrs removed.
func removeDuplicateAddresses(addrs []string) []string {
@@ -150,29 +230,24 @@ func removeDuplicateAddresses(addrs []string) []string {
return result
}
// normalizeAndRemoveDuplicateAddresses return a new slice with all the passed
// addresses normalized and duplicates removed.
func normalizeAndRemoveDuplicateAddresses(addrs []string) []string {
for i, addr := range addrs {
addrs[i] = normalizePeerAddress(addr)
// normalizeAddress returns addr with the passed default port appended if
// there is not already a port specified.
func normalizeAddress(addr, defaultPort string) string {
_, _, err := net.SplitHostPort(addr)
if err != nil {
return net.JoinHostPort(addr, defaultPort)
}
addrs = removeDuplicateAddresses(addrs)
return addrs
return addr
}
// updateConfigWithActiveParams update the passed config with parameters
// from the active net params if the relevant options in the passed config
// object are the default so options specified by the user on the command line
// are not overridden.
func updateConfigWithActiveParams(cfg *config) {
if cfg.Port == netParams(defaultBtcnet).listenPort {
cfg.Port = activeNetParams.listenPort
// normalizeAddresses returns a new slice with all the passed peer addresses
// normalized with the given default port, and all duplicates removed.
func normalizeAddresses(addrs []string, defaultPort string) []string {
for i, addr := range addrs {
addrs[i] = normalizeAddress(addr, defaultPort)
}
if cfg.RPCPort == netParams(defaultBtcnet).rpcPort {
cfg.RPCPort = activeNetParams.rpcPort
}
return removeDuplicateAddresses(addrs)
}
// filesExists reports whether the named file or directory exists.
@@ -185,6 +260,15 @@ func fileExists(name string) bool {
return true
}
// newConfigParser returns a new command line flags parser.
func newConfigParser(cfg *config, so *serviceOptions, options flags.Options) *flags.Parser {
parser := flags.NewParser(cfg, options)
if runtime.GOOS == "windows" {
parser.AddGroup("Service Options", "Service Options", so)
}
return parser
}
// loadConfig initializes and parses the config using a config file and command
// line options.
//
@@ -200,27 +284,36 @@ func fileExists(name string) bool {
func loadConfig() (*config, []string, error) {
// Default config.
cfg := config{
DebugLevel: defaultLogLevel,
Port: netParams(defaultBtcnet).listenPort,
RPCPort: netParams(defaultBtcnet).rpcPort,
MaxPeers: defaultMaxPeers,
BanDuration: defaultBanDuration,
ConfigFile: defaultConfigFile,
DataDir: defaultDataDir,
DbType: defaultDbType,
ConfigFile: defaultConfigFile,
DebugLevel: defaultLogLevel,
MaxPeers: defaultMaxPeers,
BanDuration: defaultBanDuration,
RPCMaxClients: defaultMaxRPCClients,
RPCMaxWebsockets: defaultMaxRPCWebsockets,
DataDir: defaultDataDir,
LogDir: defaultLogDir,
DbType: defaultDbType,
RPCKey: defaultRPCKeyFile,
RPCCert: defaultRPCCertFile,
FreeTxRelayLimit: defaultFreeTxRelayLimit,
}
// Service options which are only added on Windows.
serviceOpts := serviceOptions{}
// Create the home directory if it doesn't already exist.
err := os.MkdirAll(btcdHomeDir, 0700)
if err != nil {
btcdLog.Errorf("%v", err)
return nil, nil, err
}
// Pre-parse the command line options to see if an alternative config
// file or the version flag was specified.
// file or the version flag was specified. Any errors can be ignored
// here since they will be caught be the final parse below.
preCfg := cfg
preParser := flags.NewParser(&preCfg, flags.Default)
_, err := preParser.Parse()
if err != nil {
if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp {
preParser.WriteHelp(os.Stderr)
}
return nil, nil, err
}
preParser := newConfigParser(&preCfg, &serviceOpts, flags.None)
preParser.Parse()
// Show the version and exit if the version flag was specified.
if preCfg.ShowVersion {
@@ -230,16 +323,30 @@ func loadConfig() (*config, []string, error) {
os.Exit(0)
}
// Load additional config from file.
parser := flags.NewParser(&cfg, flags.Default)
err = parser.ParseIniFile(preCfg.ConfigFile)
if err != nil {
if _, ok := err.(*os.PathError); !ok {
// Perform service command and exit if specified. Invalid service
// commands show an appropriate error. Only runs on Windows since
// the runServiceCommand function will be nil when not on Windows.
if serviceOpts.ServiceCommand != "" && runServiceCommand != nil {
err := runServiceCommand(serviceOpts.ServiceCommand)
if err != nil {
fmt.Fprintln(os.Stderr, err)
parser.WriteHelp(os.Stderr)
return nil, nil, err
}
log.Warnf("%v", err)
os.Exit(0)
}
// Load additional config from file.
var configFileError error
parser := newConfigParser(&cfg, &serviceOpts, flags.Default)
if !preCfg.RegressionTest || preCfg.ConfigFile != defaultConfigFile {
err := flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile)
if err != nil {
if _, ok := err.(*os.PathError); !ok {
fmt.Fprintln(os.Stderr, err)
parser.WriteHelp(os.Stderr)
return nil, nil, err
}
configFileError = err
}
}
// Don't add peers from the config file when in regression test mode.
@@ -273,12 +380,34 @@ func loadConfig() (*config, []string, error) {
} else if cfg.RegressionTest {
activeNetParams = netParams(btcwire.TestNet)
}
updateConfigWithActiveParams(&cfg)
// Validate debug log level.
if !validLogLevel(cfg.DebugLevel) {
str := "%s: The specified debug level [%v] is invalid"
err := fmt.Errorf(str, "loadConfig", cfg.DebugLevel)
// Append the network type to the data directory so it is "namespaced"
// per network. In addition to the block database, there are other
// pieces of data that are saved to disk such as address manager state.
// All data is specific to a network, so namespacing the data directory
// means each individual piece of serialized data does not have to
// worry about changing names per network and such.
cfg.DataDir = cleanAndExpandPath(cfg.DataDir)
cfg.DataDir = filepath.Join(cfg.DataDir, activeNetParams.netName)
// Append the network type to the log directory so it is "namespaced"
// per network in the same fashion as the data directory.
cfg.LogDir = cleanAndExpandPath(cfg.LogDir)
cfg.LogDir = filepath.Join(cfg.LogDir, activeNetParams.netName)
// Special show command to list supported subsystems and exit.
if cfg.DebugLevel == "show" {
fmt.Println("Supported subsystems", supportedSubsystems())
os.Exit(0)
}
// Initialize logging at the default logging level.
initSeelogLogger(filepath.Join(cfg.LogDir, defaultLogFilename))
setLogLevels(defaultLogLevel)
// Parse, validate, and set debug log level(s).
if err := parseAndSetDebugLevels(cfg.DebugLevel); err != nil {
err := fmt.Errorf("%s: %v", "loadConfig", err.Error())
fmt.Fprintln(os.Stderr, err)
parser.WriteHelp(os.Stderr)
return nil, nil, err
@@ -306,15 +435,6 @@ func loadConfig() (*config, []string, error) {
}
}
// Append the network type to the data directory so it is "namespaced"
// per network. In addition to the block database, there are other
// pieces of data that are saved to disk such as address manager state.
// All data is specific to a network, so namespacing the data directory
// means each individual piece of serialized data does not have to
// worry about changing names per network and such.
cfg.DataDir = cleanAndExpandPath(cfg.DataDir)
cfg.DataDir = filepath.Join(cfg.DataDir, activeNetParams.netName)
// Don't allow ban durations that are too short.
if cfg.BanDuration < time.Duration(time.Second) {
str := "%s: The banduration option may not be less than 1s -- parsed [%v]"
@@ -334,24 +454,24 @@ func loadConfig() (*config, []string, error) {
return nil, nil, err
}
// --tor requires --proxy to be set.
if cfg.UseTor && cfg.Proxy == "" {
str := "%s: the --tor option requires --proxy to be set"
err := fmt.Errorf(str, "loadConfig")
fmt.Fprintln(os.Stderr, err)
parser.WriteHelp(os.Stderr)
return nil, nil, err
}
// --proxy without --tor means no listening.
if cfg.Proxy != "" && !cfg.UseTor {
// --proxy or --connect without --listen disables listening.
if (cfg.Proxy != "" || len(cfg.ConnectPeers) > 0) &&
len(cfg.Listeners) == 0 {
cfg.DisableListen = true
}
// Connect means no seeding or listening.
// Connect means no DNS seeding.
if len(cfg.ConnectPeers) > 0 {
cfg.DisableDNSSeed = true
cfg.DisableListen = true
}
// Add the default listener if none were specified. The default
// listener is all addresses on the listen port for the network
// we are to connect to.
if len(cfg.Listeners) == 0 {
cfg.Listeners = []string{
net.JoinHostPort("", activeNetParams.listenPort),
}
}
// The RPC server is disabled if no username or password is provided.
@@ -359,10 +479,127 @@ func loadConfig() (*config, []string, error) {
cfg.DisableRPC = true
}
// Default RPC to listen on localhost only.
if !cfg.DisableRPC && len(cfg.RPCListeners) == 0 {
addrs, err := net.LookupHost("localhost")
if err != nil {
return nil, nil, err
}
cfg.RPCListeners = make([]string, 0, len(addrs))
for _, addr := range addrs {
addr = net.JoinHostPort(addr, activeNetParams.rpcPort)
cfg.RPCListeners = append(cfg.RPCListeners, addr)
}
}
// Add default port to all listener addresses if needed and remove
// duplicate addresses.
cfg.Listeners = normalizeAddresses(cfg.Listeners,
activeNetParams.listenPort)
// Add default port to all rpc listener addresses if needed and remove
// duplicate addresses.
cfg.RPCListeners = normalizeAddresses(cfg.RPCListeners,
activeNetParams.rpcPort)
// Add default port to all added peer addresses if needed and remove
// duplicate addresses.
cfg.AddPeers = normalizeAndRemoveDuplicateAddresses(cfg.AddPeers)
cfg.ConnectPeers = normalizeAndRemoveDuplicateAddresses(cfg.ConnectPeers)
cfg.AddPeers = normalizeAddresses(cfg.AddPeers,
activeNetParams.peerPort)
cfg.ConnectPeers = normalizeAddresses(cfg.ConnectPeers,
activeNetParams.peerPort)
// Setup dial and DNS resolution (lookup) functions depending on the
// specified options. The default is to use the standard net.Dial
// function as well as the system DNS resolver. When a proxy is
// specified, the dial function is set to the proxy specific dial
// function and the lookup is set to use tor (unless --noonion is
// specified in which case the system DNS resolver is used).
cfg.dial = net.Dial
cfg.lookup = net.LookupIP
if cfg.Proxy != "" {
proxy := &socks.Proxy{
Addr: cfg.Proxy,
Username: cfg.ProxyUser,
Password: cfg.ProxyPass,
}
cfg.dial = proxy.Dial
if !cfg.NoOnion {
cfg.lookup = func(host string) ([]net.IP, error) {
return torLookupIP(host, cfg.Proxy)
}
}
}
// Setup onion address dial and DNS resolution (lookup) functions
// depending on the specified options. The default is to use the
// same dial and lookup functions selected above. However, when an
// onion-specific proxy is specified, the onion address dial and
// lookup functions are set to use the onion-specific proxy while
// leaving the normal dial and lookup functions as selected above.
// This allows .onion address traffic to be routed through a different
// proxy than normal traffic.
if cfg.OnionProxy != "" {
cfg.oniondial = func(a, b string) (net.Conn, error) {
proxy := &socks.Proxy{
Addr: cfg.OnionProxy,
Username: cfg.OnionProxyUser,
Password: cfg.OnionProxyPass,
}
return proxy.Dial(a, b)
}
cfg.onionlookup = func(host string) ([]net.IP, error) {
return torLookupIP(host, cfg.OnionProxy)
}
} else {
cfg.oniondial = cfg.dial
cfg.onionlookup = cfg.lookup
}
// Specifying --noonion means the onion address dial and DNS resolution
// (lookup) functions result in an error.
if cfg.NoOnion {
cfg.oniondial = func(a, b string) (net.Conn, error) {
return nil, errors.New("tor has been disabled")
}
cfg.onionlookup = func(a string) ([]net.IP, error) {
return nil, errors.New("tor has been disabled")
}
}
// Warn about missing config file only after all other configuration is
// done. This prevents the warning on help messages and invalid
// options. Note this should go directly before the return.
if configFileError != nil {
btcdLog.Warnf("%v", configFileError)
}
return &cfg, remainingArgs, nil
}
// btcdDial connects to the address on the named network using the appropriate
// dial function depending on the address and configuration options. For
// example, .onion addresses will be dialed using the onion specific proxy if
// one was specified, but will otherwise use the normal dial function (which
// could itself use a proxy or not).
func btcdDial(network, address string) (net.Conn, error) {
if strings.HasSuffix(address, ".onion") {
return cfg.oniondial(network, address)
}
return cfg.dial(network, address)
}
// btcdLookup returns the correct DNS lookup function to use depending on the
// passed host and configuration options. For example, .onion addresses will be
// resolved using the onion specific proxy if one was specified, but will
// otherwise treat the normal proxy as tor unless --noonion was specified in
// which case the lookup will fail. Meanwhile, normal IP addresses will be
// resolved using tor if a proxy was specified unless --noonion was also
// specified in which case the normal system DNS resolver will be used.
func btcdLookup(host string) ([]net.IP, error) {
if strings.HasSuffix(host, ".onion") {
return cfg.onionlookup(host)
}
return cfg.lookup(host)
}

View File

@@ -42,3 +42,90 @@ go-flags 91d04d6c04078a9a1b665530c08b758ec2fbef85
go-socks 92ce162c38f029f7fa66c4336b8b5168f2c75d78
goleveldb 1d0f0aa639f784f38fa998bdf50911ae8ccbcff3
seelog 6b91ad56123bb473755caa213db2bde5422177bf
btcd 0.3.3 Alpha
----------------
btcchain 2300b357319ccf3c27029ef90da7eb7f9ff792ba
btcdb d2269684720afa0fa79f7b5f4c65e398304274c2
btcec a97fd5fe2c670030f8d77dc13b9fa8401ef9f349
btcjson d20f958c92e1444d83215c3cf98d6eef41898dcb
btcscript f4a6449ad3b90d0c830bf2895b83ced8d5fb91e9
btcutil aa811871654079f5036d3692dcf6c66928d19447
btcwire dd41f7e91a682b7c1ceed633e12ece6ba7b6bc72
btcws 497f1770445677372557d70621782d921a5318e3
go-flags fa177a84d3b73bf7e4b79125b2a963bc134eff77
go-socks 92ce162c38f029f7fa66c4336b8b5168f2c75d78
goleveldb 1d0f0aa639f784f38fa998bdf50911ae8ccbcff3
seelog 6b91ad56123bb473755caa213db2bde5422177bf
btcd 0.4.0 Alpha
----------------
btcchain 55331de532b4a4749e242cc434f86e7cd0255166
btcdb 472c998c0d761fa29ef2e05ddfa2460e943033a6
btcec 95b3c063e382af78e4c6876408c31f68efc67840
btcjson 1f52db626dd6b1df6103c6b11cf6e73b72cbe536
btclog fa3217f76bac7375db18868dfcbc7c69b8c36552
btcscript c0c167cc15aa3b721c983fd775cdef7afb42de38
btcutil 9759e8dc64c227fc99c2a01b5c3e52f6700d58f0
btcwire e5a09bdfaa139999d8195c10cea07312dbeb1065
btcws 630d38b1b91215e711868593790ddcd1e50161ec
fastsha256 88f426c18509f838fa498c0e82b0faadd86ecc72
go-flags a53ab6481be8dd78e060df308a9f577859dfeab5
go-socks 92ce162c38f029f7fa66c4336b8b5168f2c75d78
goleveldb 1d0f0aa639f784f38fa998bdf50911ae8ccbcff3
seelog 6b91ad56123bb473755caa213db2bde5422177bf
winsvc 2a5f78f6f2059b884aad8f6907fb029afda48c43
btcd 0.5.0 Alpha
----------------
btcchain 28f485a1d1163b378972708cd478f24e64384b3d
btcdb 6578e7345f7dafe775b56f0d2db1e08f5cd0e328
btcec 58cab817f0863f60fa3c8c14c4b56e115ee549de
btcjson bf90ed21428dac774da33a6b965e46c54cb14d38
btclog 1cd4812f9be0b0c88dd43510d6ce98adfd083b75
btcscript 565f11409c4a1fca39814e9f616a3dada8ca78c7
btcutil 2af3c8263a76bc901c2e2cc7a23f71b91ab97189
btcwire 6c7f45fdb74e92e8de38775a691545ef888d8998
btcws 05a0ba18b48a12b8d0d615a1371befdaa6bdc5dc
fastsha256 a3150791c7d7ccb8050fc0d13528b873fd67e8c3
go-flags a53ab6481be8dd78e060df308a9f577859dfeab5
go-socks 92ce162c38f029f7fa66c4336b8b5168f2c75d78
goleveldb 3ce16077443eab51c7bc8371fef66fddee0b5870
seelog 6b91ad56123bb473755caa213db2bde5422177bf
winsvc 2a5f78f6f2059b884aad8f6907fb029afda48c43
btcd 0.6.0 Alpha
----------------
btcchain e1f66f6103a8775f5a21bf2c531d0167a8789100
btcdb 0a86df4a162ddd8311194d44231f69e94abd1d23
btcec 58cab817f0863f60fa3c8c14c4b56e115ee549de
btcjson 0d1539118b5b6be03b4d2c260177ac978fdb4f3a
btclog 1cd4812f9be0b0c88dd43510d6ce98adfd083b75
btcscript 971fbf8b28711c26c939833bdf53ab286cde02a4
btcutil ca515e278dbc106e15a597e8ac5dc39239672f09
btcwire f6b03bf8a8308837a5663e537be297956279dd67
btcws 3cba42282e40cbc423ad1302bc44ffd060fc5824
fastsha256 a3150791c7d7ccb8050fc0d13528b873fd67e8c3
go-flags a53ab6481be8dd78e060df308a9f577859dfeab5
go-socks 92ce162c38f029f7fa66c4336b8b5168f2c75d78
goleveldb 3ce16077443eab51c7bc8371fef66fddee0b5870
seelog 6b91ad56123bb473755caa213db2bde5422177bf
winsvc 2a5f78f6f2059b884aad8f6907fb029afda48c43
btcd 0.7.0 Alpha
----------------
btcchain 149d8176b0ff0b1fc848bca46ab8bca2079b7ab8
btcdb 0a86df4a162ddd8311194d44231f69e94abd1d23
btcec ff3fac426d4d037505ea8208b79e93c2852451e0
btcjson 21b974e2715f48e36dbcb759314dfe96cdfb094d
btclog 1cd4812f9be0b0c88dd43510d6ce98adfd083b75
btcscript 2b0b512a83acb2bdfa9766b7dc44b6f81cb89c02
btcutil ca515e278dbc106e15a597e8ac5dc39239672f09
btcwire f6b03bf8a8308837a5663e537be297956279dd67
btcws 5c666417351a31c54a7569553198d0763a2337e9
fastsha256 a3150791c7d7ccb8050fc0d13528b873fd67e8c3
go-flags a53ab6481be8dd78e060df308a9f577859dfeab5
go-socks 92ce162c38f029f7fa66c4336b8b5168f2c75d78
goleveldb 3ce16077443eab51c7bc8371fef66fddee0b5870
seelog 6b91ad56123bb473755caa213db2bde5422177bf
winsvc 2a5f78f6f2059b884aad8f6907fb029afda48c43

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2013 Conformal Systems LLC.
// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
@@ -7,7 +7,6 @@ package main
import (
"encoding/binary"
"errors"
"fmt"
"net"
)
@@ -41,29 +40,9 @@ var (
}
)
// try individual DNS server return list of strings for responses.
func doDNSLookup(host, proxy string) ([]net.IP, error) {
var err error
var addrs []net.IP
if proxy != "" {
addrs, err = torLookupIP(host, proxy)
} else {
addrs, err = net.LookupIP(host)
}
if err != nil {
return nil, err
}
return addrs, nil
}
// Use Tor to resolve DNS.
/*
TODO:
* this function must be documented internally
* this function does not handle IPv6
*/
// torLookupIP uses Tor to resolve DNS via the SOCKS extension they provide for
// resolution over the Tor network. Tor itself doesnt support ipv6 so this
// doesn't either.
func torLookupIP(host, proxy string) ([]net.IP, error) {
conn, err := net.Dial("tcp", proxy)
if err != nil {
@@ -149,17 +128,12 @@ func torLookupIP(host, proxy string) ([]net.IP, error) {
// resolution. If any errors occur then the seeder that errored will not have
// any hosts in the list. Therefore if all hosts failed an empty slice of
// strings will be returned.
func dnsDiscover(seeder string, proxy string) []net.IP {
log.Debugf("DISC: Fetching list of seeds from %v", seeder)
peers, err := doDNSLookup(seeder, proxy)
func dnsDiscover(seeder string) []net.IP {
discLog.Debugf("Fetching list of seeds from %v", seeder)
peers, err := btcdLookup(seeder)
if err != nil {
seederPlusProxy := seeder
if proxy != "" {
seederPlusProxy = fmt.Sprintf("%s (proxy %s)",
seeder, proxy)
}
log.Debugf("DISC: Unable to fetch dns seeds "+
"from %s: %v", seederPlusProxy, err)
discLog.Debugf("Unable to fetch dns seeds from %s: %v",
seeder, err)
return []net.IP{}
}

97
doc.go
View File

@@ -1,4 +1,4 @@
// Copyright (c) 2013 Conformal Systems LLC.
// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
@@ -13,48 +13,73 @@ The following section provides a usage overview which enumerates the flags. An
interesting point to note is that the long form of all of these options
(except -C) can be specified in a configuration file that is automatically
parsed when btcd starts up. By default, the configuration file is located at
~/.btcd/btcd.conf on POSIX-style operating systems and %APPDATA%\btcd\btcd.conf
~/.btcd/btcd.conf on POSIX-style operating systems and %LOCALAPPDATA%\btcd\btcd.conf
on Windows. The -C (--configfile) flag, as shown below, can be used to override
this location.
Usage:
btcd [OPTIONS]
The flags are:
Application Options:
-V, --version Display version information and exit
-C, --configfile= Path to configuration file
-b, --datadir= Directory to store data
-a, --addpeer= Add a peer to connect with at startup
--connect= Connect only to the specified peers at startup
--nolisten Disable listening for incoming connections -- NOTE:
Listening is automatically disabled if the --connect
or --proxy options are used without also specifying
listen interfaces via --listen
--listen= Add an interface/port to listen for connections
(default all interfaces port: 8333, testnet: 18333)
--maxpeers= Max number of inbound and outbound peers (125)
--banduration= How long to ban misbehaving peers. Valid time units
are {s, m, h}. Minimum 1 second (24h0m0s)
-u, --rpcuser= Username for RPC connections
-P, --rpcpass= Password for RPC connections
--rpclisten= Add an interface/port to listen for RPC connections
(default port: 8334, testnet: 18334)
--rpccert= File containing the certificate file
--rpckey= File containing the certificate key
--rpcmaxclients= Max number of RPC clients for standard connections
(10)
--rpcmaxwebsockets= Max number of RPC clients for standard connections
(25)
--norpc Disable built-in RPC server -- NOTE: The RPC server
is disabled by default if no rpcuser/rpcpass is
specified
--nodnsseed Disable DNS seeding for peers
--externalip: Add an ip to the list of local addresses we claim to
listen on to peers
--proxy= Connect via SOCKS5 proxy (eg. 127.0.0.1:9050)
--proxyuser= Username for proxy server
--proxypass= Password for proxy server
--onion= Connect to tor hidden services via SOCKS5 proxy (eg.
127.0.0.1:9050)
--onionuser= Username for onion proxy server
--onionpass= Password for onion proxy server
--noonion= Disable connecting to tor hidden services
--tor= Specifies the proxy server used is a Tor node
--testnet= Use the test network
--regtest= Use the regression test network
--nocheckpoints= Disable built-in checkpoints. Don't do this unless
you know what you're doing.
--dbtype= Database backend to use for the Block Chain (leveldb)
--profile= Enable HTTP profiling on given port -- NOTE port must
be between 1024 and 65536 (6060)
--cpuprofile= Write CPU profile to the specified file
-d, --debuglevel: Logging level for all subsystems {trace, debug, info,
warn, error, critical} -- You may also specify
<subsystem>=<level>,<subsystem2>=<level>,... to set
the log level for individual subsystems -- Use show
to list available subsystems (info)
--upnp Use UPnP to map our listening port outside of NAT
--limitfreerelay= Limit relay of transactions with no transaction fee
to the given amount in thousands of bytes per minute
(15)
Help Options:
-h, --help Show this help message
-V, --version Display version information and exit
-C, --configfile= Path to configuration file
-b, --datadir= Directory to store data
-a, --addpeer= Add a peer to connect with at startup
--connect= Connect only to the specified peers at startup
--nolisten Disable listening for incoming connections -- NOTE:
Listening is automatically disabled if the --connect
option is used or if the --proxy option is used without
the --tor option
-p, --port= Listen for connections on this port (default: 8333,
testnet: 18333)
--maxpeers= Max number of inbound and outbound peers
--banduration= How long to ban misbehaving peers. Valid time units are
{s, m, h}. Minimum 1 second
-u, --rpcuser= Username for RPC connections
-P, --rpcpass= Password for RPC connections
-r, --rpcport= Listen for JSON/RPC messages on this port
--norpc Disable built-in RPC server -- NOTE: The RPC server is
disabled by default if no rpcuser/rpcpass is specified
--nodnsseed Disable DNS seeding for peers
--proxy= Connect via SOCKS5 proxy (eg. 127.0.0.1:9050)
--proxyuser= Username for proxy server
--proxypass= Password for proxy server
--tor Specifies the proxy server used is a Tor node
--testnet Use the test network
--regtest Use the regression test network
--nocheckpoints Disable built-in checkpoints. Don't do this unless you
know what you're doing
--dbtype= Database backend to use for the Block Chain
--profile= Enable HTTP profiling on given port -- NOTE port must be
between 1024 and 65536
-d, --debuglevel= Logging level {trace, debug, info, warn, error,
critical}
*/
package main

View File

@@ -1,10 +1,10 @@
// Copyright (c) 2013 Conformal Systems LLC.
// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package main
package limits
// Plan 9 has no process accounting. no-op here
func setLimits() error {
func SetLimits() error {
return nil
}

View File

@@ -1,10 +1,10 @@
// Copyright (c) 2013 Conformal Systems LLC.
// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
// +build !windows,!plan9
package main
package limits
import (
"fmt"
@@ -16,7 +16,7 @@ const (
fileLimitMin = 1024
)
func setLimits() error {
func SetLimits() error {
var rLimit syscall.Rlimit
err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rLimit)

View File

@@ -1,9 +1,9 @@
// Copyright (c) 2013 Conformal Systems LLC.
// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package main
package limits
func setLimits() error {
func SetLimits() error {
return nil
}

163
log.go
View File

@@ -1,4 +1,4 @@
// Copyright (c) 2013 Conformal Systems LLC.
// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
@@ -8,6 +8,7 @@ import (
"fmt"
"github.com/conformal/btcchain"
"github.com/conformal/btcdb"
"github.com/conformal/btclog"
"github.com/conformal/btcscript"
"github.com/conformal/btcwire"
"github.com/conformal/seelog"
@@ -24,10 +25,40 @@ const (
lockTimeThreshold uint32 = 5e8 // Tue Nov 5 00:53:20 1985 UTC
)
// Loggers per subsytem. Note that backendLog is a seelog logger that all of
// the subsystem loggers route their messages to. When adding new subsystems,
// add a reference here, to the subsystemLoggers map, and the useLogger
// function.
var (
log = seelog.Disabled
backendLog = seelog.Disabled
btcdLog = btclog.Disabled
bcdbLog = btclog.Disabled
chanLog = btclog.Disabled
scrpLog = btclog.Disabled
amgrLog = btclog.Disabled
bmgrLog = btclog.Disabled
discLog = btclog.Disabled
peerLog = btclog.Disabled
rpcsLog = btclog.Disabled
srvrLog = btclog.Disabled
txmpLog = btclog.Disabled
)
// subsystemLoggers maps each subsystem identifier to its associated logger.
var subsystemLoggers = map[string]btclog.Logger{
"BTCD": btcdLog,
"BCDB": bcdbLog,
"CHAN": chanLog,
"SCRP": scrpLog,
"AMGR": amgrLog,
"BMGR": bmgrLog,
"DISC": discLog,
"PEER": peerLog,
"RPCS": rpcsLog,
"SRVR": srvrLog,
"TXMP": txmpLog,
}
// logClosure is used to provide a closure over expensive logging operations
// so don't have to be performed when the logging level doesn't warrant it.
type logClosure func() string
@@ -44,22 +75,68 @@ func newLogClosure(c func() string) logClosure {
return logClosure(c)
}
// newLogger creates a new seelog logger using the provided logging level and
// log message prefix.
func newLogger(level string, prefix string) seelog.LoggerInterface {
//<seelog type="adaptive" mininterval="2000000" maxinterval="100000000"
// critmsgcount="500" minlevel="%s">
// useLogger updates the logger references for subsystemID to logger. Invalid
// subsystems are ignored.
func useLogger(subsystemID string, logger btclog.Logger) {
if _, ok := subsystemLoggers[subsystemID]; !ok {
return
}
subsystemLoggers[subsystemID] = logger
fmtstring := `
<seelog type="sync" minlevel="%s">
switch subsystemID {
case "BTCD":
btcdLog = logger
case "BCDB":
bcdbLog = logger
btcdb.UseLogger(logger)
case "CHAN":
chanLog = logger
btcchain.UseLogger(logger)
case "SCRP":
scrpLog = logger
btcscript.UseLogger(logger)
case "AMGR":
amgrLog = logger
case "BMGR":
bmgrLog = logger
case "DISC":
discLog = logger
case "PEER":
peerLog = logger
case "RPCS":
rpcsLog = logger
case "SRVR":
srvrLog = logger
case "TXMP":
txmpLog = logger
}
}
// initSeelogLogger initializes a new seelog logger that is used as the backend
// for all logging subsytems.
func initSeelogLogger(logFile string) {
config := `
<seelog type="adaptive" mininterval="2000000" maxinterval="100000000"
critmsgcount="500" minlevel="trace">
<outputs formatid="all">
<console/>
<console />
<rollingfile type="size" filename="%s" maxsize="10485760" maxrolls="3" />
</outputs>
<formats>
<format id="all" format="%%Time %%Date [%%LEV] %s: %%Msg%%n" />
<format id="all" format="%%Time %%Date [%%LEV] %%Msg%%n" />
</formats>
</seelog>`
config := fmt.Sprintf(fmtstring, level, prefix)
config = fmt.Sprintf(config, logFile)
logger, err := seelog.LoggerFromConfigAsString(config)
if err != nil {
@@ -67,40 +144,42 @@ func newLogger(level string, prefix string) seelog.LoggerInterface {
os.Exit(1)
}
return logger
backendLog = logger
}
// useLogger sets the btcd logger to the passed logger.
func useLogger(logger seelog.LoggerInterface) {
log = logger
// setLogLevel sets the logging level for provided subsystem. Invalid
// subsystems are ignored. Uninitialized subsystems are dynamically created as
// needed.
func setLogLevel(subsystemID string, logLevel string) {
// Ignore invalid subsystems.
logger, ok := subsystemLoggers[subsystemID]
if !ok {
return
}
// Default to info if the log level is invalid.
level, ok := btclog.LogLevelFromString(logLevel)
if !ok {
level = btclog.InfoLvl
}
// Create new logger for the subsystem if needed.
if logger == btclog.Disabled {
logger = btclog.NewSubsystemLogger(backendLog, subsystemID+": ")
useLogger(subsystemID, logger)
}
logger.SetLevel(level)
}
// setLogLevel sets the log level for the logging system. It initializes a
// logger for each subsystem at the provided level.
func setLogLevel(logLevel string) []seelog.LoggerInterface {
var loggers []seelog.LoggerInterface
// Define sub-systems.
subSystems := []struct {
level string
prefix string
useLogger func(seelog.LoggerInterface)
}{
{logLevel, "BTCD", useLogger},
{logLevel, "BCDB", btcdb.UseLogger},
{logLevel, "CHAN", btcchain.UseLogger},
{logLevel, "SCRP", btcscript.UseLogger},
// setLogLevels sets the log level for all subsystem loggers to the passed
// level. It also dynamically creates the subsystem loggers as needed, so it
// can be used to initialize the logging system.
func setLogLevels(logLevel string) {
// Configure all sub-systems with the new logging level. Dynamically
// create loggers as needed.
for subsystemID := range subsystemLoggers {
setLogLevel(subsystemID, logLevel)
}
// Configure all sub-systems with new loggers while keeping track of
// the created loggers to return so they can be flushed.
for _, s := range subSystems {
newLog := newLogger(s.level, s.prefix)
loggers = append(loggers, newLog)
s.useLogger(newLog)
}
return loggers
}
// directionString is a helper function that returns a string that represents
@@ -201,7 +280,7 @@ func messageSummary(msg btcwire.Message) string {
header := &msg.Header
hash, _ := msg.BlockSha()
return fmt.Sprintf("hash %s, ver %d, %d tx, %s", hash,
header.Version, header.TxnCount, header.Timestamp)
header.Version, len(msg.Transactions), header.Timestamp)
case *btcwire.MsgInv:
return invSummary(msg.InvList)

View File

@@ -1,17 +1,17 @@
// Copyright (c) 2013 Conformal Systems LLC.
// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"container/list"
"crypto/rand"
"fmt"
"github.com/conformal/btcchain"
"github.com/conformal/btcdb"
"github.com/conformal/btcscript"
"github.com/conformal/btcutil"
"github.com/conformal/btcwire"
"math"
"math/big"
@@ -49,7 +49,7 @@ const (
// maxStandardTxSize is the maximum size allowed for transactions that
// are considered standard and will therefore be relayed and considered
// for mining.
maxStandardTxSize = btcwire.MaxBlockPayload / 10
maxStandardTxSize = 100000
// maxStandardSigScriptSize is the maximum size allowed for a
// transaction input signature script to be considered standard. This
@@ -60,27 +60,46 @@ const (
// prosperity. 3*80 + 3*65 + 65 = 500
maxStandardSigScriptSize = 500
// maxStandardMultiSigs is the maximum number of signatures
// allowed in a multi-signature transaction output script for it to be
// maxStandardMultiSigKeys is the maximum number of public keys allowed
// in a multi-signature transaction output script for it to be
// considered standard.
maxStandardMultiSigs = 3
maxStandardMultiSigKeys = 3
// minTxRelayFee is the minimum fee in satoshi that is required for
// a transaction to be treated as free for relay purposes. It is also
// used to help determine if a transation is considered dust.
// minTxRelayFee is the minimum fee in satoshi that is required for a
// transaction to be treated as free for relay purposes. It is also
// used to help determine if a transaction is considered dust and as a
// base for calculating minimum required fees for larger transactions.
// This value is in Satoshi/KB (kilobyte, not kibibyte).
minTxRelayFee = 10000
// blockPrioritySize is the number of bytes reserved in a block for
// high-priority transactions. It is mainly used to help determine the
// minimum required fee for a transaction.
blockPrioritySize = 50000
)
// TxDesc is a descriptor containing a transaction in the mempool and the
// metadata we store about it.
type TxDesc struct {
Tx *btcutil.Tx // Transaction.
Added time.Time // Time when added to pool.
Height int64 // Blockheight when added to pool.
Fee int64 // Transaction fees.
}
// txMemPool is used as a source of transactions that need to be mined into
// blocks and relayed to other peers. It is safe for concurrent access from
// multiple peers.
type txMemPool struct {
sync.RWMutex
server *server
pool map[btcwire.ShaHash]*btcwire.MsgTx
orphans map[btcwire.ShaHash]*btcwire.MsgTx
pool map[btcwire.ShaHash]*TxDesc
orphans map[btcwire.ShaHash]*btcutil.Tx
orphansByPrev map[btcwire.ShaHash]*list.List
outpoints map[btcwire.OutPoint]*btcwire.MsgTx
outpoints map[btcwire.OutPoint]*btcutil.Tx
pennyTotal float64 // exponentially decaying total for penny spends.
lastPennyUnix int64 // unix time of last ``penny spend''
}
// isDust returns whether or not the passed transaction output amount is
@@ -88,14 +107,6 @@ type txMemPool struct {
// relay fee. In particular, if the cost to the network to spend coins is more
// than 1/3 of the minimum transaction relay fee, it is considered dust.
func isDust(txOut *btcwire.TxOut) bool {
// Get the serialized size of the transaction output.
//
// TODO(davec): The serialized size should come from btcwire, but it
// currently doesn't provide a way to do so for transaction outputs, so
// calculate it here based on the current format.
// 8 bytes for value + 1 byte for script length + script length
txOutSize := 9 + len(txOut.PkScript)
// The total serialized size consists of the output and the associated
// input script to redeem it. Since there is no input script
// to redeem it yet, use the minimum size of a typical input script.
@@ -138,17 +149,17 @@ func isDust(txOut *btcwire.TxOut) bool {
// The most common scripts are pay-to-pubkey-hash, and as per the above
// breakdown, the minimum size of a p2pkh input script is 148 bytes. So
// that figure is used.
totalSize := txOutSize + 148
totalSize := txOut.SerializeSize() + 148
// The output is considered dust if the cost to the network to spend the
// coins is more than 1/3 of the minimum transaction relay fee.
// minTxRelayFee is in Satoshi/KB (kilobyte, not kibibyte), so
// coins is more than 1/3 of the minimum free transaction relay fee.
// minFreeTxRelayFee is in Satoshi/KB (kilobyte, not kibibyte), so
// multiply by 1000 to convert bytes.
//
// Using the typical values for a pay-to-pubkey-hash transaction from
// the breakdown above and the default minimum transaction relay fee of
// 10000, this equates to values less than 5460 satoshi being considered
// dust.
// the breakdown above and the default minimum free transaction relay
// fee of 10000, this equates to values less than 5460 satoshi being
// considered dust.
//
// The following is equivalent to (value/totalSize) * (1/3) * 1000
// without needing to do floating point math.
@@ -158,22 +169,42 @@ func isDust(txOut *btcwire.TxOut) bool {
// checkPkScriptStandard performs a series of checks on a transaction ouput
// script (public key script) to ensure it is a "standard" public key script.
// A standard public key script is one that is a recognized form, and for
// multi-signature scripts, only contains from 1 to 3 signatures.
func checkPkScriptStandard(pkScript []byte) error {
scriptClass := btcscript.GetScriptClass(pkScript)
// multi-signature scripts, only contains from 1 to maxStandardMultiSigKeys
// public keys.
func checkPkScriptStandard(pkScript []byte, scriptClass btcscript.ScriptClass) error {
switch scriptClass {
case btcscript.MultiSigTy:
// TODO(davec): Need to get the actual number of signatures.
numSigs := 1
numPubKeys, numSigs, err := btcscript.CalcMultiSigStats(pkScript)
if err != nil {
return err
}
// A standard multi-signature public key script must contain
// from 1 to maxStandardMultiSigKeys public keys.
if numPubKeys < 1 {
str := fmt.Sprintf("multi-signature script with no " +
"pubkeys")
return TxRuleError(str)
}
if numPubKeys > maxStandardMultiSigKeys {
str := fmt.Sprintf("multi-signature script with %d "+
"public keys which is more than the allowed "+
"max of %d", numPubKeys, maxStandardMultiSigKeys)
return TxRuleError(str)
}
// A standard multi-signature public key script must have at
// least 1 signature and no more signatures than available
// public keys.
if numSigs < 1 {
str := fmt.Sprintf("multi-signature script with no " +
"signatures")
return TxRuleError(str)
}
if numSigs > maxStandardMultiSigs {
if numSigs > numPubKeys {
str := fmt.Sprintf("multi-signature script with %d "+
"signatures which is more than the allowed max "+
"of %d", numSigs, maxStandardMultiSigs)
"signatures which is more than the available "+
"%d public keys", numSigs, numPubKeys)
return TxRuleError(str)
}
@@ -191,11 +222,13 @@ func checkPkScriptStandard(pkScript []byte) error {
// finalized, conforming to more stringent size constraints, having scripts
// of recognized forms, and not containing "dust" outputs (those that are
// so small it costs more to process them than they are worth).
func checkTransactionStandard(tx *btcwire.MsgTx, height int64) error {
func checkTransactionStandard(tx *btcutil.Tx, height int64) error {
msgTx := tx.MsgTx()
// The transaction must be a currently supported version.
if tx.Version > btcwire.TxVersion || tx.Version < 1 {
if msgTx.Version > btcwire.TxVersion || msgTx.Version < 1 {
str := fmt.Sprintf("transaction version %d is not in the "+
"valid range of %d-%d", tx.Version, 1,
"valid range of %d-%d", msgTx.Version, 1,
btcwire.TxVersion)
return TxRuleError(str)
}
@@ -211,19 +244,14 @@ func checkTransactionStandard(tx *btcwire.MsgTx, height int64) error {
// almost as much to process as the sender fees, limit the maximum
// size of a transaction. This also helps mitigate CPU exhaustion
// attacks.
var serializedTxBuf bytes.Buffer
err := tx.Serialize(&serializedTxBuf)
if err != nil {
return err
}
serializedLen := serializedTxBuf.Len()
serializedLen := msgTx.SerializeSize()
if serializedLen > maxStandardTxSize {
str := fmt.Sprintf("transaction size of %v is larger than max "+
"allowed size of %v", serializedLen, maxStandardTxSize)
return TxRuleError(str)
}
for i, txIn := range tx.TxIn {
for i, txIn := range msgTx.TxIn {
// Each transaction input signature script must not exceed the
// maximum size allowed for a standard transaction. See
// the comment on maxStandardSigScriptSize for more details.
@@ -243,17 +271,34 @@ func checkTransactionStandard(tx *btcwire.MsgTx, height int64) error {
"script is not push only", i)
return TxRuleError(str)
}
// Each transaction input signature script must only contain
// canonical data pushes. A canonical data push is one where
// the minimum possible number of bytes is used to represent
// the data push as possible.
if !btcscript.HasCanonicalPushes(txIn.SignatureScript) {
str := fmt.Sprintf("transaction input %d: signature "+
"script has a non-canonical data push", i)
return TxRuleError(str)
}
}
// None of the output public key scripts can be a non-standard script or
// be "dust".
for i, txOut := range tx.TxOut {
err := checkPkScriptStandard(txOut.PkScript)
numNullDataOutputs := 0
for i, txOut := range msgTx.TxOut {
scriptClass := btcscript.GetScriptClass(txOut.PkScript)
err := checkPkScriptStandard(txOut.PkScript, scriptClass)
if err != nil {
str := fmt.Sprintf("transaction output %d: %v", i, err)
return TxRuleError(str)
}
// Accumulate the number of outputs which only carry data.
if scriptClass == btcscript.NullDataTy {
numNullDataOutputs++
}
if isDust(txOut) {
str := fmt.Sprintf("transaction output %d: payment "+
"of %d is dust", i, txOut.Value)
@@ -261,20 +306,99 @@ func checkTransactionStandard(tx *btcwire.MsgTx, height int64) error {
}
}
// A standard transaction must not have more than one output script that
// only carries data.
if numNullDataOutputs > 1 {
return TxRuleError("more than one transaction output is a " +
"nulldata script")
}
return nil
}
// checkInputsStandard performs a series of checks on a transaction's inputs
// to ensure they are "standard". A standard transaction input is one that
// that consumes the same number of outputs from the stack as the output script
// pushes. This help prevent resource exhaustion attacks by "creative" use of
// scripts that are super expensive to process like OP_DUP OP_CHECKSIG OP_DROP
// repeated a large number of times followed by a final OP_TRUE.
func checkInputsStandard(tx *btcwire.MsgTx) error {
// TODO(davec): Implement
// that consumes the expected number of elements from the stack and that number
// is the same as the output script pushes. This help prevent resource
// exhaustion attacks by "creative" use of scripts that are super expensive to
// process like OP_DUP OP_CHECKSIG OP_DROP repeated a large number of times
// followed by a final OP_TRUE.
func checkInputsStandard(tx *btcutil.Tx, txStore btcchain.TxStore) error {
// NOTE: The reference implementation also does a coinbase check here,
// but coinbases have already been rejected prior to calling this
// function so no need to recheck.
for i, txIn := range tx.MsgTx().TxIn {
// It is safe to elide existence and index checks here since
// they have already been checked prior to calling this
// function.
prevOut := txIn.PreviousOutpoint
originTx := txStore[prevOut.Hash].Tx.MsgTx()
originPkScript := originTx.TxOut[prevOut.Index].PkScript
// Calculate stats for the script pair.
scriptInfo, err := btcscript.CalcScriptInfo(txIn.SignatureScript,
originPkScript, true)
if err != nil {
return err
}
// A negative value for expected inputs indicates the script is
// non-standard in some way.
if scriptInfo.ExpectedInputs < 0 {
str := fmt.Sprintf("transaction input #%d expects %d "+
"inputs", i, scriptInfo.ExpectedInputs)
return TxRuleError(str)
}
// The script pair is non-standard if the number of available
// inputs does not match the number of expected inputs.
if scriptInfo.NumInputs != scriptInfo.ExpectedInputs {
str := fmt.Sprintf("transaction input #%d expects %d "+
"inputs, but referenced output script only "+
"provides %d", i, scriptInfo.ExpectedInputs,
scriptInfo.NumInputs)
return TxRuleError(str)
}
}
return nil
}
// calcMinRelayFee retuns the minimum transaction fee required for the passed
// transaction to be accepted into the memory pool and relayed.
func calcMinRelayFee(tx *btcutil.Tx) int64 {
// Most miners allow a free transaction area in blocks they mine to go
// alongside the area used for high-priority transactions as well as
// transactions with fees. A transaction size of up to 1000 bytes is
// considered safe to go into this section. Further, the minimum fee
// calculated below on its own would encourage several small
// transactions to avoid fees rather than one single larger transaction
// which is more desirable. Therefore, as long as the size of the
// transaction does not exceeed 1000 less than the reserved space for
// high-priority transactions, don't require a fee for it.
serializedLen := int64(tx.MsgTx().SerializeSize())
if serializedLen < (blockPrioritySize - 1000) {
return 0
}
// Calculate the minimum fee for a transaction to be allowed into the
// mempool and relayed by scaling the base fee (which is the minimum
// free transaction relay fee). minTxRelayFee is in Satoshi/KB
// (kilobyte, not kibibyte), so divide the transaction size by 1000 to
// convert to kilobytes. Also, integer division is used so fees only
// increase on full kilobyte boundaries.
minFee := (1 + serializedLen/1000) * minTxRelayFee
// Set the minimum fee to the maximum possible value if the calculated
// fee is not in the valid range for monetary amounts.
if minFee < 0 || minFee > btcutil.MaxSatoshi {
minFee = btcutil.MaxSatoshi
}
return minFee
}
// removeOrphan removes the passed orphan transaction from the orphan pool and
// previous orphan index.
//
@@ -287,11 +411,11 @@ func (mp *txMemPool) removeOrphan(txHash *btcwire.ShaHash) {
}
// Remove the reference from the previous orphan index.
for _, txIn := range tx.TxIn {
for _, txIn := range tx.MsgTx().TxIn {
originTxHash := txIn.PreviousOutpoint.Hash
if orphans, exists := mp.orphansByPrev[originTxHash]; exists {
for e := orphans.Front(); e != nil; e = e.Next() {
if e.Value.(*btcwire.MsgTx) == tx {
if e.Value.(*btcutil.Tx) == tx {
orphans.Remove(e)
break
}
@@ -349,13 +473,13 @@ func (mp *txMemPool) limitNumOrphans() error {
// addOrphan adds an orphan transaction to the orphan pool.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *txMemPool) addOrphan(tx *btcwire.MsgTx, txHash *btcwire.ShaHash) {
func (mp *txMemPool) addOrphan(tx *btcutil.Tx) {
// Limit the number orphan transactions to prevent memory exhaustion. A
// random orphan is evicted to make room if needed.
mp.limitNumOrphans()
mp.orphans[*txHash] = tx
for _, txIn := range tx.TxIn {
mp.orphans[*tx.Sha()] = tx
for _, txIn := range tx.MsgTx().TxIn {
originTxHash := txIn.PreviousOutpoint.Hash
if mp.orphansByPrev[originTxHash] == nil {
mp.orphansByPrev[originTxHash] = list.New()
@@ -363,14 +487,14 @@ func (mp *txMemPool) addOrphan(tx *btcwire.MsgTx, txHash *btcwire.ShaHash) {
mp.orphansByPrev[originTxHash].PushBack(tx)
}
log.Debugf("TXMP: Stored orphan transaction %v (total: %d)", txHash,
txmpLog.Debugf("Stored orphan transaction %v (total: %d)", tx.Sha(),
len(mp.orphans))
}
// maybeAddOrphan potentially adds an orphan to the orphan pool.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *txMemPool) maybeAddOrphan(tx *btcwire.MsgTx, txHash *btcwire.ShaHash) error {
func (mp *txMemPool) maybeAddOrphan(tx *btcutil.Tx) error {
// Ignore orphan transactions that are too large. This helps avoid
// a memory exhaustion attack based on sending a lot of really large
// orphans. In the case there is a valid transaction larger than this,
@@ -381,12 +505,7 @@ func (mp *txMemPool) maybeAddOrphan(tx *btcwire.MsgTx, txHash *btcwire.ShaHash)
// also limited, so this equates to a maximum memory used of
// maxOrphanTxSize * maxOrphanTransactions (which is 500MB as of the
// time this comment was written).
var serializedTxBuf bytes.Buffer
err := tx.Serialize(&serializedTxBuf)
if err != nil {
return err
}
serializedLen := serializedTxBuf.Len()
serializedLen := tx.MsgTx().SerializeSize()
if serializedLen > maxOrphanTxSize {
str := fmt.Sprintf("orphan transaction size of %d bytes is "+
"larger than max allowed size of %d bytes",
@@ -395,7 +514,7 @@ func (mp *txMemPool) maybeAddOrphan(tx *btcwire.MsgTx, txHash *btcwire.ShaHash)
}
// Add the orphan if the none of the above disqualified it.
mp.addOrphan(tx, txHash)
mp.addOrphan(tx)
return nil
}
@@ -468,14 +587,15 @@ func (mp *txMemPool) HaveTransaction(hash *btcwire.ShaHash) bool {
return mp.haveTransaction(hash)
}
// removeTransaction removes the passed transaction from the memory pool.
// removeTransaction is the internal function which implements the public
// RemoveTransaction. See the comment for RemoveTransaction for more details.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *txMemPool) removeTransaction(tx *btcwire.MsgTx) {
func (mp *txMemPool) removeTransaction(tx *btcutil.Tx) {
// Remove any transactions which rely on this one.
txHash, _ := tx.TxSha()
for i := uint32(0); i < uint32(len(tx.TxOut)); i++ {
outpoint := btcwire.NewOutPoint(&txHash, i)
txHash := tx.Sha()
for i := uint32(0); i < uint32(len(tx.MsgTx().TxOut)); i++ {
outpoint := btcwire.NewOutPoint(txHash, i)
if txRedeemer, exists := mp.outpoints[*outpoint]; exists {
mp.removeTransaction(txRedeemer)
}
@@ -483,11 +603,44 @@ func (mp *txMemPool) removeTransaction(tx *btcwire.MsgTx) {
// Remove the transaction and mark the referenced outpoints as unspent
// by the pool.
if tx, exists := mp.pool[txHash]; exists {
for _, txIn := range tx.TxIn {
if txDesc, exists := mp.pool[*txHash]; exists {
for _, txIn := range txDesc.Tx.MsgTx().TxIn {
delete(mp.outpoints, txIn.PreviousOutpoint)
}
delete(mp.pool, txHash)
delete(mp.pool, *txHash)
}
}
// RemoveTransaction removes the passed transaction and any transactions which
// depend on it from the memory pool.
//
// This function is safe for concurrent access.
func (mp *txMemPool) RemoveTransaction(tx *btcutil.Tx) {
// Protect concurrent access.
mp.Lock()
defer mp.Unlock()
mp.removeTransaction(tx)
}
// RemoveDoubleSpends removes all transactions which spend outputs spent by the
// passed transaction from the memory pool. Removing those transactions then
// leads to removing all transactions which rely on them, recursively. This is
// necessary when a block is connected to the main chain because the block may
// contain transactions which were previously unknown to the memory pool
//
// This function is safe for concurrent access.
func (mp *txMemPool) RemoveDoubleSpends(tx *btcutil.Tx) {
// Protect concurrent access.
mp.Lock()
defer mp.Unlock()
for _, txIn := range tx.MsgTx().TxIn {
if txRedeemer, ok := mp.outpoints[txIn.PreviousOutpoint]; ok {
if !txRedeemer.Sha().IsEqual(tx.Sha()) {
mp.removeTransaction(txRedeemer)
}
}
}
}
@@ -496,11 +649,16 @@ func (mp *txMemPool) removeTransaction(tx *btcwire.MsgTx) {
// helper for maybeAcceptTransaction.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *txMemPool) addTransaction(tx *btcwire.MsgTx, txHash *btcwire.ShaHash) {
func (mp *txMemPool) addTransaction(tx *btcutil.Tx, height, fee int64) {
// Add the transaction to the pool and mark the referenced outpoints
// as spent by the pool.
mp.pool[*txHash] = tx
for _, txIn := range tx.TxIn {
mp.pool[*tx.Sha()] = &TxDesc{
Tx: tx,
Added: time.Now(),
Height: height,
Fee: fee,
}
for _, txIn := range tx.MsgTx().TxIn {
mp.outpoints[txIn.PreviousOutpoint] = tx
}
}
@@ -511,12 +669,11 @@ func (mp *txMemPool) addTransaction(tx *btcwire.MsgTx, txHash *btcwire.ShaHash)
// main chain.
//
// This function MUST be called with the mempool lock held (for reads).
func (mp *txMemPool) checkPoolDoubleSpend(tx *btcwire.MsgTx) error {
for _, txIn := range tx.TxIn {
func (mp *txMemPool) checkPoolDoubleSpend(tx *btcutil.Tx) error {
for _, txIn := range tx.MsgTx().TxIn {
if txR, exists := mp.outpoints[txIn.PreviousOutpoint]; exists {
hash, _ := txR.TxSha()
str := fmt.Sprintf("transaction %v in the pool "+
"already spends the same coins", hash)
"already spends the same coins", txR.Sha())
return TxRuleError(str)
}
}
@@ -529,7 +686,7 @@ func (mp *txMemPool) checkPoolDoubleSpend(tx *btcwire.MsgTx) error {
// fetch any missing inputs from the transaction pool.
//
// This function MUST be called with the mempool lock held (for reads).
func (mp *txMemPool) fetchInputTransactions(tx *btcwire.MsgTx) (btcchain.TxStore, error) {
func (mp *txMemPool) fetchInputTransactions(tx *btcutil.Tx) (btcchain.TxStore, error) {
txStore, err := mp.server.blockManager.blockChain.FetchTransactionStore(tx)
if err != nil {
return nil, err
@@ -538,10 +695,11 @@ func (mp *txMemPool) fetchInputTransactions(tx *btcwire.MsgTx) (btcchain.TxStore
// Attempt to populate any missing inputs from the transaction pool.
for _, txD := range txStore {
if txD.Err == btcdb.TxShaMissing || txD.Tx == nil {
if poolTx, exists := mp.pool[*txD.Hash]; exists {
if poolTxDesc, exists := mp.pool[*txD.Hash]; exists {
poolTx := poolTxDesc.Tx
txD.Tx = poolTx
txD.BlockHeight = mempoolHeight
txD.Spent = make([]bool, len(poolTx.TxOut))
txD.Spent = make([]bool, len(poolTx.MsgTx().TxOut))
txD.Err = nil
}
}
@@ -555,35 +713,33 @@ func (mp *txMemPool) fetchInputTransactions(tx *btcwire.MsgTx) (btcchain.TxStore
// orphans.
//
// This function is safe for concurrent access.
func (mp *txMemPool) FetchTransaction(txHash *btcwire.ShaHash) (*btcwire.MsgTx, error) {
func (mp *txMemPool) FetchTransaction(txHash *btcwire.ShaHash) (*btcutil.Tx, error) {
// Protect concurrent access.
mp.RLock()
defer mp.RUnlock()
if tx, exists := mp.pool[*txHash]; exists {
return tx, nil
if txDesc, exists := mp.pool[*txHash]; exists {
return txDesc.Tx, nil
}
return nil, fmt.Errorf("transaction is not in the pool")
}
// maybeAcceptTransaction is the main workhorse for handling insertion of new
// free-standing transactions into a memory pool. It includes functionality
// such as rejecting duplicate transactions, ensuring transactions follow all
// rules, orphan transaction handling, and insertion into the memory pool.
// maybeAcceptTransaction is the internal function which implements the public
// MaybeAcceptTransaction. See the comment for MaybeAcceptTransaction for
// more details.
//
// This function MUST be called with the mempool lock held (for writes).
func (mp *txMemPool) maybeAcceptTransaction(tx *btcwire.MsgTx, isOrphan *bool) error {
*isOrphan = false
txHash, err := tx.TxSha()
if err != nil {
return err
func (mp *txMemPool) maybeAcceptTransaction(tx *btcutil.Tx, isOrphan *bool, isNew bool) error {
if isOrphan != nil {
*isOrphan = false
}
txHash := tx.Sha()
// Don't accept the transaction if it already exists in the pool. This
// applies to orphan transactions as well. This check is intended to
// be a quick check to weed out duplicates.
if mp.haveTransaction(&txHash) {
if mp.haveTransaction(txHash) {
str := fmt.Sprintf("already have transaction %v", txHash)
return TxRuleError(str)
}
@@ -591,7 +747,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcwire.MsgTx, isOrphan *bool) e
// Perform preliminary sanity checks on the transaction. This makes
// use of btcchain which contains the invariant rules for what
// transactions are allowed into blocks.
err = btcchain.CheckTransactionSanity(tx)
err := btcchain.CheckTransactionSanity(tx)
if err != nil {
if _, ok := err.(btcchain.RuleError); ok {
return TxRuleError(err.Error())
@@ -610,14 +766,15 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcwire.MsgTx, isOrphan *bool) e
// value for now. This is an artifact of older bitcoind clients which
// treated this field as an int32 and would treat anything larger
// incorrectly (as negative).
if tx.LockTime > math.MaxInt32 {
if tx.MsgTx().LockTime > math.MaxInt32 {
str := fmt.Sprintf("transaction %v is has a lock time after "+
"2038 which is not accepted yet", txHash)
return TxRuleError(str)
}
// Get the current height of the main chain. A standalone transaction
// will be mined into the next block at best, so
// will be mined into the next block at best, so it's height is at least
// one more than the current height.
_, curHeight, err := mp.server.db.NewestSha()
if err != nil {
return err
@@ -658,7 +815,7 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcwire.MsgTx, isOrphan *bool) e
// Don't allow the transaction if it exists in the main chain and is not
// not already fully spent.
if txD, exists := txStore[txHash]; exists && txD.Err == nil {
if txD, exists := txStore[*txHash]; exists && txD.Err == nil {
for _, isOutputSpent := range txD.Spent {
if !isOutputSpent {
str := fmt.Sprintf("transaction already exists")
@@ -666,12 +823,14 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcwire.MsgTx, isOrphan *bool) e
}
}
}
delete(txStore, txHash)
delete(txStore, *txHash)
// Transaction is an orphan if any of the inputs don't exist.
for _, txD := range txStore {
if txD.Err == btcdb.TxShaMissing {
*isOrphan = true
if isOrphan != nil {
*isOrphan = true
}
return nil
}
}
@@ -682,13 +841,16 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcwire.MsgTx, isOrphan *bool) e
// used later.
txFee, err := btcchain.CheckTransactionInputs(tx, nextBlockHeight, txStore)
if err != nil {
if _, ok := err.(btcchain.RuleError); ok {
return TxRuleError(err.Error())
}
return err
}
// Don't allow transactions with non-standard inputs on the main
// network.
if activeNetParams.btcnet == btcwire.MainNet {
err := checkInputsStandard(tx)
err := checkInputsStandard(tx, txStore)
if err != nil {
str := fmt.Sprintf("transaction %v has a non-standard "+
"input: %v", txHash, err)
@@ -696,38 +858,87 @@ func (mp *txMemPool) maybeAcceptTransaction(tx *btcwire.MsgTx, isOrphan *bool) e
}
}
// Note: if you modify this code to accept non-standard transactions,
// NOTE: if you modify this code to accept non-standard transactions,
// you should add code here to check that the transaction does a
// reasonable number of ECDSA signature verifications.
// TODO(davec): Don't allow the transaction if the transation fee
// would be too low to get into an empty block.
_ = txFee
// Don't allow transactions with fees too low to get into a mined block.
minRequiredFee := calcMinRelayFee(tx)
if txFee < minRequiredFee {
str := fmt.Sprintf("transaction %v has %d fees which is under "+
"the required amount of %d", txHash, txFee,
minRequiredFee)
return TxRuleError(str)
}
// Free-to-relay transactions are rate limited here to prevent
// penny-flooding with tiny transactions as a form of attack.
if minRequiredFee == 0 {
nowUnix := time.Now().Unix()
// we decay passed data with an exponentially decaying ~10
// minutes window - matches bitcoind handling.
mp.pennyTotal *= math.Pow(1.0-1.0/600.0,
float64(nowUnix-mp.lastPennyUnix))
mp.lastPennyUnix = nowUnix
// Are we still over the limit?
if mp.pennyTotal >= cfg.FreeTxRelayLimit*10*1000 {
str := fmt.Sprintf("transaction %v has 0 fees and has "+
"been rejected by the rate limiter", txHash)
return TxRuleError(str)
}
oldTotal := mp.pennyTotal
mp.pennyTotal += float64(tx.MsgTx().SerializeSize())
txmpLog.Debugf("rate limit: curTotal %v, nextTotal: %v, "+
"limit %v", oldTotal, mp.pennyTotal,
cfg.FreeTxRelayLimit*10*1000)
}
// Verify crypto signatures for each input and reject the transaction if
// any don't verify.
err = btcchain.ValidateTransactionScripts(tx, &txHash, time.Now(), txStore)
flags := btcscript.ScriptBip16 | btcscript.ScriptCanonicalSignatures
err = btcchain.ValidateTransactionScripts(tx, txStore, flags)
if err != nil {
return err
}
// TODO(davec): Rate-limit free transactions
// Add to transaction pool.
mp.addTransaction(tx, &txHash)
mp.addTransaction(tx, curHeight, txFee)
log.Debugf("TXMP: Accepted transaction %v (pool size: %v)", txHash,
txmpLog.Debugf("Accepted transaction %v (pool size: %v)", txHash,
len(mp.pool))
// TODO(davec): Notifications
// Notify websocket clients about mempool transactions.
if mp.server.rpcServer != nil {
go func() {
mp.server.rpcServer.ntfnMgr.NotifyForTxOuts(tx, nil)
// Generate the inventory vector and relay it.
iv := btcwire.NewInvVect(btcwire.InvTypeTx, &txHash)
mp.server.RelayInventory(iv)
if isNew {
mp.server.rpcServer.ntfnMgr.NotifyForNewTx(tx)
}
}()
}
return nil
}
// MaybeAcceptTransaction is the main workhorse for handling insertion of new
// free-standing transactions into a memory pool. It includes functionality
// such as rejecting duplicate transactions, ensuring transactions follow all
// rules, orphan transaction handling, and insertion into the memory pool. The
// isOrphan parameter can be nil if the caller does not need to know whether
// or not the transaction is an orphan.
//
// This function is safe for concurrent access.
func (mp *txMemPool) MaybeAcceptTransaction(tx *btcutil.Tx, isOrphan *bool, isNew bool) error {
// Protect concurrent access.
mp.Lock()
defer mp.Unlock()
return mp.maybeAcceptTransaction(tx, isOrphan, isNew)
}
// processOrphans determines if there are any orphans which depend on the passed
// transaction hash (they are no longer orphans if true) and potentially accepts
// them. It repeats the process for the newly accepted transactions (to detect
@@ -756,31 +967,32 @@ func (mp *txMemPool) processOrphans(hash *btcwire.ShaHash) error {
var enext *list.Element
for e := orphans.Front(); e != nil; e = enext {
enext = e.Next()
tx := e.Value.(*btcwire.MsgTx)
tx := e.Value.(*btcutil.Tx)
// Remove the orphan from the orphan pool.
orphanHash, err := tx.TxSha()
if err != nil {
return err
}
mp.removeOrphan(&orphanHash)
orphanHash := tx.Sha()
mp.removeOrphan(orphanHash)
// Potentially accept the transaction into the
// transaction pool.
var isOrphan bool
err = mp.maybeAcceptTransaction(tx, &isOrphan)
err := mp.maybeAcceptTransaction(tx, &isOrphan, true)
if err != nil {
return err
}
if isOrphan {
mp.removeOrphan(&orphanHash)
if !isOrphan {
// Generate the inventory vector and relay it.
iv := btcwire.NewInvVect(btcwire.InvTypeTx, tx.Sha())
mp.server.RelayInventory(iv)
} else {
mp.removeOrphan(orphanHash)
}
// Add this transaction to the list of transactions to
// process so any orphans that depend on this one are
// handled too.
processHashes.PushBack(&orphanHash)
processHashes.PushBack(orphanHash)
}
}
@@ -788,41 +1000,41 @@ func (mp *txMemPool) processOrphans(hash *btcwire.ShaHash) error {
}
// ProcessTransaction is the main workhorse for handling insertion of new
// free-standing transactions into a memory pool. It includes functionality
// free-standing transactions into the memory pool. It includes functionality
// such as rejecting duplicate transactions, ensuring transactions follow all
// rules, orphan transaction handling, and insertion into the memory pool.
//
// This function is safe for concurrent access.
func (mp *txMemPool) ProcessTransaction(tx *btcwire.MsgTx) error {
func (mp *txMemPool) ProcessTransaction(tx *btcutil.Tx) error {
// Protect concurrent access.
mp.Lock()
defer mp.Unlock()
txHash, err := tx.TxSha()
if err != nil {
return err
}
log.Tracef("TXMP: Processing transaction %v", txHash)
txmpLog.Tracef("Processing transaction %v", tx.Sha())
// Potentially accept the transaction to the memory pool.
var isOrphan bool
err = mp.maybeAcceptTransaction(tx, &isOrphan)
err := mp.maybeAcceptTransaction(tx, &isOrphan, true)
if err != nil {
return err
}
if !isOrphan {
// Generate the inventory vector and relay it.
iv := btcwire.NewInvVect(btcwire.InvTypeTx, tx.Sha())
mp.server.RelayInventory(iv)
// Accept any orphan transactions that depend on this
// transaction (they are no longer orphans) and repeat for those
// accepted transactions until there are no more.
err = mp.processOrphans(&txHash)
err := mp.processOrphans(tx.Sha())
if err != nil {
return err
}
} else {
// When the transaction is an orphan (has inputs missing),
// potentially add it to the orphan pool.
err := mp.maybeAddOrphan(tx, &txHash)
err := mp.maybeAddOrphan(tx)
if err != nil {
return err
}
@@ -850,14 +1062,32 @@ func (mp *txMemPool) TxShas() []*btcwire.ShaHash {
return hashes
}
// TxDescs returns a slice of descriptors for all the transactions in the pool.
// The descriptors are to be treated as read only.
//
// This function is safe for concurrent access.
func (mp *txMemPool) TxDescs() []*TxDesc {
mp.RLock()
defer mp.RUnlock()
descs := make([]*TxDesc, len(mp.pool))
i := 0
for _, desc := range mp.pool {
descs[i] = desc
i++
}
return descs
}
// newTxMemPool returns a new memory pool for validating and storing standalone
// transactions until they are mined into a block.
func newTxMemPool(server *server) *txMemPool {
return &txMemPool{
server: server,
pool: make(map[btcwire.ShaHash]*btcwire.MsgTx),
orphans: make(map[btcwire.ShaHash]*btcwire.MsgTx),
pool: make(map[btcwire.ShaHash]*TxDesc),
orphans: make(map[btcwire.ShaHash]*btcutil.Tx),
orphansByPrev: make(map[btcwire.ShaHash]*list.List),
outpoints: make(map[btcwire.OutPoint]*btcwire.MsgTx),
outpoints: make(map[btcwire.OutPoint]*btcutil.Tx),
}
}

View File

@@ -1,20 +1,21 @@
// Copyright (c) 2013 Conformal Systems LLC.
// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package main
import (
"container/list"
"fmt"
"github.com/conformal/btcwire"
"time"
)
// MruInventoryMap provides a map that is limited to a maximum number of items
// with eviction for the oldest entry when the limit is exceeded.
type MruInventoryMap struct {
invMap map[btcwire.InvVect]int64 // Use int64 for time for less mem.
limit uint
invMap map[btcwire.InvVect]*list.Element // nearly O(1) lookups
invList *list.List // O(1) insert, update, delete
limit uint
}
// String returns the map as a human-readable string.
@@ -34,49 +35,62 @@ func (m *MruInventoryMap) Exists(iv *btcwire.InvVect) bool {
// item if adding the new item would exceed the max limit.
func (m *MruInventoryMap) Add(iv *btcwire.InvVect) {
// When the limit is zero, nothing can be added to the map, so just
// return
// return.
if m.limit == 0 {
return
}
// When the entry already exists update its last seen time.
if m.Exists(iv) {
m.invMap[*iv] = time.Now().Unix()
// When the entry already exists move it to the front of the list
// thereby marking it most recently used.
if node, exists := m.invMap[*iv]; exists {
m.invList.MoveToFront(node)
return
}
// Evict the oldest entry if the the new entry would exceed the size
// limit for the map.
// Evict the least recently used entry (back of the list) if the the new
// entry would exceed the size limit for the map. Also reuse the list
// node so a new one doesn't have to be allocated.
if uint(len(m.invMap))+1 > m.limit {
var oldestEntry btcwire.InvVect
var oldestTime int64
for iv, lastUpdated := range m.invMap {
if oldestTime == 0 || lastUpdated < oldestTime {
oldestEntry = iv
oldestTime = lastUpdated
}
node := m.invList.Back()
lru, ok := node.Value.(*btcwire.InvVect)
if !ok {
return
}
m.Delete(&oldestEntry)
// Evict least recently used item.
delete(m.invMap, *lru)
// Reuse the list node of the item that was just evicted for the
// new item.
node.Value = iv
m.invList.MoveToFront(node)
m.invMap[*iv] = node
return
}
m.invMap[*iv] = time.Now().Unix()
// The limit hasn't been reached yet, so just add the new item.
node := m.invList.PushFront(iv)
m.invMap[*iv] = node
return
}
// Delete deletes the passed inventory item from the map (if it exists).
func (m *MruInventoryMap) Delete(iv *btcwire.InvVect) {
delete(m.invMap, *iv)
if node, exists := m.invMap[*iv]; exists {
m.invList.Remove(node)
delete(m.invMap, *iv)
}
}
// NewMruInventoryMap returns a new inventory map that is limited to the number
// of entries specified by limit. When the number of entries exceeds the limit,
// the oldest (least recently used) entry will be removed to make room for the
// new entry..
// new entry.
func NewMruInventoryMap(limit uint) *MruInventoryMap {
m := MruInventoryMap{
invMap: make(map[btcwire.InvVect]int64),
limit: limit,
invMap: make(map[btcwire.InvVect]*list.Element),
invList: list.New(),
limit: limit,
}
return &m
}

36
mruinvmap_test.go Normal file
View File

@@ -0,0 +1,36 @@
// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package main
import (
"crypto/rand"
"github.com/conformal/btcwire"
"testing"
)
// BenchmarkMruInventoryList performs basic benchmarks on the most recently
// used inventory handling.
func BenchmarkMruInventoryList(b *testing.B) {
// Create a bunch of fake inventory vectors to use in benchmarking
// the mru inventory code.
b.StopTimer()
numInvVects := 100000
invVects := make([]*btcwire.InvVect, 0, numInvVects)
for i := 0; i < numInvVects; i++ {
hashBytes := make([]byte, btcwire.HashSize)
rand.Read(hashBytes)
hash, _ := btcwire.NewShaHash(hashBytes)
iv := btcwire.NewInvVect(btcwire.InvTypeBlock, hash)
invVects = append(invVects, iv)
}
b.StartTimer()
// Benchmark the add plus evicition code.
limit := 20000
mruInvMap := NewMruInventoryMap(uint(limit))
for i := 0; i < b.N; i++ {
mruInvMap.Add(invVects[i%numInvVects])
}
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2013 Conformal Systems LLC.
// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
@@ -49,6 +49,7 @@ var mainNetParams = params{
"seed.bitcoin.sipa.be",
"dnsseed.bluematt.me",
"dnsseed.bitcoin.dashjr.org",
"seed.bitcoinstats.com",
"bitseed.xf2.org",
},
}

707
peer.go

File diff suppressed because it is too large Load Diff

View File

@@ -26,6 +26,7 @@ PROJECT=btcd
PROJECT_UC=$(echo $PROJECT | tr '[:lower:]' '[:upper:]')
SCRIPT=$(basename $0)
VERFILE=../version.go
VERFILES="$VERFILE ../util/btcctl/version.go"
PROJ_CHANGES=../CHANGES
# verify params
@@ -37,11 +38,13 @@ fi
CUR_DIR=$(pwd)
cd "$(dirname $0)"
# verify version file exists
if [ ! -f "$VERFILE" ]; then
echo "$SCRIPT: error: $VERFILE does not exist" 1>&2
exit 1
fi
# verify version files exist
for verfile in $VERFILES; do
if [ ! -f "$verfile" ]; then
echo "$SCRIPT: error: $verfile does not exist" 1>&2
exit 1
fi
done
# verify changes file exists
if [ ! -f "$PROJ_CHANGES" ]; then
@@ -172,17 +175,21 @@ awk '
second_line==1 { print $0 }
' <"$PROJ_CHANGES" >>"${PROJ_CHANGES}.tmp"
# update version file with new version
sed -E "
s/${PAT_PREFIX}Major${PAT_SUFFIX}/\1${MAJOR}/;
s/${PAT_PREFIX}Minor${PAT_SUFFIX}/\1${MINOR}/;
s/${PAT_PREFIX}Patch${PAT_SUFFIX}/\1${PATCH}/;
" <"$VERFILE" >"${VERFILE}.tmp"
# update version filef with new version
for verfile in $VERFILES; do
sed -E "
s/${PAT_PREFIX}Major${PAT_SUFFIX}/\1${MAJOR}/;
s/${PAT_PREFIX}Minor${PAT_SUFFIX}/\1${MINOR}/;
s/${PAT_PREFIX}Patch${PAT_SUFFIX}/\1${PATCH}/;
" <"$verfile" >"${verfile}.tmp"
done
# Apply changes
mv "${PROJ_CHANGES}.tmp" "$PROJ_CHANGES"
mv "${VERFILE}.tmp" "$VERFILE"
for verfile in $VERFILES; do
mv "${verfile}.tmp" "$verfile"
done
echo "All files have been prepared for release."
echo "Use the following commands to review the changes for accuracy:"

File diff suppressed because it is too large Load Diff

1373
rpcwebsocket.go Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -6,10 +6,11 @@
; The directory to store data such as the block chain and peer addresses. The
; block chain takes several GB, so this location must have a lot of free space.
; The default is ~/.btcd/data on POSIX OSes and $APPDATA/btcd/data on Windows.
; Environment variables are expanded so they may be used. NOTE: Windows
; The default is ~/.btcd/data on POSIX OSes, $LOCALAPPDATA/Btcd/data on Windows,
; ~/Library/Application Support/Btcd/data on Mac OS, and $home/btcd/data on
; Plan9. Environment variables are expanded so they may be used. NOTE: Windows
; environment variables are typically %VARIABLE%, but they must be accessed with
; $VARIABLE here. Also, ~ is expanded to $APPDATA on Windows.
; $VARIABLE here. Also, ~ is expanded to $LOCALAPPDATA on Windows.
; datadir=~/.btcd/data
@@ -20,22 +21,27 @@
; Use testnet.
; testnet=1
; Connect via a SOCKS5 proxy. NOTE: Specifying a proxy without the 'tor' option
; below will disable listening for incoming connections.
; Connect via a SOCKS5 proxy. NOTE: Specifying a proxy will disable listening
; for incoming connections unless listen addresses are provided via the 'listen'
; option.
; proxy=127.0.0.1:9050
; proxyuser=
; proxypass=
; The SOCKS5 proxy above is Tor (https://www.torproject.org).
; Although not required if the proxy set is indeed Tor, setting this option
; does the following:
; - Sends DNS queries over the Tor network (during DNS seed lookup). This
; stops your IP from being leaked via DNS.
; - Does not disable the listening port. This allows the hidden services
; feature of Tor to be used.
; tor=1
; The SOCKS5 proxy above is assumed to be Tor (https://www.torproject.org).
; If the proxy is not tor the the following my be used to prevent using
; tor specific SOCKS queries to lookup addresses (this increases anonymity when
; tor is used by preventing your IP being leaked via DNS).
; noonion=1
; Use an alternative proxy to connect to .onion addresses. The proxy is assumed
; to be a Tor node. Non .onion addresses will be contacted with the main proxy
; or without a proxy if none is set.
; onion=127.0.0.1:9051
; ******************************************************************************
; Summary of 'addpeer' versus 'connect'.
;
; Only one of the following two options, 'addpeer' and 'connect', may be
; specified. Both allow you to specify peers that you want to stay connected
; with, but the behavior is slightly different. By default, btcd will query DNS
@@ -49,7 +55,8 @@
; reason (perhaps due to a firewall).
;
; 'connect', on the other hand, will ONLY connect to the specified peers and
; no others. It also disables listening and DNS seeding, so you will not be
; no others. It also disables listening (unless you explicitly set listen
; addresses via the 'listen' option) and DNS seeding, so you will not be
; advertised as an available peer to the peers you connect to and won't accept
; connections from any other peers. So, the 'connect' option effectively allows
; you to only connect to "trusted" peers.
@@ -66,8 +73,8 @@
; Add persistent peers that you ONLY want to connect to as desired. One peer
; per line. You may specify each IP address with or without a port. The
; default port will be added automatically if one is not specified here.
; NOTE: Specifying this option will disable listening for incoming connections
; and DNS seeding for peers.
; NOTE: Specifying this option has other side effects as described above in
; the 'addpeer' versus 'connect' summary section.
; connect=192.168.1.1
; connect=10.0.0.2:8333
; connect=fe80::1
@@ -85,7 +92,24 @@
; DNS to query for available peers to connect with.
; nodnsseed=1
; Disable listening for incoming connections.
; Specify the interfaces to listen on. One listen address per line.
; NOTE: The default port is modified by some options such as 'testnet', so it is
; recommended to not specify a port and allow a proper default to be chosen
; unless you have a specific reason to do otherwise.
; listen= ; all interfaces on default port (this is the default)
; listen=0.0.0.0 ; all ipv4 interfaces on default port
; listen=:: ; all ipv6 interfaces on default port
; listen=:8333 ; all interfaces on port 8333
; listen=0.0.0.0:8333 ; all ipv4 interfaces on port 8333
; listen=[::]:8333 ; all ipv6 interfaces on port 8333
; listen=127.0.0.1:8333 ; only ipv4 localhost on port 8333
; listen=[::1]:8333 ; only ipv6 localhost on port 8333
; listen=127.0.0.1:8336 ; only ipv4 localhost on non-standard port 8336
; listen=:8336 ; all interfaces on non-standard port 8336
; listen=0.0.0.0:8336 ; all ipv4 interfaces on non-standard port 8336
; listen=[::]:8336 ; all ipv6 interfaces on non-standard port 8336
; Disable listening for incoming connections. This will override all listeners.
; nolisten=1
@@ -102,14 +126,34 @@
; rpcuser=whatever_username_you_want
; rpcpass=
; Specify the interfaces for the RPC server listen on. One listen address per
; line. NOTE: The default port is modified by some options such as 'testnet',
; so it is recommended to not specify a port and allow a proper default to be
; chosen unless you have a specific reason to do otherwise.
; rpclisten= ; all interfaces on default port (this is the default)
; rpclisten=0.0.0.0 ; all ipv4 interfaces on default port
; rpclisten=:: ; all ipv6 interfaces on default port
; rpclisten=:8334 ; all interfaces on port 8334
; rpclisten=0.0.0.0:8334 ; all ipv4 interfaces on port 8334
; rpclisten=[::]:8334 ; all ipv6 interfaces on port 8334
; rpclisten=127.0.0.1:8334 ; only ipv4 localhost on port 8334
; rpclisten=[::1]:8334 ; only ipv6 localhost on port 8334
; rpclisten=127.0.0.1:8337 ; only ipv4 localhost on non-standard port 8337
; rpclisten=:8337 ; all interfaces on non-standard port 8337
; rpclisten=0.0.0.0:8337 ; all ipv4 interfaces on non-standard port 8337
; rpclisten=[::]:8337 ; all ipv6 interfaces on non-standard port 8337
; Specify the maximum number of concurrent RPC clients for standard connections.
; rpcmaxclients=10
; Specify the maximum number of concurrent RPC websocket clients.
; rpcmaxwebsockets=25
; Use the following setting to disable the RPC server even if the rpcuser and
; rpcpass are specified above. This allows one to quickly disable the RPC
; server without having to remove credentials from the config file.
; norpc=1
; The port used to listen for RPC connections.
; rpcport=8334
; ------------------------------------------------------------------------------
; Debug

666
server.go
View File

@@ -1,4 +1,4 @@
// Copyright (c) 2013 Conformal Systems LLC.
// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
@@ -6,10 +6,13 @@ package main
import (
"container/list"
"errors"
"fmt"
"github.com/conformal/btcdb"
"github.com/conformal/btcjson"
"github.com/conformal/btcwire"
"net"
"runtime"
"strconv"
"sync"
"sync/atomic"
@@ -49,9 +52,12 @@ type server struct {
nonce uint64
listeners []net.Listener
btcnet btcwire.BitcoinNet
started int32 // atomic
shutdown int32 // atomic
shutdownSched int32 // atomic
started int32 // atomic
shutdown int32 // atomic
shutdownSched int32 // atomic
bytesMutex sync.Mutex // For the following two fields.
bytesReceived uint64 // Total bytes received from all peers since start.
bytesSent uint64 // Total bytes sent by all peers since start.
addrManager *AddrManager
rpcServer *rpcServer
blockManager *blockManager
@@ -60,23 +66,67 @@ type server struct {
donePeers chan *peer
banPeers chan *peer
wakeup chan bool
query chan interface{}
relayInv chan *btcwire.InvVect
broadcast chan broadcastMsg
wg sync.WaitGroup
quit chan bool
nat NAT
db btcdb.Db
}
type peerState struct {
peers *list.List
outboundPeers *list.List
persistentPeers *list.List
banned map[string]time.Time
outboundGroups map[string]int
maxOutboundPeers int
}
func (p *peerState) Count() int {
return p.peers.Len() + p.outboundPeers.Len() + p.persistentPeers.Len()
}
func (p *peerState) OutboundCount() int {
return p.outboundPeers.Len() + p.persistentPeers.Len()
}
func (p *peerState) NeedMoreOutbound() bool {
return p.OutboundCount() < p.maxOutboundPeers &&
p.Count() < cfg.MaxPeers
}
// forAllOutboundPeers is a helper function that runs closure on all outbound
// peers known to peerState.
func (p *peerState) forAllOutboundPeers(closure func(p *peer)) {
for e := p.outboundPeers.Front(); e != nil; e = e.Next() {
closure(e.Value.(*peer))
}
for e := p.persistentPeers.Front(); e != nil; e = e.Next() {
closure(e.Value.(*peer))
}
}
// forAllPeers is a helper function that runs closure on all peers known to
// peerState.
func (p *peerState) forAllPeers(closure func(p *peer)) {
for e := p.peers.Front(); e != nil; e = e.Next() {
closure(e.Value.(*peer))
}
p.forAllOutboundPeers(closure)
}
// handleAddPeerMsg deals with adding new peers. It is invoked from the
// peerHandler goroutine.
func (s *server) handleAddPeerMsg(peers *list.List, banned map[string]time.Time, p *peer) bool {
func (s *server) handleAddPeerMsg(state *peerState, p *peer) bool {
if p == nil {
return false
}
// Ignore new peers if we're shutting down.
if atomic.LoadInt32(&s.shutdown) != 0 {
log.Infof("SRVR: New peer %s ignored - server is shutting "+
srvrLog.Infof("New peer %s ignored - server is shutting "+
"down", p)
p.Shutdown()
return false
@@ -85,27 +135,27 @@ func (s *server) handleAddPeerMsg(peers *list.List, banned map[string]time.Time,
// Disconnect banned peers.
host, _, err := net.SplitHostPort(p.addr)
if err != nil {
log.Debugf("SRVR: can't split hostport %v", err)
srvrLog.Debugf("can't split hostport %v", err)
p.Shutdown()
return false
}
if banEnd, ok := banned[host]; ok {
if banEnd, ok := state.banned[host]; ok {
if time.Now().Before(banEnd) {
log.Debugf("SRVR: Peer %s is banned for another %v - "+
srvrLog.Debugf("Peer %s is banned for another %v - "+
"disconnecting", host, banEnd.Sub(time.Now()))
p.Shutdown()
return false
}
log.Infof("SRVR: Peer %s is no longer banned", host)
delete(banned, host)
srvrLog.Infof("Peer %s is no longer banned", host)
delete(state.banned, host)
}
// TODO: Check for max peers from a single IP.
// Limit max number of total peers.
if peers.Len() >= cfg.MaxPeers {
log.Infof("SRVR: Max peers reached [%d] - disconnecting "+
if state.Count() >= cfg.MaxPeers {
srvrLog.Infof("Max peers reached [%d] - disconnecting "+
"peer %s", cfg.MaxPeers, p)
p.Shutdown()
// TODO(oga) how to handle permanent peers here?
@@ -114,10 +164,17 @@ func (s *server) handleAddPeerMsg(peers *list.List, banned map[string]time.Time,
}
// Add the new peer and start it.
log.Debugf("SRVR: New peer %s", p)
peers.PushBack(p)
srvrLog.Debugf("New peer %s", p)
if p.inbound {
state.peers.PushBack(p)
p.Start()
} else {
state.outboundGroups[GroupKey(p.na)]++
if p.persistent {
state.persistentPeers.PushBack(p)
} else {
state.outboundPeers.PushBack(p)
}
}
return true
@@ -125,69 +182,76 @@ func (s *server) handleAddPeerMsg(peers *list.List, banned map[string]time.Time,
// handleDonePeerMsg deals with peers that have signalled they are done. It is
// invoked from the peerHandler goroutine.
func (s *server) handleDonePeerMsg(peers *list.List, p *peer) bool {
for e := peers.Front(); e != nil; e = e.Next() {
func (s *server) handleDonePeerMsg(state *peerState, p *peer) {
var list *list.List
if p.persistent {
list = state.persistentPeers
} else if p.inbound {
list = state.peers
} else {
list = state.outboundPeers
}
for e := list.Front(); e != nil; e = e.Next() {
if e.Value == p {
// Issue an asynchronous reconnect if the peer was a
// persistent outbound connection.
if !p.inbound && p.persistent &&
atomic.LoadInt32(&s.shutdown) == 0 {
e.Value = newOutboundPeer(s, p.addr, true)
return false
return
}
peers.Remove(e)
log.Debugf("SRVR: Removed peer %s", p)
return true
if !p.inbound {
state.outboundGroups[GroupKey(p.na)]--
}
list.Remove(e)
srvrLog.Debugf("Removed peer %s", p)
return
}
}
log.Warnf("SRVR: Lost peer %v that we never had!", p)
return false
// If we get here it means that either we didn't know about the peer
// or we purposefully deleted it.
}
// handleBanPeerMsg deals with banning peers. It is invoked from the
// peerHandler goroutine.
func (s *server) handleBanPeerMsg(banned map[string]time.Time, p *peer) {
func (s *server) handleBanPeerMsg(state *peerState, p *peer) {
host, _, err := net.SplitHostPort(p.addr)
if err != nil {
log.Debugf("SRVR: can't split ban peer %s %v", p.addr, err)
srvrLog.Debugf("can't split ban peer %s %v", p.addr, err)
return
}
direction := directionString(p.inbound)
log.Infof("SRVR: Banned peer %s (%s) for %v", host, direction,
srvrLog.Infof("Banned peer %s (%s) for %v", host, direction,
cfg.BanDuration)
banned[host] = time.Now().Add(cfg.BanDuration)
state.banned[host] = time.Now().Add(cfg.BanDuration)
}
// handleRelayInvMsg deals with relaying inventory to peers that are not already
// known to have it. It is invoked from the peerHandler goroutine.
func (s *server) handleRelayInvMsg(peers *list.List, iv *btcwire.InvVect) {
// Loop through all connected peers and relay the inventory to those
// which are not already known to have it.
for e := peers.Front(); e != nil; e = e.Next() {
p := e.Value.(*peer)
func (s *server) handleRelayInvMsg(state *peerState, iv *btcwire.InvVect) {
state.forAllPeers(func(p *peer) {
if !p.Connected() {
continue
return
}
// Queue the inventory to be relayed with the next batch. It
// will be ignored if the peer is already known to have the
// inventory.
p.QueueInventory(iv)
}
})
}
// handleBroadcastMsg deals with broadcasting messages to peers. It is invoked
// from the peerHandler goroutine.
func (s *server) handleBroadcastMsg(peers *list.List, bmsg *broadcastMsg) {
for e := peers.Front(); e != nil; e = e.Next() {
func (s *server) handleBroadcastMsg(state *peerState, bmsg *broadcastMsg) {
state.forAllPeers(func(p *peer) {
excluded := false
for _, p := range bmsg.excludePeers {
if e.Value == p {
for _, ep := range bmsg.excludePeers {
if p == ep {
excluded = true
}
}
p := e.Value.(*peer)
// Don't broadcast to still connecting outbound peers .
if !p.Connected() {
excluded = true
@@ -195,19 +259,148 @@ func (s *server) handleBroadcastMsg(peers *list.List, bmsg *broadcastMsg) {
if !excluded {
p.QueueMessage(bmsg.message, nil)
}
})
}
type getConnCountMsg struct {
reply chan int
}
type getPeerInfoMsg struct {
reply chan []*btcjson.GetPeerInfoResult
}
type addNodeMsg struct {
addr string
permanent bool
reply chan error
}
type delNodeMsg struct {
addr string
reply chan error
}
type getAddedNodesMsg struct {
reply chan []*peer
}
// handleQuery is the central handler for all queries and commands from other
// goroutines related to peer state.
func (s *server) handleQuery(querymsg interface{}, state *peerState) {
switch msg := querymsg.(type) {
case getConnCountMsg:
nconnected := 0
state.forAllPeers(func(p *peer) {
if p.Connected() {
nconnected++
}
})
msg.reply <- nconnected
case getPeerInfoMsg:
syncPeer := s.blockManager.SyncPeer()
infos := make([]*btcjson.GetPeerInfoResult, 0, state.peers.Len())
state.forAllPeers(func(p *peer) {
if !p.Connected() {
return
}
// A lot of this will make the race detector go mad,
// however it is statistics for purely informational purposes
// and we don't really care if they are raced to get the new
// version.
info := &btcjson.GetPeerInfoResult{
Addr: p.addr,
Services: fmt.Sprintf("%08d", p.services),
LastSend: p.lastSend.Unix(),
LastRecv: p.lastRecv.Unix(),
BytesSent: p.bytesSent,
BytesRecv: p.bytesReceived,
ConnTime: p.timeConnected.Unix(),
Version: p.protocolVersion,
SubVer: p.userAgent,
Inbound: p.inbound,
StartingHeight: p.lastBlock,
BanScore: 0,
SyncNode: p == syncPeer,
}
p.pingStatsMtx.Lock()
info.PingTime = p.lastPingMicros
if p.lastPingNonce != 0 {
wait := time.Now().Sub(p.lastPingTime).Nanoseconds()
// We actually want microseconds.
info.PingWait = wait / 1000
}
p.pingStatsMtx.Unlock()
infos = append(infos, info)
})
msg.reply <- infos
case addNodeMsg:
// XXX(oga) duplicate oneshots?
if msg.permanent {
for e := state.persistentPeers.Front(); e != nil; e = e.Next() {
peer := e.Value.(*peer)
if peer.addr == msg.addr {
msg.reply <- errors.New("peer already connected")
return
}
}
}
// TODO(oga) if too many, nuke a non-perm peer.
if s.handleAddPeerMsg(state,
newOutboundPeer(s, msg.addr, msg.permanent)) {
msg.reply <- nil
} else {
msg.reply <- errors.New("failed to add peer")
}
case delNodeMsg:
found := false
for e := state.persistentPeers.Front(); e != nil; e = e.Next() {
peer := e.Value.(*peer)
if peer.addr == msg.addr {
// Keep group counts ok since we remove from
// the list now.
state.outboundGroups[GroupKey(peer.na)]--
// This is ok because we are not continuing
// to iterate so won't corrupt the loop.
state.persistentPeers.Remove(e)
peer.Disconnect()
found = true
break
}
}
if found {
msg.reply <- nil
} else {
msg.reply <- errors.New("peer not found")
}
// Request a list of the persistent (added) peers.
case getAddedNodesMsg:
// Respond with a slice of the relavent peers.
peers := make([]*peer, 0, state.persistentPeers.Len())
for e := state.persistentPeers.Front(); e != nil; e = e.Next() {
peer := e.Value.(*peer)
peers = append(peers, peer)
}
msg.reply <- peers
}
}
// listenHandler is the main listener which accepts incoming connections for the
// server. It must be run as a goroutine.
func (s *server) listenHandler(listener net.Listener) {
log.Infof("SRVR: Server listening on %s", listener.Addr())
srvrLog.Infof("Server listening on %s", listener.Addr())
for atomic.LoadInt32(&s.shutdown) == 0 {
conn, err := listener.Accept()
if err != nil {
// Only log the error if we're not forcibly shutting down.
if atomic.LoadInt32(&s.shutdown) == 0 {
log.Errorf("SRVR: can't accept connection: %v",
srvrLog.Errorf("can't accept connection: %v",
err)
}
continue
@@ -215,7 +408,7 @@ func (s *server) listenHandler(listener net.Listener) {
s.AddPeer(newInboundPeer(s, conn))
}
s.wg.Done()
log.Tracef("SRVR: Listener handler done for %s", listener.Addr())
srvrLog.Tracef("Listener handler done for %s", listener.Addr())
}
// seedFromDNS uses DNS seeding to populate the address manager with peers.
@@ -225,12 +418,8 @@ func (s *server) seedFromDNS() {
return
}
proxy := ""
if cfg.Proxy != "" && cfg.UseTor {
proxy = cfg.Proxy
}
for _, seeder := range activeNetParams.dnsSeeds {
seedpeers := dnsDiscover(seeder, proxy)
seedpeers := dnsDiscover(seeder)
if len(seedpeers) == 0 {
continue
}
@@ -271,13 +460,17 @@ func (s *server) peerHandler() {
s.addrManager.Start()
s.blockManager.Start()
log.Tracef("SRVR: Starting peer handler")
peers := list.New()
bannedPeers := make(map[string]time.Time)
outboundPeers := 0
maxOutbound := defaultMaxOutbound
if cfg.MaxPeers < maxOutbound {
maxOutbound = cfg.MaxPeers
srvrLog.Tracef("Starting peer handler")
state := &peerState{
peers: list.New(),
persistentPeers: list.New(),
outboundPeers: list.New(),
banned: make(map[string]time.Time),
maxOutboundPeers: defaultMaxOutbound,
outboundGroups: make(map[string]int),
}
if cfg.MaxPeers < state.maxOutboundPeers {
state.maxOutboundPeers = cfg.MaxPeers
}
// Add peers discovered through DNS to the address manager.
@@ -289,10 +482,7 @@ func (s *server) peerHandler() {
permanentPeers = cfg.AddPeers
}
for _, addr := range permanentPeers {
if s.handleAddPeerMsg(peers, bannedPeers,
newOutboundPeer(s, addr, true)) {
outboundPeers++
}
s.handleAddPeerMsg(state, newOutboundPeer(s, addr, true))
}
// if nothing else happens, wake us up soon.
@@ -303,67 +493,54 @@ out:
select {
// New peers connected to the server.
case p := <-s.newPeers:
if s.handleAddPeerMsg(peers, bannedPeers, p) &&
!p.inbound {
outboundPeers++
}
s.handleAddPeerMsg(state, p)
// Disconnected peers.
case p := <-s.donePeers:
// handleDonePeerMsg return true if it removed a peer
if s.handleDonePeerMsg(peers, p) {
outboundPeers--
}
s.handleDonePeerMsg(state, p)
// Peer to ban.
case p := <-s.banPeers:
s.handleBanPeerMsg(bannedPeers, p)
s.handleBanPeerMsg(state, p)
// New inventory to potentially be relayed to other peers.
case invMsg := <-s.relayInv:
s.handleRelayInvMsg(peers, invMsg)
s.handleRelayInvMsg(state, invMsg)
// Message to broadcast to all connected peers except those
// which are excluded by the message.
case bmsg := <-s.broadcast:
s.handleBroadcastMsg(peers, &bmsg)
s.handleBroadcastMsg(state, &bmsg)
// Used by timers below to wake us back up.
case <-s.wakeup:
// this page left intentionally blank
case qmsg := <-s.query:
s.handleQuery(qmsg, state)
// Shutdown the peer handler.
case <-s.quit:
// Shutdown peers.
for e := peers.Front(); e != nil; e = e.Next() {
p := e.Value.(*peer)
state.forAllPeers(func(p *peer) {
p.Shutdown()
}
})
break out
}
// Only try connect to more peers if we actually need more
if outboundPeers >= maxOutbound || len(cfg.ConnectPeers) > 0 ||
if !state.NeedMoreOutbound() || len(cfg.ConnectPeers) > 0 ||
atomic.LoadInt32(&s.shutdown) != 0 {
continue
}
groups := make(map[string]int)
for e := peers.Front(); e != nil; e = e.Next() {
peer := e.Value.(*peer)
if !peer.inbound {
groups[GroupKey(peer.na)]++
}
}
tries := 0
for outboundPeers < maxOutbound &&
peers.Len() < cfg.MaxPeers &&
for state.NeedMoreOutbound() &&
atomic.LoadInt32(&s.shutdown) == 0 {
// We bias like bitcoind does, 10 for no outgoing
// up to 90 (8) for the selection of new vs tried
//addresses.
nPeers := outboundPeers
nPeers := state.OutboundCount()
if nPeers > 8 {
nPeers = 8
}
@@ -379,7 +556,7 @@ out:
// to the same network segment at the expense of
// others. bitcoind breaks out of the loop here, but
// we continue to try other addresses.
if groups[key] != 0 {
if state.outboundGroups[key] != 0 {
continue
}
@@ -410,15 +587,13 @@ out:
tries = 0
// any failure will be due to banned peers etc. we have
// already checked that we have room for more peers.
if s.handleAddPeerMsg(peers, bannedPeers,
if s.handleAddPeerMsg(state,
newOutboundPeer(s, addrStr, false)) {
outboundPeers++
groups[key]++
}
}
// We we need more peers, wake up in ten seconds and try again.
if outboundPeers < maxOutbound && peers.Len() < cfg.MaxPeers {
if state.NeedMoreOutbound() {
time.AfterFunc(10*time.Second, func() {
s.wakeup <- true
})
@@ -428,7 +603,7 @@ out:
s.blockManager.Stop()
s.addrManager.Stop()
s.wg.Done()
log.Tracef("SRVR: Peer handler done")
srvrLog.Tracef("Peer handler done")
}
// AddPeer adds a new peer that has already been connected to the server.
@@ -456,6 +631,81 @@ func (s *server) BroadcastMessage(msg btcwire.Message, exclPeers ...*peer) {
s.broadcast <- bmsg
}
// ConnectedCount returns the number of currently connected peers.
func (s *server) ConnectedCount() int {
replyChan := make(chan int)
s.query <- getConnCountMsg{reply: replyChan}
return <-replyChan
}
// AddedNodeInfo returns an array of btcjson.GetAddedNodeInfoResult structures
// describing the persistent (added) nodes.
func (s *server) AddedNodeInfo() []*peer {
replyChan := make(chan []*peer)
s.query <- getAddedNodesMsg{reply: replyChan}
return <-replyChan
}
// PeerInfo returns an array of PeerInfo structures describing all connected
// peers.
func (s *server) PeerInfo() []*btcjson.GetPeerInfoResult {
replyChan := make(chan []*btcjson.GetPeerInfoResult)
s.query <- getPeerInfoMsg{reply: replyChan}
return <-replyChan
}
// AddAddr adds `addr' as a new outbound peer. If permanent is true then the
// peer will be persistent and reconnect if the connection is lost.
// It is an error to call this with an already existing peer.
func (s *server) AddAddr(addr string, permanent bool) error {
replyChan := make(chan error)
s.query <- addNodeMsg{addr: addr, permanent: permanent, reply: replyChan}
return <-replyChan
}
// RemoveAddr removes `addr' from the list of persistent peers if present.
// An error will be returned if the peer was not found.
func (s *server) RemoveAddr(addr string) error {
replyChan := make(chan error)
s.query <- delNodeMsg{addr: addr, reply: replyChan}
return <-replyChan
}
// AddBytesSent adds the passed number of bytes to the total bytes sent counter
// for the server. It is safe for concurrent access.
func (s *server) AddBytesSent(bytesSent uint64) {
s.bytesMutex.Lock()
defer s.bytesMutex.Unlock()
s.bytesSent += bytesSent
}
// AddBytesReceived adds the passed number of bytes to the total bytes received
// counter for the server. It is safe for concurrent access.
func (s *server) AddBytesReceived(bytesReceived uint64) {
s.bytesMutex.Lock()
defer s.bytesMutex.Unlock()
s.bytesReceived += bytesReceived
}
// NetTotals returns the sum of all bytes received and sent across the network
// for all peers. It is safe for concurrent access.
func (s *server) NetTotals() (uint64, uint64) {
s.bytesMutex.Lock()
defer s.bytesMutex.Unlock()
return s.bytesReceived, s.bytesSent
}
// Start begins accepting connections from peers.
func (s *server) Start() {
// Already started?
@@ -463,7 +713,7 @@ func (s *server) Start() {
return
}
log.Trace("SRVR: Starting server")
srvrLog.Trace("Starting server")
// Start all the listeners. There will not be any if listening is
// disabled.
@@ -476,6 +726,10 @@ func (s *server) Start() {
// managers.
s.wg.Add(1)
go s.peerHandler()
if s.nat != nil {
s.wg.Add(1)
go s.upnpUpdateThread()
}
// Start the RPC server if it's not disabled.
if !cfg.DisableRPC {
@@ -488,11 +742,11 @@ func (s *server) Start() {
func (s *server) Stop() error {
// Make sure this only happens once.
if atomic.AddInt32(&s.shutdown, 1) != 1 {
log.Infof("SRVR: Server is already in the process of shutting down")
srvrLog.Infof("Server is already in the process of shutting down")
return nil
}
log.Warnf("SRVR: Server shutting down")
srvrLog.Warnf("Server shutting down")
// Stop all the listeners. There will not be any listeners if
// listening is disabled.
@@ -516,7 +770,6 @@ func (s *server) Stop() error {
// WaitForShutdown blocks until the main listener and peer handlers are stopped.
func (s *server) WaitForShutdown() {
s.wg.Wait()
log.Infof("SRVR: Server shutdown complete")
}
// ScheduleShutdown schedules a server shutdown after the specified duration.
@@ -527,7 +780,7 @@ func (s *server) ScheduleShutdown(duration time.Duration) {
if atomic.AddInt32(&s.shutdownSched, 1) != 1 {
return
}
log.Warnf("SRVR: Server shutdown in %v", duration)
srvrLog.Warnf("Server shutdown in %v", duration)
go func() {
remaining := duration
tickDuration := dynamicTickDuration(remaining)
@@ -553,50 +806,243 @@ func (s *server) ScheduleShutdown(duration time.Duration) {
ticker.Stop()
ticker = time.NewTicker(tickDuration)
}
log.Warnf("SRVR: Server shutdown in %v", remaining)
srvrLog.Warnf("Server shutdown in %v", remaining)
}
}
}()
}
// parseListeners splits the list of listen addresses passed in addrs into
// IPv4 and IPv6 slices and returns them. This allows easy creation of the
// listeners on the correct interface "tcp4" and "tcp6". It also properly
// detects addresses which apply to "all interfaces" and adds the address to
// both slices.
func parseListeners(addrs []string) ([]string, []string, bool, error) {
ipv4ListenAddrs := make([]string, 0, len(addrs)*2)
ipv6ListenAddrs := make([]string, 0, len(addrs)*2)
haveWildcard := false
for _, addr := range addrs {
host, _, err := net.SplitHostPort(addr)
if err != nil {
// Shouldn't happen due to already being normalized.
return nil, nil, false, err
}
// Empty host or host of * on plan9 is both IPv4 and IPv6.
if host == "" || (host == "*" && runtime.GOOS == "plan9") {
ipv4ListenAddrs = append(ipv4ListenAddrs, addr)
ipv6ListenAddrs = append(ipv6ListenAddrs, addr)
haveWildcard = true
continue
}
// Parse the IP.
ip := net.ParseIP(host)
if ip == nil {
return nil, nil, false, fmt.Errorf("'%s' is not a "+
"valid IP address", host)
}
// To4 returns nil when the IP is not an IPv4 address, so use
// this determine the address type.
if ip.To4() == nil {
ipv6ListenAddrs = append(ipv6ListenAddrs, addr)
} else {
ipv4ListenAddrs = append(ipv4ListenAddrs, addr)
}
}
return ipv4ListenAddrs, ipv6ListenAddrs, haveWildcard, nil
}
func (s *server) upnpUpdateThread() {
// Go off immediately to prevent code duplication, thereafter we renew
// lease every 15 minutes.
timer := time.NewTimer(0 * time.Second)
lport, _ := strconv.ParseInt(activeNetParams.listenPort, 10, 16)
first := true
out:
for {
select {
case <-timer.C:
// TODO(oga) pick external port more cleverly
// TODO(oga) know which ports we are listening to on an external net.
// TODO(oga) if specific listen port doesn't work then ask for wildcard
// listen port?
// XXX this assumes timeout is in seconds.
listenPort, err := s.nat.AddPortMapping("tcp", int(lport), int(lport),
"btcd listen port", 20*60)
if err != nil {
srvrLog.Warnf("can't add UPnP port mapping: %v", err)
}
if first && err == nil {
// TODO(oga): look this up periodically to see if upnp domain changed
// and so did ip.
externalip, err := s.nat.GetExternalAddress()
if err != nil {
srvrLog.Warnf("UPnP can't get external address: %v", err)
continue out
}
na := btcwire.NewNetAddressIPPort(externalip, uint16(listenPort),
btcwire.SFNodeNetwork)
s.addrManager.addLocalAddress(na, UpnpPrio)
srvrLog.Warnf("Successfully bound via UPnP to %s", NetAddressKey(na))
first = false
}
timer.Reset(time.Minute * 15)
case <-s.quit:
break out
}
}
timer.Stop()
if err := s.nat.DeletePortMapping("tcp", int(lport), int(lport)); err != nil {
srvrLog.Warnf("unable to remove UPnP port mapping: %v", err)
} else {
srvrLog.Debugf("succesfully disestablished UPnP port mapping")
}
s.wg.Done()
}
// newServer returns a new btcd server configured to listen on addr for the
// bitcoin network type specified in btcnet. Use start to begin accepting
// connections from peers.
func newServer(addr string, db btcdb.Db, btcnet btcwire.BitcoinNet) (*server, error) {
func newServer(listenAddrs []string, db btcdb.Db, btcnet btcwire.BitcoinNet) (*server, error) {
nonce, err := btcwire.RandomUint64()
if err != nil {
return nil, err
}
var listeners []net.Listener
if !cfg.DisableListen {
// IPv4 listener.
listener4, err := net.Listen("tcp4", addr)
if err != nil {
return nil, err
}
listeners = append(listeners, listener4)
amgr := NewAddrManager()
// IPv6 listener.
listener6, err := net.Listen("tcp6", addr)
var listeners []net.Listener
var nat NAT
if !cfg.DisableListen {
ipv4Addrs, ipv6Addrs, wildcard, err :=
parseListeners(listenAddrs)
if err != nil {
return nil, err
}
listeners = append(listeners, listener6)
listeners = make([]net.Listener, 0, len(ipv4Addrs)+len(ipv6Addrs))
discover := true
if len(cfg.ExternalIPs) != 0 {
discover = false
// if this fails we have real issues.
port, _ := strconv.ParseUint(
activeNetParams.listenPort, 10, 16)
for _, sip := range cfg.ExternalIPs {
eport := uint16(port)
host, portstr, err := net.SplitHostPort(sip)
if err != nil {
// no port, use default.
host = sip
} else {
port, err := strconv.ParseUint(
portstr, 10, 16)
if err != nil {
srvrLog.Warnf("Can not parse "+
"port from %s for "+
"externalip: %v", sip,
err)
continue
}
eport = uint16(port)
}
na, err := hostToNetAddress(host, eport,
btcwire.SFNodeNetwork)
if err != nil {
srvrLog.Warnf("Not adding %s as "+
"externalip: %v", sip, err)
continue
}
amgr.addLocalAddress(na, ManualPrio)
}
} else if discover && cfg.Upnp {
nat, err = Discover()
if err != nil {
srvrLog.Warnf("Can't discover upnp: %v", err)
}
// nil nat here is fine, just means no upnp on network.
}
// TODO(oga) nonstandard port...
if wildcard {
port, err :=
strconv.ParseUint(activeNetParams.listenPort,
10, 16)
if err != nil {
// I can't think of a cleaner way to do this...
goto nowc
}
addrs, err := net.InterfaceAddrs()
for _, a := range addrs {
ip, _, err := net.ParseCIDR(a.String())
if err != nil {
continue
}
na := btcwire.NewNetAddressIPPort(ip,
uint16(port), btcwire.SFNodeNetwork)
if discover {
amgr.addLocalAddress(na, InterfacePrio)
}
}
}
nowc:
for _, addr := range ipv4Addrs {
listener, err := net.Listen("tcp4", addr)
if err != nil {
srvrLog.Warnf("Can't listen on %s: %v", addr,
err)
continue
}
listeners = append(listeners, listener)
if discover {
if na, err := deserialiseNetAddress(addr); err == nil {
amgr.addLocalAddress(na, BoundPrio)
}
}
}
for _, addr := range ipv6Addrs {
listener, err := net.Listen("tcp6", addr)
if err != nil {
srvrLog.Warnf("Can't listen on %s: %v", addr,
err)
continue
}
listeners = append(listeners, listener)
if discover {
if na, err := deserialiseNetAddress(addr); err == nil {
amgr.addLocalAddress(na, BoundPrio)
}
}
}
if len(listeners) == 0 {
return nil, errors.New("No valid listen address")
}
}
s := server{
nonce: nonce,
listeners: listeners,
btcnet: btcnet,
addrManager: NewAddrManager(),
addrManager: amgr,
newPeers: make(chan *peer, cfg.MaxPeers),
donePeers: make(chan *peer, cfg.MaxPeers),
banPeers: make(chan *peer, cfg.MaxPeers),
wakeup: make(chan bool),
query: make(chan interface{}),
relayInv: make(chan *btcwire.InvVect, cfg.MaxPeers),
broadcast: make(chan broadcastMsg, cfg.MaxPeers),
quit: make(chan bool),
nat: nat,
db: db,
}
bm, err := newBlockManager(&s)
@@ -607,7 +1053,7 @@ func newServer(addr string, db btcdb.Db, btcnet btcwire.BitcoinNet) (*server, er
s.txMemPool = newTxMemPool(&s)
if !cfg.DisableRPC {
s.rpcServer, err = newRPCServer(&s)
s.rpcServer, err = newRPCServer(cfg.RPCListeners, &s)
if err != nil {
return nil, err
}

326
service_windows.go Normal file
View File

@@ -0,0 +1,326 @@
// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"github.com/conformal/winsvc/eventlog"
"github.com/conformal/winsvc/mgr"
"github.com/conformal/winsvc/svc"
"os"
"path/filepath"
"time"
)
const (
// svcName is the name of btcd service.
svcName = "btcdsvc"
// svcDisplayName is the service name that will be shown in the windows
// services list. Not the svcName is the "real" name which is used
// to control the service. This is only for display purposes.
svcDisplayName = "Btcd Service"
// svcDesc is the description of the service.
svcDesc = "Downloads and stays synchronized with the bitcoin block " +
"chain and provides chain services to applications."
)
// elog is used to send messages to the Windows event log.
var elog *eventlog.Log
// logServiceStartOfDay logs information about btcd when the main server has
// been started to the Windows event log.
func logServiceStartOfDay(srvr *server) {
var message string
message += fmt.Sprintf("Version %s\n", version())
message += fmt.Sprintf("Configuration directory: %s\n", btcdHomeDir)
message += fmt.Sprintf("Configuration file: %s\n", cfg.ConfigFile)
message += fmt.Sprintf("Data directory: %s\n", cfg.DataDir)
elog.Info(1, message)
}
// btcdService houses the main service handler which handles all service
// updates and launching btcdMain.
type btcdService struct{}
// Execute is the main entry point the winsvc package calls when receiving
// information from the Windows service control manager. It launches the
// long-running btcdMain (which is the real meat of btcd), handles service
// change requests, and notifies the service control manager of changes.
func (s *btcdService) Execute(args []string, r <-chan svc.ChangeRequest, changes chan<- svc.Status) (bool, uint32) {
// Service start is pending.
const cmdsAccepted = svc.AcceptStop | svc.AcceptShutdown
changes <- svc.Status{State: svc.StartPending}
// Start btcdMain in a separate goroutine so the service can start
// quickly. Shutdown (along with a potential error) is reported via
// doneChan. serverChan is notified with the main server instance once
// it is started so it can be gracefully stopped.
doneChan := make(chan error)
serverChan := make(chan *server)
go func() {
err := btcdMain(serverChan)
doneChan <- err
}()
// Service is now started.
changes <- svc.Status{State: svc.Running, Accepts: cmdsAccepted}
var mainServer *server
loop:
for {
select {
case c := <-r:
switch c.Cmd {
case svc.Interrogate:
changes <- c.CurrentStatus
case svc.Stop, svc.Shutdown:
// Service stop is pending. Don't accept any
// more commands while pending.
changes <- svc.Status{State: svc.StopPending}
// Stop the main server gracefully when it is
// already setup or just break out and allow
// the service to exit immediately if it's not
// setup yet. Note that calling Stop will cause
// btcdMain to exit in the goroutine above which
// will in turn send a signal (and a potential
// error) to doneChan.
if mainServer != nil {
mainServer.Stop()
} else {
break loop
}
default:
elog.Error(1, fmt.Sprintf("Unexpected control "+
"request #%d.", c))
}
case srvr := <-serverChan:
mainServer = srvr
logServiceStartOfDay(mainServer)
case err := <-doneChan:
if err != nil {
elog.Error(1, err.Error())
}
break loop
}
}
// Service is now stopped.
changes <- svc.Status{State: svc.Stopped}
return false, 0
}
// installService attempts to install the btcd service. Typically this should
// be done by the msi installer, but it is provided here since it can be useful
// for development.
func installService() error {
// Get the path of the current executable. This is needed because
// os.Args[0] can vary depending on how the application was launched.
// For example, under cmd.exe it will only be the name of the app
// without the path or extension, but under mingw it will be the full
// path including the extension.
exePath, err := filepath.Abs(os.Args[0])
if err != nil {
return err
}
if filepath.Ext(exePath) == "" {
exePath += ".exe"
}
// Connect to the windows service manager.
serviceManager, err := mgr.Connect()
if err != nil {
return err
}
defer serviceManager.Disconnect()
// Ensure the service doesn't already exist.
service, err := serviceManager.OpenService(svcName)
if err == nil {
service.Close()
return fmt.Errorf("service %s already exists", svcName)
}
// Install the service.
service, err = serviceManager.CreateService(svcName, exePath, mgr.Config{
DisplayName: svcDisplayName,
Description: svcDesc,
})
if err != nil {
return err
}
defer service.Close()
// Support events to the event log using the standard "standard" Windows
// EventCreate.exe message file. This allows easy logging of custom
// messges instead of needing to create our own message catalog.
eventlog.Remove(svcName)
eventsSupported := uint32(eventlog.Error | eventlog.Warning | eventlog.Info)
err = eventlog.InstallAsEventCreate(svcName, eventsSupported)
if err != nil {
return err
}
return nil
}
// removeService attempts to uninstall the btcd service. Typically this should
// be done by the msi uninstaller, but it is provided here since it can be
// useful for development. Not the eventlog entry is intentionally not removed
// since it would invalidate any existing event log messages.
func removeService() error {
// Connect to the windows service manager.
serviceManager, err := mgr.Connect()
if err != nil {
return err
}
defer serviceManager.Disconnect()
// Ensure the service exists.
service, err := serviceManager.OpenService(svcName)
if err != nil {
return fmt.Errorf("service %s is not installed", svcName)
}
defer service.Close()
// Remove the service.
err = service.Delete()
if err != nil {
return err
}
return nil
}
// startService attempts to start the btcd service.
func startService() error {
// Connect to the windows service manager.
serviceManager, err := mgr.Connect()
if err != nil {
return err
}
defer serviceManager.Disconnect()
service, err := serviceManager.OpenService(svcName)
if err != nil {
return fmt.Errorf("could not access service: %v", err)
}
defer service.Close()
err = service.Start(os.Args)
if err != nil {
return fmt.Errorf("could not start service: %v", err)
}
return nil
}
// controlService allows commands which change the status of the service. It
// also waits for up to 10 seconds for the service to change to the passed
// state.
func controlService(c svc.Cmd, to svc.State) error {
// Connect to the windows service manager.
serviceManager, err := mgr.Connect()
if err != nil {
return err
}
defer serviceManager.Disconnect()
service, err := serviceManager.OpenService(svcName)
if err != nil {
return fmt.Errorf("could not access service: %v", err)
}
defer service.Close()
status, err := service.Control(c)
if err != nil {
return fmt.Errorf("could not send control=%d: %v", c, err)
}
// Send the control message.
timeout := time.Now().Add(10 * time.Second)
for status.State != to {
if timeout.Before(time.Now()) {
return fmt.Errorf("timeout waiting for service to go "+
"to state=%d", to)
}
time.Sleep(300 * time.Millisecond)
status, err = service.Query()
if err != nil {
return fmt.Errorf("could not retrieve service "+
"status: %v", err)
}
}
return nil
}
// performServiceCommand attempts to run one of the supported service commands
// provided on the command line via the service command flag. An appropriate
// error is returned if an invalid command is specified.
func performServiceCommand(command string) error {
var err error
switch command {
case "install":
err = installService()
case "remove":
err = removeService()
case "start":
err = startService()
case "stop":
err = controlService(svc.Stop, svc.Stopped)
default:
err = fmt.Errorf("invalid service command [%s]", command)
}
return err
}
// serviceMain checks whether we're being invoked as a service, and if so uses
// the service control manager to start the long-running server. A flag is
// returned to the caller so the application can determine whether to exit (when
// running as a service) or launch in normal interactive mode.
func serviceMain() (bool, error) {
// Don't run as a service if we're running interactively (or that can't
// be determined due to an error).
isInteractive, err := svc.IsAnInteractiveSession()
if err != nil {
return false, err
}
if isInteractive {
return false, nil
}
elog, err = eventlog.Open(svcName)
if err != nil {
return false, err
}
defer elog.Close()
err = svc.Run(svcName, &btcdService{})
if err != nil {
elog.Error(1, fmt.Sprintf("Service start failed: %v", err))
return true, err
}
return true, nil
}
// Set windows specific functions to real functions.
func init() {
runServiceCommand = performServiceCommand
winServiceMain = serviceMain
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2013 Conformal Systems LLC.
// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
@@ -27,10 +27,16 @@ func mainInterruptHandler() {
for {
select {
case <-interruptChannel:
for _, callback := range interruptCallbacks {
btcdLog.Infof("Received SIGINT (Ctrl+C). Shutting down...")
// run handlers in LIFO order.
for i := range interruptCallbacks {
idx := len(interruptCallbacks) - 1 - i
callback := interruptCallbacks[idx]
callback()
}
os.Exit(0)
// Signal the main goroutine to shutdown.
shutdownChannel <- true
case handler := <-addHandlerChannel:
interruptCallbacks = append(interruptCallbacks, handler)

View File

@@ -1,14 +1,50 @@
// Copyright (c) 2013 Conformal Systems LLC.
// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package main
import (
"io"
"os"
"path/filepath"
)
// dirEmpty returns whether or not the specified directory path is empty.
func dirEmpty(dirPath string) (bool, error) {
f, err := os.Open(dirPath)
defer f.Close()
// Read the names of a max of one entry from the directory. When the
// directory is empty, an io.EOF error will be returned, so allow it.
names, err := f.Readdirnames(1)
if err != nil && err != io.EOF {
return false, err
}
return len(names) == 0, nil
}
// oldBtcdHomeDir returns the OS specific home directory btcd used prior to
// version 0.3.3. This has since been replaced with btcutil.AppDataDir, but
// this function is still provided for the automatic upgrade path.
func oldBtcdHomeDir() string {
// Search for Windows APPDATA first. This won't exist on POSIX OSes.
appData := os.Getenv("APPDATA")
if appData != "" {
return filepath.Join(appData, "btcd")
}
// Fall back to standard HOME directory that works for most POSIX OSes.
home := os.Getenv("HOME")
if home != "" {
return filepath.Join(home, ".btcd")
}
// In the worst case, use the current directory.
return "."
}
// upgradeDBPathNet moves the database for a specific network from its
// location prior to btcd version 0.2.0 and uses heuristics to ascertain the old
// database type to rename to the new format.
@@ -56,7 +92,7 @@ func upgradeDBPaths() error {
// their names were suffixed by "testnet" and "regtest" for their
// respective networks. Check for the old database and update it to the
// new path introduced with version 0.2.0 accodingly.
oldDbRoot := filepath.Join(btcdHomeDir(), "db")
oldDbRoot := filepath.Join(oldBtcdHomeDir(), "db")
upgradeDBPathNet(filepath.Join(oldDbRoot, "btcd.db"), "mainnet")
upgradeDBPathNet(filepath.Join(oldDbRoot, "btcd_testnet.db"), "testnet")
upgradeDBPathNet(filepath.Join(oldDbRoot, "btcd_regtest.db"), "regtest")
@@ -70,7 +106,72 @@ func upgradeDBPaths() error {
return nil
}
// upgradeDataPaths moves the application data from its location prior to btcd
// version 0.3.3 to its new location.
func upgradeDataPaths() error {
// No need to migrate if the old and new home paths are the same.
oldHomePath := oldBtcdHomeDir()
newHomePath := btcdHomeDir
if oldHomePath == newHomePath {
return nil
}
// Only migrate if the old path exists and the new one doesn't.
if fileExists(oldHomePath) && !fileExists(newHomePath) {
// Create the new path.
btcdLog.Infof("Migrating application home path from '%s' to '%s'",
oldHomePath, newHomePath)
err := os.MkdirAll(newHomePath, 0700)
if err != nil {
return err
}
// Move old btcd.conf into new location if needed.
oldConfPath := filepath.Join(oldHomePath, defaultConfigFilename)
newConfPath := filepath.Join(newHomePath, defaultConfigFilename)
if fileExists(oldConfPath) && !fileExists(newConfPath) {
err := os.Rename(oldConfPath, newConfPath)
if err != nil {
return err
}
}
// Move old data directory into new location if needed.
oldDataPath := filepath.Join(oldHomePath, defaultDataDirname)
newDataPath := filepath.Join(newHomePath, defaultDataDirname)
if fileExists(oldDataPath) && !fileExists(newDataPath) {
err := os.Rename(oldDataPath, newDataPath)
if err != nil {
return err
}
}
// Remove the old home if it is empty or show a warning if not.
ohpEmpty, err := dirEmpty(oldHomePath)
if err != nil {
return err
}
if ohpEmpty {
err := os.Remove(oldHomePath)
if err != nil {
return err
}
} else {
btcdLog.Warnf("Not removing '%s' since it contains files "+
"not created by this application. You may "+
"want to manually move them or delete them.",
oldHomePath)
}
}
return nil
}
// doUpgrades performs upgrades to btcd as new versions require it.
func doUpgrades() error {
return upgradeDBPaths()
err := upgradeDBPaths()
if err != nil {
return err
}
return upgradeDataPaths()
}

405
upnp.go Normal file
View File

@@ -0,0 +1,405 @@
package main
// Upnp code taken from Taipei Torrent license is below:
// Copyright (c) 2010 Jack Palevich. All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Just enough UPnP to be able to forward ports
//
import (
"bytes"
"encoding/xml"
"errors"
"net"
"net/http"
"os"
"strconv"
"strings"
"time"
)
// NAT is an interface representing a NAT traversal options for example UPNP or
// NAT-PMP. It provides methods to query and manipulate this traversal to allow
// access to services.
type NAT interface {
// Get the external address from outside the NAT.
GetExternalAddress() (addr net.IP, err error)
// Add a port mapping for protocol ("udp" or "tcp") from externalport to
// internal port with description lasting for timeout.
AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error)
// Remove a previously added port mapping from externalport to
// internal port.
DeletePortMapping(protocol string, externalPort, internalPort int) (err error)
}
type upnpNAT struct {
serviceURL string
ourIP string
}
// Discover searches the local network for a UPnP router returning a NAT
// for the network if so, nil if not.
func Discover() (nat NAT, err error) {
ssdp, err := net.ResolveUDPAddr("udp4", "239.255.255.250:1900")
if err != nil {
return
}
conn, err := net.ListenPacket("udp4", ":0")
if err != nil {
return
}
socket := conn.(*net.UDPConn)
defer socket.Close()
err = socket.SetDeadline(time.Now().Add(3 * time.Second))
if err != nil {
return
}
st := "ST: urn:schemas-upnp-org:device:InternetGatewayDevice:1\r\n"
buf := bytes.NewBufferString(
"M-SEARCH * HTTP/1.1\r\n" +
"HOST: 239.255.255.250:1900\r\n" +
st +
"MAN: \"ssdp:discover\"\r\n" +
"MX: 2\r\n\r\n")
message := buf.Bytes()
answerBytes := make([]byte, 1024)
for i := 0; i < 3; i++ {
_, err = socket.WriteToUDP(message, ssdp)
if err != nil {
return
}
var n int
n, _, err = socket.ReadFromUDP(answerBytes)
if err != nil {
continue
// socket.Close()
// return
}
answer := string(answerBytes[0:n])
if strings.Index(answer, "\r\n"+st) < 0 {
continue
}
// HTTP header field names are case-insensitive.
// http://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html#sec4.2
locString := "\r\nlocation: "
answer = strings.ToLower(answer)
locIndex := strings.Index(answer, locString)
if locIndex < 0 {
continue
}
loc := answer[locIndex+len(locString):]
endIndex := strings.Index(loc, "\r\n")
if endIndex < 0 {
continue
}
locURL := loc[0:endIndex]
var serviceURL string
serviceURL, err = getServiceURL(locURL)
if err != nil {
return
}
var ourIP string
ourIP, err = getOurIP()
if err != nil {
return
}
nat = &upnpNAT{serviceURL: serviceURL, ourIP: ourIP}
return
}
err = errors.New("UPnP port discovery failed")
return
}
// service represents the Service type in an UPnP xml description.
// Only the parts we care about are present and thus the xml may have more
// fields than present in the structure.
type service struct {
ServiceType string `xml:"serviceType"`
ControlURL string `xml:"controlURL"`
}
// deviceList represents the deviceList type in an UPnP xml description.
// Only the parts we care about are present and thus the xml may have more
// fields than present in the structure.
type deviceList struct {
XMLName xml.Name `xml:"deviceList"`
Device []device `xml:"device"`
}
// serviceList represents the serviceList type in an UPnP xml description.
// Only the parts we care about are present and thus the xml may have more
// fields than present in the structure.
type serviceList struct {
XMLName xml.Name `xml:"serviceList"`
Service []service `xml:"service"`
}
// device represents the device type in an UPnP xml description.
// Only the parts we care about are present and thus the xml may have more
// fields than present in the structure.
type device struct {
XMLName xml.Name `xml:"device"`
DeviceType string `xml:"deviceType"`
DeviceList deviceList `xml:"deviceList"`
ServiceList serviceList `xml:"serviceList"`
}
// specVersion represents the specVersion in a UPnP xml description.
// Only the parts we care about are present and thus the xml may have more
// fields than present in the structure.
type specVersion struct {
XMLName xml.Name `xml:"specVersion"`
Major int `xml:"major"`
Minor int `xml:"minor"`
}
// root represents the Root document for a UPnP xml description.
// Only the parts we care about are present and thus the xml may have more
// fields than present in the structure.
type root struct {
XMLName xml.Name `xml:"root"`
SpecVersion specVersion
Device device
}
// getChildDevice searches the children of device for a device with the given
// type.
func getChildDevice(d *device, deviceType string) *device {
for i := range d.DeviceList.Device {
if d.DeviceList.Device[i].DeviceType == deviceType {
return &d.DeviceList.Device[i]
}
}
return nil
}
// getChildDevice searches the service list of device for a service with the
// given type.
func getChildService(d *device, serviceType string) *service {
for i := range d.ServiceList.Service {
if d.ServiceList.Service[i].ServiceType == serviceType {
return &d.ServiceList.Service[i]
}
}
return nil
}
// getOurIP returns a best guess at what the local IP is.
func getOurIP() (ip string, err error) {
hostname, err := os.Hostname()
if err != nil {
return
}
return net.LookupCNAME(hostname)
}
// getServiceURL parses the xml description at the given root url to find the
// url for the WANIPConnection service to be used for port forwarding.
func getServiceURL(rootURL string) (url string, err error) {
r, err := http.Get(rootURL)
if err != nil {
return
}
defer r.Body.Close()
if r.StatusCode >= 400 {
err = errors.New(string(r.StatusCode))
return
}
var root root
err = xml.NewDecoder(r.Body).Decode(&root)
if err != nil {
return
}
a := &root.Device
if a.DeviceType != "urn:schemas-upnp-org:device:InternetGatewayDevice:1" {
err = errors.New("no InternetGatewayDevice")
return
}
b := getChildDevice(a, "urn:schemas-upnp-org:device:WANDevice:1")
if b == nil {
err = errors.New("no WANDevice")
return
}
c := getChildDevice(b, "urn:schemas-upnp-org:device:WANConnectionDevice:1")
if c == nil {
err = errors.New("no WANConnectionDevice")
return
}
d := getChildService(c, "urn:schemas-upnp-org:service:WANIPConnection:1")
if d == nil {
err = errors.New("no WANIPConnection")
return
}
url = combineURL(rootURL, d.ControlURL)
return
}
// combineURL appends subURL onto rootURL.
func combineURL(rootURL, subURL string) string {
protocolEnd := "://"
protoEndIndex := strings.Index(rootURL, protocolEnd)
a := rootURL[protoEndIndex+len(protocolEnd):]
rootIndex := strings.Index(a, "/")
return rootURL[0:protoEndIndex+len(protocolEnd)+rootIndex] + subURL
}
// soapBody represents the <s:Body> element in a SOAP reply.
// fields we don't care about are elided.
type soapBody struct {
XMLName xml.Name `xml:"Body"`
Data []byte `xml:",innerxml"`
}
// soapEnvelope represents the <s:Envelope> element in a SOAP reply.
// fields we don't care about are elided.
type soapEnvelope struct {
XMLName xml.Name `xml:"Envelope"`
Body soapBody `xml:"Body"`
}
// soapRequests performs a soap request with the given parameters and returns
// the xml replied stripped of the soap headers. in the case that the request is
// unsuccessful the an error is returned.
func soapRequest(url, function, message string) (replyXML []byte, err error) {
fullMessage := "<?xml version=\"1.0\" ?>" +
"<s:Envelope xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">\r\n" +
"<s:Body>" + message + "</s:Body></s:Envelope>"
req, err := http.NewRequest("POST", url, strings.NewReader(fullMessage))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "text/xml ; charset=\"utf-8\"")
req.Header.Set("User-Agent", "Darwin/10.0.0, UPnP/1.0, MiniUPnPc/1.3")
//req.Header.Set("Transfer-Encoding", "chunked")
req.Header.Set("SOAPAction", "\"urn:schemas-upnp-org:service:WANIPConnection:1#"+function+"\"")
req.Header.Set("Connection", "Close")
req.Header.Set("Cache-Control", "no-cache")
req.Header.Set("Pragma", "no-cache")
r, err := http.DefaultClient.Do(req)
if err != nil {
return nil, err
}
if r.Body != nil {
defer r.Body.Close()
}
if r.StatusCode >= 400 {
// log.Stderr(function, r.StatusCode)
err = errors.New("Error " + strconv.Itoa(r.StatusCode) + " for " + function)
r = nil
return
}
var reply soapEnvelope
err = xml.NewDecoder(r.Body).Decode(&reply)
if err != nil {
return nil, err
}
return reply.Body.Data, nil
}
// getExternalIPAddressResponse represents the XML response to a
// GetExternalIPAddress SOAP request.
type getExternalIPAddressResponse struct {
XMLName xml.Name `xml:"GetExternalIPAddressResponse"`
ExternalIPAddress string `xml:"NewExternalIPAddress"`
}
// GetExternalAddress implements the NAT interface by fetching the external IP
// from the UPnP router.
func (n *upnpNAT) GetExternalAddress() (addr net.IP, err error) {
message := "<u:GetExternalIPAddress xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\"/>\r\n"
response, err := soapRequest(n.serviceURL, "GetExternalIPAddress", message)
if err != nil {
return nil, err
}
var reply getExternalIPAddressResponse
err = xml.Unmarshal(response, &reply)
if err != nil {
return nil, err
}
addr = net.ParseIP(reply.ExternalIPAddress)
if addr == nil {
return nil, errors.New("unable to parse ip address")
}
return addr, nil
}
// AddPortMapping implements the NAT interface by setting up a port forwarding
// from the UPnP router to the local machine with the given ports and protocol.
func (n *upnpNAT) AddPortMapping(protocol string, externalPort, internalPort int, description string, timeout int) (mappedExternalPort int, err error) {
// A single concatenation would break ARM compilation.
message := "<u:AddPortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
"<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(externalPort)
message += "</NewExternalPort><NewProtocol>" + protocol + "</NewProtocol>"
message += "<NewInternalPort>" + strconv.Itoa(internalPort) + "</NewInternalPort>" +
"<NewInternalClient>" + n.ourIP + "</NewInternalClient>" +
"<NewEnabled>1</NewEnabled><NewPortMappingDescription>"
message += description +
"</NewPortMappingDescription><NewLeaseDuration>" + strconv.Itoa(timeout) +
"</NewLeaseDuration></u:AddPortMapping>"
response, err := soapRequest(n.serviceURL, "AddPortMapping", message)
if err != nil {
return
}
// TODO: check response to see if the port was forwarded
// If the port was not wildcard we don't get an reply with the port in
// it. Not sure about wildcard yet. miniupnpc just checks for error
// codes here.
mappedExternalPort = externalPort
_ = response
return
}
// AddPortMapping implements the NAT interface by removing up a port forwarding
// from the UPnP router to the local machine with the given ports and.
func (n *upnpNAT) DeletePortMapping(protocol string, externalPort, internalPort int) (err error) {
message := "<u:DeletePortMapping xmlns:u=\"urn:schemas-upnp-org:service:WANIPConnection:1\">\r\n" +
"<NewRemoteHost></NewRemoteHost><NewExternalPort>" + strconv.Itoa(externalPort) +
"</NewExternalPort><NewProtocol>" + protocol + "</NewProtocol>" +
"</u:DeletePortMapping>"
response, err := soapRequest(n.serviceURL, "DeletePortMapping", message)
if err != nil {
return
}
// TODO: check response to see if the port was deleted
// log.Println(message, response)
_ = response
return
}

View File

@@ -1,266 +1,134 @@
// Copyright (c) 2013 Conformal Systems LLC.
// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package main
import (
"encoding/binary"
"fmt"
"github.com/conformal/btcchain"
"github.com/conformal/btcd/limits"
"github.com/conformal/btcdb"
_ "github.com/conformal/btcdb/ldb"
_ "github.com/conformal/btcdb/sqlite3"
"github.com/conformal/btcutil"
"github.com/conformal/btcwire"
"github.com/conformal/go-flags"
"github.com/conformal/seelog"
"io"
"github.com/conformal/btclog"
"os"
"path/filepath"
"runtime"
"runtime/pprof"
)
type ShaHash btcwire.ShaHash
type config struct {
DataDir string `short:"b" long:"datadir" description:"Directory to store data"`
DbType string `long:"dbtype" description:"Database backend"`
TestNet3 bool `long:"testnet" description:"Use the test network"`
Progress bool `short:"p" description:"show progress"`
InFile string `short:"i" long:"infile" description:"File containing the block(s)" required:"true"`
}
var log seelog.LoggerInterface
const (
ArgSha = iota
ArgHeight
// blockDbNamePrefix is the prefix for the btcd block database.
blockDbNamePrefix = "blocks"
)
type bufQueue struct {
height int64
blkbuf []byte
}
var (
cfg *config
log btclog.Logger
)
type blkQueue struct {
complete chan bool
height int64
blk *btcutil.Block
}
func main() {
cfg := config{
DbType: "leveldb",
DataDir: filepath.Join(btcdHomeDir(), "data"),
}
parser := flags.NewParser(&cfg, flags.Default)
_, err := parser.Parse()
if err != nil {
if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp {
parser.WriteHelp(os.Stderr)
}
return
}
runtime.GOMAXPROCS(runtime.NumCPU())
log, err = seelog.LoggerFromWriterWithMinLevel(os.Stdout,
seelog.InfoLvl)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to create logger: %v", err)
return
}
defer log.Flush()
btcdb.UseLogger(log)
var testnet string
if cfg.TestNet3 {
testnet = "testnet"
} else {
testnet = "mainnet"
}
cfg.DataDir = filepath.Join(cfg.DataDir, testnet)
err = os.MkdirAll(cfg.DataDir, 0700)
if err != nil {
fmt.Printf("unable to create db repo area %v, %v", cfg.DataDir, err)
}
blockDbNamePrefix := "blocks"
// loadBlockDB opens the block database and returns a handle to it.
func loadBlockDB() (btcdb.Db, error) {
// The database name is based on the database type.
dbName := blockDbNamePrefix + "_" + cfg.DbType
if cfg.DbType == "sqlite" {
dbName = dbName + ".db"
}
dbPath := filepath.Join(cfg.DataDir, dbName)
log.Infof("loading db")
db, err := btcdb.CreateDB(cfg.DbType, dbPath)
log.Infof("Loading block database from '%s'", dbPath)
db, err := btcdb.OpenDB(cfg.DbType, dbPath)
if err != nil {
log.Warnf("db open failed: %v", err)
return
// Return the error if it's not because the database doesn't
// exist.
if err != btcdb.DbDoesNotExist {
return nil, err
}
// Create the db if it does not exist.
err = os.MkdirAll(cfg.DataDir, 0700)
if err != nil {
return nil, err
}
db, err = btcdb.CreateDB(cfg.DbType, dbPath)
if err != nil {
return nil, err
}
}
// Get the latest block height from the database.
_, height, err := db.NewestSha()
if err != nil {
db.Close()
return nil, err
}
log.Infof("Block database loaded with block height %d", height)
return db, nil
}
// realMain is the real main function for the utility. It is necessary to work
// around the fact that deferred functions do not run when os.Exit() is called.
func realMain() error {
// Load configuration and parse command line.
tcfg, _, err := loadConfig()
if err != nil {
return err
}
cfg = tcfg
// Setup logging.
backendLogger := btclog.NewDefaultBackendLogger()
defer backendLogger.Flush()
log = btclog.NewSubsystemLogger(backendLogger, "")
btcdb.UseLogger(btclog.NewSubsystemLogger(backendLogger, "BCDB: "))
btcchain.UseLogger(btclog.NewSubsystemLogger(backendLogger, "CHAN: "))
// Load the block database.
db, err := loadBlockDB()
if err != nil {
log.Errorf("Failed to load database: %v", err)
return err
}
defer db.Close()
log.Infof("db created")
var fi io.ReadCloser
fi, err = os.Open(cfg.InFile)
fi, err := os.Open(cfg.InFile)
if err != nil {
log.Warnf("failed to open file %v, err %v", cfg.InFile, err)
log.Errorf("Failed to open file %v: %v", cfg.InFile, err)
return err
}
defer func() {
if err := fi.Close(); err != nil {
log.Warn("failed to close file %v %v", cfg.InFile, err)
}
}()
defer fi.Close()
bufqueue := make(chan *bufQueue, 2)
blkqueue := make(chan *blkQueue, 2)
// Create a block importer for the database and input file and start it.
// The done channel returned from start will contain an error if
// anything went wrong.
importer := newBlockImporter(db, fi)
for i := 0; i < runtime.NumCPU(); i++ {
go processBuf(i, bufqueue, blkqueue)
// Perform the import asynchronously. This allows blocks to be
// processed and read in parallel. The results channel returned from
// Import contains the statistics about the import including an error
// if something went wrong.
log.Info("Starting import")
resultsChan := importer.Import()
results := <-resultsChan
if results.err != nil {
log.Errorf("%v", results.err)
return results.err
}
go processBuf(0, bufqueue, blkqueue)
go readBlocks(fi, bufqueue)
var eheight int64
doneMap := map[int64]*blkQueue{}
for {
select {
case blkM := <-blkqueue:
doneMap[blkM.height] = blkM
for {
if blkP, ok := doneMap[eheight]; ok {
delete(doneMap, eheight)
blkP.complete <- true
db.InsertBlock(blkP.blk)
if cfg.Progress && eheight%int64(1) == 0 {
log.Infof("Processing block %v", eheight)
}
eheight++
if eheight%2000 == 0 {
f, err := os.Create(fmt.Sprintf("profile.%d", eheight))
if err == nil {
pprof.WriteHeapProfile(f)
f.Close()
} else {
log.Warnf("profile failed %v", err)
}
}
} else {
break
}
}
}
}
log.Infof("Processed a total of %d blocks (%d imported, %d already "+
"known)", results.blocksProcessed, results.blocksImported,
results.blocksProcessed-results.blocksImported)
return nil
}
func processBuf(idx int, bufqueue chan *bufQueue, blkqueue chan *blkQueue) {
complete := make(chan bool)
for {
select {
case bq := <-bufqueue:
var blkmsg blkQueue
blkmsg.height = bq.height
if len(bq.blkbuf) == 0 {
// we are done
blkqueue <- &blkmsg
}
blk, err := btcutil.NewBlockFromBytes(bq.blkbuf)
if err != nil {
fmt.Printf("failed to parse block %v", bq.height)
return
}
blkmsg.blk = blk
blkmsg.complete = complete
blkqueue <- &blkmsg
select {
case <-complete:
}
}
}
}
func readBlocks(fi io.Reader, bufqueue chan *bufQueue) {
var height int64
for {
var net, blen uint32
var bufM bufQueue
bufM.height = height
// generate and write header values
err := binary.Read(fi, binary.LittleEndian, &net)
if err != nil {
break
bufqueue <- &bufM
}
if net != uint32(btcwire.MainNet) {
fmt.Printf("network mismatch %v %v",
net, uint32(btcwire.MainNet))
bufqueue <- &bufM
}
err = binary.Read(fi, binary.LittleEndian, &blen)
if err != nil {
bufqueue <- &bufM
}
blkbuf := make([]byte, blen)
err = binary.Read(fi, binary.LittleEndian, blkbuf)
bufM.blkbuf = blkbuf
bufqueue <- &bufM
height++
}
}
// newLogger creates a new seelog logger using the provided logging level and
// log message prefix.
func newLogger(level string, prefix string) seelog.LoggerInterface {
fmtstring := `
<seelog type="adaptive" mininterval="2000000" maxinterval="100000000"
critmsgcount="500" minlevel="%s">
<outputs formatid="all">
<console/>
</outputs>
<formats>
<format id="all" format="[%%Time %%Date] [%%LEV] [%s] %%Msg%%n" />
</formats>
</seelog>`
config := fmt.Sprintf(fmtstring, level, prefix)
logger, err := seelog.LoggerFromConfigAsString(config)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to create logger: %v", err)
func main() {
// Use all processor cores and up some limits.
runtime.GOMAXPROCS(runtime.NumCPU())
if err := limits.SetLimits(); err != nil {
os.Exit(1)
}
return logger
}
// btcdHomeDir returns an OS appropriate home directory for btcd.
func btcdHomeDir() string {
// Search for Windows APPDATA first. This won't exist on POSIX OSes.
appData := os.Getenv("APPDATA")
if appData != "" {
return filepath.Join(appData, "btcd")
// Work around defer not working after os.Exit()
if err := realMain(); err != nil {
os.Exit(1)
}
// Fall back to standard HOME directory that works for most POSIX OSes.
home := os.Getenv("HOME")
if home != "" {
return filepath.Join(home, ".btcd")
}
// In the worst case, use the current directory.
return "."
}

125
util/addblock/config.go Normal file
View File

@@ -0,0 +1,125 @@
// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"github.com/conformal/btcdb"
_ "github.com/conformal/btcdb/ldb"
"github.com/conformal/btcutil"
"github.com/conformal/btcwire"
"github.com/conformal/go-flags"
"os"
"path/filepath"
)
const (
defaultDbType = "leveldb"
defaultDataFile = "bootstrap.dat"
defaultProgress = 10000
)
var (
btcdHomeDir = btcutil.AppDataDir("btcd", false)
defaultDataDir = filepath.Join(btcdHomeDir, "data")
knownDbTypes = btcdb.SupportedDBs()
activeNetwork = btcwire.MainNet
)
// config defines the configuration options for findcheckpoint.
//
// See loadConfig for details on the configuration load process.
type config struct {
DataDir string `short:"b" long:"datadir" description:"Location of the btcd data directory"`
DbType string `long:"dbtype" description:"Database backend to use for the Block Chain"`
TestNet3 bool `long:"testnet" description:"Use the test network"`
InFile string `short:"i" long:"infile" description:"File containing the block(s)"`
Progress int `short:"p" long:"progress" description:"Show a progress message every time this number of blocks is processed -- Use 0 to disable progress announcements"`
}
// filesExists reports whether the named file or directory exists.
func fileExists(name string) bool {
if _, err := os.Stat(name); err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
}
// validDbType returns whether or not dbType is a supported database type.
func validDbType(dbType string) bool {
for _, knownType := range knownDbTypes {
if dbType == knownType {
return true
}
}
return false
}
// netName returns a human-readable name for the passed bitcoin network.
func netName(btcnet btcwire.BitcoinNet) string {
net := "mainnet"
if btcnet == btcwire.TestNet3 {
net = "testnet"
}
return net
}
// loadConfig initializes and parses the config using command line options.
func loadConfig() (*config, []string, error) {
// Default config.
cfg := config{
DataDir: defaultDataDir,
DbType: defaultDbType,
InFile: defaultDataFile,
Progress: defaultProgress,
}
// Parse command line options.
parser := flags.NewParser(&cfg, flags.Default)
remainingArgs, err := parser.Parse()
if err != nil {
if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp {
parser.WriteHelp(os.Stderr)
}
return nil, nil, err
}
// Choose the active network based on the flags.
if cfg.TestNet3 {
activeNetwork = btcwire.TestNet3
}
// Validate database type.
if !validDbType(cfg.DbType) {
str := "%s: The specified database type [%v] is invalid -- " +
"supported types %v"
err := fmt.Errorf(str, "loadConfig", cfg.DbType, knownDbTypes)
fmt.Fprintln(os.Stderr, err)
parser.WriteHelp(os.Stderr)
return nil, nil, err
}
// Append the network type to the data directory so it is "namespaced"
// per network. In addition to the block database, there are other
// pieces of data that are saved to disk such as address manager state.
// All data is specific to a network, so namespacing the data directory
// means each individual piece of serialized data does not have to
// worry about changing names per network and such.
cfg.DataDir = filepath.Join(cfg.DataDir, netName(activeNetwork))
// Ensure the specified block file exists.
if !fileExists(cfg.InFile) {
str := "%s: The specified block file [%v] does not exist"
err := fmt.Errorf(str, "loadConfig", cfg.InFile)
fmt.Fprintln(os.Stderr, err)
parser.WriteHelp(os.Stderr)
return nil, nil, err
}
return &cfg, remainingArgs, nil
}

251
util/addblock/import.go Normal file
View File

@@ -0,0 +1,251 @@
// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package main
import (
"encoding/binary"
"fmt"
"github.com/conformal/btcchain"
"github.com/conformal/btcdb"
_ "github.com/conformal/btcdb/ldb"
"github.com/conformal/btcutil"
"github.com/conformal/btcwire"
"io"
"sync"
)
var zeroHash = btcwire.ShaHash{}
// importResults houses the stats and result as an import operation.
type importResults struct {
blocksProcessed int64
blocksImported int64
err error
}
// blockImporter houses information about an ongoing import from a block data
// file to the block database.
type blockImporter struct {
db btcdb.Db
chain *btcchain.BlockChain
r io.ReadSeeker
processQueue chan []byte
doneChan chan bool
errChan chan error
quit chan bool
wg sync.WaitGroup
blocksProcessed int64
blocksImported int64
}
// readBlock reads the next block from the input file.
func (bi *blockImporter) readBlock() ([]byte, error) {
// The block file format is:
// <network> <block length> <serialized block>
var net uint32
err := binary.Read(bi.r, binary.LittleEndian, &net)
if err != nil {
if err != io.EOF {
return nil, err
}
// No block and no error means there are no more blocks to read.
return nil, nil
}
if net != uint32(activeNetwork) {
return nil, fmt.Errorf("network mismatch -- got %x, want %x",
net, uint32(activeNetwork))
}
// Read the block length and ensure it is sane.
var blockLen uint32
if err := binary.Read(bi.r, binary.LittleEndian, &blockLen); err != nil {
return nil, err
}
if blockLen > btcwire.MaxBlockPayload {
return nil, fmt.Errorf("block payload of %d bytes is larger "+
"than the max allowed %d bytes", blockLen,
btcwire.MaxBlockPayload)
}
serializedBlock := make([]byte, blockLen)
if _, err := io.ReadFull(bi.r, serializedBlock); err != nil {
return nil, err
}
return serializedBlock, nil
}
// processBlock potentially imports the block into the database. It first
// deserializes the raw block while checking for errors. Already known blocks
// are skipped and orphan blocks are considered errors. Finally, it runs the
// block through the chain rules to ensure it follows all rules and matches
// up to the known checkpoint. Returns whether the block was imported along
// with any potential errors.
func (bi *blockImporter) processBlock(serializedBlock []byte) (bool, error) {
// Deserialize the block which includes checks for malformed blocks.
block, err := btcutil.NewBlockFromBytes(serializedBlock)
if err != nil {
return false, err
}
blockSha, err := block.Sha()
if err != nil {
return false, err
}
// Skip blocks that already exist.
if bi.db.ExistsSha(blockSha) {
return false, nil
}
// Don't bother trying to process orphans.
prevHash := &block.MsgBlock().Header.PrevBlock
if !prevHash.IsEqual(&zeroHash) && !bi.db.ExistsSha(prevHash) {
return false, fmt.Errorf("import file contains block %v which "+
"does not link to the available block chain", blockSha)
}
// Ensure the blocks follows all of the chain rules and match up to the
// known checkpoints.
if err := bi.chain.ProcessBlock(block, true); err != nil {
return false, err
}
return true, nil
}
// readHandler is the main handler for reading blocks from the import file.
// This allows block processing to take place in parallel with block reads.
// It must be run as a goroutine.
func (bi *blockImporter) readHandler() {
out:
for {
// Read the next block from the file and if anything goes wrong
// notify the status handler with the error and bail.
serializedBlock, err := bi.readBlock()
if err != nil {
bi.errChan <- fmt.Errorf("Error reading from input "+
"file: %v", err.Error())
break out
}
// A nil block with no error means we're done.
if serializedBlock == nil {
break out
}
// Send the block or quit if we've been signalled to exit by
// the status handler due to an error elsewhere.
select {
case bi.processQueue <- serializedBlock:
case <-bi.quit:
break out
}
}
// Close the processing channel to signal no more blocks are coming.
close(bi.processQueue)
bi.wg.Done()
}
// processHandler is the main handler for processing blocks. This allows block
// processing to take place in parallel with block reads from the import file.
// It must be run as a goroutine.
func (bi *blockImporter) processHandler() {
out:
for {
select {
case serializedBlock, ok := <-bi.processQueue:
// We're done when the channel is closed.
if !ok {
break out
}
bi.blocksProcessed++
imported, err := bi.processBlock(serializedBlock)
if err != nil {
bi.errChan <- err
break out
}
if imported {
bi.blocksImported++
}
if cfg.Progress != 0 && bi.blocksProcessed > 0 &&
bi.blocksProcessed%int64(cfg.Progress) == 0 {
log.Infof("Processed %d blocks", bi.blocksProcessed)
}
case <-bi.quit:
break out
}
}
bi.wg.Done()
}
// statusHandler waits for updates from the import operation and notifies
// the passed doneChan with the results of the import. It also causes all
// goroutines to exit if an error is reported from any of them.
func (bi *blockImporter) statusHandler(resultsChan chan *importResults) {
select {
// An error from either of the goroutines means we're done so signal
// caller with the error and signal all goroutines to quit.
case err := <-bi.errChan:
resultsChan <- &importResults{
blocksProcessed: bi.blocksProcessed,
blocksImported: bi.blocksImported,
err: err,
}
close(bi.quit)
// The import finished normally.
case <-bi.doneChan:
resultsChan <- &importResults{
blocksProcessed: bi.blocksProcessed,
blocksImported: bi.blocksImported,
err: nil,
}
}
}
// Import is the core function which handles importing the blocks from the file
// associated with the block importer to the database. It returns a channel
// on which the results will be returned when the operation has completed.
func (bi *blockImporter) Import() chan *importResults {
// Start up the read and process handling goroutines. This setup allows
// blocks to be read from disk in parallel while being processed.
bi.wg.Add(2)
go bi.readHandler()
go bi.processHandler()
// Wait for the import to finish in a separate goroutine and signal
// the status handler when done.
go func() {
bi.wg.Wait()
bi.doneChan <- true
}()
// Start the status handler and return the result the channel that it
// will send the results on when the import is done.
resultChan := make(chan *importResults)
go bi.statusHandler(resultChan)
return resultChan
}
// newBlockImporter returns a new importer for the provided file reader seeker
// and database.
func newBlockImporter(db btcdb.Db, r io.ReadSeeker) *blockImporter {
return &blockImporter{
db: db,
r: r,
processQueue: make(chan []byte, 2),
doneChan: make(chan bool),
errChan: make(chan error),
quit: make(chan bool),
chain: btcchain.New(db, activeNetwork, nil),
}
}

View File

@@ -1,23 +1,20 @@
package main
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"github.com/conformal/btcjson"
"github.com/conformal/btcutil"
"github.com/conformal/go-flags"
"github.com/davecgh/go-spew/spew"
"io/ioutil"
"os"
"sort"
"strconv"
)
type config struct {
Help bool `short:"h" long:"help" description:"Help"`
RpcUser string `short:"u" description:"RPC username"`
RpcPassword string `short:"P" long:"rpcpass" description:"RPC password"`
RpcServer string `short:"s" long:"rpcserver" description:"RPC server to connect to"`
}
// conversionHandler is a handler that is used to convert parameters from the
// command line to a specific type. This is needed since the btcjson API
// expects certain types for various parameters.
@@ -34,9 +31,11 @@ type handlerData struct {
optionalArgs int
displayHandler displayHandler
conversionHandlers []conversionHandler
makeCmd func([]interface{}) (btcjson.Cmd, error)
usage string
}
// Errors used in the various handlers.
var (
ErrNoData = errors.New("No data returned.")
ErrNoDisplayHandler = errors.New("No display handler specified.")
@@ -46,19 +45,78 @@ var (
// commandHandlers is a map of commands and associated handler data that is used
// to validate correctness and perform the command.
var commandHandlers = map[string]*handlerData{
"addnode": &handlerData{2, 0, displaySpewDump, nil, "<ip> <add/remove/onetry>"},
"decoderawtransaction": &handlerData{1, 0, displaySpewDump, nil, "<txhash>"},
"getbestblockhash": &handlerData{0, 0, displayGeneric, nil, ""},
"getblock": &handlerData{1, 0, displaySpewDump, nil, "<blockhash>"},
"getblockcount": &handlerData{0, 0, displayFloat64, nil, ""},
"getblockhash": &handlerData{1, 0, displayGeneric, []conversionHandler{toInt}, "<blocknumber>"},
"getconnectioncount": &handlerData{0, 0, displayFloat64, nil, ""},
"getdifficulty": &handlerData{0, 0, displayFloat64, nil, ""},
"getgenerate": &handlerData{0, 0, displayGeneric, nil, ""},
"getpeerinfo": &handlerData{0, 0, displaySpewDump, nil, ""},
"getrawmempool": &handlerData{0, 0, displaySpewDump, nil, ""},
"getrawtransaction": &handlerData{1, 1, displaySpewDump, []conversionHandler{nil, toInt}, "<txhash> [verbose=0]"},
"stop": &handlerData{0, 0, displayGeneric, nil, ""},
"addnode": {2, 0, displayJSONDump, nil, makeAddNode, "<ip> <add/remove/onetry>"},
"createrawtransaction": {2, 0, displayGeneric, nil, makeCreateRawTransaction, "\"[{\"txid\":\"id\",\"vout\":n},...]\" \"{\"address\":amount,...}\""},
"debuglevel": {1, 0, displayGeneric, nil, makeDebugLevel, "<levelspec>"},
"decoderawtransaction": {1, 0, displayJSONDump, nil, makeDecodeRawTransaction, "<txhash>"},
"decodescript": {1, 0, displayJSONDump, nil, makeDecodeScript, "<hex>"},
"dumpprivkey": {1, 0, displayGeneric, nil, makeDumpPrivKey, "<bitcoinaddress>"},
"getaccount": {1, 0, displayGeneric, nil, makeGetAccount, "<address>"},
"getaccountaddress": {1, 0, displayGeneric, nil, makeGetAccountAddress, "<account>"},
"getaddednodeinfo": {1, 1, displayJSONDump, []conversionHandler{toBool, nil}, makeGetAddedNodeInfo, "<dns> [node]"},
"getaddressesbyaccount": {1, 0, displayJSONDump, nil, makeGetAddressesByAccount, "[account]"},
"getbalance": {0, 2, displayGeneric, []conversionHandler{nil, toInt}, makeGetBalance, "[account] [minconf=1]"},
"getbestblockhash": {0, 0, displayGeneric, nil, makeGetBestBlockHash, ""},
"getblock": {1, 2, displayJSONDump, []conversionHandler{nil, toBool, toBool}, makeGetBlock, "<blockhash>"},
"getblockcount": {0, 0, displayGeneric, nil, makeGetBlockCount, ""},
"getblockhash": {1, 0, displayGeneric, []conversionHandler{toInt64}, makeGetBlockHash, "<blocknumber>"},
"getblocktemplate": {0, 1, displayJSONDump, nil, makeGetBlockTemplate, "[jsonrequestobject]"},
"getconnectioncount": {0, 0, displayGeneric, nil, makeGetConnectionCount, ""},
"getdifficulty": {0, 0, displayFloat64, nil, makeGetDifficulty, ""},
"getgenerate": {0, 0, displayGeneric, nil, makeGetGenerate, ""},
"gethashespersec": {0, 0, displayGeneric, nil, makeGetHashesPerSec, ""},
"getinfo": {0, 0, displayJSONDump, nil, makeGetInfo, ""},
"getnetworkhashps": {0, 2, displayGeneric, []conversionHandler{toInt, toInt}, makeGetNetworkHashPS, "[blocks height]"},
"getnettotals": {0, 0, displayJSONDump, nil, makeGetNetTotals, ""},
"getnewaddress": {0, 1, displayGeneric, nil, makeGetNewAddress, "[account]"},
"getpeerinfo": {0, 0, displayJSONDump, nil, makeGetPeerInfo, ""},
"getrawchangeaddress": {0, 0, displayGeneric, nil, makeGetRawChangeAddress, ""},
"getrawmempool": {0, 1, displayJSONDump, []conversionHandler{toBool}, makeGetRawMempool, "[verbose=false]"},
"getrawtransaction": {1, 1, displayJSONDump, []conversionHandler{nil, toInt}, makeGetRawTransaction, "<txhash> [verbose=0]"},
"getreceivedbyaccount": {1, 1, displayGeneric, []conversionHandler{nil, toInt}, makeGetReceivedByAccount, "<account> [minconf=1]"},
"getreceivedbyaddress": {1, 1, displayGeneric, []conversionHandler{nil, toInt}, makeGetReceivedByAddress, "<address> [minconf=1]"},
"gettransaction": {1, 1, displayJSONDump, nil, makeGetTransaction, "txid"},
"gettxoutsetinfo": {0, 0, displayJSONDump, nil, makeGetTxOutSetInfo, ""},
"getwork": {0, 1, displayJSONDump, nil, makeGetWork, "[data]"},
"help": {0, 1, displayGeneric, nil, makeHelp, "[commandName]"},
"importprivkey": {1, 2, displayGeneric, []conversionHandler{nil, nil, toBool}, makeImportPrivKey, "<wifprivkey> [label] [rescan=true]"},
"keypoolrefill": {0, 1, displayGeneric, []conversionHandler{toInt}, makeKeyPoolRefill, "[newsize]"},
"listaccounts": {0, 1, displayJSONDump, []conversionHandler{toInt}, makeListAccounts, "[minconf=1]"},
"listaddressgroupings": {0, 0, displayJSONDump, nil, makeListAddressGroupings, ""},
"listreceivedbyaccount": {0, 2, displayJSONDump, []conversionHandler{toInt, toBool}, makeListReceivedByAccount, "[minconf] [includeempty]"},
"listreceivedbyaddress": {0, 2, displayJSONDump, []conversionHandler{toInt, toBool}, makeListReceivedByAddress, "[minconf] [includeempty]"},
"listlockunspent": {0, 0, displayJSONDump, nil, makeListLockUnspent, ""},
"listsinceblock": {0, 2, displayJSONDump, []conversionHandler{nil, toInt}, makeListSinceBlock, "[blockhash] [minconf=10]"},
"listtransactions": {0, 3, displayJSONDump, []conversionHandler{nil, toInt, toInt}, makeListTransactions, "[account] [count=10] [from=0]"},
"listunspent": {0, 3, displayJSONDump, []conversionHandler{toInt, toInt, nil}, makeListUnspent, "[minconf=1] [maxconf=9999999] [jsonaddressarray]"},
"ping": {0, 0, displayGeneric, nil, makePing, ""},
"sendfrom": {3, 3, displayGeneric, []conversionHandler{nil, nil, toSatoshi, toInt, nil, nil},
makeSendFrom, "<account> <address> <amount> [minconf=1] [comment] [comment-to]"},
"sendmany": {2, 2, displayGeneric, []conversionHandler{nil, nil, toInt, nil}, makeSendMany, "<account> <{\"address\":amount,...}> [minconf=1] [comment]"},
"sendrawtransaction": {1, 0, displayGeneric, nil, makeSendRawTransaction, "<hextx>"},
"sendtoaddress": {2, 2, displayGeneric, []conversionHandler{nil, toSatoshi, nil, nil}, makeSendToAddress, "<address> <amount> [comment] [comment-to]"},
"settxfee": {1, 0, displayGeneric, []conversionHandler{toSatoshi}, makeSetTxFee, "<amount>"},
"signmessage": {2, 2, displayGeneric, nil, makeSignMessage, "<address> <message>"},
"stop": {0, 0, displayGeneric, nil, makeStop, ""},
"submitblock": {1, 1, displayGeneric, nil, makeSubmitBlock, "<hexdata> [jsonparametersobject]"},
"validateaddress": {1, 0, displayJSONDump, nil, makeValidateAddress, "<address>"},
"verifychain": {0, 2, displayJSONDump, []conversionHandler{toInt, toInt}, makeVerifyChain, "[level] [numblocks]"},
"verifymessage": {3, 0, displayGeneric, nil, makeVerifyMessage, "<address> <signature> <message>"},
"walletlock": {0, 0, displayGeneric, nil, makeWalletLock, ""},
"walletpassphrase": {1, 1, displayGeneric, []conversionHandler{nil, toInt64}, makeWalletPassphrase, "<passphrase> [timeout]"},
"walletpassphrasechange": {2, 0, displayGeneric, nil, makeWalletPassphraseChange, "<oldpassphrase> <newpassphrase>"},
}
// toSatoshi attempts to convert the passed string to a satoshi amount returned
// as an int64. It returns the int64 packed into an interface so it can be used
// in the calls which expect interfaces. An error will be returned if the string
// can't be converted first to a float64.
func toSatoshi(val string) (interface{}, error) {
idx, err := strconv.ParseFloat(val, 64)
if err != nil {
return nil, err
}
return int64(float64(btcutil.SatoshiPerBitcoin) * idx), nil
}
// toInt attempts to convert the passed string to an integer. It returns the
@@ -74,6 +132,27 @@ func toInt(val string) (interface{}, error) {
return idx, nil
}
// toInt64 attempts to convert the passed string to an int64. It returns the
// integer packed into an interface so it can be used in the calls which expect
// interfaces. An error will be returned if the string can't be converted to an
// integer.
func toInt64(val string) (interface{}, error) {
idx, err := strconv.ParseInt(val, 10, 64)
if err != nil {
return nil, err
}
return idx, nil
}
// toBool attempts to convert the passed string to a bool. It returns the
// bool packed into the empty interface so it can be used in the calls which
// accept interfaces. An error will be returned if the string can't be
// converted to a bool.
func toBool(val string) (interface{}, error) {
return strconv.ParseBool(val)
}
// displayGeneric is a displayHandler that simply displays the passed interface
// using fmt.Println.
func displayGeneric(reply interface{}) error {
@@ -82,11 +161,11 @@ func displayGeneric(reply interface{}) error {
}
// displayFloat64 is a displayHandler that ensures the concrete type of the
// passed interface is a float64 and displays it using fmt.Println. An error
// passed interface is a float64 and displays it using fmt.Printf. An error
// is returned if a float64 is not passed.
func displayFloat64(reply interface{}) error {
if val, ok := reply.(float64); ok {
fmt.Println(val)
fmt.Printf("%f\n", val)
return nil
}
@@ -100,11 +179,581 @@ func displaySpewDump(reply interface{}) error {
return nil
}
// displayJSONDump is a displayHandler that uses json.Indent to display the
// passed interface.
func displayJSONDump(reply interface{}) error {
marshaledBytes, err := json.Marshal(reply)
if err != nil {
return err
}
var buf bytes.Buffer
err = json.Indent(&buf, marshaledBytes, "", "\t")
if err != nil {
return err
}
fmt.Println(buf.String())
return nil
}
// makeAddNode generates the cmd structure for addnode commands.
func makeAddNode(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewAddNodeCmd("btcctl", args[0].(string),
args[1].(string))
}
// makeCreateRawTransaction generates the cmd structure for createrawtransaction
// commands.
func makeCreateRawTransaction(args []interface{}) (btcjson.Cmd, error) {
// First unmarshal the JSON provided by the parameters into interfaces.
var iinputs, iamounts interface{}
err := json.Unmarshal([]byte(args[0].(string)), &iinputs)
if err != nil {
return nil, err
}
err = json.Unmarshal([]byte(args[1].(string)), &iamounts)
if err != nil {
return nil, err
}
// Validate and convert the interfaces to concrete types.
inputs, amounts, err := btcjson.ConvertCreateRawTxParams(iinputs,
iamounts)
if err != nil {
return nil, err
}
return btcjson.NewCreateRawTransactionCmd("btcctl", inputs, amounts)
}
// makeDebugLevel generates the cmd structure for debuglevel commands.
func makeDebugLevel(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewDebugLevelCmd("btcctl", args[0].(string))
}
// makeDecodeRawTransaction generates the cmd structure for
// decoderawtransaction commands.
func makeDecodeRawTransaction(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewDecodeRawTransactionCmd("btcctl", args[0].(string))
}
// makeDecodeScript generates the cmd structure for decodescript commands.
func makeDecodeScript(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewDecodeScriptCmd("btcctl", args[0].(string))
}
// makeDumpPrivKey generates the cmd structure for
// dumpprivkey commands.
func makeDumpPrivKey(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewDumpPrivKeyCmd("btcctl", args[0].(string))
}
// makeGetAccount generates the cmd structure for
// getaccount commands.
func makeGetAccount(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewGetAccountCmd("btcctl", args[0].(string))
}
// makeGetAccountAddress generates the cmd structure for
// getaccountaddress commands.
func makeGetAccountAddress(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewGetAccountAddressCmd("btcctl", args[0].(string))
}
// makeGetAddedNodeInfo generates the cmd structure for
// getaccountaddress commands.
func makeGetAddedNodeInfo(args []interface{}) (btcjson.Cmd, error) {
// Create the getaddednodeinfo command with defaults for the optional
// parameters.
cmd, err := btcjson.NewGetAddedNodeInfoCmd("btcctl", args[0].(bool))
if err != nil {
return nil, err
}
// Override the optional parameter if it was specified.
if len(args) > 1 {
cmd.Node = args[1].(string)
}
return cmd, nil
}
// makeGetAddressesByAccount generates the cmd structure for
// getaddressesbyaccount commands.
func makeGetAddressesByAccount(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewGetAddressesByAccountCmd("btcctl", args[0].(string))
}
// makeGetBalance generates the cmd structure for
// getbalance commands.
func makeGetBalance(args []interface{}) (btcjson.Cmd, error) {
optargs := make([]interface{}, 0, 2)
if len(args) > 0 {
optargs = append(optargs, args[0].(string))
}
if len(args) > 1 {
optargs = append(optargs, args[1].(int))
}
return btcjson.NewGetBalanceCmd("btcctl", optargs...)
}
// makeGetBestBlockHash generates the cmd structure for
// makebestblockhash commands.
func makeGetBestBlockHash(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewGetBestBlockHashCmd("btcctl")
}
// makeGetBlock generates the cmd structure for getblock commands.
func makeGetBlock(args []interface{}) (btcjson.Cmd, error) {
// Create the getblock command with defaults for the optional
// parameters.
getBlockCmd, err := btcjson.NewGetBlockCmd("btcctl", args[0].(string))
if err != nil {
return nil, err
}
// Override the optional parameters if they were specified.
if len(args) > 1 {
getBlockCmd.Verbose = args[1].(bool)
}
if len(args) > 2 {
getBlockCmd.VerboseTx = args[2].(bool)
}
return getBlockCmd, nil
}
// makeGetBlockCount generates the cmd structure for getblockcount commands.
func makeGetBlockCount(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewGetBlockCountCmd("btcctl")
}
// makeGetBlockHash generates the cmd structure for getblockhash commands.
func makeGetBlockHash(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewGetBlockHashCmd("btcctl", args[0].(int64))
}
// makeGetBlockTemplate generates the cmd structure for getblocktemplate commands.
func makeGetBlockTemplate(args []interface{}) (btcjson.Cmd, error) {
cmd, err := btcjson.NewGetBlockTemplateCmd("btcctl")
if err != nil {
return nil, err
}
if len(args) == 1 {
err = cmd.UnmarshalJSON([]byte(args[0].(string)))
if err != nil {
return nil, err
}
}
return cmd, nil
}
// makeGetConnectionCount generates the cmd structure for
// getconnectioncount commands.
func makeGetConnectionCount(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewGetConnectionCountCmd("btcctl")
}
// makeGetDifficulty generates the cmd structure for
// getdifficulty commands.
func makeGetDifficulty(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewGetDifficultyCmd("btcctl")
}
// makeGetGenerate generates the cmd structure for
// getgenerate commands.
func makeGetGenerate(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewGetGenerateCmd("btcctl")
}
// makeGetHashesPerSec generates the cmd structure for gethashespersec commands.
func makeGetHashesPerSec(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewGetHashesPerSecCmd("btcctl")
}
// makeGetInfo generates the cmd structure for getinfo commands.
func makeGetInfo(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewGetInfoCmd("btcctl")
}
// makeGetNetworkHashPS generates the cmd structure for getnetworkhashps
// commands.
func makeGetNetworkHashPS(args []interface{}) (btcjson.Cmd, error) {
// Create the getnetworkhashps command with defaults for the optional
// parameters.
cmd, err := btcjson.NewGetNetworkHashPSCmd("btcctl")
if err != nil {
return nil, err
}
// Override the optional blocks if specified.
if len(args) > 0 {
cmd.Blocks = args[0].(int)
}
// Override the optional height if specified.
if len(args) > 1 {
cmd.Height = args[1].(int)
}
return cmd, nil
}
// makeGetNetTotals generates the cmd structure for getnettotals commands.
func makeGetNetTotals(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewGetNetTotalsCmd("btcctl")
}
// makeGetNewAddress generates the cmd structure for getnewaddress commands.
func makeGetNewAddress(args []interface{}) (btcjson.Cmd, error) {
var account string
if len(args) > 0 {
account = args[0].(string)
}
return btcjson.NewGetNewAddressCmd("btcctl", account)
}
// makePeerInfo generates the cmd structure for
// getpeerinfo commands.
func makeGetPeerInfo(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewGetPeerInfoCmd("btcctl")
}
// makeGetRawChangeAddress generates the cmd structure for getrawchangeaddress commands.
func makeGetRawChangeAddress(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewGetRawChangeAddressCmd("btcctl")
}
// makeRawMempool generates the cmd structure for
// getrawmempool commands.
func makeGetRawMempool(args []interface{}) (btcjson.Cmd, error) {
opt := make([]bool, 0, 1)
if len(args) > 0 {
opt = append(opt, args[0].(bool))
}
return btcjson.NewGetRawMempoolCmd("btcctl", opt...)
}
// makeGetReceivedByAccount generates the cmd structure for
// getreceivedbyaccount commands.
func makeGetReceivedByAccount(args []interface{}) (btcjson.Cmd, error) {
opt := make([]int, 0, 1)
if len(args) > 1 {
opt = append(opt, args[1].(int))
}
return btcjson.NewGetReceivedByAccountCmd("btcctl", args[0].(string), opt...)
}
// makeGetReceivedByAddress generates the cmd structure for
// getreceivedbyaddress commands.
func makeGetReceivedByAddress(args []interface{}) (btcjson.Cmd, error) {
opt := make([]int, 0, 1)
if len(args) > 1 {
opt = append(opt, args[1].(int))
}
return btcjson.NewGetReceivedByAddressCmd("btcctl", args[0].(string), opt...)
}
// makeGetTransaction generates the cmd structure for gettransaction commands.
func makeGetTransaction(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewGetTransactionCmd("btcctl", args[0].(string))
}
// makeGetTxOutSetInfo generates the cmd structure for gettxoutsetinfo commands.
func makeGetTxOutSetInfo(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewGetTxOutSetInfoCmd("btcctl")
}
func makeGetWork(args []interface{}) (btcjson.Cmd, error) {
cmd, err := btcjson.NewGetWorkCmd("btcctl")
if err != nil {
return nil, err
}
if len(args) == 1 {
cmd.Data = args[0].(string)
}
return cmd, nil
}
func makeHelp(args []interface{}) (btcjson.Cmd, error) {
opt := make([]string, 0, 1)
if len(args) > 0 {
opt = append(opt, args[0].(string))
}
return btcjson.NewHelpCmd("btcctl", opt...)
}
// makeRawTransaction generates the cmd structure for
// getrawtransaction commands.
func makeGetRawTransaction(args []interface{}) (btcjson.Cmd, error) {
opt := make([]int, 0, 1)
if len(args) > 1 {
opt = append(opt, args[1].(int))
}
return btcjson.NewGetRawTransactionCmd("btcctl", args[0].(string), opt...)
}
// makeImportPrivKey generates the cmd structure for
// importprivkey commands.
func makeImportPrivKey(args []interface{}) (btcjson.Cmd, error) {
var optargs = make([]interface{}, 0, 2)
if len(args) > 1 {
optargs = append(optargs, args[1].(string))
}
if len(args) > 2 {
optargs = append(optargs, args[2].(bool))
}
return btcjson.NewImportPrivKeyCmd("btcctl", args[0].(string), optargs...)
}
// makeKeyPoolRefill generates the cmd structure for keypoolrefill commands.
func makeKeyPoolRefill(args []interface{}) (btcjson.Cmd, error) {
var optargs = make([]uint, 0, 1)
if len(args) > 0 {
optargs = append(optargs, uint(args[0].(int)))
}
return btcjson.NewKeyPoolRefillCmd("btcctl", optargs...)
}
// makeListAccounts generates the cmd structure for listaccounts commands.
func makeListAccounts(args []interface{}) (btcjson.Cmd, error) {
var optargs = make([]int, 0, 1)
if len(args) > 0 {
optargs = append(optargs, args[0].(int))
}
return btcjson.NewListAccountsCmd("btcctl", optargs...)
}
// makeListAddressGroupings generates the cmd structure for listaddressgroupings commands.
func makeListAddressGroupings(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewListAddressGroupingsCmd("btcctl")
}
// makeListReceivedByAccount generates the cmd structure for listreceivedbyaccount commands.
func makeListReceivedByAccount(args []interface{}) (btcjson.Cmd, error) {
var optargs = make([]interface{}, 0, 2)
if len(args) > 0 {
optargs = append(optargs, args[0].(int))
}
if len(args) > 1 {
optargs = append(optargs, args[1].(bool))
}
return btcjson.NewListReceivedByAccountCmd("btcctl", optargs...)
}
// makeListReceivedByAddress generates the cmd structure for listreceivedbyaddress commands.
func makeListReceivedByAddress(args []interface{}) (btcjson.Cmd, error) {
var optargs = make([]interface{}, 0, 2)
if len(args) > 0 {
optargs = append(optargs, args[0].(int))
}
if len(args) > 1 {
optargs = append(optargs, args[1].(bool))
}
return btcjson.NewListReceivedByAddressCmd("btcctl", optargs...)
}
// makeListLockUnspent generates the cmd structure for listlockunspent commands.
func makeListLockUnspent(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewListLockUnspentCmd("btcctl")
}
// makeListSinceBlock generates the cmd structure for
// listsinceblock commands.
func makeListSinceBlock(args []interface{}) (btcjson.Cmd, error) {
var optargs = make([]interface{}, 0, 2)
if len(args) > 0 {
optargs = append(optargs, args[0].(string))
}
if len(args) > 1 {
optargs = append(optargs, args[1].(int))
}
return btcjson.NewListSinceBlockCmd("btcctl", optargs...)
}
// makeListTransactions generates the cmd structure for
// listtransactions commands.
func makeListTransactions(args []interface{}) (btcjson.Cmd, error) {
var optargs = make([]interface{}, 0, 3)
if len(args) > 0 {
optargs = append(optargs, args[0].(string))
}
if len(args) > 1 {
optargs = append(optargs, args[1].(int))
}
if len(args) > 2 {
optargs = append(optargs, args[2].(int))
}
return btcjson.NewListTransactionsCmd("btcctl", optargs...)
}
// makeListUnspent generates the cmd structure for listunspent commands.
func makeListUnspent(args []interface{}) (btcjson.Cmd, error) {
var optargs = make([]interface{}, 0, 3)
if len(args) > 0 {
optargs = append(optargs, args[0].(int))
}
if len(args) > 1 {
optargs = append(optargs, args[1].(int))
}
if len(args) > 2 {
var addrs []string
err := json.Unmarshal([]byte(args[2].(string)), &addrs)
if err != nil {
return nil, err
}
optargs = append(optargs, addrs)
}
return btcjson.NewListUnspentCmd("btcctl", optargs...)
}
// makePing generates the cmd structure for ping commands.
func makePing(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewPingCmd("btcctl")
}
// makeSendFrom generates the cmd structure for sendfrom commands.
func makeSendFrom(args []interface{}) (btcjson.Cmd, error) {
var optargs = make([]interface{}, 0, 3)
if len(args) > 3 {
optargs = append(optargs, args[3].(int))
}
if len(args) > 4 {
optargs = append(optargs, args[4].(string))
}
if len(args) > 5 {
optargs = append(optargs, args[5].(string))
}
return btcjson.NewSendFromCmd("btcctl", args[0].(string),
args[1].(string), args[2].(int64), optargs...)
}
// makeSendMany generates the cmd structure for sendmany commands.
func makeSendMany(args []interface{}) (btcjson.Cmd, error) {
origPairs := make(map[string]float64)
err := json.Unmarshal([]byte(args[1].(string)), &origPairs)
if err != nil {
return nil, err
}
pairs := make(map[string]int64)
for addr, value := range origPairs {
pairs[addr] = int64(float64(btcutil.SatoshiPerBitcoin) * value)
}
var optargs = make([]interface{}, 0, 2)
if len(args) > 2 {
optargs = append(optargs, args[2].(int))
}
if len(args) > 3 {
optargs = append(optargs, args[3].(string))
}
return btcjson.NewSendManyCmd("btcctl", args[0].(string), pairs, optargs...)
}
// makeSendRawTransaction generates the cmd structure for sendrawtransaction
// commands.
func makeSendRawTransaction(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewSendRawTransactionCmd("btcctl", args[0].(string))
}
// makeSendToAddress generates the cmd struture for sendtoaddress commands.
func makeSendToAddress(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewSendToAddressCmd("btcctl", args[0].(string), args[1].(int64), args[2:]...)
}
// makeSetTxFee generates the cmd structure for settxfee commands.
func makeSetTxFee(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewSetTxFeeCmd("btcctl", args[0].(int64))
}
// makeSignMessage generates the cmd structure for signmessage commands.
func makeSignMessage(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewSignMessageCmd("btcctl", args[0].(string),
args[1].(string))
}
// makeStop generates the cmd structure for stop commands.
func makeStop(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewStopCmd("btcctl")
}
// makeSubmitBlock generates the cmd structure for submitblock commands.
func makeSubmitBlock(args []interface{}) (btcjson.Cmd, error) {
opts := &btcjson.SubmitBlockOptions{}
if len(args) == 2 {
opts.WorkId = args[1].(string)
}
return btcjson.NewSubmitBlockCmd("btcctl", args[0].(string), opts)
}
// makeValidateAddress generates the cmd structure for validateaddress commands.
func makeValidateAddress(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewValidateAddressCmd("btcctl", args[0].(string))
}
// makeVerifyChain generates the cmd structure for verifychain commands.
func makeVerifyChain(args []interface{}) (btcjson.Cmd, error) {
iargs := make([]int32, 0, 2)
for _, i := range args {
iargs = append(iargs, int32(i.(int)))
}
return btcjson.NewVerifyChainCmd("btcctl", iargs...)
}
func makeVerifyMessage(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewVerifyMessageCmd("btcctl", args[0].(string),
args[1].(string), args[2].(string))
}
// makeWalletLock generates the cmd structure for walletlock commands.
func makeWalletLock(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewWalletLockCmd("btcctl")
}
// makeWalletPassphrase generates the cmd structure for walletpassphrase commands.
func makeWalletPassphrase(args []interface{}) (btcjson.Cmd, error) {
timeout := int64(60)
if len(args) > 1 {
timeout = args[1].(int64)
}
return btcjson.NewWalletPassphraseCmd("btcctl", args[0].(string), timeout)
}
// makeWalletPassphraseChange generates the cmd structure for
// walletpassphrasechange commands.
func makeWalletPassphraseChange(args []interface{}) (btcjson.Cmd, error) {
return btcjson.NewWalletPassphraseChangeCmd("btcctl", args[0].(string),
args[1].(string))
}
// send sends a JSON-RPC command to the specified RPC server and examines the
// results for various error conditions. It either returns a valid result or
// an appropriate error.
func send(cfg *config, msg []byte) (interface{}, error) {
reply, err := btcjson.RpcCommand(cfg.RpcUser, cfg.RpcPassword, cfg.RpcServer, msg)
var reply btcjson.Reply
var err error
if cfg.NoTls || (cfg.RPCCert == "" && !cfg.TlsSkipVerify) {
reply, err = btcjson.RpcCommand(cfg.RPCUser, cfg.RPCPassword,
cfg.RPCServer, msg)
} else {
var pem []byte
if cfg.RPCCert != "" {
pem, err = ioutil.ReadFile(cfg.RPCCert)
if err != nil {
return nil, err
}
}
reply, err = btcjson.TlsRpcCommand(cfg.RPCUser,
cfg.RPCPassword, cfg.RPCServer, msg, pem,
cfg.TlsSkipVerify)
}
if err != nil {
return nil, err
}
@@ -123,8 +772,8 @@ func send(cfg *config, msg []byte) (interface{}, error) {
// sendCommand creates a JSON-RPC command using the passed command and arguments
// and then sends it. A prefix is added to any errors that occur indicating
// what step failed.
func sendCommand(cfg *config, command string, args ...interface{}) (interface{}, error) {
msg, err := btcjson.CreateMessage(command, args...)
func sendCommand(cfg *config, command btcjson.Cmd) (interface{}, error) {
msg, err := json.Marshal(command)
if err != nil {
return nil, fmt.Errorf("CreateMessage: %v", err.Error())
}
@@ -177,9 +826,13 @@ func commandHandler(cfg *config, command string, data *handlerData, args []strin
}
}
}
cmd, err := data.makeCmd(iargs)
if err != nil {
return err
}
// Create and send the appropriate JSON-RPC command.
reply, err := sendCommand(cfg, command, iargs...)
reply, err := sendCommand(cfg, cmd)
if err != nil {
return err
}
@@ -216,19 +869,12 @@ func usage(parser *flags.Parser) {
}
func main() {
// Parse command line and show usage if needed.
cfg := config{
RpcServer: "127.0.0.1:8334",
}
parser := flags.NewParser(&cfg, flags.PassDoubleDash)
args, err := parser.Parse()
parser, cfg, args, err := loadConfig()
if err != nil {
if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp {
usage(parser)
}
usage(parser)
os.Exit(1)
}
if len(args) < 1 || cfg.Help {
if len(args) < 1 {
usage(parser)
return
}
@@ -242,7 +888,7 @@ func main() {
}
// Execute the command.
err = commandHandler(&cfg, args[0], data, args[1:])
err = commandHandler(cfg, args[0], data, args[1:])
if err != nil {
if err == ErrUsage {
usage(parser)

92
util/btcctl/config.go Normal file
View File

@@ -0,0 +1,92 @@
package main
import (
"fmt"
"github.com/conformal/btcutil"
"github.com/conformal/go-flags"
"os"
"path/filepath"
"strings"
)
var (
btcdHomeDir = btcutil.AppDataDir("btcd", false)
btcctlHomeDir = btcutil.AppDataDir("btcctl", false)
defaultConfigFile = filepath.Join(btcctlHomeDir, "btcctl.conf")
defaultRPCCertFile = filepath.Join(btcdHomeDir, "rpc.cert")
)
// config defines the configuration options for btcctl.
//
// See loadConfig for details on the configuration load process.
type config struct {
ShowVersion bool `short:"V" long:"version" description:"Display version information and exit"`
ConfigFile string `short:"C" long:"configfile" description:"Path to configuration file"`
RPCUser string `short:"u" long:"rpcuser" description:"RPC username"`
RPCPassword string `short:"P" long:"rpcpass" default-mask:"-" description:"RPC password"`
RPCServer string `short:"s" long:"rpcserver" description:"RPC server to connect to"`
RPCCert string `short:"c" long:"rpccert" description:"RPC server certificate chain for validation"`
NoTls bool `long:"notls" description:"Disable TLS"`
TlsSkipVerify bool `long:"skipverify" description:"Do not verify tls certificates (not recommended!)"`
}
// loadConfig initializes and parses the config using a config file and command
// line options.
//
// The configuration proceeds as follows:
// 1) Start with a default config with sane settings
// 2) Pre-parse the command line to check for an alternative config file
// 3) Load configuration file overwriting defaults with any specified options
// 4) Parse CLI options and overwrite/add any specified options
//
// The above results in functioning properly without any config settings
// while still allowing the user to override settings with config files and
// command line options. Command line options always take precedence.
func loadConfig() (*flags.Parser, *config, []string, error) {
// Default config.
cfg := config{
ConfigFile: defaultConfigFile,
RPCServer: "localhost:8334",
RPCCert: defaultRPCCertFile,
}
// Create the home directory if it doesn't already exist.
err := os.MkdirAll(btcdHomeDir, 0700)
if err != nil {
fmt.Fprintf(os.Stderr, "%v\n", err)
os.Exit(-1)
}
// Pre-parse the command line options to see if an alternative config
// file or the version flag was specified. Any errors can be ignored
// here since they will be caught be the final parse below.
preCfg := cfg
preParser := flags.NewParser(&preCfg, flags.None)
preParser.Parse()
// Show the version and exit if the version flag was specified.
if preCfg.ShowVersion {
appName := filepath.Base(os.Args[0])
appName = strings.TrimSuffix(appName, filepath.Ext(appName))
fmt.Println(appName, "version", version())
os.Exit(0)
}
// Load additional config from file.
parser := flags.NewParser(&cfg, flags.PassDoubleDash|flags.HelpFlag)
err = flags.NewIniParser(parser).ParseFile(preCfg.ConfigFile)
if err != nil {
if _, ok := err.(*os.PathError); !ok {
fmt.Fprintln(os.Stderr, err)
return parser, nil, nil, err
}
}
// Parse command line options again to ensure they take precedence.
remainingArgs, err := parser.Parse()
if err != nil {
return parser, nil, nil, err
}
return parser, &cfg, remainingArgs, nil
}

72
util/btcctl/version.go Normal file
View File

@@ -0,0 +1,72 @@
// Copyright (c) 2013 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package main
import (
"bytes"
"fmt"
"strings"
)
// semanticAlphabet
const semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-"
// These constants define the application version and follow the semantic
// versioning 2.0.0 spec (http://semver.org/).
const (
appMajor uint = 0
appMinor uint = 7
appPatch uint = 0
// appPreRelease MUST only contain characters from semanticAlphabet
// per the semantic versioning spec.
appPreRelease = "alpha"
)
// appBuild is defined as a variable so it can be overridden during the build
// process with '-ldflags "-X main.appBuild foo' if needed. It MUST only
// contain characters from semanticAlphabet per the semantic versioning spec.
var appBuild string
// version returns the application version as a properly formed string per the
// semantic versioning 2.0.0 spec (http://semver.org/).
func version() string {
// Start with the major, minor, and patch versions.
version := fmt.Sprintf("%d.%d.%d", appMajor, appMinor, appPatch)
// Append pre-release version if there is one. The hyphen called for
// by the semantic versioning spec is automatically appended and should
// not be contained in the pre-release string. The pre-release version
// is not appended if it contains invalid characters.
preRelease := normalizeVerString(appPreRelease)
if preRelease != "" {
version = fmt.Sprintf("%s-%s", version, preRelease)
}
// Append build metadata if there is any. The plus called for
// by the semantic versioning spec is automatically appended and should
// not be contained in the build metadata string. The build metadata
// string is not appended if it contains invalid characters.
build := normalizeVerString(appBuild)
if build != "" {
version = fmt.Sprintf("%s+%s", version, build)
}
return version
}
// normalizeVerString returns the passed string stripped of all characters which
// are not valid according to the semantic versioning guidelines for pre-release
// version and build metadata strings. In particular they MUST only contain
// characters in semanticAlphabet.
func normalizeVerString(str string) string {
var result bytes.Buffer
for _, r := range str {
if strings.ContainsRune(semanticAlphabet, r) {
result.WriteRune(r)
}
}
return result.String()
}

View File

@@ -9,9 +9,10 @@ import (
"fmt"
"github.com/conformal/btcdb"
_ "github.com/conformal/btcdb/ldb"
"github.com/conformal/btclog"
"github.com/conformal/btcutil"
"github.com/conformal/btcwire"
"github.com/conformal/go-flags"
"github.com/conformal/seelog"
"os"
"path/filepath"
"strconv"
@@ -26,7 +27,11 @@ type config struct {
ShaString string `short:"s" description:"Block SHA to process" required:"true"`
}
var log seelog.LoggerInterface
var (
btcdHomeDir = btcutil.AppDataDir("btcd", false)
defaultDataDir = filepath.Join(btcdHomeDir, "data")
log btclog.Logger
)
const (
ArgSha = iota
@@ -36,7 +41,7 @@ const (
func main() {
cfg := config{
DbType: "leveldb",
DataDir: filepath.Join(btcdHomeDir(), "data"),
DataDir: defaultDataDir,
}
parser := flags.NewParser(&cfg, flags.Default)
_, err := parser.Parse()
@@ -47,13 +52,9 @@ func main() {
return
}
log, err = seelog.LoggerFromWriterWithMinLevel(os.Stdout,
seelog.TraceLvl)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to create logger: %v", err)
return
}
defer log.Flush()
backendLogger := btclog.NewDefaultBackendLogger()
defer backendLogger.Flush()
log = btclog.NewSubsystemLogger(backendLogger, "")
btcdb.UseLogger(log)
var testnet string
@@ -185,21 +186,3 @@ func parsesha(argstr string) (argtype int, height int64, psha *btcwire.ShaHash,
psha = &sha
return
}
// btcdHomeDir returns an OS appropriate home directory for btcd.
func btcdHomeDir() string {
// Search for Windows APPDATA first. This won't exist on POSIX OSes.
appData := os.Getenv("APPDATA")
if appData != "" {
return filepath.Join(appData, "btcd")
}
// Fall back to standard HOME directory that works for most POSIX OSes.
home := os.Getenv("HOME")
if home != "" {
return filepath.Join(home, ".btcd")
}
// In the worst case, use the current directory.
return "."
}

View File

@@ -8,7 +8,7 @@ import (
"fmt"
"github.com/conformal/btcdb"
_ "github.com/conformal/btcdb/ldb"
_ "github.com/conformal/btcdb/sqlite3"
"github.com/conformal/btcutil"
"github.com/conformal/btcwire"
"github.com/conformal/go-flags"
"os"
@@ -23,7 +23,8 @@ const (
)
var (
defaultDataDir = filepath.Join(btcdHomeDir(), "data")
btcdHomeDir = btcutil.AppDataDir("btcd", false)
defaultDataDir = filepath.Join(btcdHomeDir, "data")
knownDbTypes = btcdb.SupportedDBs()
activeNetwork = btcwire.MainNet
)
@@ -39,24 +40,6 @@ type config struct {
UseGoOutput bool `short:"g" long:"gooutput" description:"Display the candidates using Go syntax that is ready to insert into the btcchain checkpoint list"`
}
// btcdHomeDir returns an OS appropriate home directory for btcd.
func btcdHomeDir() string {
// Search for Windows APPDATA first. This won't exist on POSIX OSes.
appData := os.Getenv("APPDATA")
if appData != "" {
return filepath.Join(appData, "btcd")
}
// Fall back to standard HOME directory that works for most POSIX OSes.
home := os.Getenv("HOME")
if home != "" {
return filepath.Join(home, ".btcd")
}
// In the worst case, use the current directory.
return "."
}
// validDbType returns whether or not dbType is a supported database type.
func validDbType(dbType string) bool {
for _, knownType := range knownDbTypes {

View File

@@ -9,7 +9,6 @@ import (
"github.com/conformal/btcchain"
"github.com/conformal/btcdb"
_ "github.com/conformal/btcdb/ldb"
_ "github.com/conformal/btcdb/sqlite3"
"github.com/conformal/btcwire"
"os"
"path/filepath"

101
util/gencerts/gencerts.go Normal file
View File

@@ -0,0 +1,101 @@
// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
package main
import (
"fmt"
"github.com/conformal/btcutil"
"github.com/conformal/go-flags"
"io/ioutil"
"os"
"path/filepath"
"strings"
"time"
)
type config struct {
Directory string `short:"d" long:"directory" description:"Directory to write certificate pair"`
Years int `short:"y" long:"years" description:"How many years a certificate is valid for"`
Organization string `short:"o" long:"org" description:"Organization in certificate"`
ExtraHosts []string `short:"H" long:"host" description:"Additional hosts/IPs to create certificate for"`
Force bool `short:"f" long:"force" description:"Force overwriting of any old certs and keys"`
}
func main() {
cfg := config{
Years: 10,
Organization: "gencerts",
}
parser := flags.NewParser(&cfg, flags.Default)
_, err := parser.Parse()
if err != nil {
if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp {
parser.WriteHelp(os.Stderr)
}
return
}
if cfg.Directory == "" {
var err error
cfg.Directory, err = os.Getwd()
if err != nil {
fmt.Fprintf(os.Stderr, "no directory specified and cannot get working directory\n")
os.Exit(1)
}
}
cfg.Directory = cleanAndExpandPath(cfg.Directory)
certFile := filepath.Join(cfg.Directory, "rpc.cert")
keyFile := filepath.Join(cfg.Directory, "rpc.key")
if !cfg.Force {
if fileExists(certFile) || fileExists(keyFile) {
fmt.Fprintf(os.Stderr, "%v: certificate and/or key files exist; use -f to force\n", cfg.Directory)
os.Exit(1)
}
}
validUntil := time.Now().Add(time.Duration(cfg.Years) * 365 * 24 * time.Hour)
cert, key, err := btcutil.NewTLSCertPair(cfg.Organization, validUntil, cfg.ExtraHosts)
if err != nil {
fmt.Fprintf(os.Stderr, "cannot generate certificate pair: %v\n", err)
os.Exit(1)
}
// Write cert and key files.
if err = ioutil.WriteFile(certFile, cert, 0666); err != nil {
fmt.Fprintf(os.Stderr, "cannot write cert: %v\n", err)
os.Exit(1)
}
if err = ioutil.WriteFile(keyFile, key, 0600); err != nil {
os.Remove(certFile)
fmt.Fprintf(os.Stderr, "cannot write key: %v\n", err)
os.Exit(1)
}
}
// cleanAndExpandPath expands environement variables and leading ~ in the
// passed path, cleans the result, and returns it.
func cleanAndExpandPath(path string) string {
// Expand initial ~ to OS specific home directory.
if strings.HasPrefix(path, "~") {
appHomeDir := btcutil.AppDataDir("gencerts", false)
homeDir := filepath.Dir(appHomeDir)
path = strings.Replace(path, "~", homeDir, 1)
}
// NOTE: The os.ExpandEnv doesn't work with Windows-style %VARIABLE%,
// but they variables can still be expanded via POSIX-style $VARIABLE.
return filepath.Clean(os.ExpandEnv(path))
}
// filesExists reports whether the named file or directory exists.
func fileExists(name string) bool {
if _, err := os.Stat(name); err != nil {
if os.IsNotExist(err) {
return false
}
}
return true
}

View File

@@ -7,13 +7,12 @@ package main
import (
"encoding/binary"
"errors"
"fmt"
"github.com/conformal/btcdb"
_ "github.com/conformal/btcdb/ldb"
_ "github.com/conformal/btcdb/sqlite3"
"github.com/conformal/btclog"
"github.com/conformal/btcutil"
"github.com/conformal/btcwire"
"github.com/conformal/go-flags"
"github.com/conformal/seelog"
"github.com/davecgh/go-spew/spew"
"io"
"os"
@@ -36,7 +35,11 @@ type config struct {
ShowTx bool `short:"t" description:"Show transaction"`
}
var log seelog.LoggerInterface
var (
btcdHomeDir = btcutil.AppDataDir("btcd", false)
defaultDataDir = filepath.Join(btcdHomeDir, "data")
log btclog.Logger
)
const (
ArgSha = iota
@@ -48,7 +51,7 @@ func main() {
cfg := config{
DbType: "leveldb",
DataDir: filepath.Join(btcdHomeDir(), "data"),
DataDir: defaultDataDir,
}
parser := flags.NewParser(&cfg, flags.Default)
_, err := parser.Parse()
@@ -59,13 +62,9 @@ func main() {
return
}
log, err = seelog.LoggerFromWriterWithMinLevel(os.Stdout,
seelog.InfoLvl)
if err != nil {
fmt.Fprintf(os.Stderr, "failed to create logger: %v", err)
return
}
defer log.Flush()
backendLogger := btclog.NewDefaultBackendLogger()
defer backendLogger.Flush()
log = btclog.NewSubsystemLogger(backendLogger, "")
btcdb.UseLogger(log)
var testnet string
@@ -270,21 +269,3 @@ func parsesha(argstr string) (argtype int, height int64, psha *btcwire.ShaHash,
psha = &sha
return
}
// btcdHomeDir returns an OS appropriate home directory for btcd.
func btcdHomeDir() string {
// Search for Windows APPDATA first. This won't exist on POSIX OSes.
appData := os.Getenv("APPDATA")
if appData != "" {
return filepath.Join(appData, "btcd")
}
// Fall back to standard HOME directory that works for most POSIX OSes.
home := os.Getenv("HOME")
if home != "" {
return filepath.Join(home, ".btcd")
}
// In the worst case, use the current directory.
return "."
}

View File

@@ -1,4 +1,4 @@
// Copyright (c) 2013 Conformal Systems LLC.
// Copyright (c) 2013-2014 Conformal Systems LLC.
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.
@@ -17,8 +17,8 @@ const semanticAlphabet = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr
// versioning 2.0.0 spec (http://semver.org/).
const (
appMajor uint = 0
appMinor uint = 3
appPatch uint = 2
appMinor uint = 7
appPatch uint = 0
// appPreRelease MUST only contain characters from semanticAlphabet
// per the semantic versioning spec.
@@ -33,7 +33,7 @@ var appBuild string
// version returns the application version as a properly formed string per the
// semantic versioning 2.0.0 spec (http://semver.org/).
func version() string {
// Start with the major, minor, and path versions.
// Start with the major, minor, and patch versions.
version := fmt.Sprintf("%d.%d.%d", appMajor, appMinor, appPatch)
// Append pre-release version if there is one. The hyphen called for