singular-registry
LicenseApache-2.0
Safe HaskellNone
LanguageGHC2021

Singular.Registry.Deployment

Description

A runner boots its own registry and publishes its own reference scripts on every run. On a private devnet that is free and right. On a public network it means every run pays to publish the same scripts again and creates a registry nothing else will ever look at — a demonstration of the code rather than of a deployment.

This module is the other half: a deployment manifest recording one registry booted once and one set of reference-script outputs published once, and the means to attach a later run to them. It is the facade the caller imports; the definitions live in the three focused owners behind it — Singular.Registry.Deployment.Manifest for the manifest, its JSON and the paths around it, Singular.Registry.Deployment.Mirror for the proof mirror persisted beside it, and Singular.Registry.Deployment.Attach for the node decisions. See the deployment ownership guide for who owns what and which way the dependencies run.

Three operations:

  • record — the deploy command writes the manifest after booting and publishing (writeDeployment).
  • check — verifyDeployment asks a node whether it still agrees: every reference output is live and carries the hash the manifest pins, the registry's state output carries the recorded token, and the release in hand compiles to the hashes the manifest was made with.
  • attach — attach rebuilds the cage configuration from the manifest and resolves the reference outputs and the state output, so a run uses them instead of creating its own.

What the manifest cannot carry. Writing to a registry — folding a claim in — means proving the key against the registry's current trie, and that proof needs the whole trie, not the root the chain reports. Nothing on chain hands it over in one query, so a deployment carries it as a file beside the manifest (mirrorPathFor), written by each run and read by the next.

That file is the deployment's one non-chain dependency: a second machine needs a copy of it to fold, though not to read — proving a name is alive needs the registry entry and the NFT, and no trie at all. Rebuilding the trie from the chain instead, by following the registry token from the bootstrap transaction and replaying each fold's request datums, is the next milestone's work.

That file is a hazard, so it is checked rather than hoped away — by the caller that attaches, not by attach itself: after attaching, the retained journey callers compare the loaded mirror's root with the chain's root, so a mirror that drifted (a run that died mid-fold, a copy belonging to another deployment) fails by name instead of building proofs against a trie the chain does not have.

Synopsis

The manifest

data Deployment Source #

A registry that exists, recorded well enough to find it again and to refuse to use the wrong one.

Constructors

Deployment 

Fields

  • depRelease :: Text

    Release tag or candidate commit the deployment was made from

  • depLeanRevision :: Text

    Revision of the model that release was built against

  • depNetworkMagic :: Word32

    Network the deployment lives on

  • depSeedOutRef :: Text

    txid#index the boot mint consumed; the registry's identity

  • depCageToken :: Text

    Hex asset name of the registry token (SHA-256 of the seed)

  • depStatePolicy :: Text

    Applied state script hash, which is the registry's policy id

  • depRequestHash :: Text

    Applied request validator hash

  • depApplicationHash :: Text

    Naming application validator hash

  • depRepresentativePolicy :: Text

    The registry-bound ACTIVE token policy: `witness(1, registry)` applied (#157 C5/C7). Renamed from the representative policy it became; the manifest key keeps its spelling so existing manifests and the attach check still read it.

  • depProcessTime :: Integer

    Phase-1 window (ms) the registry was booted with

  • depRetractTime :: Integer

    Phase-2 window (ms) the registry was booted with

  • depTip :: Integer

    Oracle tip (lovelace) the registry was booted with

  • depReferenceScripts :: [ReferenceScript]

    The outputs published once and reused by every later run

  • depBootstrapTxs :: [Text]

    Transaction ids that created all of the above, in order

Instances

Instances details
FromJSON Deployment Source # 
Instance details

Defined in Singular.Registry.Deployment.Manifest

Methods

parseJSON :: Value -> Parser Deployment

parseJSONList :: Value -> Parser [Deployment]

omittedField :: Maybe Deployment

ToJSON Deployment Source # 
Instance details

Defined in Singular.Registry.Deployment.Manifest

Methods

toJSON :: Deployment -> Value

toEncoding :: Deployment -> Encoding

toJSONList :: [Deployment] -> Value

toEncodingList :: [Deployment] -> Encoding

omitField :: Deployment -> Bool

Generic Deployment Source # 
Instance details

Defined in Singular.Registry.Deployment.Manifest

Associated Types

type Rep Deployment 
Instance details

Defined in Singular.Registry.Deployment.Manifest

type Rep Deployment = D1 ('MetaData "Deployment" "Singular.Registry.Deployment.Manifest" "singular-registry-0.1.0.0-Ixhbs5JE28qkUXbRQkkWU" 'False) (C1 ('MetaCons "Deployment" 'PrefixI 'True) (((S1 ('MetaSel ('Just "depRelease") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text) :*: (S1 ('MetaSel ('Just "depLeanRevision") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text) :*: S1 ('MetaSel ('Just "depNetworkMagic") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Word32))) :*: ((S1 ('MetaSel ('Just "depSeedOutRef") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text) :*: S1 ('MetaSel ('Just "depCageToken") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text)) :*: (S1 ('MetaSel ('Just "depStatePolicy") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text) :*: S1 ('MetaSel ('Just "depRequestHash") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text)))) :*: ((S1 ('MetaSel ('Just "depApplicationHash") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text) :*: (S1 ('MetaSel ('Just "depRepresentativePolicy") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text) :*: S1 ('MetaSel ('Just "depProcessTime") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Integer))) :*: ((S1 ('MetaSel ('Just "depRetractTime") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Integer) :*: S1 ('MetaSel ('Just "depTip") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Integer)) :*: (S1 ('MetaSel ('Just "depReferenceScripts") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 [ReferenceScript]) :*: S1 ('MetaSel ('Just "depBootstrapTxs") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 [Text]))))))

Methods

from :: Deployment -> Rep Deployment x Source #

to :: Rep Deployment x -> Deployment Source #

Show Deployment Source # 
Instance details

Defined in Singular.Registry.Deployment.Manifest

Methods

showsPrec :: Int -> Deployment -> ShowS Source #

show :: Deployment -> String Source #

showList :: [Deployment] -> ShowS Source #

Eq Deployment Source # 
Instance details

Defined in Singular.Registry.Deployment.Manifest

Methods

(==) :: Deployment -> Deployment -> Bool Source #

(/=) :: Deployment -> Deployment -> Bool Source #

type Rep Deployment Source # 
Instance details

Defined in Singular.Registry.Deployment.Manifest

type Rep Deployment = D1 ('MetaData "Deployment" "Singular.Registry.Deployment.Manifest" "singular-registry-0.1.0.0-Ixhbs5JE28qkUXbRQkkWU" 'False) (C1 ('MetaCons "Deployment" 'PrefixI 'True) (((S1 ('MetaSel ('Just "depRelease") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text) :*: (S1 ('MetaSel ('Just "depLeanRevision") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text) :*: S1 ('MetaSel ('Just "depNetworkMagic") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Word32))) :*: ((S1 ('MetaSel ('Just "depSeedOutRef") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text) :*: S1 ('MetaSel ('Just "depCageToken") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text)) :*: (S1 ('MetaSel ('Just "depStatePolicy") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text) :*: S1 ('MetaSel ('Just "depRequestHash") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text)))) :*: ((S1 ('MetaSel ('Just "depApplicationHash") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text) :*: (S1 ('MetaSel ('Just "depRepresentativePolicy") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text) :*: S1 ('MetaSel ('Just "depProcessTime") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Integer))) :*: ((S1 ('MetaSel ('Just "depRetractTime") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Integer) :*: S1 ('MetaSel ('Just "depTip") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Integer)) :*: (S1 ('MetaSel ('Just "depReferenceScripts") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 [ReferenceScript]) :*: S1 ('MetaSel ('Just "depBootstrapTxs") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 [Text]))))))

data ReferenceScript Source #

One reference-script output: what it carries, where it is, and the hash a node must agree it holds.

Constructors

ReferenceScript 

Fields

  • refRole :: Text

    Which script this is (state, request, application, …)

  • refHash :: Text

    Hex script hash the output must carry

  • refOutRef :: Text

    txid#index of the output

  • refAddress :: Text

    Address the output sits at, in the bech32 form a person reads. Informational: refAddressBytes is what the code queries.

  • refAddressBytes :: Text

    The same address as the hex bytes the ledger serialises, which round-trips exactly

Instances

Instances details
FromJSON ReferenceScript Source # 
Instance details

Defined in Singular.Registry.Deployment.Manifest

Methods

parseJSON :: Value -> Parser ReferenceScript

parseJSONList :: Value -> Parser [ReferenceScript]

omittedField :: Maybe ReferenceScript

ToJSON ReferenceScript Source # 
Instance details

Defined in Singular.Registry.Deployment.Manifest

Methods

toJSON :: ReferenceScript -> Value

toEncoding :: ReferenceScript -> Encoding

toJSONList :: [ReferenceScript] -> Value

toEncodingList :: [ReferenceScript] -> Encoding

omitField :: ReferenceScript -> Bool

Generic ReferenceScript Source # 
Instance details

Defined in Singular.Registry.Deployment.Manifest

Associated Types

type Rep ReferenceScript 
Instance details

Defined in Singular.Registry.Deployment.Manifest

type Rep ReferenceScript = D1 ('MetaData "ReferenceScript" "Singular.Registry.Deployment.Manifest" "singular-registry-0.1.0.0-Ixhbs5JE28qkUXbRQkkWU" 'False) (C1 ('MetaCons "ReferenceScript" 'PrefixI 'True) ((S1 ('MetaSel ('Just "refRole") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text) :*: S1 ('MetaSel ('Just "refHash") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text)) :*: (S1 ('MetaSel ('Just "refOutRef") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text) :*: (S1 ('MetaSel ('Just "refAddress") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text) :*: S1 ('MetaSel ('Just "refAddressBytes") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text)))))

Methods

from :: ReferenceScript -> Rep ReferenceScript x Source #

to :: Rep ReferenceScript x -> ReferenceScript Source #

Show ReferenceScript Source # 
Instance details

Defined in Singular.Registry.Deployment.Manifest

Methods

showsPrec :: Int -> ReferenceScript -> ShowS Source #

show :: ReferenceScript -> String Source #

showList :: [ReferenceScript] -> ShowS Source #

Eq ReferenceScript Source # 
Instance details

Defined in Singular.Registry.Deployment.Manifest

Methods

(==) :: ReferenceScript -> ReferenceScript -> Bool Source #

(/=) :: ReferenceScript -> ReferenceScript -> Bool Source #

type Rep ReferenceScript Source # 
Instance details

Defined in Singular.Registry.Deployment.Manifest

type Rep ReferenceScript = D1 ('MetaData "ReferenceScript" "Singular.Registry.Deployment.Manifest" "singular-registry-0.1.0.0-Ixhbs5JE28qkUXbRQkkWU" 'False) (C1 ('MetaCons "ReferenceScript" 'PrefixI 'True) ((S1 ('MetaSel ('Just "refRole") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text) :*: S1 ('MetaSel ('Just "refHash") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text)) :*: (S1 ('MetaSel ('Just "refOutRef") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text) :*: (S1 ('MetaSel ('Just "refAddress") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text) :*: S1 ('MetaSel ('Just "refAddressBytes") 'NoSourceUnpackedness 'NoSourceStrictness 'DecidedStrict) (Rec0 Text)))))

readDeployment :: FilePath -> IO Deployment Source #

Read a manifest, naming the file when it will not parse.

writeDeployment :: FilePath -> Deployment -> IO () Source #

Write a manifest, formatted for a person to read in review.

Choosing one

deploymentPathFromArgs :: [String] -> Maybe FilePath Source #

The manifest a command line names, if any. --deployment PATH and --deployment=PATH are both accepted; absent, the caller keeps its existing behaviour of booting its own registry.

deploymentPathFromEnvironment :: IO (Maybe FilePath) Source #

deploymentPathFromArgs for this process, falling back to SINGULAR_DEPLOYMENT.

The release halves the manifest pins only by hash

data CageParts Source #

The compiled bytes a release ships. The manifest records their hashes; the bytes themselves come from the blueprints in hand, so a manifest can never be used to smuggle in a different validator.

Constructors

CageParts 

Fields

cageConfigFor :: Deployment -> CageParts -> Either String CageConfig Source #

The cage configuration a manifest describes: this release's compiled scripts, with the seed and the economics the deployment was booted with.

Refuses when the release in hand compiles the state validator to a different hash than the deployment was made with — the manifest then belongs to another release and attaching to it would spend against a registry whose validator this code does not implement.

Checking one against a node

verifyDeployment :: Session NoWitness IO -> Deployment -> CageParts -> IO [String] Source #

Ask a node whether it still agrees with a manifest, and return what it answered, one line per claim.

Every claim is about the chain or about the compiled release, never about the file restating itself: the seed determines the recorded token, this release compiles to the recorded state hash, every recorded reference output is live at its recorded address carrying the hash the manifest pins, and the registry's state output carries the recorded token under the recorded policy. The first claim that fails is raised by name.

Attaching a run to one

data Attached Source #

What a run gets instead of booting and publishing.

Constructors

Attached 

Fields

  • attCfg :: CageConfig

    The deployed registry's configuration

  • attToken :: TokenId

    The deployed registry's token

  • attRefUtxos :: [(TxIn, TxOut ConwayEra)]

    The published reference outputs, in manifest order

  • attStateUtxo :: (TxIn, TxOut ConwayEra)

    The registry's current state output

attach :: Session NoWitness IO -> Deployment -> CageParts -> IO Attached Source #

Attach a run to a recorded deployment: the release, token and reference/state resolution verifyDeployment performs, plus the resolved outputs a runner needs in hand. The live state's active policy and windows are not checked here; verification is the operation that reads them.

The proof mirror beside the manifest

mirrorPathFor :: FilePath -> FilePath Source #

Where the proof mirror for a manifest lives: the manifest's own path with a .mirror.json extension, so the two travel together and neither is guessed from a separate flag.

loadMirror :: FilePath -> IO (Map TokenId MPFInMemoryDB) Source #

Read the mirror beside a manifest. A missing file is the empty mirror, which is what a registry booted a moment ago actually has — the caller that attaches compares the loaded root with the chain's, so an empty mirror against a registry that has grown is refused by that comparison rather than assumed.

saveMirror :: FilePath -> Map TokenId MPFInMemoryDB -> IO () Source #

Write the mirror beside a manifest, replacing what was there whole or not at all (replaceDurably).

replaceDurably :: FilePath -> ByteString -> IO () Source #

Replace a file whole, durably: the bytes go to a sibling PATH.partial, synchronised to disk, which is then renamed over PATH, and the directory is synchronised. A process killed at any point leaves PATH holding either its previous bytes or the new ones, never a prefix; a killed replacement may leave the .partial sibling, which the next replacement overwrites.

Output references

renderOutRef :: TxIn -> Text Source #

txid#index, the spelling every Cardano tool prints.

parseOutRef :: Text -> Either String TxIn Source #

Parse txid#index back, naming what was wrong with it.

renderAddrBytes :: Addr -> Text Source #

An address as the manifest records it: exact bytes, in hex.