commit f4e9cfba198c30c6f4209351a0966cdc2a36e4f0
parent 726ce096461b3d5321d01d2b60cd492cae10face
Author: chris-belcher <chris-belcher@users.noreply.github.com>
Date: Mon, 26 Feb 2018 16:15:36 +0000
added support for bech32 addresses, requires bitcoin core 0.16
Diffstat:
4 files changed, 60 insertions(+), 35 deletions(-)
diff --git a/README.md b/README.md
@@ -21,8 +21,10 @@ See also the Electrum bitcoin wallet [website](https://electrum.org/).
## How To Use
-This application requires python3 and a Bitcoin full node built with wallet
-capability.
+This application requires python3 and a Bitcoin full node version 0.16 or
+higher. Make sure you
+[verify the digital signatures](https://bitcoin.stackexchange.com/questions/50185/how-to-verify-bitcoin-core-release-signing-keys)
+of any binaries before running them, or compile from source.
Download the latest release or clone the git repository. Enter the directory
and rename the file `config.cfg_sample` to `config.cfg`, edit this file to
@@ -66,8 +68,6 @@ features such as:
* Deterministic wallets and master public keys are not supported. Addresses
must be imported individually.
-* Bech32 bitcoin addresses are not supported.
-
* The Electrum server protocol has a caveat about multiple transactions included
in the same block. So there may be weird behaviour if that happens.
diff --git a/config.cfg_sample b/config.cfg_sample
@@ -5,12 +5,12 @@
[wallets]
## Add addresses to this section
-# These are just random addresses I found on a blockchain explorer
+# These are just random example addresses found on a blockchain explorer
# A key can be anything
addr = 1DuqpoeTB9zLvVCXQG53VbMxvMkijk494n
# A comma separated list is also accepted
-my_test_addresses = 1KKszdQEpXgSY4EvsnGcGEzbQFmdcFwuNS,1EuEVUtQQ8hmuMHNdMdjLwjpkm6Ef7RYVk
+my_test_addresses = 1KKszdQEpXgSY4EvsnGcGEzbQFmdcFwuNS,1EuEVUtQQ8hmuMHNdMdjLwjpkm6Ef7RYVk,bc1qar0srrr7xfkvy5l643lydnw9re59gtzzwf5mdq
# And space separated
more_test_addresses = 3Hh7QujVLqz11tiQsnUE5CSL16WEHBmiyR 1Arzu6mWZuXGTF9yR2hZhMgBJgu1Xh2bNC 1PXRLo1FQoZyF1Jhnz4qbG5x8Bo3pFpybz
@@ -23,7 +23,7 @@ password = password
#how often in seconds to poll for new transactions when electrum not connected
poll_interval_listening = 30
#how often in seconds to poll for new transactions when electrum is connected
-poll_interval_connected = 5
+poll_interval_connected = 2
[electrum-server]
#0.0.0.0 to accept connections from any IP
diff --git a/server.py b/server.py
@@ -57,7 +57,6 @@ from decimal import Decimal
from jsonrpc import JsonRpc, JsonRpcError
import util
-import bitcoin as btc
ADDRESSES_LABEL = "electrum-watchonly-addresses"
@@ -327,18 +326,17 @@ def run_electrum_server(hostport, rpc, address_history, unconfirmed_txes,
def get_input_and_output_scriptpubkeys(rpc, txid):
gettx = rpc.call("gettransaction", [txid])
- txd = btc.deserialize(gettx["hex"])
- output_scriptpubkeys = [sc['script'] for sc in txd['outs']]
+ txd = rpc.call("decoderawtransaction", [gettx["hex"]])
+ output_scriptpubkeys = [out["scriptPubKey"]["hex"] for out in txd["vout"]]
input_scriptpubkeys = []
- for ins in txd["ins"]:
+ for inn in txd["vin"]:
try:
- wallet_tx = rpc.call("gettransaction", [ins["outpoint"][
- "hash"]])
+ wallet_tx = rpc.call("gettransaction", [inn["txid"]])
except JsonRpcError:
#wallet doesnt know about this tx, so the input isnt ours
continue
- script = btc.deserialize(str(wallet_tx["hex"]))["outs"][ins[
- "outpoint"]["index"]]["script"]
+ input_decoded = rpc.call("decoderawtransaction", [wallet_tx["hex"]])
+ script = input_decoded["vout"][inn["vout"]]["scriptPubKey"]["hex"]
input_scriptpubkeys.append(script)
return output_scriptpubkeys, input_scriptpubkeys, txd
@@ -346,12 +344,10 @@ def generate_new_history_element(rpc, tx, txd):
if tx["confirmations"] == 0:
unconfirmed_input = False
total_input_value = 0
- for ins in txd["ins"]:
- utxo = rpc.call("gettxout", [ins["outpoint"]["hash"],
- ins["outpoint"]["index"], True])
+ for inn in txd["vin"]:
+ utxo = rpc.call("gettxout", [inn["txid"], inn["vout"], True])
if utxo is None:
- utxo = rpc.call("gettxout", [ins["outpoint"]["hash"],
- ins["outpoint"]["index"], False])
+ utxo = rpc.call("gettxout", [inn["txid"], inn["vout"], False])
if utxo is None:
debug("utxo not found(!)")
#TODO detect this and figure out how to tell
@@ -360,7 +356,8 @@ def generate_new_history_element(rpc, tx, txd):
unconfirmed_input = unconfirmed_input or utxo["confirmations"] == 0
debug("total_input_value = " + str(total_input_value))
- fee = total_input_value - sum([sc["value"] for sc in txd["outs"]])
+ fee = total_input_value - sum([int(Decimal(out["value"])*Decimal(1e8))
+ for out in txd["vout"]])
height = -1 if unconfirmed_input else 0
new_history_element = ({"tx_hash": tx["txid"], "height": height,
"fee": fee})
@@ -505,7 +502,7 @@ def build_address_history_index(rpc, wallet_addresses):
st = time.time()
address_history = {}
for addr in wallet_addresses:
- scripthash = util.address_to_scripthash(addr)
+ scripthash = util.address_to_scripthash(addr, rpc)
address_history[scripthash] = {'addr': addr, 'history': [],
'subscribed': False}
wallet_addr_scripthashes = set(address_history.keys())
@@ -530,6 +527,7 @@ def build_address_history_index(rpc, wallet_addresses):
continue
if tx["txid"] in obtained_txids:
continue
+ debug("adding obtained tx=" + str(tx["txid"]))
obtained_txids.add(tx["txid"])
#obtain all the addresses this transaction is involved with
diff --git a/util.py b/util.py
@@ -31,9 +31,11 @@ def script_to_scripthash(script):
h = sha256(bytes.fromhex(script))[0:32]
return bh2u(bytes(reversed(h)))
-def address_to_scripthash(addr):
- script = btc.address_to_script(addr)
- return script_to_scripthash(script)
+def address_to_script(addr, rpc):
+ return rpc.call("validateaddress", [addr])["scriptPubKey"]
+
+def address_to_scripthash(addr, rpc):
+ return script_to_scripthash(address_to_script(addr, rpc))
#the 'result' field in the blockchain.scripthash.subscribe method
# reply uses this as a summary of the address
@@ -66,8 +68,10 @@ def hash_merkle_root(merkle_s, target_hash, pos):
def script_to_address(script):
+ #TODO why is this even here? its not used anywhere, maybe old code
#TODO bech32 addresses
- #TODO testnet, although everything uses scripthash so the address vbyte doesnt matter
+ #TODO testnet, although everything uses scripthash so the address
+ # vbyte doesnt matter
return btc.script_to_address(script, 0x00)
def calc_tree_width(height, txcount):
@@ -85,23 +89,27 @@ def decend_merkle_tree(hashes, flags, height, txcount, pos):
left = decend_merkle_tree(hashes, flags, height-1, txcount, pos*2)
#bitcoin has a rule that if theres an odd number of nodes in
# the merkle tree, the last hash is duplicated
+ #in the electrum format we must hash together the duplicate
+ # tree branch
if pos*2+1 < calc_tree_width(height-1, txcount):
right = decend_merkle_tree(hashes, flags, height-1,
txcount, pos*2+1)
else:
- right = left
- #TODO decend down one branch and hash it up, place in right
+ if isinstance(left, tuple):
+ right = expand_tree_hashing(left)
+ else:
+ right = left
return (left, right)
else:
hs = next(hashes)
- hs = hs[:4] + '...' + hs[-4:]
- print(hs)
+ #hs = hs[:4] + '...' + hs[-4:]
+ #print(hs)
return hs
else:
#txid node
hs = next(hashes)
- hs = hs[:4] + '...' + hs[-4:]
- print(hs)
+ #hs = hs[:4] + '...' + hs[-4:]
+ #print(hs)
if flag:
return "tx:" + str(pos) + ":" + hs
else:
@@ -130,6 +138,25 @@ def expand_tree_electrum_format(node, result):
if not isinstance(right, tuple):
result.append(right)
+def deserialize_hash_node(node):
+ if node.startswith("tx"):
+ return node.split(":")[2]
+ else:
+ return node
+
+#recurse down into the tree, hashing everything and returning root hash
+def expand_tree_hashing(node):
+ left, right = node
+ if isinstance(left, tuple):
+ hash_left = expand_tree_hashing(left)
+ else:
+ hash_left = deserialize_hash_node(left)
+ if isinstance(right, tuple):
+ hash_right = expand_tree_hashing(right)
+ else:
+ hash_right = deserialize_hash_node(right)
+ return hash_encode(Hash(hash_decode(hash_left) + hash_decode(hash_right)))
+
#https://github.com/bitcoin/bitcoin/blob/master/src/merkleblock.h
#https://github.com/breadwallet/breadwallet-core/blob/master/BRMerkleBlock.c
def convert_core_to_electrum_merkle_proof(proof):
@@ -279,20 +306,19 @@ merkle_test_vectors = [
#response electrum3.hachre.de {'result': {'pos': 2860, 'block_height': 503961, 'merkle': ['590e0d07c8d33b0453748d1034d3fd4e779e1e78a2c8ef20c2e66830a6d4230d', '0f1d4d6aaa71beaf8d30b7df3cd776ece4bcd1169d1550e8abfb2b3053388ac8', 'cbd44606e7d8ca49ccaa409a51b60854d6e31534c5c6315a257ef571f1398db3', '7d4d426bb8a3b5146b0c35845b7e12dc7bcd7f44c570ff712632d0d86b695cbd', '20e5e6a7eb7cf42e4d3a9ac803f160973b10da3da74d68afb8bfef04d9a46d85', '9032b3b57d81862168733b5a6b6370eaeafb4aaaea5023bf4cf3a998f8ca67e2', 'a16ed5aa6bab2c9b64e91f033aa1fdffa44270f0907aeb8eedd31840514f8f26', 'a53a1448437ac49c9f560f3e5c4a769c6295df2a04242b713d1f0747d90a8fe4', '6922f4bd74e95ae28fcd71c35dfb95e4551876ba78cb828cbc863870b34add53', 'bf152261c5f22dc73cb2fe5ee85984f0c8d71ab8db28bd0e39931f43d6766f1e', '2cbe3c851f5a58e2a407bf38bb829fde76e4fd22005b5c3124d3eff4de55c3a5', '0b7ceffc6a25d3b3c0619fd2d254881e8987f9182c3fb12bf5db14311cd7208d']}, 'method': 'blockchain.transaction.get_merkle', 'id': 32, 'jsonrpc': '2.0', 'params': ['590e0d07c8d33b0453748d1034d3fd4e779e1e78a2c8ef20c2e66830a6d4230d', 503961]}
#proof = "00000020c656c90b521a2bbca14174f2939b882a28d23d86144b0e000000000000000000cf5185a8e369c3de5f15e039e777760994fd66184b619d839dace3aec9953fd6d861595ac1910018ee097a972d0b0000078d20d71c3114dbf52bb13f2c18f987891e8854d2d29f61c0b3d3256afcef7c0b1e6f76d6431f93390ebd28dbb81ad7c8f08459e85efeb23cc72df2c5612215bf53dd4ab3703886bc8c82cb78ba761855e495fb5dc371cd8fe25ae974bdf42269e267caf898a9f34cbf2350eaaa4afbeaea70636b5a3b73682186817db5b33290bd5c696bd8d0322671ff70c5447fcd7bdc127e5b84350c6b14b5a3b86b424d7db38d39f171f57e255a31c6c53415e3d65408b6519a40aacc49cad8e70646d4cb0d23d4a63068e6c220efc8a2781e9e774efdd334108d7453043bd3c8070d0e5903ad5b07"
-#has 7 txes
+#has 7 txes, duplicated entry in the last depth, at tx level
# 0000000000007d1bdd2cfd23ffb3c2bae3143772bd6577aecae9c6b29f88c2af
#lasttx c40bbed8f34cb1c24660e2e0cb51e09a180f1ab97037265293fceab88247bccf
#addr 15dcVuX7UT4fB74dikaAE4MXhCTkFZpV8F
#response electrum3.hachre.de {'id': 33, 'params': ['c40bbed8f34cb1c24660e2e0cb51e09a180f1ab97037265293fceab88247bccf', 120013], 'result': {'block_height': 120013, 'pos': 6, 'merkle': ['c40bbed8f34cb1c24660e2e0cb51e09a180f1ab97037265293fceab88247bccf', 'ad69c91b8e9b7122dc2a2575cfa12a36de05595e0e8f59092d04b263b4c8f70f', '8ae24d1f1c3b0d65ec88f8c84cad7e02e98b26d7ad566bf3653158b72ebb3acd']}, 'jsonrpc': '2.0', 'method': 'blockchain.transaction.get_merkle'}
#proof = "0100000056e02c6d3278c754e0699517834741f7c4ad3dcbfeb7803a3462000000000000af3bdd5dd465443fd003e9281455e60aae573dd4d46304d7ba17276ea33d506488cbb44dacb5001b9ebb193b0700000003cd3abb2eb7583165f36b56add7268be9027ead4cc8f888ec650d3b1c1f4de28a0ff7c8b463b2042d09598f0e5e5905de362aa1cf75252adc22719b8e1bc969adcfbc4782b8eafc9352263770b91a0f189ae051cbe0e26046c2b14cf3d8be0bc40135"
-#has 6 txes
+#has 6 txes, duplicated entry in the last-but-one depth
# 00000000000005163d8d16192985a3f2d0f6f44e668ad05b26f7edcd3385a37f
# last tx eaefedc6dbb37223c771d8ccbbe4dac9e9d646ab90f17e387b63c866fad6e2c3
# addr 1NwNmR7sd6NqxXBJMXrwt9yUms29pSDmm
#response electrum3.hachre.de {'jsonrpc': '2.0', 'id': 33, 'method': 'blockchain.transaction.get_merkle', 'result': {'pos': 5, 'block_height': 150106, 'merkle': ['1f12a4c866548ab51766172f97a6741fbd62834ddfcadba249909ea8150eca88', 'f5a5aa78bd1f1ee5de900b7d1928864912425b67ece4a07e40af8eeb86f10d94', 'd52e599bc0ecc5e17bcb1e7539b61586c7457170923eab6d36243995ed452bf5']}, 'params': ['eaefedc6dbb37223c771d8ccbbe4dac9e9d646ab90f17e387b63c866fad6e2c3', 150106]}
proof = "01000000299edfd28524eae4fb6012e4087afdb6e1b912db85e612374b03000000000000e16572394f8578a47bf36e15cd16faa5e3b9e18805cf4e271ae4ef95aa8cea7eb31fa14e4b6d0b1a42857d960600000003f52b45ed953924366dab3e92707145c78615b639751ecb7be1c5ecc09b592ed588ca0e15a89e9049a2dbcadf4d8362bd1f74a6972f176617b58a5466c8a4121fc3e2d6fa66c8637b387ef190ab46d6e9c9dae4bbccd871c72372b3dbc6edefea012d"
-
'''
ix = 1
try:
@@ -302,6 +328,7 @@ try:
except ValueError:
print("valueerror")
'''
+#the function electrum uses to verify merkle branches is in verifer.py called hash_merkle_root()
'''
h1 = b"1f12a4c866548ab51766172f97a6741fbd62834ddfcadba249909ea8150eca88"