Integration tests: protocol versions, 1.6/1.7 methods, daemon limits and reorg
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
b32548fd01
commit
fc2672c898
1 file changed
+118
@@ -0,0 +1,118 @@
|
||||
'''Electrum protocol methods per protocol version, daemon limits and reorgs, against the regtest node (see conftest.py).'''
|
||||
import pytest
|
||||
|
||||
from tests.integration.conftest import ElectrumClient, new_address, scripthash, wait_for
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def client(electrumx):
|
||||
client = ElectrumClient(electrumx)
|
||||
yield client
|
||||
client.close()
|
||||
|
||||
|
||||
def negotiate(client, version):
|
||||
return client.call('server.version', 'plm-integration-test', version)
|
||||
|
||||
|
||||
def spk_of(node, address):
|
||||
return node.call('getaddressinfo', address)['scriptPubKey']
|
||||
|
||||
|
||||
@pytest.mark.parametrize('version', ['1.4', '1.4.2', '1.5', '1.6', '1.7'])
|
||||
def test_protocol_versions(client, version):
|
||||
assert negotiate(client, version)[1] == version
|
||||
|
||||
|
||||
def test_unsupported_protocol_versions(electrumx):
|
||||
for version in ('1.3', '2.0'):
|
||||
client = ElectrumClient(electrumx)
|
||||
try:
|
||||
with pytest.raises(RuntimeError, match='unsupported protocol version'):
|
||||
negotiate(client, version)
|
||||
finally:
|
||||
client.close()
|
||||
|
||||
|
||||
def test_server_features(node, client):
|
||||
negotiate(client, '1.4')
|
||||
features = client.call('server.features')
|
||||
assert features['genesis_hash'] == node.call('getblockhash', 0)
|
||||
assert features['hash_function'] == 'sha256'
|
||||
assert features['protocol_min'] == '1.4'
|
||||
|
||||
|
||||
def test_relayfee_and_estimatefee(client):
|
||||
negotiate(client, '1.5') # blockchain.relayfee was removed in 1.6
|
||||
assert client.call('blockchain.relayfee') > 0
|
||||
assert isinstance(client.call('blockchain.estimatefee', 2), (int, float))
|
||||
|
||||
|
||||
def test_mempool_info(client):
|
||||
'''The daemon reports 'incrementalfee' in getnetworkinfo, not 'incrementalrelayfee' in getmempoolinfo'''
|
||||
negotiate(client, '1.6')
|
||||
info = client.call('mempool.get_info')
|
||||
assert set(info) == {'mempoolminfee', 'minrelaytxfee', 'incrementalrelayfee'}
|
||||
assert all(fee > 0 for fee in info.values())
|
||||
|
||||
|
||||
def test_daemon_limits_are_reported_as_errors(client):
|
||||
'''The Palladium daemon has no submitpackage and no gettxspendingprevout: clients get an error.
|
||||
If a newer daemon gains them, update CLAUDE.md, README.md and DEPLOY.md and this test.'''
|
||||
negotiate(client, '1.7')
|
||||
with pytest.raises(RuntimeError, match='rejected by network rules'):
|
||||
client.call('blockchain.transaction.broadcast_package', ['00'])
|
||||
# a subscription would be accepted for an outpoint in the mempool and then close the session at the next block
|
||||
with pytest.raises(RuntimeError, match='outpoint subscriptions are not supported'):
|
||||
client.call('blockchain.outpoint.subscribe', '00' * 32, 0, '')
|
||||
|
||||
|
||||
def test_protocol_1_7_scriptpubkey_methods(node, wallet, client):
|
||||
negotiate(client, '1.7')
|
||||
target = node.call('getnewaddress', '', 'bech32m', wallet=wallet)
|
||||
spk = spk_of(node, target)
|
||||
txid = node.call('sendtoaddress', target, 2, wallet=wallet)
|
||||
wait_for(lambda: client.call('blockchain.scriptpubkey.get_mempool', spk)['history'], 'the tx in the mempool')
|
||||
assert txid in [h['tx_hash'] for h in client.call('blockchain.scriptpubkey.get_history', spk)['history']]
|
||||
assert client.call('blockchain.scriptpubkey.get_balance', spk)['unconfirmed'] == 200_000_000
|
||||
assert client.call('blockchain.scriptpubkey.subscribe', spk) is not None
|
||||
# mempool.recent lists it
|
||||
assert txid in [t['txid'] for t in client.call('mempool.recent')]
|
||||
node.mine(1, new_address(node, wallet))
|
||||
wait_for(lambda: client.call('blockchain.scriptpubkey.get_balance', spk)['confirmed'] == 200_000_000, 'confirmation')
|
||||
unspent = client.call('blockchain.scriptpubkey.listunspent', spk)['utxos']
|
||||
assert [(u['tx_hash'], u['value']) for u in unspent] == [(txid, 200_000_000)]
|
||||
assert client.call('blockchain.scriptpubkey.unsubscribe', spk) is True
|
||||
|
||||
|
||||
def test_testmempoolaccept(node, wallet, client):
|
||||
negotiate(client, '1.7')
|
||||
target = new_address(node, wallet)
|
||||
funded = node.call('fundrawtransaction', node.call('createrawtransaction', [], {target: 0.1}), wallet=wallet)
|
||||
signed = node.call('signrawtransactionwithwallet', funded['hex'], wallet=wallet)['hex']
|
||||
result, = client.call('blockchain.transaction.testmempoolaccept', [signed])
|
||||
assert result['allowed'] is True
|
||||
|
||||
|
||||
def test_reorg_replaces_blocks(node, wallet, electrumx, client):
|
||||
'''The server follows a reorg: the block is replaced by a longer chain that confirms the tx again.
|
||||
Like upstream ElectrumX it reorganises when the daemon's chain gets longer than its own, not before.'''
|
||||
negotiate(client, '1.4.2')
|
||||
miner = new_address(node, wallet)
|
||||
target = node.call('getnewaddress', '', 'bech32', wallet=wallet)
|
||||
sh = scripthash(node, target)
|
||||
txid = node.call('sendtoaddress', target, 1, wallet=wallet)
|
||||
block = node.mine(1, miner)[0]
|
||||
height = node.call('getblockcount')
|
||||
wait_for(lambda: client.call('blockchain.scripthash.get_balance', sh)['confirmed'] == 100_000_000, 'confirmation')
|
||||
old_header = client.call('blockchain.block.header', height)
|
||||
|
||||
node.call('invalidateblock', block)
|
||||
new_blocks = node.mine(2, new_address(node, wallet)) # other coinbase, else the same block; the tx goes back to the mempool and is mined again in the first one
|
||||
assert block not in new_blocks
|
||||
wait_for(lambda: client.call('blockchain.headers.subscribe')['height'] == height + 1, 'the server to follow the new chain')
|
||||
assert client.call('blockchain.block.header', height) != old_header
|
||||
wait_for(lambda: client.call('blockchain.scripthash.get_balance', sh) == {'confirmed': 100_000_000, 'unconfirmed': 0},
|
||||
'the balance after the reorg')
|
||||
history = client.call('blockchain.scripthash.get_history', sh)
|
||||
assert [(h['tx_hash'], h['height']) for h in history] == [(txid, height)]
|
||||
Reference in new issue
Block a user