test-infra: replace WrappedClock with OracularClock
This removes the recently added LogicalClock from ThreadNet infrastructure. So instead of using a mock SystemTime that only jumps forward an an entire slot length whenever the underlying logical clock ticks, it exposes the “real” io-sim clock like it used to. Using both a mock clock and also some oracular elements became confusing. Morever, we don’t have to mock up the BlockchainTime, we can use the real system to derive it from our SystemTime; we’re mocking less. So I think this is both simpler and less artificial. io-sim already mocks time, so I don’t think we have much to gain by introducing another layer of mock.
The oracular clock is used in two ways.
The general simplifying rule in the tests is that an error causes the node to “sleep” until the next slot onset, for various meanings of “sleep”. So the infrastructure that manages (ie imitates a node operator with really fast fingers) that needs to know the duration until that onset. In particular, when the ledger is too far behind for the node to know the current slot number, the node must rely on the oracle in order to sleep until the next slot onset. (See
Again for simplicity, all schedules that are generated for the test configuration are specified in terms of slots, so the infrastructure needs always know the current slot in order to realize those schedules. (EG When a node joins.)
As long as there is no Common Prefix violation, then our ledger rules are designed so that the immutable common prefix determines the time. Thus all nodes indeed respect the same timeline. So having one oracle is sensible.
We have plans to revisit the slot-alignment of schedules etc, but that’s not yet prioritized. Removing that may simplify this clock-related infrastructure, but it will have to complicate and/or weaken the properties’ expectations.
There were a few impedance mismatches in the
Network.hs infrastructure that
the oracular clock forced us to address.
When the test is over, the SystemTime now blocks. This prevents nodes from doing particularly problematic things while the infrastructure is “racing” to cancel them. So our SystemTime is only very slightly mocked: it becomes unresponsive by blocking forever once the test is over.
The hardForkBlockchainTime function in the Ouroboros.Consensus.BlockchainTime.WallClock.HardFork module uses threadDelay and then reads the clock. Because of our “simplifying” rule that requires nodes to sleep for exactly one slot, this means we require
t2 >= t1 + din the following.
do t1 <- now; threadDelay (nominalDelay d); t2 <- now
When the SystemTime was mocked via LogicalClock, a race condition could cause
t2 == t1here, since that mocking and
threadDelaywere independent. Using a real SystemTime resolves this, since
systemTimeCurrentwins the race against simultaneous threads waking in io-sim, in the absence of clock fudgery like backjumps.
We had to jiggle some of the per-slot behaviors in the infra-structure: reading the chain’s block number at the onset of the slot and persistently adding crucial txs.