Changeset View
Changeset View
Standalone View
Standalone View
contrib/devtools/chainparams/make_chainparams.py
#!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||
# Copyright (c) 2019 The Bitcoin developers | # Copyright (c) 2019 The Bitcoin developers | ||||
# Distributed under the MIT software license, see the accompanying | # Distributed under the MIT software license, see the accompanying | ||||
# file COPYING or http://www.opensource.org/licenses/mit-license.php. | # file COPYING or http://www.opensource.org/licenses/mit-license.php. | ||||
import argparse | import argparse | ||||
import math | import math | ||||
import os.path | import os.path | ||||
import re | import re | ||||
import sys | import sys | ||||
from enum import Enum | from enum import Enum | ||||
sys.path.append('../../../test/functional/test_framework') | sys.path.append("../../../test/functional/test_framework") | ||||
from authproxy import AuthServiceProxy # noqa: E402 | from authproxy import AuthServiceProxy # noqa: E402 | ||||
class Chain(Enum): | class Chain(Enum): | ||||
MainNet = "MAINNET" | MainNet = "MAINNET" | ||||
TestNet = "TESTNET" | TestNet = "TESTNET" | ||||
GIGABYTE = 1024 * 1024 * 1024 | GIGABYTE = 1024 * 1024 * 1024 | ||||
def get_chainparams(rpc_caller, block): | def get_chainparams(rpc_caller, block): | ||||
# Fetch initial chain info | # Fetch initial chain info | ||||
chaininfo = rpc_caller.getblockchaininfo() | chaininfo = rpc_caller.getblockchaininfo() | ||||
if chaininfo['chain'] == 'main': | if chaininfo["chain"] == "main": | ||||
chain = Chain.MainNet | chain = Chain.MainNet | ||||
else: | else: | ||||
chain = Chain.TestNet | chain = Chain.TestNet | ||||
# Use highest valid chainwork. This doesn't need to match the block hash | # Use highest valid chainwork. This doesn't need to match the block hash | ||||
# used by assume valid. | # used by assume valid. | ||||
chainwork = chaininfo['chainwork'] | chainwork = chaininfo["chainwork"] | ||||
if not re.match('^[0-9a-z]{64}$', chainwork): | if not re.match("^[0-9a-z]{64}$", chainwork): | ||||
raise Exception("Chain work is not a valid uint256 hex value.") | raise Exception("Chain work is not a valid uint256 hex value.") | ||||
# Default to N blocks from the chain tip, depending on which chain we're on | # Default to N blocks from the chain tip, depending on which chain we're on | ||||
if not block: | if not block: | ||||
block = chaininfo['blocks'] | block = chaininfo["blocks"] | ||||
if chain == Chain.MainNet: | if chain == Chain.MainNet: | ||||
block -= 10 | block -= 10 | ||||
else: | else: | ||||
block -= 2000 | block -= 2000 | ||||
block = str(block) | block = str(block) | ||||
if not re.match('^[0-9a-z]{64}$', block): | if not re.match("^[0-9a-z]{64}$", block): | ||||
if re.match('^[0-9]*$', block): | if re.match("^[0-9]*$", block): | ||||
# Fetch block hash using block height | # Fetch block hash using block height | ||||
block = rpc_caller.getblockhash(int(block)) | block = rpc_caller.getblockhash(int(block)) | ||||
else: | else: | ||||
raise Exception("Block hash is not a valid block hash or height.") | raise Exception("Block hash is not a valid block hash or height.") | ||||
# Make sure the block hash is part of the chain. This call with raise an | # Make sure the block hash is part of the chain. This call with raise an | ||||
# exception if not. | # exception if not. | ||||
rpc_caller.getblockheader(block) | rpc_caller.getblockheader(block) | ||||
# Block size on disk (in GB) with some margin for growth | # Block size on disk (in GB) with some margin for growth | ||||
diskSizeBlocks = str( | diskSizeBlocks = str(int(math.ceil(chaininfo["size_on_disk"] / GIGABYTE * 1.3))) | ||||
int(math.ceil(chaininfo['size_on_disk'] / GIGABYTE * 1.3))) | |||||
# Chainstate size on disk (in GB) with some margin for growth | # Chainstate size on disk (in GB) with some margin for growth | ||||
utxos = rpc_caller.gettxoutsetinfo() | utxos = rpc_caller.gettxoutsetinfo() | ||||
diskSizeChainstate = str( | diskSizeChainstate = str(int(math.ceil(utxos["disk_size"] / GIGABYTE * 1.3))) | ||||
int(math.ceil(utxos['disk_size'] / GIGABYTE * 1.3))) | |||||
return (block, chainwork, diskSizeBlocks, diskSizeChainstate) | return (block, chainwork, diskSizeBlocks, diskSizeChainstate) | ||||
def main(args): | def main(args): | ||||
return "\n".join(get_chainparams(args['rpc'], args['block'])) | return "\n".join(get_chainparams(args["rpc"], args["block"])) | ||||
if __name__ == "__main__": | if __name__ == "__main__": | ||||
parser = argparse.ArgumentParser(description=( | parser = argparse.ArgumentParser( | ||||
"Make chainparams file.\n" | description=( | ||||
"Prerequisites: RPC access to a bitcoind node.\n\n"), | "Make chainparams file.\nPrerequisites: RPC access to a bitcoind node.\n\n" | ||||
formatter_class=argparse.RawTextHelpFormatter) | ), | ||||
parser.add_argument('--address', '-a', default="127.0.0.1:8332", | formatter_class=argparse.RawTextHelpFormatter, | ||||
help="Node address for making RPC calls.\n" | ) | ||||
parser.add_argument( | |||||
"--address", | |||||
"-a", | |||||
default="127.0.0.1:8332", | |||||
help=( | |||||
"Node address for making RPC calls.\n" | |||||
"The chain (MainNet or TestNet) will be automatically detected.\n" | "The chain (MainNet or TestNet) will be automatically detected.\n" | ||||
"Default: '127.0.0.1:8332'") | "Default: '127.0.0.1:8332'" | ||||
parser.add_argument('--block', '-b', | ), | ||||
help="The block hash or height to use for fetching chainparams.\n" | ) | ||||
parser.add_argument( | |||||
"--block", | |||||
"-b", | |||||
help=( | |||||
"The block hash or height to use for fetching chainparams.\n" | |||||
"MainNet default: 10 blocks from the chain tip." | "MainNet default: 10 blocks from the chain tip." | ||||
"TestNet default: 2000 blocks from the chain tip.") | "TestNet default: 2000 blocks from the chain tip." | ||||
parser.add_argument('--config', '-c', default="~/.bitcoin/bitcoin.conf", | ), | ||||
help="Path to bitcoin.conf for RPC authentication arguments (rpcuser & rpcpassword).\n" | ) | ||||
"Default: ~/.bitcoin/bitcoin.conf") | parser.add_argument( | ||||
"--config", | |||||
"-c", | |||||
default="~/.bitcoin/bitcoin.conf", | |||||
help=( | |||||
"Path to bitcoin.conf for RPC authentication arguments (rpcuser &" | |||||
" rpcpassword).\nDefault: ~/.bitcoin/bitcoin.conf" | |||||
), | |||||
) | |||||
args = parser.parse_args() | args = parser.parse_args() | ||||
args.config = os.path.expanduser(args.config) | args.config = os.path.expanduser(args.config) | ||||
# Get user and password from config | # Get user and password from config | ||||
user = None | user = None | ||||
password = None | password = None | ||||
if os.path.isfile(args.config): | if os.path.isfile(args.config): | ||||
with open(args.config, 'r', encoding='utf8') as f: | with open(args.config, "r", encoding="utf8") as f: | ||||
for line in f: | for line in f: | ||||
if line.startswith("rpcuser="): | if line.startswith("rpcuser="): | ||||
# Ensure that there is only one rpcuser line | # Ensure that there is only one rpcuser line | ||||
assert user is None | assert user is None | ||||
user = line.split("=")[1].strip("\n") | user = line.split("=")[1].strip("\n") | ||||
if line.startswith("rpcpassword="): | if line.startswith("rpcpassword="): | ||||
# Ensure that there is only one rpcpassword line | # Ensure that there is only one rpcpassword line | ||||
assert password is None | assert password is None | ||||
password = line.split("=")[1].strip("\n") | password = line.split("=")[1].strip("\n") | ||||
else: | else: | ||||
raise FileNotFoundError("Missing bitcoin.conf") | raise FileNotFoundError("Missing bitcoin.conf") | ||||
if user is None: | if user is None: | ||||
raise ValueError("Config is missing rpcuser") | raise ValueError("Config is missing rpcuser") | ||||
if password is None: | if password is None: | ||||
raise ValueError("Config is missing rpcpassword") | raise ValueError("Config is missing rpcpassword") | ||||
args.rpc = AuthServiceProxy( | args.rpc = AuthServiceProxy( | ||||
service_url=f'http://{user}:{password}@{args.address}', timeout=1200) | service_url=f"http://{user}:{password}@{args.address}", timeout=1200 | ||||
) | |||||
output = main(vars(args)) | output = main(vars(args)) | ||||
if output: | if output: | ||||
print(output) | print(output) |