mirror of
https://github.com/simplex-chat/simplexmq.git
synced 2026-09-17 05:55:04 +00:00
compare block instead of wall clock
This commit is contained in:
@@ -152,10 +152,12 @@ about it. `expires` and `graceEnds` are Unix timestamps in seconds; both are
|
||||
|
||||
The split between `grace` and `expired` mirrors the registrar's own
|
||||
`available(id)` rule (`expires + GRACE_PERIOD < now`), with `GRACE_PERIOD` read
|
||||
from the contract rather than assumed. Note that `available(id)` alone cannot
|
||||
distinguish these: it is also true for a name nobody ever registered, since
|
||||
`0 + GRACE_PERIOD < now`. A zero expiry is what separates *never taken* from
|
||||
*taken and since released*.
|
||||
from the contract rather than assumed and `now` taken from the latest block's
|
||||
timestamp rather than the host clock — the registrar compares against that same
|
||||
clock, so a skewed machine cannot misstate a registration. Note that
|
||||
`available(id)` alone cannot distinguish these: it is also true for a name
|
||||
nobody ever registered, since `0 + GRACE_PERIOD < now`. A zero expiry is what
|
||||
separates *never taken* from *taken and since released*.
|
||||
|
||||
Subnames report the status of the 2LD they sit under, which is the useful
|
||||
answer — a subname is only as valid as the name above it.
|
||||
|
||||
@@ -66,7 +66,6 @@ import hashlib
|
||||
import json
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
||||
from urllib.parse import unquote, urlparse
|
||||
from urllib.request import Request, urlopen
|
||||
@@ -202,6 +201,16 @@ def node_of(name: str) -> bytes:
|
||||
# ---------- Registration status ----------
|
||||
|
||||
|
||||
def chain_now() -> int:
|
||||
"""The latest block's timestamp - the same clock the registrar reads.
|
||||
|
||||
Asked explicitly rather than taken from the host clock, so a skewed clock
|
||||
on this machine cannot misstate a registration.
|
||||
"""
|
||||
block = rpc("eth_getBlockByNumber", ["latest", False])
|
||||
return decode_uint(block["timestamp"])
|
||||
|
||||
|
||||
def grace_period(registrar: str) -> int:
|
||||
"""The registrar's own GRACE_PERIOD, in seconds.
|
||||
|
||||
@@ -279,7 +288,7 @@ def name_status(name: str):
|
||||
status, grace = "unregistered", 0
|
||||
else:
|
||||
grace = grace_period(registrar)
|
||||
status = expiry_status(expires, grace, int(time.time()))
|
||||
status = expiry_status(expires, grace, chain_now())
|
||||
|
||||
# `reserved` only displaces the two states that read as "you could take
|
||||
# this". A registered name is registered, and one in grace belongs to its
|
||||
|
||||
@@ -101,12 +101,13 @@ class EncodedLabelhashTests(unittest.TestCase):
|
||||
GRACE = 90 * 86400
|
||||
|
||||
def setUp(self):
|
||||
self._saved = (snrc.REGISTRARS, snrc.CONTROLLERS, snrc.eth_call)
|
||||
self._saved = (snrc.REGISTRARS, snrc.CONTROLLERS, snrc.eth_call, snrc.chain_now)
|
||||
snrc.REGISTRARS = {"testing": self.REGISTRAR}
|
||||
snrc.CONTROLLERS = {"testing": ""}
|
||||
snrc.chain_now = lambda: int(time.time())
|
||||
|
||||
def tearDown(self):
|
||||
snrc.REGISTRARS, snrc.CONTROLLERS, snrc.eth_call = self._saved
|
||||
snrc.REGISTRARS, snrc.CONTROLLERS, snrc.eth_call, snrc.chain_now = self._saved
|
||||
|
||||
def test_the_encoded_form_is_recognised(self):
|
||||
self.assertTrue(
|
||||
@@ -217,14 +218,44 @@ class NameStatusTests(unittest.TestCase):
|
||||
return eth_call
|
||||
|
||||
def setUp(self):
|
||||
self._saved = (snrc.REGISTRARS, snrc.CONTROLLERS, snrc.eth_call)
|
||||
self._saved = (
|
||||
snrc.REGISTRARS,
|
||||
snrc.CONTROLLERS,
|
||||
snrc.eth_call,
|
||||
snrc.chain_now,
|
||||
snrc.rpc,
|
||||
)
|
||||
snrc.REGISTRARS = {"testing": self.REGISTRAR}
|
||||
# These cases are about expiry alone. ReservedTests covers what a
|
||||
# configured controller adds.
|
||||
snrc.CONTROLLERS = {"testing": ""}
|
||||
snrc.chain_now = lambda: int(time.time())
|
||||
|
||||
def tearDown(self):
|
||||
snrc.REGISTRARS, snrc.CONTROLLERS, snrc.eth_call = self._saved
|
||||
(
|
||||
snrc.REGISTRARS,
|
||||
snrc.CONTROLLERS,
|
||||
snrc.eth_call,
|
||||
snrc.chain_now,
|
||||
snrc.rpc,
|
||||
) = self._saved
|
||||
|
||||
def test_now_is_the_latest_blocks_timestamp(self):
|
||||
# setUp replaces chain_now with the fixture clock; this is about the
|
||||
# real one, saved as the 4th element of the setUp snapshot
|
||||
real_chain_now = self._saved[3]
|
||||
snrc.rpc = lambda method, params: {"timestamp": "0x65f1a2c0", "number": "0x123"}
|
||||
self.assertEqual(real_chain_now(), 0x65F1A2C0)
|
||||
|
||||
def test_status_reads_the_chain_clock_not_the_host_clock(self):
|
||||
"""The registrar compares expiry to block.timestamp, so the resolver
|
||||
must too - a host clock years ahead must not turn a live name into a
|
||||
claimable one."""
|
||||
future = int(time.time()) + 3600
|
||||
snrc.eth_call = self._expiry(future)
|
||||
self.assertEqual(snrc.name_status("alice.testing")["status"], "registered")
|
||||
snrc.chain_now = lambda: future + 3650 * 86400
|
||||
self.assertEqual(snrc.name_status("alice.testing")["status"], "expired")
|
||||
|
||||
def test_zero_expiry_means_never_registered(self):
|
||||
snrc.eth_call = self._expiry(0)
|
||||
@@ -313,12 +344,13 @@ class ReservedTests(unittest.TestCase):
|
||||
CONTROLLER = "0x281ca41311c2aa808c917c4674639d7567b75714"
|
||||
|
||||
def setUp(self):
|
||||
self._saved = (snrc.REGISTRARS, snrc.CONTROLLERS, snrc.eth_call)
|
||||
self._saved = (snrc.REGISTRARS, snrc.CONTROLLERS, snrc.eth_call, snrc.chain_now)
|
||||
snrc.REGISTRARS = {"testing": self.REGISTRAR}
|
||||
snrc.CONTROLLERS = {"testing": self.CONTROLLER}
|
||||
snrc.chain_now = lambda: int(time.time())
|
||||
|
||||
def tearDown(self):
|
||||
snrc.REGISTRARS, snrc.CONTROLLERS, snrc.eth_call = self._saved
|
||||
snrc.REGISTRARS, snrc.CONTROLLERS, snrc.eth_call, snrc.chain_now = self._saved
|
||||
|
||||
def _chain(self, expires, reserved):
|
||||
def eth_call(to, data):
|
||||
@@ -380,10 +412,12 @@ class ReservedReasonTests(unittest.TestCase):
|
||||
snrc.REGISTRARS,
|
||||
snrc.CONTROLLERS,
|
||||
snrc.eth_call,
|
||||
snrc.chain_now,
|
||||
)
|
||||
snrc.REGISTRIES = {"testing": self.REGISTRY}
|
||||
snrc.REGISTRARS = {"testing": self.REGISTRAR}
|
||||
snrc.CONTROLLERS = {"testing": self.CONTROLLER}
|
||||
snrc.chain_now = lambda: int(time.time())
|
||||
|
||||
def tearDown(self):
|
||||
(
|
||||
@@ -391,6 +425,7 @@ class ReservedReasonTests(unittest.TestCase):
|
||||
snrc.REGISTRARS,
|
||||
snrc.CONTROLLERS,
|
||||
snrc.eth_call,
|
||||
snrc.chain_now,
|
||||
) = self._saved
|
||||
|
||||
def _chain(self, expires, reserved):
|
||||
|
||||
Reference in New Issue
Block a user