-
Notifications
You must be signed in to change notification settings - Fork 212
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[WIP] Adds support for NetworkAPI
to use a remote Bitcoin node
#76
Conversation
Wow, this is awesome! Will need to look closer later. Thank you! |
This is so cool!!! I'll try to review sometime this week. Can we add docs? |
I have added "docs" to the TODO list :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This LGTM! Do you think it would be worth it/possible to test this in CI?
Yes, I also think we should be able to test it. I will look into this :) |
Finally looked over the code. I agree that CI testing would be good if possible, but I have to say that this looks great as it is. Very nice work! |
Can we merge? |
I have been a bit busy and therefore didn't have the time to add tests yet. But I will look into this in the next two weeks. After that I think we should be ready to merge! |
Fixes precision error due to Bitcoin Core node returning the balance in BTC as float instead of in satoshis as integer.
I have made two testing classes for the To make the tests I have created two mock classes:
The tests for The tests for Due to the import of |
Great! Sure, add it here Line 8 in 0b0e553
|
response = self._host._session.post( | ||
self._host._url, headers=self._host._headers, data=payload | ||
) | ||
except requests.exceptions.ConnectionError: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Question: should we catch more exceptions to avoid it printing/logging the url w/ password inside?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting point, but I think logs are generally not regarded as public anyway. I don't see that being a blocker here. Does requests already block passwords, perhaps?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Requests does not, but no need, you're right
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would be open for suggestions to improve this. I am just not sure how.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks pretty good overall, just a few small comments.
bit/network/services.py
Outdated
def get_balance(self, address): | ||
getcontext().prec = 9 | ||
b = Decimal(self.getreceivedbyaddress(address, 0)) | ||
return int(float(str(b * 100000000))) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Don't we already have constants in place for this?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, you are right. They are inside network/rates.py
. I will change it to make use of them.
bit/network/services.py
Outdated
def broadcast_tx(self, tx_hex): | ||
try: | ||
_ = self.sendrawtransaction(tx_hex) | ||
except BitcoinNodeException: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Would it be worth at least logging the exeption message as a warning? I'm not certain but curious what your thoughts are.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Makes sense. I forgot about it but it is important to find out why a transaction eventually fails to be broadcast. Will add a logging warning!
response = self._host._session.post( | ||
self._host._url, headers=self._host._headers, data=payload | ||
) | ||
except requests.exceptions.ConnectionError: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Interesting point, but I think logs are generally not regarded as public anyway. I don't see that being a blocker here. Does requests already block passwords, perhaps?
if args[2][0] in (MAIN_ADDRESS_UNUSED, TEST_ADDRESS_UNUSED): | ||
return [] | ||
return [{ | ||
"txid": "381f1605dd927151fbfac2e88608464414fa5b01bd6298cd1e2d9d991907aa9e", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
These are fine, but like with my earlier comment about constants, I think we have samples with a lot of these.
I guess if they're used only once there's no reason to add them as samples.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I agree! However, transaction constants are found inside test_transaction.py
, not samples.py
. And I think we should probably generally clean up the sample constants. Over time we may have even introduced redundant constants, etc. Maybe this is something that should be done in a future PR, which cleans up the sample constants?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure, that's fine.
I have added a few more tests, of which especially under |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looks great to me. Nice work!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great job!!!
Thanks! I have just added one little commit. Since we are actually only using the equality method of |
Removes the equal method from `RPCHost` and adds it as a helper function to `test_services.py`. Also improves a little on the readability of the tests `test_connect_to_node_main` and `test_connect_to_node_test`.
d3f0af0
to
bde036a
Compare
Looks great to me! Merge whenever you feel ready. |
The only last thing I think is missing is a helper function that adds an address to the local node. I will try to add this very soon. |
Hello again 👋 |
Can be used to e.g. call `importaddress` on the Bitcoin Core node, etc.
Hey again. I hope everyone had a nice summer holiday. I have actually come to think that the best way to allow adding addresses to the node's wallet is to simply expose direct access the node's RPCs? That is a simple step and I have added documentation of it. Let me know what you think about it this way? |
Uhhh... Looks like Travis CI is breaking because one of our used addresses |
We should probably change the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Excellent! Just left a few fix suggestions for docs formatting
Co-Authored-By: Ofek Lev <ofekmeister@gmail.com>
Just a comment to push notification. I have committed the suggested docs changes! |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Merge away!!!!!
Nice work! I'm going to go ahead and merge this. |
This PR adds a method allowing the
NetworkAPI
to connect to a remote Bitcoin node instead of relying on web APIs. It is marked as a work-in-progress to allow testing, code improvements and test cases to be added.It has been implemented in a way to minimize changes to the codebase and to not break already existing tests. Any suggestions however on how to better connect the new class
bit.network.services.RPCHost
with theNetworkAPI
is welcome! Right now insideNetworkAPI
it simply overrides its web-API values using theRPCHost
instead.This implementation also allows to connect to two different Bitcoin nodes: one for mainnet and one for testnet; and keeps them separate.
Example code
Remarks
Bitcoin Core is not a blockchain explorer and thus only keeps full track of addresses in its wallet! Therefore to use it as a remote node all addresses generated with Bit and that should be tracked must be added as (watch-only) addresses to Bitcoin Core as follows:
See importaddress for more details.
It may be useful to add a helper function that can add generated Bit addresses to the Bitcoin Core wallet.
Bitcoin Core RPC behaves particular with regard to coinbase transactions
Coinbase transactions directly received inside a Bitcoin Core wallet address will as expected be returned as a list of
Unspent
objects when usingPrivateKey().get_unused()
. However, they will not show up when usingPrivateKey().get_transactions()
. A fix would be to rewrite theRPCHost
methodget_transactions
to use the RPC methodlistsinceblock
, which returns all transactions associated to the Bitcoin Core wallet but hence also requires Bit to filter them by addresses. As this is an edge case and wasteful for big wallets it has been ignored (for now).See the discussion here.
TODOs
Add test casesAdd helper function adding addresses as watch-only to the nodeAdd documentationCheck SSL to allow self-signed certs (rpcssl
is deprecated, see https://bitcoin.org/en/release/v0.12.0#rpc-ssl-support-dropped)