diff --git a/contrib/devtools/update-chainparams.py b/contrib/devtools/update-chainparams.py new file mode 100755 --- /dev/null +++ b/contrib/devtools/update-chainparams.py @@ -0,0 +1,89 @@ +#!/usr/bin/env python3 +# Copyright (c) 2019 The Bitcoin developers +# Distributed under the MIT software license, see the accompanying +# file COPYING or http://www.opensource.org/licenses/mit-license.php. + +import argparse +import json +import subprocess +import re + +parser = argparse.ArgumentParser(description=( + "Fetch a block from the bitcoin HTTP RPC to update chainparams" + "(nMinimumChainWork and defaultAssumeValid).\n\n" + "NOTE: bitcoind must have `--rest=1` set (either as a cli argument or in" + "the config)!"), formatter_class=argparse.RawTextHelpFormatter) +parser.add_argument('--hostaddr', '-a', + default="localhost", + help="Host address of the HTTP RPC bitcoind server to call" + "for fetching chain info. (defaults to `localhost`)") +parser.add_argument('--hostport', '-p', default="8332", + help="Host port of the HTTP RPC bitcoind server to call" + "for fetching chain info. (defaults to 8332)") +parser.add_argument('--testnet', '-t', action="store_true", + help="If set, testnet chainparams will be updated instead" + "of mainnet chainparams. If testnet is set, but hostport is not set, hostport will default to 18332.") +parser.add_argument('--blockhash', '-b', + help="(optional) The block hash to update chainparams" + "with. If not specified, bestblockhash is used.") +args = parser.parse_args() + + +def curl(url): + # Call bitcoin HTTP RPC using curl + print("Fetching URL: {}".format(url)) + curlArgs = ['curl', '-s', url] + p = subprocess.Popen('curl -s ' + url, stdout=subprocess.PIPE, shell=True) + (output, err) = p.communicate() + status = p.wait() + if status != 0: + print("curl error status: {}\nSee https://curl.haxx.se/libcurl/c/libcurl-errors.html for error descriptions.".format(status)) + exit(1) + return json.loads(output.decode('utf-8')) + + +port = args.hostport +if args.testnet and port == '8332': + port = '18332' +url = args.hostaddr + ':' + port + +# Get best block hash and chainwork +chaininfo = curl(url + '/rest/chaininfo.json') + +chainTestnet = chaininfo['chain'] == 'test' +if args.testnet != chainTestnet: + print("The chain network specified did not match the one given by the host node!") + print("Did you mean to call with a different `--hostaddr` or `--hostport`, or with `--testnet` set?") + exit(1) + +blockhash = chaininfo['bestblockhash'] +chainwork = chaininfo['chainwork'] +if args.blockhash: + blockhash = args.blockhash + print("Fetching chainwork for block {}".format(blockhash)) + block = curl(url + '/rest/block/' + blockhash + '.json') + chainwork = block['chainwork'] + +chainParamsClass = 'MainParams' +if args.testnet: + chainParamsClass = 'TestNetParams' + +# Get top-level path for the project +p = subprocess.Popen('git rev-parse --show-toplevel', + stdout=subprocess.PIPE, shell=True) +(toplevel, err) = p.communicate() +p.wait() + +# Read-write to chainparams.cpp +f = open(toplevel.decode('utf-8').rstrip() + '/src/chainparams.cpp', 'r+') +chainParamsContents = f.read() + +# Replace chainparams with the new values for the specified chainparams class +chainParamsContents = re.sub('(' + chainParamsClass + '.*?nMinimumChainWork.*?")(\w+)("\);)', + '\g<1>' + chainwork + '\g<3>', chainParamsContents, flags=re.MULTILINE | re.DOTALL) +chainParamsContents = re.sub('(' + chainParamsClass + '.*?defaultAssumeValid.*?")(\w+)("\);)', + '\g<1>' + blockhash + '\g<3>', chainParamsContents, flags=re.MULTILINE | re.DOTALL) + +f.seek(0) +f.write(chainParamsContents) +f.close()