Skip to content
Snippets Groups Projects
Commit 66c632d2 authored by inso's avatar inso
Browse files

Fix some wrong behaviours due to rollbacks

parent a84f0acc
No related branches found
No related tags found
No related merge requests found
doc/uml/tx_lifecycle.png

63.8 KiB | W: | H:

doc/uml/tx_lifecycle.png

65.7 KiB | W: | H:

doc/uml/tx_lifecycle.png
doc/uml/tx_lifecycle.png
doc/uml/tx_lifecycle.png
doc/uml/tx_lifecycle.png
  • 2-up
  • Swipe
  • Onion skin
@startuml note "With B a Block\nWith W the Median fork window\nWith Cur the current block of the main branch\nWith T a time" as N1 state Local_Tx { [*] --> To_send : Signed locally To_send : B = none To_send --> Awaiting : Node answered\n200 OK to POST Awaiting : Time = Cur.MedianTime Awaiting --> Refused : Not registered in [T; T+W*MedianTime] Refused --> To_send : Send back Refused --> [*] : Drop } state Registered { [*] --> Validating : Posted\nsin the blockchain Validating : B = Block containing the Tx Awaiting --> Validating : Found in the blockchain Validating --> Validated : Cur-B > W Validated --> Validating : Blockchain\nrollback Validated --> Awaiting : Blockchain\nrollback\ntx local removed Validated --> [*] : Blockchain\nrollback\ntx removed Validating --> [*] : Blockchain\nrollback\ntx removed } @enduml
\ No newline at end of file
@startuml note "With B a Block\nWith W the Median fork window\nWith Cur the current block of the main branch\nWith T a time" as N1 state Local_Tx { [*] --> To_send : Signed locally To_send : B = none To_send --> Awaiting : Node answered\n200 OK to POST Awaiting : Time = Cur.MedianTime Awaiting --> Refused : Not registered in [T; T+W*MedianTime] Refused --> To_send : Send back Refused --> [*] : Drop } state Registered { [*] --> Validating : Posted\nsin the blockchain Validating : B = Block containing the Tx Awaiting --> Validating : Found in the blockchain Validating --> Validated : Cur-B > W Validated --> Validating : Blockchain\nrollback\ntx in fork window Validated --> Awaiting : Blockchain\nrollback\ntx local removed Validated --> [*] : Blockchain\nrollback\ntx removed Validating --> [*] : Blockchain\nrollback\ntx removed } @enduml
\ No newline at end of file
......
......@@ -252,6 +252,7 @@ class Application(QObject):
self.accounts[account_name] = account
for community in account.communities:
community.network.blockchain_rollback.connect(community.rollback_cache)
community.network.new_block_mined.connect(lambda b, co=community: account.refresh_transactions(self, co))
community.network.blockchain_rollback.connect(lambda b, co=community: account.rollback_transaction(self, co))
......
......@@ -309,6 +309,9 @@ class Community(QObject):
def stop_coroutines(self):
self.network.stop_coroutines()
def rollback_cache(self):
self._bma_access.rollback()
def jsonify(self):
"""
Jsonify the community datas.
......
......@@ -165,6 +165,12 @@ class BmaAccess(QObject):
return True
return False
def rollback(self):
"""
When a rollback is detected, we move the rollback cursor to 0
"""
self._rollback_to = 0
@asyncio.coroutine
def future_request(self, request, req_args={}, get_args={}):
"""
......
......@@ -88,9 +88,11 @@ class Transfer(QObject):
(TransferState.VALIDATING, (bool, Block)):
((self._rollback_and_removed, lambda r, b: self._drop(), TransferState.DROPPED),),
(TransferState.VALIDATED, (bool, Block, int)):
((self._rollback_in_fork_window, lambda r, b: self._be_validating(b), TransferState.VALIDATING),),
(TransferState.VALIDATED, (bool, Block)):
(
(self._rollback_still_present, lambda r, b: self._be_validating(b), TransferState.VALIDATING),
(self._rollback_and_removed, lambda r, b: self._drop(), TransferState.DROPPED),
(self._rollback_and_local, lambda r, b: self._wait(b), TransferState.AWAITING),
),
......@@ -227,15 +229,15 @@ class Transfer(QObject):
return self.sha_hash not in [t.sha_hash for t in block.transactions]
return False
def _rollback_still_present(self, rollback, block):
def _rollback_in_fork_window(self, rollback, current_block, fork_window):
"""
Check if the transfer is not in the block anymore
:param bool rollback: True if we are in a rollback procedure
:param ucoinpy.documents.Block block: The block to check for the transaction
:param ucoinpy.documents.Block current_block: The block to check for the transaction
:return: True if the transfer is found in the block
"""
if rollback and block.blockid == self.blockid:
return self.sha_hash in [t.sha_hash for t in block.transactions]
if rollback:
return self.blockid.number + fork_window > current_block.number
return False
def _rollback_and_local(self, rollback, block):
......
......@@ -74,6 +74,34 @@ class TxHistory():
def stop_coroutines(self):
self._stop_coroutines = True
def _get_block_doc(self, community, number):
"""
Retrieve the current block document
:param cutecoin.core.Community community: The community we look for a block
:param int number: The block number to retrieve
:return: the block doc or None if no block was found
"""
tries = 0
block_doc = None
block = None
while block is None and tries < 3:
try:
block = yield from community.bma_access.future_request(bma.blockchain.Block,
req_args={'number': number})
signed_raw = "{0}{1}\n".format(block['raw'],
block['signature'])
try:
block_doc = Block.from_signed_raw(signed_raw)
except TypeError:
logging.debug("Error in {0}".format(number))
block = None
tries += 1
except ValueError as e:
if '404' in str(e):
block = None
tries += 1
return block_doc
@asyncio.coroutine
def _parse_transaction(self, community, tx, blockid,
mediantime, received_list, txid):
......@@ -153,36 +181,17 @@ class TxHistory():
return None
@asyncio.coroutine
def _parse_block(self, community, block_number, received_list, current_block, txmax):
def _parse_block(self, community, block_number, received_list, txmax):
"""
Parse a block
:param cutecoin.core.Community community: The community
:param int block_number: The block to request
:param list received_list: The list where we are appending transactions
:param int current_block: The current block of the network
:param int txmax: Latest tx id
:return: The list of transfers sent
"""
block = None
block_doc = None
tries = 0
while block is None and tries < 3:
try:
block = yield from community.bma_access.future_request(bma.blockchain.Block,
req_args={'number': block_number})
signed_raw = "{0}{1}\n".format(block['raw'],
block['signature'])
transfers = []
try:
block_doc = Block.from_signed_raw(signed_raw)
except TypeError:
logging.debug("Error in {0}".format(block_number))
block = None
tries += 1
except ValueError as e:
if '404' in str(e):
block = None
tries += 1
block_doc = yield from self._get_block_doc(community, block_number)
transfers = []
if block_doc:
for transfer in [t for t in self._transfers if t.state == TransferState.AWAITING]:
transfer.run_state_transitions((False, block_doc))
......@@ -259,7 +268,7 @@ class TxHistory():
# We parse only blocks with transactions
if block_number_from in blocks_with_tx:
transfers = yield from self._parse_block(community, block_number_from,
received_list, block_to,
received_list,
udid + len(new_transfers))
new_transfers += transfers
......@@ -300,25 +309,9 @@ class TxHistory():
:param cutecoin.core.Community community: The community
:param int block_number: The block to check for transfers
"""
block = None
block_doc = None
tries = 0
while block is None and tries < 3:
try:
block = yield from community.bma_access.future_request(bma.blockchain.Block,
req_args={'number': block_number})
signed_raw = "{0}{1}\n".format(block['raw'],
block['signature'])
try:
block_doc = Block.from_signed_raw(signed_raw)
except TypeError:
logging.debug("Error in {0}".format(block_number))
block = None
tries += 1
except ValueError as e:
if '404' in str(e):
block = None
tries += 1
block_doc = yield from self._get_block_doc(community, block_number)
# We check if transactions are still present
for transfer in [t for t in self._transfers
if t.state in (TransferState.VALIDATING, TransferState.VALIDATED) and
t.blockid.number == block_number]:
......@@ -341,10 +334,19 @@ class TxHistory():
tx_blocks = [tx.blockid.number for tx in self._transfers
if tx.state in (TransferState.VALIDATED, TransferState.VALIDATING) and
tx.blockid is not None]
tx_blocks.reverse()
for i, block_number in enumerate(tx_blocks):
self.wallet.refresh_progressed.emit(i, len(tx_blocks), self.wallet.pubkey)
if (yield from self._check_block(community, block_number)):
return
break
current_block = yield from self._get_block_doc(community, community.network.current_blockid.number)
members_pubkeys = yield from community.members_pubkeys()
fork_window = community.network.fork_window(members_pubkeys)
# We check if transactions VALIDATED are in the fork window now
for transfer in [t for t in self._transfers
if t.state == TransferState.VALIDATED]:
transfer.run_state_transitions((True, current_block, fork_window))
except NoPeerAvailable:
logging.debug("No peer available")
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment