add tests for takeovers from amount changes in updates before/on/after activation

This commit is contained in:
Jack Robison 2021-09-25 14:59:42 -04:00 committed by Victor Shyba
parent f77f2f6e80
commit 6155cda66f
4 changed files with 346 additions and 60 deletions

View file

@ -1,20 +1,15 @@
import time import time
import asyncio import asyncio
import typing import typing
import struct
from bisect import bisect_right from bisect import bisect_right
from struct import pack, unpack from struct import pack, unpack
from concurrent.futures.thread import ThreadPoolExecutor
from typing import Optional, List, Tuple, Set, DefaultDict, Dict, NamedTuple from typing import Optional, List, Tuple, Set, DefaultDict, Dict, NamedTuple
from prometheus_client import Gauge, Histogram from prometheus_client import Gauge, Histogram
from collections import defaultdict from collections import defaultdict
import array
import lbry import lbry
from lbry.schema.claim import Claim from lbry.schema.claim import Claim
from lbry.schema.mime_types import guess_stream_type
from lbry.wallet.ledger import Ledger, TestNetLedger, RegTestLedger from lbry.wallet.ledger import Ledger, TestNetLedger, RegTestLedger
from lbry.wallet.constants import TXO_TYPES
from lbry.wallet.server.db.common import STREAM_TYPES, CLAIM_TYPES
from lbry.wallet.transaction import OutputScript, Output, Transaction from lbry.wallet.transaction import OutputScript, Output, Transaction
from lbry.wallet.server.tx import Tx, TxOutput, TxInput from lbry.wallet.server.tx import Tx, TxOutput, TxInput
@ -222,6 +217,7 @@ class BlockProcessor:
# attributes used for calculating stake activations and takeovers per block # attributes used for calculating stake activations and takeovers per block
################################# #################################
self.taken_over_names: Set[str] = set()
# txo to pending claim # txo to pending claim
self.txo_to_claim: Dict[Tuple[int, int], StagedClaimtrieItem] = {} self.txo_to_claim: Dict[Tuple[int, int], StagedClaimtrieItem] = {}
# claim hash to pending claim txo # claim hash to pending claim txo
@ -234,6 +230,7 @@ class BlockProcessor:
self.removed_support_txos_by_name_by_claim: DefaultDict[str, DefaultDict[bytes, List[Tuple[int, int]]]] = \ self.removed_support_txos_by_name_by_claim: DefaultDict[str, DefaultDict[bytes, List[Tuple[int, int]]]] = \
defaultdict(lambda: defaultdict(list)) defaultdict(lambda: defaultdict(list))
self.abandoned_claims: Dict[bytes, StagedClaimtrieItem] = {} self.abandoned_claims: Dict[bytes, StagedClaimtrieItem] = {}
self.updated_claims: Set[bytes] = set()
# removed activated support amounts by claim hash # removed activated support amounts by claim hash
self.removed_active_support_amount_by_claim: DefaultDict[bytes, List[int]] = defaultdict(list) self.removed_active_support_amount_by_claim: DefaultDict[bytes, List[int]] = defaultdict(list)
# pending activated support amounts by claim hash # pending activated support amounts by claim hash
@ -513,6 +510,7 @@ class BlockProcessor:
).get_remove_activate_ops() ).get_remove_activate_ops()
) )
previous_amount = previous_claim.amount previous_amount = previous_claim.amount
self.updated_claims.add(claim_hash)
self.db.claim_to_txo[claim_hash] = ClaimToTXOValue( self.db.claim_to_txo[claim_hash] = ClaimToTXOValue(
tx_num, nout, root_tx_num, root_idx, txo.amount, channel_signature_is_valid, claim_name tx_num, nout, root_tx_num, root_idx, txo.amount, channel_signature_is_valid, claim_name
@ -730,6 +728,8 @@ class BlockProcessor:
def _get_pending_claim_amount(self, name: str, claim_hash: bytes, height=None) -> int: def _get_pending_claim_amount(self, name: str, claim_hash: bytes, height=None) -> int:
if (name, claim_hash) in self.activated_claim_amount_by_name_and_hash: if (name, claim_hash) in self.activated_claim_amount_by_name_and_hash:
if claim_hash in self.claim_hash_to_txo:
return self.txo_to_claim[self.claim_hash_to_txo[claim_hash]].amount
return self.activated_claim_amount_by_name_and_hash[(name, claim_hash)] return self.activated_claim_amount_by_name_and_hash[(name, claim_hash)]
if (name, claim_hash) in self.possible_future_claim_amount_by_name_and_hash: if (name, claim_hash) in self.possible_future_claim_amount_by_name_and_hash:
return self.possible_future_claim_amount_by_name_and_hash[(name, claim_hash)] return self.possible_future_claim_amount_by_name_and_hash[(name, claim_hash)]
@ -771,7 +771,7 @@ class BlockProcessor:
_controlling = controlling_claims[_name] _controlling = controlling_claims[_name]
return _controlling return _controlling
names_with_abandoned_controlling_claims: List[str] = [] names_with_abandoned_or_updated_controlling_claims: List[str] = []
# get the claims and supports previously scheduled to be activated at this block # get the claims and supports previously scheduled to be activated at this block
activated_at_height = self.db.get_activated_at_height(height) activated_at_height = self.db.get_activated_at_height(height)
@ -784,7 +784,7 @@ class BlockProcessor:
nothing_is_controlling = not controlling nothing_is_controlling = not controlling
staged_is_controlling = False if not controlling else claim_hash == controlling.claim_hash staged_is_controlling = False if not controlling else claim_hash == controlling.claim_hash
controlling_is_abandoned = False if not controlling else \ controlling_is_abandoned = False if not controlling else \
controlling.claim_hash in names_with_abandoned_controlling_claims controlling.claim_hash in names_with_abandoned_or_updated_controlling_claims
if nothing_is_controlling or staged_is_controlling or controlling_is_abandoned: if nothing_is_controlling or staged_is_controlling or controlling_is_abandoned:
delay = 0 delay = 0
@ -822,7 +822,7 @@ class BlockProcessor:
for claim_hash, staged in self.abandoned_claims.items(): for claim_hash, staged in self.abandoned_claims.items():
controlling = get_controlling(staged.normalized_name) controlling = get_controlling(staged.normalized_name)
if controlling and controlling.claim_hash == claim_hash: if controlling and controlling.claim_hash == claim_hash:
names_with_abandoned_controlling_claims.append(staged.normalized_name) names_with_abandoned_or_updated_controlling_claims.append(staged.normalized_name)
# print(f"\t{staged.name} needs takeover") # print(f"\t{staged.name} needs takeover")
activation = self.db.get_activation(staged.tx_num, staged.position) activation = self.db.get_activation(staged.tx_num, staged.position)
if activation > 0: # db returns -1 for non-existent txos if activation > 0: # db returns -1 for non-existent txos
@ -845,13 +845,31 @@ class BlockProcessor:
continue continue
controlling = get_controlling(name) controlling = get_controlling(name)
if controlling and controlling.claim_hash == claim_hash and \ if controlling and controlling.claim_hash == claim_hash and \
name not in names_with_abandoned_controlling_claims: name not in names_with_abandoned_or_updated_controlling_claims:
abandoned_support_check_need_takeover[(name, claim_hash)].extend(amounts) abandoned_support_check_need_takeover[(name, claim_hash)].extend(amounts)
# get the controlling claims with updates to the claim to check if takeover is needed
for claim_hash in self.updated_claims:
if claim_hash in self.abandoned_claims:
continue
name = self._get_pending_claim_name(claim_hash)
if name is None:
continue
controlling = get_controlling(name)
if controlling and controlling.claim_hash == claim_hash and \
name not in names_with_abandoned_or_updated_controlling_claims:
names_with_abandoned_or_updated_controlling_claims.append(name)
# prepare to activate or delay activation of the pending claims being added this block # prepare to activate or delay activation of the pending claims being added this block
for (tx_num, nout), staged in self.txo_to_claim.items(): for (tx_num, nout), staged in self.txo_to_claim.items():
is_delayed = not staged.is_update
if staged.claim_hash in self.db.claim_to_txo:
prev_txo = self.db.claim_to_txo[staged.claim_hash]
prev_activation = self.db.get_activation(prev_txo.tx_num, prev_txo.position)
if height < prev_activation or prev_activation < 0:
is_delayed = True
self.db_op_stack.extend_ops(get_delayed_activate_ops( self.db_op_stack.extend_ops(get_delayed_activate_ops(
staged.normalized_name, staged.claim_hash, not staged.is_update, tx_num, nout, staged.amount, staged.normalized_name, staged.claim_hash, is_delayed, tx_num, nout, staged.amount,
is_support=False is_support=False
)) ))
@ -919,7 +937,7 @@ class BlockProcessor:
# go through claims where the controlling claim or supports to the controlling claim have been abandoned # go through claims where the controlling claim or supports to the controlling claim have been abandoned
# check if takeovers are needed or if the name node is now empty # check if takeovers are needed or if the name node is now empty
need_reactivate_if_takes_over = {} need_reactivate_if_takes_over = {}
for need_takeover in names_with_abandoned_controlling_claims: for need_takeover in names_with_abandoned_or_updated_controlling_claims:
existing = self.db.get_claim_txos_for_name(need_takeover) existing = self.db.get_claim_txos_for_name(need_takeover)
has_candidate = False has_candidate = False
# add existing claims to the queue for the takeover # add existing claims to the queue for the takeover
@ -995,7 +1013,7 @@ class BlockProcessor:
amounts[controlling.claim_hash] = self._get_pending_effective_amount(name, controlling.claim_hash) amounts[controlling.claim_hash] = self._get_pending_effective_amount(name, controlling.claim_hash)
winning_claim_hash = max(amounts, key=lambda x: amounts[x]) winning_claim_hash = max(amounts, key=lambda x: amounts[x])
if not controlling or (winning_claim_hash != controlling.claim_hash and if not controlling or (winning_claim_hash != controlling.claim_hash and
name in names_with_abandoned_controlling_claims) or \ name in names_with_abandoned_or_updated_controlling_claims) or \
((winning_claim_hash != controlling.claim_hash) and (amounts[winning_claim_hash] > amounts[controlling.claim_hash])): ((winning_claim_hash != controlling.claim_hash) and (amounts[winning_claim_hash] > amounts[controlling.claim_hash])):
amounts_with_future_activations = {claim_hash: amount for claim_hash, amount in amounts.items()} amounts_with_future_activations = {claim_hash: amount for claim_hash, amount in amounts.items()}
amounts_with_future_activations.update( amounts_with_future_activations.update(
@ -1056,13 +1074,13 @@ class BlockProcessor:
k.position, height, name, amount k.position, height, name, amount
).get_activate_ops() ).get_activate_ops()
) )
self.taken_over_names.add(name)
self.db_op_stack.extend_ops(get_takeover_name_ops(name, winning_including_future_activations, height, controlling)) self.db_op_stack.extend_ops(get_takeover_name_ops(name, winning_including_future_activations, height, controlling))
self.touched_claim_hashes.add(winning_including_future_activations) self.touched_claim_hashes.add(winning_including_future_activations)
if controlling and controlling.claim_hash not in self.abandoned_claims: if controlling and controlling.claim_hash not in self.abandoned_claims:
self.touched_claim_hashes.add(controlling.claim_hash) self.touched_claim_hashes.add(controlling.claim_hash)
elif not controlling or (winning_claim_hash != controlling.claim_hash and elif not controlling or (winning_claim_hash != controlling.claim_hash and
name in names_with_abandoned_controlling_claims) or \ name in names_with_abandoned_or_updated_controlling_claims) or \
((winning_claim_hash != controlling.claim_hash) and (amounts[winning_claim_hash] > amounts[controlling.claim_hash])): ((winning_claim_hash != controlling.claim_hash) and (amounts[winning_claim_hash] > amounts[controlling.claim_hash])):
# print(f"\ttakeover by {winning_claim_hash.hex()} at {height}") # print(f"\ttakeover by {winning_claim_hash.hex()} at {height}")
if (name, winning_claim_hash) in need_reactivate_if_takes_over: if (name, winning_claim_hash) in need_reactivate_if_takes_over:
@ -1090,6 +1108,7 @@ class BlockProcessor:
position, height, name, amount position, height, name, amount
).get_activate_ops() ).get_activate_ops()
) )
self.taken_over_names.add(name)
self.db_op_stack.extend_ops(get_takeover_name_ops(name, winning_claim_hash, height, controlling)) self.db_op_stack.extend_ops(get_takeover_name_ops(name, winning_claim_hash, height, controlling))
if controlling and controlling.claim_hash not in self.abandoned_claims: if controlling and controlling.claim_hash not in self.abandoned_claims:
self.touched_claim_hashes.add(controlling.claim_hash) self.touched_claim_hashes.add(controlling.claim_hash)
@ -1114,7 +1133,9 @@ class BlockProcessor:
if controlling and controlling.claim_hash not in self.abandoned_claims: if controlling and controlling.claim_hash not in self.abandoned_claims:
amounts[controlling.claim_hash] = self._get_pending_effective_amount(name, controlling.claim_hash) amounts[controlling.claim_hash] = self._get_pending_effective_amount(name, controlling.claim_hash)
winning = max(amounts, key=lambda x: amounts[x]) winning = max(amounts, key=lambda x: amounts[x])
if (controlling and winning != controlling.claim_hash) or (not controlling and winning): if (controlling and winning != controlling.claim_hash) or (not controlling and winning):
self.taken_over_names.add(name)
# print(f"\ttakeover from abandoned support {controlling.claim_hash.hex()} -> {winning.hex()}") # print(f"\ttakeover from abandoned support {controlling.claim_hash.hex()} -> {winning.hex()}")
self.db_op_stack.extend_ops(get_takeover_name_ops(name, winning, height, controlling)) self.db_op_stack.extend_ops(get_takeover_name_ops(name, winning, height, controlling))
if controlling: if controlling:
@ -1126,6 +1147,13 @@ class BlockProcessor:
self.activation_info_to_send_es[claim_id].append(TrendingNotification(height, added, prev_amount, new_amount)) self.activation_info_to_send_es[claim_id].append(TrendingNotification(height, added, prev_amount, new_amount))
def _get_cumulative_update_ops(self, height: int): def _get_cumulative_update_ops(self, height: int):
# update the last takeover height for names with takeovers
for name in self.taken_over_names:
self.touched_claim_hashes.update(
{claim_hash for claim_hash in self.db.get_claims_for_name(name)
if claim_hash not in self.abandoned_claims}
)
# gather cumulative removed/touched sets to update the search index # gather cumulative removed/touched sets to update the search index
self.removed_claim_hashes.update(set(self.abandoned_claims.keys())) self.removed_claim_hashes.update(set(self.abandoned_claims.keys()))
self.touched_claim_hashes.difference_update(self.removed_claim_hashes) self.touched_claim_hashes.difference_update(self.removed_claim_hashes)
@ -1359,6 +1387,8 @@ class BlockProcessor:
self.touched_claim_hashes.clear() self.touched_claim_hashes.clear()
self.pending_reposted.clear() self.pending_reposted.clear()
self.pending_channel_counts.clear() self.pending_channel_counts.clear()
self.updated_claims.clear()
self.taken_over_names.clear()
async def backup_block(self): async def backup_block(self):
# self.db.assert_flushed(self.flush_data()) # self.db.assert_flushed(self.flush_data())

View file

@ -95,6 +95,7 @@ class SearchIndex:
if index_version != self.VERSION: if index_version != self.VERSION:
self.logger.error("es search index has an incompatible version: %s vs %s", index_version, self.VERSION) self.logger.error("es search index has an incompatible version: %s vs %s", index_version, self.VERSION)
raise IndexVersionMismatch(index_version, self.VERSION) raise IndexVersionMismatch(index_version, self.VERSION)
await self.sync_client.indices.refresh(self.index)
return acked return acked
def stop(self): def stop(self):

View file

@ -1299,10 +1299,10 @@ class HashXHistoryPrefixRow(PrefixRow):
return a.tobytes() return a.tobytes()
@classmethod @classmethod
def unpack_value(cls, data: bytes) -> HashXHistoryValue: def unpack_value(cls, data: bytes) -> array.array:
a = array.array('I') a = array.array('I')
a.frombytes(data) a.frombytes(data)
return HashXHistoryValue(a.tolist()) return a
@classmethod @classmethod
def pack_item(cls, hashX: bytes, height: int, history: typing.List[int]): def pack_item(cls, hashX: bytes, height: int, history: typing.List[int]):

View file

@ -4,6 +4,7 @@ import hashlib
from bisect import bisect_right from bisect import bisect_right
from binascii import hexlify, unhexlify from binascii import hexlify, unhexlify
from collections import defaultdict from collections import defaultdict
from typing import NamedTuple, List
from lbry.testcase import CommandTestCase from lbry.testcase import CommandTestCase
from lbry.wallet.transaction import Transaction, Output from lbry.wallet.transaction import Transaction, Output
from lbry.schema.compat import OldClaimMessage from lbry.schema.compat import OldClaimMessage
@ -11,6 +12,12 @@ from lbry.crypto.hash import sha256
from lbry.crypto.base58 import Base58 from lbry.crypto.base58 import Base58
class ClaimStateValue(NamedTuple):
claim_id: str
activation_height: int
active_in_lbrycrd: bool
class BaseResolveTestCase(CommandTestCase): class BaseResolveTestCase(CommandTestCase):
async def assertResolvesToClaimId(self, name, claim_id): async def assertResolvesToClaimId(self, name, claim_id):
@ -86,8 +93,6 @@ class BaseResolveTestCase(CommandTestCase):
) )
self.assertEqual(len(claim_from_es[0]), 1) self.assertEqual(len(claim_from_es[0]), 1)
self.assertEqual(claim_from_es[0][0]['claim_hash'][::-1].hex(), claim.claim_hash.hex()) self.assertEqual(claim_from_es[0][0]['claim_hash'][::-1].hex(), claim.claim_hash.hex())
self.assertEqual(claim_from_es[0][0]['claim_id'], claim.claim_hash.hex()) self.assertEqual(claim_from_es[0][0]['claim_id'], claim.claim_hash.hex())
self.assertEqual(claim_from_es[0][0]['activation_height'], claim.activation_height) self.assertEqual(claim_from_es[0][0]['activation_height'], claim.activation_height)
self.assertEqual(claim_from_es[0][0]['last_take_over_height'], claim.last_takeover_height) self.assertEqual(claim_from_es[0][0]['last_take_over_height'], claim.last_takeover_height)
@ -607,63 +612,313 @@ class ResolveClaimTakeovers(BaseResolveTestCase):
await self.assertNoClaimForName(name) await self.assertNoClaimForName(name)
await self._test_activation_delay() await self._test_activation_delay()
async def test_claim_and_update_delays(self): async def create_stream_claim(self, amount: str, name='derp') -> str:
return (await self.stream_create(name, amount, allow_duplicate_name=True))['outputs'][0]['claim_id']
async def assertNameState(self, height: int, name: str, winning_claim_id: str, last_takeover_height: int,
non_winning_claims: List[ClaimStateValue]):
self.assertEqual(height, self.conductor.spv_node.server.bp.db.db_height)
await self.assertMatchClaimIsWinning(name, winning_claim_id)
for non_winning in non_winning_claims:
claim = await self.assertMatchClaim(
non_winning.claim_id, is_active_in_lbrycrd=non_winning.active_in_lbrycrd
)
self.assertEqual(non_winning.activation_height, claim.activation_height)
self.assertEqual(last_takeover_height, claim.last_takeover_height)
async def test_delay_takeover_with_update(self):
name = 'derp' name = 'derp'
first_claim_id = (await self.stream_create(name, '0.2', allow_duplicate_name=True))['outputs'][0]['claim_id'] first_claim_id = await self.create_stream_claim('0.2', name)
await self.assertMatchClaimIsWinning(name, first_claim_id) await self.assertMatchClaimIsWinning(name, first_claim_id)
await self.generate(320) await self.generate(320)
second_claim_id = (await self.stream_create(name, '0.1', allow_duplicate_name=True))['outputs'][0]['claim_id'] second_claim_id = await self.create_stream_claim('0.1', name)
third_claim_id = (await self.stream_create(name, '0.1', allow_duplicate_name=True))['outputs'][0]['claim_id'] third_claim_id = await self.create_stream_claim('0.1', name)
await self.generate(8) await self.generate(8)
await self.assertNameState(
self.assertEqual(537, self.conductor.spv_node.server.bp.db.db_height) height=537, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
await self.assertMatchClaimIsWinning(name, first_claim_id) non_winning_claims=[
second_claim = await self.assertMatchClaim(second_claim_id, is_active_in_lbrycrd=False) ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=False),
self.assertEqual(538, second_claim.activation_height) ClaimStateValue(third_claim_id, activation_height=539, active_in_lbrycrd=False)
self.assertEqual(207, second_claim.last_takeover_height) ]
third_claim = await self.assertMatchClaim(third_claim_id, is_active_in_lbrycrd=False) )
self.assertEqual(539, third_claim.activation_height)
self.assertEqual(207, third_claim.last_takeover_height)
await self.generate(1) await self.generate(1)
await self.assertNameState(
self.assertEqual(538, self.conductor.spv_node.server.bp.db.db_height) height=538, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
await self.assertMatchClaimIsWinning(name, first_claim_id) non_winning_claims=[
second_claim = await self.assertMatchClaim(second_claim_id) ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=True),
self.assertEqual(538, second_claim.activation_height) ClaimStateValue(third_claim_id, activation_height=539, active_in_lbrycrd=False)
self.assertEqual(207, second_claim.last_takeover_height) ]
third_claim = await self.assertMatchClaim(third_claim_id, is_active_in_lbrycrd=False) )
self.assertEqual(539, third_claim.activation_height)
self.assertEqual(207, third_claim.last_takeover_height)
await self.generate(1) await self.generate(1)
await self.assertNameState(
self.assertEqual(539, self.conductor.spv_node.server.bp.db.db_height) height=539, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
await self.assertMatchClaimIsWinning(name, first_claim_id) non_winning_claims=[
second_claim = await self.assertMatchClaim(second_claim_id) ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=True),
self.assertEqual(538, second_claim.activation_height) ClaimStateValue(third_claim_id, activation_height=539, active_in_lbrycrd=True)
self.assertEqual(207, second_claim.last_takeover_height) ]
third_claim = await self.assertMatchClaim(third_claim_id) )
self.assertEqual(539, third_claim.activation_height)
self.assertEqual(207, third_claim.last_takeover_height)
await self.daemon.jsonrpc_stream_update(third_claim_id, '0.21') await self.daemon.jsonrpc_stream_update(third_claim_id, '0.21')
await self.generate(1) await self.generate(1)
await self.assertNameState(
height=540, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
non_winning_claims=[
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=True),
ClaimStateValue(third_claim_id, activation_height=550, active_in_lbrycrd=False)
]
)
self.assertEqual(540, self.conductor.spv_node.server.bp.db.db_height) await self.generate(9)
await self.assertNameState(
height=549, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
non_winning_claims=[
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=True),
ClaimStateValue(third_claim_id, activation_height=550, active_in_lbrycrd=False)
]
)
await self.generate(1)
await self.assertNameState(
height=550, name=name, winning_claim_id=third_claim_id, last_takeover_height=550,
non_winning_claims=[
ClaimStateValue(first_claim_id, activation_height=207, active_in_lbrycrd=True),
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=True)
]
)
async def test_delay_takeover_with_update_then_update_to_lower_before_takeover(self):
name = 'derp'
first_claim_id = await self.create_stream_claim('0.2', name)
await self.assertMatchClaimIsWinning(name, first_claim_id) await self.assertMatchClaimIsWinning(name, first_claim_id)
second_claim = await self.assertMatchClaim(second_claim_id) await self.generate(320)
self.assertEqual(538, second_claim.activation_height) second_claim_id = await self.create_stream_claim('0.1', name)
self.assertEqual(207, second_claim.last_takeover_height) third_claim_id = await self.create_stream_claim('0.1', name)
third_claim = await self.assertMatchClaim(third_claim_id, is_active_in_lbrycrd=False) await self.generate(8)
self.assertEqual(550, third_claim.activation_height) await self.assertNameState(
self.assertEqual(207, third_claim.last_takeover_height) height=537, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
non_winning_claims=[
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=False),
ClaimStateValue(third_claim_id, activation_height=539, active_in_lbrycrd=False)
]
)
await self.generate(1)
await self.assertNameState(
height=538, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
non_winning_claims=[
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=True),
ClaimStateValue(third_claim_id, activation_height=539, active_in_lbrycrd=False)
]
)
await self.generate(1)
await self.assertNameState(
height=539, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
non_winning_claims=[
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=True),
ClaimStateValue(third_claim_id, activation_height=539, active_in_lbrycrd=True)
]
)
await self.daemon.jsonrpc_stream_update(third_claim_id, '0.21')
await self.generate(1)
await self.assertNameState(
height=540, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
non_winning_claims=[
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=True),
ClaimStateValue(third_claim_id, activation_height=550, active_in_lbrycrd=False)
]
)
await self.generate(8)
await self.assertNameState(
height=548, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
non_winning_claims=[
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=True),
ClaimStateValue(third_claim_id, activation_height=550, active_in_lbrycrd=False)
]
)
await self.daemon.jsonrpc_stream_update(third_claim_id, '0.09')
await self.generate(1)
await self.assertNameState(
height=549, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
non_winning_claims=[
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=True),
ClaimStateValue(third_claim_id, activation_height=559, active_in_lbrycrd=False)
]
)
await self.generate(10) await self.generate(10)
self.assertEqual(550, self.conductor.spv_node.server.bp.db.db_height) await self.assertNameState(
await self.assertMatchClaimIsWinning(name, third_claim_id) height=559, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
non_winning_claims=[
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=True),
ClaimStateValue(third_claim_id, activation_height=559, active_in_lbrycrd=True)
]
)
async def test_delay_takeover_with_update_then_update_to_lower_on_takeover(self):
name = 'derp'
first_claim_id = await self.create_stream_claim('0.2', name)
await self.assertMatchClaimIsWinning(name, first_claim_id)
await self.generate(320)
second_claim_id = await self.create_stream_claim('0.1', name)
third_claim_id = await self.create_stream_claim('0.1', name)
await self.generate(8)
await self.assertNameState(
height=537, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
non_winning_claims=[
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=False),
ClaimStateValue(third_claim_id, activation_height=539, active_in_lbrycrd=False)
]
)
await self.generate(1)
await self.assertNameState(
height=538, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
non_winning_claims=[
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=True),
ClaimStateValue(third_claim_id, activation_height=539, active_in_lbrycrd=False)
]
)
await self.generate(1)
await self.assertNameState(
height=539, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
non_winning_claims=[
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=True),
ClaimStateValue(third_claim_id, activation_height=539, active_in_lbrycrd=True)
]
)
await self.daemon.jsonrpc_stream_update(third_claim_id, '0.21')
await self.generate(1)
await self.assertNameState(
height=540, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
non_winning_claims=[
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=True),
ClaimStateValue(third_claim_id, activation_height=550, active_in_lbrycrd=False)
]
)
await self.generate(8)
await self.assertNameState(
height=548, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
non_winning_claims=[
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=True),
ClaimStateValue(third_claim_id, activation_height=550, active_in_lbrycrd=False)
]
)
await self.generate(1)
await self.assertNameState(
height=549, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
non_winning_claims=[
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=True),
ClaimStateValue(third_claim_id, activation_height=550, active_in_lbrycrd=False)
]
)
await self.daemon.jsonrpc_stream_update(third_claim_id, '0.09')
await self.generate(1)
await self.assertNameState(
height=550, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
non_winning_claims=[
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=True),
ClaimStateValue(third_claim_id, activation_height=560, active_in_lbrycrd=False)
]
)
await self.generate(10)
await self.assertNameState(
height=560, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
non_winning_claims=[
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=True),
ClaimStateValue(third_claim_id, activation_height=560, active_in_lbrycrd=True)
]
)
async def test_delay_takeover_with_update_then_update_to_lower_after_takeover(self):
name = 'derp'
first_claim_id = await self.create_stream_claim('0.2', name)
await self.assertMatchClaimIsWinning(name, first_claim_id)
await self.generate(320)
second_claim_id = await self.create_stream_claim('0.1', name)
third_claim_id = await self.create_stream_claim('0.1', name)
await self.generate(8)
await self.assertNameState(
height=537, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
non_winning_claims=[
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=False),
ClaimStateValue(third_claim_id, activation_height=539, active_in_lbrycrd=False)
]
)
await self.generate(1)
await self.assertNameState(
height=538, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
non_winning_claims=[
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=True),
ClaimStateValue(third_claim_id, activation_height=539, active_in_lbrycrd=False)
]
)
await self.generate(1)
await self.assertNameState(
height=539, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
non_winning_claims=[
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=True),
ClaimStateValue(third_claim_id, activation_height=539, active_in_lbrycrd=True)
]
)
await self.daemon.jsonrpc_stream_update(third_claim_id, '0.21')
await self.generate(1)
await self.assertNameState(
height=540, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
non_winning_claims=[
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=True),
ClaimStateValue(third_claim_id, activation_height=550, active_in_lbrycrd=False)
]
)
await self.generate(8)
await self.assertNameState(
height=548, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
non_winning_claims=[
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=True),
ClaimStateValue(third_claim_id, activation_height=550, active_in_lbrycrd=False)
]
)
await self.generate(1)
await self.assertNameState(
height=549, name=name, winning_claim_id=first_claim_id, last_takeover_height=207,
non_winning_claims=[
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=True),
ClaimStateValue(third_claim_id, activation_height=550, active_in_lbrycrd=False)
]
)
await self.generate(1)
await self.assertNameState(
height=550, name=name, winning_claim_id=third_claim_id, last_takeover_height=550,
non_winning_claims=[
ClaimStateValue(first_claim_id, activation_height=207, active_in_lbrycrd=True),
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=True)
]
)
await self.daemon.jsonrpc_stream_update(third_claim_id, '0.09')
await self.generate(1)
await self.assertNameState(
height=551, name=name, winning_claim_id=first_claim_id, last_takeover_height=551,
non_winning_claims=[
ClaimStateValue(second_claim_id, activation_height=538, active_in_lbrycrd=True),
ClaimStateValue(third_claim_id, activation_height=551, active_in_lbrycrd=True)
]
)
async def test_resolve_signed_claims_with_fees(self): async def test_resolve_signed_claims_with_fees(self):
channel_name = '@abc' channel_name = '@abc'