Skip to content

Conversation

@agustinmista
Copy link
Contributor

@agustinmista agustinmista commented Nov 28, 2025

This PR introduces some general changes and QoL improvements in anticipation to the introduction of the Peras voting rules. These are separated into hopefully digestible atomic commits:

  1. Introduce Ouroboros.Consensus.Peras.Params module to keep track of Peras protocol parameters in a single place,
  2. Add a small helper function onPerasRoundNo,
  3. Tweak HasPerasCertX field projection typeclasses,
  4. Introduce WithArrivalTime combinator to wrap values with a RelativeTime,
  5. Wrap validated Peras certs with their arrival time (and adapt tests), and
  6. Store latest certificate seen in the PerasCertDB and add a getLatestCertSeen method to retrieve it.

Please refer to the corresponding commit messages for more information.

@agustinmista agustinmista force-pushed the peras/main-pr/new-defs-and-plumbing branch 6 times, most recently from 14229b9 to 85e4b39 Compare December 1, 2025 11:49
Copy link
Contributor

@tbagrel1 tbagrel1 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

I think we may change the title of the PR though (and maybe part of its description) to better reflect that most changes are related to adding timestamps on certs and propagate the necessary stuff to make it work.

Copy link
Contributor

@nbacquey nbacquey left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Besides my comments, it seems ok to undraft

@agustinmista agustinmista force-pushed the peras/main-pr/new-defs-and-plumbing branch from 97685c7 to 64748d9 Compare December 2, 2025 15:25
@agustinmista agustinmista self-assigned this Dec 2, 2025
@agustinmista agustinmista force-pushed the peras/main-pr/new-defs-and-plumbing branch 2 times, most recently from 013f36d to 590a1e6 Compare December 3, 2025 08:42
@agustinmista agustinmista changed the title [Peras 9] More type definitions and plumbing [Peras 9] Peras voting rules prep: parameters, arrival times, and CertDB updates Dec 3, 2025
@agustinmista agustinmista force-pushed the peras/main-pr/new-defs-and-plumbing branch from 590a1e6 to 95a3906 Compare December 3, 2025 12:45
Comment on lines 109 to 180
let
runOutboundPeer outbound outboundChannel tracer =
runPeer
((\x -> "Outbound (Client): " ++ show x) `contramap` tracer)
codecObjectDiffusionId
outboundChannel
(objectDiffusionOutboundPeer outbound)
>> pure ()
runInboundPeer inbound inboundChannel tracer =
runPipelinedPeer
((\x -> "Inbound (Server): " ++ show x) `contramap` tracer)
codecObjectDiffusionId
inboundChannel
(objectDiffusionInboundPeerPipelined inbound)
>> pure ()
mkPoolInterfaces ::
forall m.
IOLike m =>
m
( ObjectPoolReader PerasRoundNo (PerasCert TestBlock) PerasCertTicketNo m
, ObjectPoolWriter PerasRoundNo (PerasCert TestBlock) m
, m [PerasCert TestBlock]
)
mkPoolInterfaces = do
outboundPool <- newCertDB perasTestParams certs
inboundPool <- newCertDB perasTestParams []
forAll arbitrary $ \systemTimeSeed ->
let
runOutboundPeer outbound outboundChannel tracer =
runPeer
((\x -> "Outbound (Client): " ++ show x) `contramap` tracer)
codecObjectDiffusionId
outboundChannel
(objectDiffusionOutboundPeer outbound)
>> pure ()
runInboundPeer inbound inboundChannel tracer =
runPipelinedPeer
((\x -> "Inbound (Server): " ++ show x) `contramap` tracer)
codecObjectDiffusionId
inboundChannel
(objectDiffusionInboundPeerPipelined inbound)
>> pure ()
mkPoolInterfaces ::
forall m.
IOLike m =>
m
( ObjectPoolReader PerasRoundNo (PerasCert TestBlock) PerasCertTicketNo m
, ObjectPoolWriter PerasRoundNo (PerasCert TestBlock) m
, m [PerasCert TestBlock]
)
mkPoolInterfaces = do
systemTime <- mockSystemTime systemTimeSeed
outboundPool <- newCertDB perasTestParams systemTime certs
inboundPool <- newCertDB perasTestParams systemTime []

let outboundPoolReader = makePerasCertPoolReaderFromCertDB outboundPool
inboundPoolWriter = makePerasCertPoolWriterFromCertDB inboundPool
getAllInboundPoolContent = atomically $ do
snap <- PerasCertDB.getCertSnapshot inboundPool
let rawContent =
Map.toAscList $
PerasCertDB.getCertsAfter snap (PerasCertDB.zeroPerasCertTicketNo)
pure $ vpcCert . snd <$> rawContent
let outboundPoolReader = makePerasCertPoolReaderFromCertDB outboundPool
inboundPoolWriter = makePerasCertPoolWriterFromCertDB systemTime inboundPool
getAllInboundPoolContent = atomically $ do
snap <- PerasCertDB.getCertSnapshot inboundPool
let rawContent =
Map.toAscList $
PerasCertDB.getCertsAfter snap (PerasCertDB.zeroPerasCertTicketNo)
pure $ vpcCert . forgetArrivalTime . snd <$> rawContent

return (outboundPoolReader, inboundPoolWriter, getAllInboundPoolContent)
in
prop_smoke_object_diffusion
protocolConstants
certs
runOutboundPeer
runInboundPeer
mkPoolInterfaces
return (outboundPoolReader, inboundPoolWriter, getAllInboundPoolContent)
in
prop_smoke_object_diffusion
protocolConstants
certs
runOutboundPeer
runInboundPeer
mkPoolInterfaces
Copy link
Contributor Author

@agustinmista agustinmista Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

FTR: actual changes here are minimal (masked by new indentation needed by forAll):

  • Parameterize the property with the seed used to derive a mocked up SystemTime via forAll, and
  • Instantiate a mocked up SystemTime and pass it around to the places needing it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Enabling "Hide whitespace" in PR-review view config proves useful here.

@agustinmista agustinmista force-pushed the peras/main-pr/new-defs-and-plumbing branch from 95a3906 to c0e0683 Compare December 3, 2025 13:10
@agustinmista agustinmista marked this pull request as ready for review December 3, 2025 15:19
@agustinmista agustinmista force-pushed the peras/main-pr/new-defs-and-plumbing branch from c0e0683 to 2957426 Compare December 4, 2025 14:50
@agustinmista agustinmista force-pushed the peras/main-pr/new-defs-and-plumbing branch from 2957426 to d51b1a1 Compare December 8, 2025 12:51
@agustinmista agustinmista changed the base branch from main to peras/main-pr/object-diffusion December 8, 2025 12:51
-- The order of the first two operations (i.e., validation and timestamping) are
-- rather arbitrary, and the abstract Peras protocol just assumes it can happen
-- "within" a slot.
addPerasCerts ::
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How does the code that calls this function deal with a potential exception?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding is that certificate validation errors should be treated as fatal and trigger the disconnection from the peer.

@tbagrel1 @nbacquey do you know where such an exception could end up getting caught? I remember Alex mentioning that unhandled exceptions in a N2N app would eventually trigger a disconnection from the peer, but I would like my memory to be challenged 😅

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In ObjectDiffusionV2 we have withInboundPipelinedPeer which is a bracket function to properly remove peer data from the global peer data map when an exception arise. But this is a slightly orthogonal concern, since the exception still bubbles up after.

I think that exception handling for the thread in charge of running the mini-protocol is defined higher-up in the call tree, probably even above ouroboros-network. From my understanding it's a per-peer disconnection handler, not a per-protocol one, so theoretically we should not have anything specific to do for the new Peras protocol.

Still, I'm on the hunt for the exact repo/location where this handling happens. I'll answer the thread again when I find it :)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Apparently:

This should be taken with a pinch of salt; I may have missed an important handler. But in the end it seems that exceptions from mini-protocols just bubble-up until they reach the connection manager.

@agustinmista agustinmista force-pushed the peras/main-pr/new-defs-and-plumbing branch from d51b1a1 to c314f45 Compare December 16, 2025 08:36
@agustinmista agustinmista force-pushed the peras/main-pr/object-diffusion branch from 912bfdb to 2fd1498 Compare December 16, 2025 09:00
@agustinmista agustinmista force-pushed the peras/main-pr/new-defs-and-plumbing branch from c314f45 to bb97f3f Compare December 16, 2025 09:40
--
-- These are documented in the section 2.1 of the Peras design report:
-- https://tweag.github.io/cardano-peras/peras-design.pdf#section.2.1
data PerasParams = PerasParams
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this datatype expected to dissolve once we start the node? in the sense that its fields will be used in some computation and all will be evaluated?

Otherwise the fields should probably be strict.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 61b3b2e

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually, we can't have strict fields if we want to conserve the error for parameters with yet-to-be-defined default values. I made the ones with default values strict, and left the others lazy.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Then please add a TODO to be fixed eventually.

OpenDB :: Action Model ()
CloseDB :: Action Model ()
AddCert :: ValidatedPerasCert TestBlock -> Action Model AddPerasCertResult
AddCert :: WithArrivalTime (ValidatedPerasCert TestBlock) -> Action Model AddPerasCertResult
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the access to the PerasCertDB single threaded? If not, I think we should consider migrating to quickcheck-state-machine and defining some parallel properties to get confidence there cannot be a deadlock. That or using the new-ish support for parallel properties in quickcheck-dynamic but I don't know how polished is that.

Was there a particular reason to use quickcheck-dynamic from the start?

@agustinmista agustinmista force-pushed the peras/main-pr/new-defs-and-plumbing branch from bb97f3f to 43c1fdc Compare December 16, 2025 10:44
agustinmista and others added 7 commits December 16, 2025 12:59
This commits introduces the module:

Ouroboros.Consensus.Peras.Params

To consolidate all the protocol parameters related to Peras in one
place. Until we defined concrete BlockSupportsPeras for the different
block types + HFC, all blocks satisfy:

type PerasCfg blk = PerasParams

Co-authored-by: Agustin Mista <[email protected]>
Co-authored-by: Alexander Esgen <[email protected]>
Co-authored-by: Georgy Lukyanov <[email protected]>
Co-authored-by: Thomas BAGREL <[email protected]>
Co-authored-by: Nicolas BACQUEY <[email protected]>
This commit adds a small helper to compute over Peras round numbers.
Will be needed later on to implement the Peras voting rules.

Co-authored-by: Agustin Mista <[email protected]>
Co-authored-by: Alexander Esgen <[email protected]>
Co-authored-by: Georgy Lukyanov <[email protected]>
Co-authored-by: Thomas BAGREL <[email protected]>
Co-authored-by: Nicolas BACQUEY <[email protected]>
This commit simplifies the interface of the HasPerasCertX typeclasses,
removing the StandardHash superclass constraint, and splitting them into
several smaller typeclasses.

Co-authored-by: Agustin Mista <[email protected]>
Co-authored-by: Alexander Esgen <[email protected]>
Co-authored-by: Georgy Lukyanov <[email protected]>
Co-authored-by: Thomas BAGREL <[email protected]>
Co-authored-by: Nicolas BACQUEY <[email protected]>
This commit defines a generic WithArrivalTime combinator to wrap a value
with its arrival time (as a Relative time). This is needed by Peras in
several places, e.g., to evaluate the voting rules.

Notably, we store a raw Relative time instead of a (arguably more apt)
SlotNo or PerasRoundNo to defer as much as possible having to deal with
the case where making this translation (timestamp -> slot/round) is not
possible due to the HFC time translation horizon. Instead, the client
will need to perform this translation in a context where such a failure
cannot occur or can be more easily dealt with.

Co-authored-by: Agustin Mista <[email protected]>
Co-authored-by: Alexander Esgen <[email protected]>
Co-authored-by: Georgy Lukyanov <[email protected]>
Co-authored-by: Thomas BAGREL <[email protected]>
Co-authored-by: Nicolas BACQUEY <[email protected]>
This commit wraps the existing ValidatedPerasCerts stored in the
PerasCertDB with their corresponding arrival time. In addition, it
adapts tests to use either a randomly generated arrival time, or (when
appropriate) one generated by a monotonically increasing SystemTime.

Co-authored-by: Agustin Mista <[email protected]>
Co-authored-by: Alexander Esgen <[email protected]>
Co-authored-by: Georgy Lukyanov <[email protected]>
Co-authored-by: Thomas BAGREL <[email protected]>
Co-authored-by: Nicolas BACQUEY <[email protected]>
This commit adds a method to the PerasCertDB API to retrieve the latest
certificate seen. This is certificate needed to implement the Peras voting
and must be kept around even after garbage collection. Because of this,
we extend the internal state of the PerasCertDB to store this special
certificate on the side, and (potentially) update it after new
certificates are added to the database.

Co-authored-by: Agustin Mista <[email protected]>
Co-authored-by: Alexander Esgen <[email protected]>
Co-authored-by: Georgy Lukyanov <[email protected]>
Co-authored-by: Thomas BAGREL <[email protected]>
Co-authored-by: Nicolas BACQUEY <[email protected]>
Co-authored-by: Agustin Mista <[email protected]>
Co-authored-by: Alexander Esgen <[email protected]>
Co-authored-by: Georgy Lukyanov <[email protected]>
Co-authored-by: Thomas BAGREL <[email protected]>
Co-authored-by: Nicolas BACQUEY <[email protected]>
@agustinmista agustinmista force-pushed the peras/main-pr/new-defs-and-plumbing branch from 43c1fdc to e7eadb3 Compare December 16, 2025 12:00
Comment on lines +50 to +56
-- | Minimum age in slots of a block before it can be voted for in order to get
-- a boost.
newtype PerasBlockMinSlots
= PerasBlockMinSlots {unPerasBlockMinSlots :: Word64}
deriving Show via Quiet PerasBlockMinSlots
deriving stock Generic
deriving newtype (Enum, Eq, Ord, NoThunks, Condense)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could we call this PerasBlockMinimumSlotAge? do we need it to be shortened?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants