Changeset View
Changeset View
Standalone View
Standalone View
contrib/devtools/update-chainparams.py
- This file was added.
Property | Old Value | New Value |
---|---|---|
File Mode | null | 100755 |
#!/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')) | |||||
Fabien: If you use the python `requests` module, the whole thing becomes:
```lang=python
import… | |||||
jasonbcoxAuthorUnsubmitted Done Inline ActionsI prefer to keep the dependencies to a minimum considering that this works and is still reasonably readable. jasonbcox: I prefer to keep the dependencies to a minimum considering that this works and is still… | |||||
port = args.hostport | |||||
if args.testnet and port == '8332': | |||||
FabienUnsubmitted Done Inline ActionsIf the user calls update-chainparams.py -t -p 8332 it ends up changing the port silently. Fabien: If the user calls `update-chainparams.py -t -p 8332` it ends up changing the port silently.
You… | |||||
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'] | |||||
FabienUnsubmitted Done Inline ActionsIf the block is not found curl would return status 0, and this will crash. Fabien: If the block is not found `curl` would return status 0, and this will crash. | |||||
chainParamsClass = 'MainParams' | |||||
if args.testnet: | |||||
chainParamsClass = 'TestNetParams' | |||||
FabienUnsubmitted Done Inline ActionsThe pythonic way: Fabien: The pythonic way:
`chainParamsClass = 'TestNetParams' if args.testnet else 'MainParams'` | |||||
# 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+)("\);)', | |||||
FabienUnsubmitted Done Inline Actions\w is not exactly what you want here. The file being utf-8 it can match a lot of characters, and even in ASCII it also includes the underscore char. I think [a-zA-Z0-9] is what you want here, maybe even only the lowercase chars. Fabien: `\w` is not exactly what you want here. The file being utf-8 it can match a lot of characters… | |||||
'\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() |
If you use the python requests module, the whole thing becomes:
This is more portable but also adds a new dependency, so I leave that up to you.
Link to the python requests documentation.