From b41ecc6feb6d8578b819481f6d336db14a0b367d Mon Sep 17 00:00:00 2001 From: Adrian Gallagher Date: Sun, 29 Jan 2017 07:00:00 -0800 Subject: [PATCH] Litecoin: Protocol and default settings 0) Adjust BIP16 & BIP30 enforcement values 1) Reduce amount that peers can adjust our time to eliminate an attack vector. Thanks to coblee for this fix. 2) Zeitgeist2 patch - thanks to Lolcust and ArtForz. This fixes an issue where a 51% attack can change difficulty at will. Go back the full period unless it's the first retarget after genesis. 3) Avoid overflow in CalculateNextWorkRequired(). Thanks to pooler for the overflow fix. 4) SegWit ContextualCheckBlockHeader adjustment and extra coverage 5) Reject peer proto version below 70002. Thanks to wtogami for this patch. 6) Adjust default settings for Litecoin --- contrib/debian/examples/bitcoin.conf | 6 +- contrib/linearize/example-linearize.cfg | 4 +- contrib/linearize/linearize-hashes.py | 2 +- contrib/qos/README.md | 6 +- contrib/qos/tc.sh | 34 +-- contrib/rpm/bitcoin.spec | 16 +- contrib/seeds/generate-seeds.py | 4 +- contrib/spendfrom/spendfrom.py | 270 ++++++++++++++++++++++++ doc/REST-interface.md | 4 +- doc/developer-notes.md | 4 +- doc/tor.md | 8 +- src/amount.h | 2 +- src/policy/policy.h | 2 +- src/pow.cpp | 25 ++- src/qt/intro.cpp | 2 +- src/rpc/net.cpp | 6 +- src/rpc/server.cpp | 2 +- src/test/compress_tests.cpp | 4 +- src/test/miner_tests.cpp | 2 +- src/timedata.h | 2 +- src/validation.cpp | 15 +- src/validation.h | 2 +- src/version.h | 2 +- test/functional/nulldummy.py | 7 +- test/functional/p2p-segwit.py | 21 +- test/functional/proxy_test.py | 8 +- 26 files changed, 373 insertions(+), 87 deletions(-) create mode 100755 contrib/spendfrom/spendfrom.py diff --git a/contrib/debian/examples/bitcoin.conf b/contrib/debian/examples/bitcoin.conf index 1029a5107..61c744c08 100644 --- a/contrib/debian/examples/bitcoin.conf +++ b/contrib/debian/examples/bitcoin.conf @@ -44,11 +44,11 @@ # Use as many addnode= settings as you like to connect to specific peers #addnode=69.164.218.197 -#addnode=10.0.0.2:8333 +#addnode=10.0.0.2:9333 # Alternatively use as many connect= settings as you like to connect ONLY to specific peers #connect=69.164.218.197 -#connect=10.0.0.1:8333 +#connect=10.0.0.1:9333 # Listening mode, enabled by default except when 'connect' is being used #listen=1 @@ -110,7 +110,7 @@ #rpcallowip=2001:db8:85a3:0:0:8a2e:370:7334/96 # Listen for RPC connections on this TCP port: -#rpcport=8332 +#rpcport=9332 # You can use Bitcoin or bitcoind to send commands to Bitcoin/bitcoind # running on another host using this option: diff --git a/contrib/linearize/example-linearize.cfg b/contrib/linearize/example-linearize.cfg index d019b06b6..7dcca3b5a 100644 --- a/contrib/linearize/example-linearize.cfg +++ b/contrib/linearize/example-linearize.cfg @@ -3,8 +3,8 @@ rpcuser=someuser rpcpassword=somepassword #datadir=~/.bitcoin host=127.0.0.1 -port=8332 -#port=18332 +port=9332 +#port=19332 # bootstrap.dat hashlist settings (linearize-hashes) max_height=313000 diff --git a/contrib/linearize/linearize-hashes.py b/contrib/linearize/linearize-hashes.py index db8eb7021..cc405b631 100755 --- a/contrib/linearize/linearize-hashes.py +++ b/contrib/linearize/linearize-hashes.py @@ -125,7 +125,7 @@ if __name__ == '__main__': if 'host' not in settings: settings['host'] = '127.0.0.1' if 'port' not in settings: - settings['port'] = 8332 + settings['port'] = 9332 if 'min_height' not in settings: settings['min_height'] = 0 if 'max_height' not in settings: diff --git a/contrib/qos/README.md b/contrib/qos/README.md index 0ded87c58..d569b6a6f 100644 --- a/contrib/qos/README.md +++ b/contrib/qos/README.md @@ -1,5 +1,5 @@ -### QoS (Quality of service) ### +### Qos ### -This is a Linux bash script that will set up tc to limit the outgoing bandwidth for connections to the Bitcoin network. It limits outbound TCP traffic with a source or destination port of 8333, but not if the destination IP is within a LAN. +This is a Linux bash script that will set up tc to limit the outgoing bandwidth for connections to the Litecoin network. It limits outbound TCP traffic with a source or destination port of 9333, but not if the destination IP is within a LAN (defined as 192.168.x.x). -This means one can have an always-on bitcoind instance running, and another local bitcoind/bitcoin-qt instance which connects to this node and receives blocks from it. +This means one can have an always-on litecoind instance running, and another local litecoind/litecoin-qt instance which connects to this node and receives blocks from it. diff --git a/contrib/qos/tc.sh b/contrib/qos/tc.sh index 0d1dd65b4..8f9bb7a8b 100644 --- a/contrib/qos/tc.sh +++ b/contrib/qos/tc.sh @@ -1,4 +1,4 @@ -# Copyright (c) 2017 The Bitcoin Core developers +# Copyright (c) 2013 The Bitcoin Core developers # Distributed under the MIT software license, see the accompanying # file COPYING or http://www.opensource.org/licenses/mit-license.php. @@ -6,12 +6,10 @@ IF="eth0" #limit of the network interface in question LINKCEIL="1gbit" -#limit outbound Bitcoin protocol traffic to this rate +#limit outbound Litecoin protocol traffic to this rate LIMIT="160kbit" -#defines the IPv4 address space for which you wish to disable rate limiting -LOCALNET_V4="192.168.0.0/16" -#defines the IPv6 address space for which you wish to disable rate limiting -LOCALNET_V6="fe80::/10" +#defines the address space for which you wish to disable rate limiting +LOCALNET="192.168.0.0/16" #delete existing rules tc qdisc del dev ${IF} root @@ -30,12 +28,6 @@ tc class add dev ${IF} parent 1:1 classid 1:11 htb rate ${LIMIT} ceil ${LIMIT} p tc filter add dev ${IF} parent 1: protocol ip prio 1 handle 1 fw classid 1:10 tc filter add dev ${IF} parent 1: protocol ip prio 2 handle 2 fw classid 1:11 -if [ ! -z "${LOCALNET_V6}" ] ; then - # v6 cannot have the same priority value as v4 - tc filter add dev ${IF} parent 1: protocol ipv6 prio 3 handle 1 fw classid 1:10 - tc filter add dev ${IF} parent 1: protocol ipv6 prio 4 handle 2 fw classid 1:11 -fi - #delete any existing rules #disable for now #ret=0 @@ -44,16 +36,10 @@ fi # ret=$? #done -#limit outgoing traffic to and from port 8333. but not when dealing with a host on the local network -# (defined by $LOCALNET_V4 and $LOCALNET_V6) -# --set-mark marks packages matching these criteria with the number "2" (v4) -# --set-mark marks packages matching these criteria with the number "4" (v6) -# these packets are filtered by the tc filter with "handle 2" +#limit outgoing traffic to and from port 9333. but not when dealing with a host on the local network +# (defined by $LOCALNET) +# --set-mark marks packages matching these criteria with the number "2" +# these packages are filtered by the tc filter with "handle 2" # this filter sends the packages into the 1:11 class, and this class is limited to ${LIMIT} -iptables -t mangle -A OUTPUT -p tcp -m tcp --dport 8333 ! -d ${LOCALNET_V4} -j MARK --set-mark 0x2 -iptables -t mangle -A OUTPUT -p tcp -m tcp --sport 8333 ! -d ${LOCALNET_V4} -j MARK --set-mark 0x2 - -if [ ! -z "${LOCALNET_V6}" ] ; then - ip6tables -t mangle -A OUTPUT -p tcp -m tcp --dport 8333 ! -d ${LOCALNET_V6} -j MARK --set-mark 0x4 - ip6tables -t mangle -A OUTPUT -p tcp -m tcp --sport 8333 ! -d ${LOCALNET_V6} -j MARK --set-mark 0x4 -fi +iptables -t mangle -A OUTPUT -p tcp -m tcp --dport 9333 ! -d ${LOCALNET} -j MARK --set-mark 0x2 +iptables -t mangle -A OUTPUT -p tcp -m tcp --sport 9333 ! -d ${LOCALNET} -j MARK --set-mark 0x2 diff --git a/contrib/rpm/bitcoin.spec b/contrib/rpm/bitcoin.spec index cc54fcaf3..ca089f715 100644 --- a/contrib/rpm/bitcoin.spec +++ b/contrib/rpm/bitcoin.spec @@ -332,10 +332,10 @@ if [ `%{_sbindir}/sestatus |grep -c "disabled"` -eq 0 ]; then for selinuxvariant in %{selinux_variants}; do %{_sbindir}/semodule -s ${selinuxvariant} -i %{_datadir}/selinux/${selinuxvariant}/bitcoin.pp &> /dev/null || : done -%{_sbindir}/semanage port -a -t bitcoin_port_t -p tcp 8332 -%{_sbindir}/semanage port -a -t bitcoin_port_t -p tcp 8333 -%{_sbindir}/semanage port -a -t bitcoin_port_t -p tcp 18332 -%{_sbindir}/semanage port -a -t bitcoin_port_t -p tcp 18333 +%{_sbindir}/semanage port -a -t bitcoin_port_t -p tcp 9332 +%{_sbindir}/semanage port -a -t bitcoin_port_t -p tcp 9333 +%{_sbindir}/semanage port -a -t bitcoin_port_t -p tcp 19332 +%{_sbindir}/semanage port -a -t bitcoin_port_t -p tcp 19333 %{_sbindir}/fixfiles -R bitcoin-server restore &> /dev/null || : %{_sbindir}/restorecon -R %{_localstatedir}/lib/bitcoin || : fi @@ -351,10 +351,10 @@ fi # SELinux if [ $1 -eq 0 ]; then if [ `%{_sbindir}/sestatus |grep -c "disabled"` -eq 0 ]; then - %{_sbindir}/semanage port -d -p tcp 8332 - %{_sbindir}/semanage port -d -p tcp 8333 - %{_sbindir}/semanage port -d -p tcp 18332 - %{_sbindir}/semanage port -d -p tcp 18333 + %{_sbindir}/semanage port -d -p tcp 9332 + %{_sbindir}/semanage port -d -p tcp 9333 + %{_sbindir}/semanage port -d -p tcp 19332 + %{_sbindir}/semanage port -d -p tcp 19333 for selinuxvariant in %{selinux_variants}; do %{_sbindir}/semodule -s ${selinuxvariant} -r bitcoin &> /dev/null || : done diff --git a/contrib/seeds/generate-seeds.py b/contrib/seeds/generate-seeds.py index b0ac92ae0..2c0627035 100755 --- a/contrib/seeds/generate-seeds.py +++ b/contrib/seeds/generate-seeds.py @@ -127,10 +127,10 @@ def main(): g.write(' * IPv4 as well as onion addresses are wrapped inside a IPv6 address accordingly.\n') g.write(' */\n') with open(os.path.join(indir,'nodes_main.txt'),'r') as f: - process_nodes(g, f, 'pnSeed6_main', 8333) + process_nodes(g, f, 'pnSeed6_main', 9333) g.write('\n') with open(os.path.join(indir,'nodes_test.txt'),'r') as f: - process_nodes(g, f, 'pnSeed6_test', 18333) + process_nodes(g, f, 'pnSeed6_test', 19333) g.write('#endif // BITCOIN_CHAINPARAMSSEEDS_H\n') if __name__ == '__main__': diff --git a/contrib/spendfrom/spendfrom.py b/contrib/spendfrom/spendfrom.py new file mode 100755 index 000000000..8c248a3f9 --- /dev/null +++ b/contrib/spendfrom/spendfrom.py @@ -0,0 +1,270 @@ +#!/usr/bin/env python +# Copyright (c) 2013 The Bitcoin Core developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. +# +# Use the raw transactions API to spend bitcoins received on particular addresses, +# and send any change back to that same address. +# +# Example usage: +# spendfrom.py # Lists available funds +# spendfrom.py --from=ADDRESS --to=ADDRESS --amount=11.00 +# +# Assumes it will talk to a bitcoind or Bitcoin-Qt running +# on localhost. +# +# Depends on jsonrpc +# + +from decimal import * +import getpass +import math +import os +import os.path +import platform +import sys +import time +from jsonrpc import ServiceProxy, json + +BASE_FEE=Decimal("0.001") + +def check_json_precision(): + """Make sure json library being used does not lose precision converting BTC values""" + n = Decimal("20000000.00000003") + satoshis = int(json.loads(json.dumps(float(n)))*1.0e8) + if satoshis != 2000000000000003: + raise RuntimeError("JSON encode/decode loses precision") + +def determine_db_dir(): + """Return the default location of the bitcoin data directory""" + if platform.system() == "Darwin": + return os.path.expanduser("~/Library/Application Support/Bitcoin/") + elif platform.system() == "Windows": + return os.path.join(os.environ['APPDATA'], "Bitcoin") + return os.path.expanduser("~/.bitcoin") + +def read_bitcoin_config(dbdir): + """Read the bitcoin.conf file from dbdir, returns dictionary of settings""" + from ConfigParser import SafeConfigParser + + class FakeSecHead(object): + def __init__(self, fp): + self.fp = fp + self.sechead = '[all]\n' + def readline(self): + if self.sechead: + try: return self.sechead + finally: self.sechead = None + else: + s = self.fp.readline() + if s.find('#') != -1: + s = s[0:s.find('#')].strip() +"\n" + return s + + config_parser = SafeConfigParser() + config_parser.readfp(FakeSecHead(open(os.path.join(dbdir, "bitcoin.conf")))) + return dict(config_parser.items("all")) + +def connect_JSON(config): + """Connect to a bitcoin JSON-RPC server""" + testnet = config.get('testnet', '0') + testnet = (int(testnet) > 0) # 0/1 in config file, convert to True/False + if not 'rpcport' in config: + config['rpcport'] = 19332 if testnet else 9332 + connect = "http://%s:%s@127.0.0.1:%s"%(config['rpcuser'], config['rpcpassword'], config['rpcport']) + try: + result = ServiceProxy(connect) + # ServiceProxy is lazy-connect, so send an RPC command mostly to catch connection errors, + # but also make sure the bitcoind we're talking to is/isn't testnet: + if result.getmininginfo()['testnet'] != testnet: + sys.stderr.write("RPC server at "+connect+" testnet setting mismatch\n") + sys.exit(1) + return result + except: + sys.stderr.write("Error connecting to RPC server at "+connect+"\n") + sys.exit(1) + +def unlock_wallet(bitcoind): + info = bitcoind.getinfo() + if 'unlocked_until' not in info: + return True # wallet is not encrypted + t = int(info['unlocked_until']) + if t <= time.time(): + try: + passphrase = getpass.getpass("Wallet is locked; enter passphrase: ") + bitcoind.walletpassphrase(passphrase, 5) + except: + sys.stderr.write("Wrong passphrase\n") + + info = bitcoind.getinfo() + return int(info['unlocked_until']) > time.time() + +def list_available(bitcoind): + address_summary = dict() + + address_to_account = dict() + for info in bitcoind.listreceivedbyaddress(0): + address_to_account[info["address"]] = info["account"] + + unspent = bitcoind.listunspent(0) + for output in unspent: + # listunspent doesn't give addresses, so: + rawtx = bitcoind.getrawtransaction(output['txid'], 1) + vout = rawtx["vout"][output['vout']] + pk = vout["scriptPubKey"] + + # This code only deals with ordinary pay-to-bitcoin-address + # or pay-to-script-hash outputs right now; anything exotic is ignored. + if pk["type"] != "pubkeyhash" and pk["type"] != "scripthash": + continue + + address = pk["addresses"][0] + if address in address_summary: + address_summary[address]["total"] += vout["value"] + address_summary[address]["outputs"].append(output) + else: + address_summary[address] = { + "total" : vout["value"], + "outputs" : [output], + "account" : address_to_account.get(address, "") + } + + return address_summary + +def select_coins(needed, inputs): + # Feel free to improve this, this is good enough for my simple needs: + outputs = [] + have = Decimal("0.0") + n = 0 + while have < needed and n < len(inputs): + outputs.append({ "txid":inputs[n]["txid"], "vout":inputs[n]["vout"]}) + have += inputs[n]["amount"] + n += 1 + return (outputs, have-needed) + +def create_tx(bitcoind, fromaddresses, toaddress, amount, fee): + all_coins = list_available(bitcoind) + + total_available = Decimal("0.0") + needed = amount+fee + potential_inputs = [] + for addr in fromaddresses: + if addr not in all_coins: + continue + potential_inputs.extend(all_coins[addr]["outputs"]) + total_available += all_coins[addr]["total"] + + if total_available < needed: + sys.stderr.write("Error, only %f BTC available, need %f\n"%(total_available, needed)); + sys.exit(1) + + # + # Note: + # Python's json/jsonrpc modules have inconsistent support for Decimal numbers. + # Instead of wrestling with getting json.dumps() (used by jsonrpc) to encode + # Decimals, I'm casting amounts to float before sending them to bitcoind. + # + outputs = { toaddress : float(amount) } + (inputs, change_amount) = select_coins(needed, potential_inputs) + if change_amount > BASE_FEE: # don't bother with zero or tiny change + change_address = fromaddresses[-1] + if change_address in outputs: + outputs[change_address] += float(change_amount) + else: + outputs[change_address] = float(change_amount) + + rawtx = bitcoind.createrawtransaction(inputs, outputs) + signed_rawtx = bitcoind.signrawtransaction(rawtx) + if not signed_rawtx["complete"]: + sys.stderr.write("signrawtransaction failed\n") + sys.exit(1) + txdata = signed_rawtx["hex"] + + return txdata + +def compute_amount_in(bitcoind, txinfo): + result = Decimal("0.0") + for vin in txinfo['vin']: + in_info = bitcoind.getrawtransaction(vin['txid'], 1) + vout = in_info['vout'][vin['vout']] + result = result + vout['value'] + return result + +def compute_amount_out(txinfo): + result = Decimal("0.0") + for vout in txinfo['vout']: + result = result + vout['value'] + return result + +def sanity_test_fee(bitcoind, txdata_hex, max_fee): + class FeeError(RuntimeError): + pass + try: + txinfo = bitcoind.decoderawtransaction(txdata_hex) + total_in = compute_amount_in(bitcoind, txinfo) + total_out = compute_amount_out(txinfo) + if total_in-total_out > max_fee: + raise FeeError("Rejecting transaction, unreasonable fee of "+str(total_in-total_out)) + + tx_size = len(txdata_hex)/2 + kb = tx_size/1000 # integer division rounds down + if kb > 1 and fee < BASE_FEE: + raise FeeError("Rejecting no-fee transaction, larger than 1000 bytes") + if total_in < 0.01 and fee < BASE_FEE: + raise FeeError("Rejecting no-fee, tiny-amount transaction") + # Exercise for the reader: compute transaction priority, and + # warn if this is a very-low-priority transaction + + except FeeError as err: + sys.stderr.write((str(err)+"\n")) + sys.exit(1) + +def main(): + import optparse + + parser = optparse.OptionParser(usage="%prog [options]") + parser.add_option("--from", dest="fromaddresses", default=None, + help="addresses to get bitcoins from") + parser.add_option("--to", dest="to", default=None, + help="address to get send bitcoins to") + parser.add_option("--amount", dest="amount", default=None, + help="amount to send") + parser.add_option("--fee", dest="fee", default="0.0", + help="fee to include") + parser.add_option("--datadir", dest="datadir", default=determine_db_dir(), + help="location of bitcoin.conf file with RPC username/password (default: %default)") + parser.add_option("--testnet", dest="testnet", default=False, action="store_true", + help="Use the test network") + parser.add_option("--dry_run", dest="dry_run", default=False, action="store_true", + help="Don't broadcast the transaction, just create and print the transaction data") + + (options, args) = parser.parse_args() + + check_json_precision() + config = read_bitcoin_config(options.datadir) + if options.testnet: config['testnet'] = True + bitcoind = connect_JSON(config) + + if options.amount is None: + address_summary = list_available(bitcoind) + for address,info in address_summary.iteritems(): + n_transactions = len(info['outputs']) + if n_transactions > 1: + print("%s %.8f %s (%d transactions)"%(address, info['total'], info['account'], n_transactions)) + else: + print("%s %.8f %s"%(address, info['total'], info['account'])) + else: + fee = Decimal(options.fee) + amount = Decimal(options.amount) + while unlock_wallet(bitcoind) == False: + pass # Keep asking for passphrase until they get it right + txdata = create_tx(bitcoind, options.fromaddresses.split(","), options.to, amount, fee) + sanity_test_fee(bitcoind, txdata, amount*Decimal("0.01")) + if options.dry_run: + print(txdata) + else: + txid = bitcoind.sendrawtransaction(txdata) + print(txid) + +if __name__ == '__main__': + main() diff --git a/doc/REST-interface.md b/doc/REST-interface.md index caf678288..fc4e2ae3f 100644 --- a/doc/REST-interface.md +++ b/doc/REST-interface.md @@ -57,7 +57,7 @@ https://github.com/bitcoin/bips/blob/master/bip-0064.mediawiki Example: ``` -$ curl localhost:18332/rest/getutxos/checkmempool/b2cdfd7b89def827ff8af7cd9bff7627ff72e5e8b0f71210f92ea7a4000c5d75-0.json 2>/dev/null | json_pp +$ curl localhost:19332/rest/getutxos/checkmempool/b2cdfd7b89def827ff8af7cd9bff7627ff72e5e8b0f71210f92ea7a4000c5d75-0.json 2>/dev/null | json_pp { "chainHeight" : 325347, "chaintipHash" : "00000000fb01a7f3745a717f8caebee056c484e6e0bfe4a9591c235bb70506fb", @@ -99,4 +99,4 @@ Only supports JSON as output format. Risks ------------- -Running a web browser on the same node with a REST enabled bitcoind can be a risk. Accessing prepared XSS websites could read out tx/block data of your node by placing links like `