Skip to content

Commit

Permalink
Blockchain timestamps fix (Chia-Network#2302)
Browse files Browse the repository at this point in the history
* incomplete push for debugging

* block timestamp in mempool

* rename

* farm tx block

Co-authored-by: matt <[email protected]>
  • Loading branch information
Yostra and matt-o-how authored Apr 21, 2021
1 parent 572110b commit 328e4cd
Show file tree
Hide file tree
Showing 4 changed files with 31 additions and 25 deletions.
24 changes: 11 additions & 13 deletions chia/full_node/mempool_check_conditions.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,38 +71,36 @@ def mempool_assert_relative_block_height_exceeds(
return None


def mempool_assert_absolute_time_exceeds(
condition: ConditionWithArgs, timestamp: Optional[uint64] = None
) -> Optional[Err]:
def mempool_assert_absolute_time_exceeds(condition: ConditionWithArgs, timestamp: uint64) -> Optional[Err]:
"""
Check if the current time in millis exceeds the time specified by condition
Check if the current time in seconds exceeds the time specified by condition
"""
try:
expected_mili_time = int_from_bytes(condition.vars[0])
expected_seconds = int_from_bytes(condition.vars[0])
except ValueError:
return Err.INVALID_CONDITION

if timestamp is None:
timestamp = uint64(int(time.time() * 1000))
if timestamp < expected_mili_time:
timestamp = uint64(int(time.time()))
if timestamp < expected_seconds:
return Err.ASSERT_SECONDS_ABSOLUTE_FAILED
return None


def mempool_assert_relative_time_exceeds(
condition: ConditionWithArgs, unspent: CoinRecord, timestamp: Optional[uint64] = None
condition: ConditionWithArgs, unspent: CoinRecord, timestamp: uint64
) -> Optional[Err]:
"""
Check if the current time in millis exceeds the time specified by condition
Check if the current time in seconds exceeds the time specified by condition
"""
try:
expected_mili_time = int_from_bytes(condition.vars[0])
expected_seconds = int_from_bytes(condition.vars[0])
except ValueError:
return Err.INVALID_CONDITION

if timestamp is None:
timestamp = uint64(int(time.time() * 1000))
if timestamp < expected_mili_time + unspent.timestamp:
timestamp = uint64(int(time.time()))
if timestamp < expected_seconds + unspent.timestamp:
return Err.ASSERT_SECONDS_RELATIVE_FAILED
return None

Expand Down Expand Up @@ -196,7 +194,7 @@ def mempool_check_conditions_dict(
puzzle_announcement_names: Set[bytes32],
conditions_dict: Dict[ConditionOpcode, List[ConditionWithArgs]],
prev_transaction_block_height: uint32,
timestamp: Optional[uint64] = None,
timestamp: uint64,
) -> Optional[Err]:
"""
Check all conditions against current state.
Expand Down
10 changes: 8 additions & 2 deletions chia/full_node/mempool_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -274,13 +274,14 @@ async def add_spendbundle(
elif name in additions_dict:
removal_coin = additions_dict[name]
# TODO(straya): what timestamp to use here?
assert self.peak.timestamp is not None
removal_record = CoinRecord(
removal_coin,
uint32(self.peak.height + 1), # In mempool, so will be included in next height
uint32(0),
False,
False,
uint64(int(time.time())),
uint64(self.peak.timestamp + 1),
)

assert removal_record is not None
Expand Down Expand Up @@ -367,12 +368,14 @@ async def add_spendbundle(
chialisp_height = (
self.peak.prev_transaction_block_height if not self.peak.is_transaction_block else self.peak.height
)
assert self.peak.timestamp is not None
error = mempool_check_conditions_dict(
coin_record,
coin_announcements_in_spend,
puzzle_announcements_in_spend,
npc.condition_dict,
uint32(chialisp_height),
self.peak.timestamp,
)

if error:
Expand Down Expand Up @@ -465,9 +468,12 @@ async def new_peak(self, new_peak: Optional[BlockRecord]) -> List[Tuple[SpendBun
"""
if new_peak is None:
return []
if new_peak.is_transaction_block is False:
return []
if self.peak == new_peak:
return []
if int(time.time()) <= self.constants.INITIAL_FREEZE_END_TIMESTAMP:
assert new_peak.timestamp is not None
if new_peak.timestamp <= self.constants.INITIAL_FREEZE_END_TIMESTAMP:
return []

self.peak = new_peak
Expand Down
19 changes: 11 additions & 8 deletions tests/core/full_node/test_mempool.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from chia.full_node.mempool import Mempool
from chia.protocols import full_node_protocol
from chia.simulator.simulator_protocol import FarmNewBlockProtocol
from chia.types.announcement import Announcement
from chia.types.blockchain_format.coin import Coin
from chia.types.coin_solution import CoinSolution
Expand Down Expand Up @@ -458,7 +459,7 @@ async def test_assert_time_exceeds(self, two_nodes):

await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 3)

time_now = uint64(int(time() * 1000))
time_now = uint64(int(time()))

cvp = ConditionWithArgs(ConditionOpcode.ASSERT_SECONDS_ABSOLUTE, [time_now.to_bytes(8, "big")])
dic = {cvp.opcode: [cvp]}
Expand All @@ -474,7 +475,7 @@ async def test_assert_time_exceeds(self, two_nodes):
assert sb1 is spend_bundle1

@pytest.mark.asyncio
async def test_assert_time_exceeds_both_cases(self, two_nodes):
async def test_assert_time_relative_exceeds(self, two_nodes):
reward_ph = WALLET_A.get_new_puzzlehash()
full_node_1, full_node_2, server_1, server_2 = two_nodes
blocks = await full_node_1.get_all_full_blocks()
Expand All @@ -493,20 +494,22 @@ async def test_assert_time_exceeds_both_cases(self, two_nodes):

await time_out_assert(60, node_height_at_least, True, full_node_1, start_height + 3)

time_now = uint64(int(time() * 1000))
time_now_plus_3 = time_now + 3000
time_relative = uint64(3)

cvp = ConditionWithArgs(ConditionOpcode.ASSERT_SECONDS_ABSOLUTE, [time_now_plus_3.to_bytes(8, "big")])
cvp = ConditionWithArgs(ConditionOpcode.ASSERT_SECONDS_RELATIVE, [time_relative.to_bytes(8, "big")])
dic = {cvp.opcode: [cvp]}

spend_bundle1 = generate_test_spend_bundle(list(blocks[-1].get_included_reward_coins())[0], dic)

assert spend_bundle1 is not None

tx1: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(spend_bundle1)
await full_node_1.respond_transaction(tx1, peer)

# Sleep so that 3 sec passes
await asyncio.sleep(3)
sb1 = full_node_1.full_node.mempool_manager.get_spendbundle(spend_bundle1.name())
assert sb1 is None

for i in range(0, 4):
await full_node_1.farm_new_transaction_block(FarmNewBlockProtocol(32 * b"0"))

tx2: full_node_protocol.RespondTransaction = full_node_protocol.RespondTransaction(spend_bundle1)
await full_node_1.respond_transaction(tx2, peer)
Expand Down
3 changes: 1 addition & 2 deletions tests/wallet/rpc/test_wallet_rpc.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,8 +118,7 @@ async def tx_in_mempool():
assert (await client.get_wallet_balance("1"))["confirmed_wallet_balance"] == initial_funds

for i in range(0, 5):
await client.farm_block(encode_puzzle_hash(ph_2, "xch"))
await asyncio.sleep(0.5)
await full_node_api.farm_new_transaction_block(FarmNewBlockProtocol(ph_2))

async def eventual_balance():
return (await client.get_wallet_balance("1"))["confirmed_wallet_balance"]
Expand Down

0 comments on commit 328e4cd

Please sign in to comment.