Changeset View
Changeset View
Standalone View
Standalone View
test/functional/test_framework/avatools.py
#!/usr/bin/env python3 | #!/usr/bin/env python3 | ||||
# Copyright (c) 2021 The Bitcoin ABC developers | # Copyright (c) 2021 The Bitcoin ABC 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. | ||||
"""Utilities for avalanche tests.""" | """Utilities for avalanche tests.""" | ||||
from typing import Any, Optional, List, Dict | from typing import Any, Optional, List, Dict | ||||
from .messages import ( | |||||
CTransaction, | |||||
FromHex, | |||||
ToHex | |||||
) | |||||
from .test_node import TestNode | from .test_node import TestNode | ||||
from .util import satoshi_round | |||||
def get_stakes(node: TestNode, | def get_coinbase_stakes(node: TestNode, | ||||
blockhashes: List[str], | blockhashes: List[str], | ||||
priv_key: str, | priv_key: str, | ||||
amount: Optional[str] = None) -> List[Dict[str, Any]]: | amount: Optional[str] = None) -> List[Dict[str, Any]]: | ||||
"""Returns a list of dictionaries representing stakes, in a format | """Returns a list of dictionaries representing stakes, in a format | ||||
compatible with the buildavalancheproof RPC. | compatible with the buildavalancheproof RPC, using only coinbase | ||||
transactions. | |||||
:param node: Test node used to get the block and coinbase data. | :param node: Test node used to get the block and coinbase data. | ||||
:param blockhashes: List of block hashes, whose coinbase tx will be used | :param blockhashes: List of block hashes, whose coinbase tx will be used | ||||
as a stake. | as a stake. | ||||
:param priv_key: Private key controlling the coinbase UTXO | :param priv_key: Private key controlling the coinbase UTXO | ||||
:param amount: If specified, this overwrites the amount information | :param amount: If specified, this overwrites the amount information | ||||
in the coinbase dicts. | in the coinbase dicts. | ||||
""" | """ | ||||
Show All 10 Lines | def get_coinbase_stakes(node: TestNode, | ||||
return [{ | return [{ | ||||
'txid': coinbase['txid'], | 'txid': coinbase['txid'], | ||||
'vout': coinbase['n'], | 'vout': coinbase['n'], | ||||
'amount': amount or coinbase['value'], | 'amount': amount or coinbase['value'], | ||||
'height': coinbase['height'], | 'height': coinbase['height'], | ||||
'iscoinbase': True, | 'iscoinbase': True, | ||||
'privatekey': priv_key, | 'privatekey': priv_key, | ||||
} for coinbase in coinbases] | } for coinbase in coinbases] | ||||
def get_stakes(node: TestNode, count: int) -> List[Dict[str, Any]]: | |||||
""" | |||||
Generate and return a list of stakes by mining enough blocks and splitting | |||||
each coinbase transaction into 10 coins. | |||||
deadalnix: I don't think this behavior can be reasonably expected from the name of the function.
This is… | |||||
This function can generate more valid stakes than `get_coinbase_stakes` | |||||
does, because on the regtest chain halving happens every 150 blocks so | |||||
the coinbase amount is below the dust threshold after only 900 blocks. | |||||
:param node: Test node used to generate blocks and send transactions | |||||
:param count: Number of stakes to generate. | |||||
""" | |||||
node.generate(int(0.1 * count) + 101) | |||||
utxos = node.listunspent() | |||||
addresses = [node.getnewaddress() for _ in range(10)] | |||||
for i in range(count // 10 + 1): | |||||
u = utxos.pop() | |||||
inputs = [{"txid": u["txid"], "vout": u["vout"]}] | |||||
outputs = { | |||||
addr: satoshi_round(u['amount'] / 10) for addr in addresses} | |||||
raw_tx = node.createrawtransaction(inputs, outputs) | |||||
ctx = FromHex(CTransaction(), raw_tx) | |||||
ctx.vout[0].nValue -= node.calculate_fee(ctx) | |||||
signed_tx = node.signrawtransactionwithwallet(ToHex(ctx))["hex"] | |||||
node.sendrawtransaction(signed_tx) | |||||
while node.getmempoolinfo()['size'] > 0: | |||||
node.generate(1) | |||||
utxos = node.listunspent() | |||||
stakes = [] | |||||
for utxo in utxos: | |||||
deadalnixUnsubmitted Not Done Inline ActionsWhy generate a stake for each utxo and then throw away all the one after count? deadalnix: Why generate a stake for each utxo and then throw away all the one after count? | |||||
blockhash = node.gettransaction(utxo["txid"])["blockhash"] | |||||
stakes.append({ | |||||
'txid': utxo['txid'], | |||||
'vout': utxo['vout'], | |||||
'amount': utxo['amount'], | |||||
'iscoinbase': utxo['label'] == "coinbase", | |||||
'height': node.getblock(blockhash, 1)["height"], | |||||
'privatekey': node.dumpprivkey(utxo["address"]), | |||||
}) | |||||
return stakes[:count] |
I don't think this behavior can be reasonably expected from the name of the function.
This is doing a ton of stuff under the hood and it is unlikely that this will compose well in general as it'll have a ton of side effects that don't have much to do with getting stakes. In fact, I'm confident than in practice, it is not really possible to use this function without knowing its implementation.