content: {-# LANGUAGE DeriveAnyClass #-}
  
  {-# OPTIONS_GHC -fno-warn-orphans #-}
  
  -- | Context needed for the translation between DSL and Cardano types
  module UTxO.Context (
      -- * Cardano provided context
      CardanoContext(..)
    , initCardanoContext
      -- * Actors
    , Actors(..)
    , Rich(..)
    , Poor(..)
    , Stakeholder(..)
    , Avvm(..)
    , initActors
      -- * Mapping between addresses
    , ActorIx(..)
    , AddrIx
    , Addr(..)
    , maxAddrSize
    , isAvvmAddr
    , isPoorAddr
    , AddrInfo(..)
    , AddrMap(..)
    , initAddrMap
      -- * Our custom context
    , TransCtxt(..)
    , initContext
      -- * Derived information
    , leaderForSlot
    , resolveAddr
    , resolveAddress
    , transCtxtAddrs
      -- ** Block sign info
    , BlockSignInfo(..)
    , blockSignInfo
    , blockSignInfoForSlot
    ) where
  
  import qualified Data.HashMap.Strict as HM
  import qualified Data.List.NonEmpty as NE
  import qualified Data.Map.Strict as Map
  import           Formatting (bprint, build, sformat, (%))
  import qualified Formatting.Buildable
  import           Serokell.Util (listJson, mapJson, pairF)
  import           Serokell.Util.Base16 (base16F)
  import           Universum
  
  import           Pos.Chain.Block (BlockHeader (..), GenesisBlock, HeaderHash,
                       blockHeaderHash, gbHeader, genesisBlock0)
  import           Pos.Chain.Delegation (ProxySKHeavy)
  import           Pos.Chain.Genesis as Genesis (Config (..),
                       GeneratedSecrets (..), GenesisData (..),
                       GenesisDelegation (..), PoorSecret (..), RichSecrets (..),
                       configEpochSlots)
  import           Pos.Chain.Lrc
  import           Pos.Chain.Txp
  import           Pos.Core as Core
  import           Pos.Core.NetworkMagic (NetworkMagic (..))
  import           Pos.Crypto
  
  import           UTxO.Crypto
  
  {-------------------------------------------------------------------------------
    Summary of the information we get about the genesis block from Cardano core
  -------------------------------------------------------------------------------}
  
  -- | The information returned by core
  data CardanoContext = CardanoContext {
        ccStakes      :: StakesMap
      , ccBlock0      :: GenesisBlock
      , ccData        :: GenesisData
      , ccUtxo        :: Utxo
      , ccSecrets     :: GeneratedSecrets
      , ccMagic       :: ProtocolMagic
  
        -- | Initial stake distribution
      , ccInitLeaders :: SlotLeaders
  
        -- | Initial balances
        --
        -- Derived from 'ccUtxo'.
      , ccBalances    :: [(Address, Coin)]
  
        -- | Hash of block0
        --
        -- NOTE: Derived from 'ccBlock0', /not/ the same as 'genesisHash'.
      , ccHash0       :: HeaderHash
  
        -- | Number of slots in an epoch
      , ccEpochSlots  :: SlotCount
      }
  
  initCardanoContext
      :: Genesis.Config
      -> CardanoContext
  initCardanoContext genesisConfig = CardanoContext
      { ccStakes      = genesisStakes ccData
      , ccBlock0      = ccBlock0
      , ccData        = ccData
      , ccUtxo        = ccUtxo
      , ccSecrets     = fromMaybe (error "initCardanoContext: no secrets") $
                            configGeneratedSecrets genesisConfig
      , ccMagic       = ccMagic
      , ccInitLeaders = ccLeaders
      , ccBalances    = map (\(addr, (coin, _)) -> (addr, coin)) $ utxoToAddressCoinPairs ccUtxo
      , ccHash0       = blockHeaderHash . BlockHeaderGenesis $ ccBlock0 ^. gbHeader
      , ccEpochSlots  = configEpochSlots genesisConfig
      }
    where
      ccData       = configGenesisData genesisConfig
      ccLeaders    = genesisLeaders genesisConfig
      ccMagic      = configProtocolMagic genesisConfig
      ccBlock0     = genesisBlock0 ccMagic (configGenesisHash genesisConfig) ccLeaders
      ccUtxo       = genesisUtxo ccData
  
  {-------------------------------------------------------------------------------
    More explicit representation of the various actors in the genesis block
  
    When heavy-weight delegation is enabled, 'generateGenesisData' creates three
    sets of actors, each with their own set of secret keys:
  
    * The 'poor' actors, with a small balance ('gsPoorSecrets')
      (these use HD addresses)
    * The 'rich' actors, with a large balance ('gsRichSecrets')
      (these do not use HD addresses)
    * The stakeholders ('gsDlgIssuersSecrets')
      (no addresses get generated for these)
    * A set of AVVM accounts, with a small balance ('gsFakeAvvmSeeds')
  
    (Using the Ouroboros-neutral word "actor" intentionally to avoid confusion.)
  
    All addresses (for the poor and rich actors) use 'BootstrapEraDistr' as their
    stake distribution attribute; in 'bootstrapEraDistr' this is interpreted as
    a distribution over the 'gdBootStakeholders' in the genesis data, which in
    turn is derived from 'gsDlgIssuersSecrets' in 'generateGenesisData'.
  
    Additionally, 'generateGenesisData' generates a set of 'ProxySKHeavy' (aka
    'ProxySecretKey EpochIndex') delegating from the stakeholders (the
    'pskIssuerPk') to the rich actors (the 'pskDelegatePk'). The following excerpt
    from Section 8.2, Delegation Schema, of the Ouroboros paper is relevant here:
  
    > A stakeholder can transfer the right to generate blocks by creating a proxy
    > signing key that allows the delegate to sign messages of the form (st, d,
    > slj) (i.e., the format of messages signed in Protocol πDPoS to authenticate
    > a block).
  
    So it's actually the rich actors that sign blocks on behalf of the
    stakeholders.
  
    The genesis UTxO computed by 'genesisUtxo', being a Utxo, is simply a set of
    unspent transaction outputs; 'genesisStakes' then uses 'utxoToStakes' to turn
    this into a 'StakeMap'. A key component of this transaction is 'txOutStake',
    which relies on 'bootstrapEtaDistr' for addresses marked 'BootstrapEraDistr'.
    Thus the 'StakesMap' computed by 'genesisStakes' will contain 'StakeholderId's
    of the stakeholders, even though (somewhat confusingly) the stakeholders are
    never actually assigned any addresses.
  
    In order to compute the stake distribution, `txOutStake` needs a series of
    weights for each of the stakeholders. In the test configuration these
    are all set to 1
  
    > 1a1ff7035103d8a9: 1
    > 281e5ae9e357970a: 1
    > 44283ce5c44e6e00: 1
    > 5f53e01e1366aeda: 1
  
    Finally, 'genesisLeaders' uses 'followTheSatoshiUtxo' applied to the
    'genesisUtxo' to compute the 'SlotLeaders' (aka 'NonEmpty StakeholderId').
    Since the stake holders have delegated their signing privilege to the rich
    actors, however, it is actually the rich actors that sign the blocks. The
    mapping from the (public keys) of the stakeholders to the (public keys) of the
    rich actors is recorded in 'gdHeavyDelegation' of 'GenesisData'.
  
    Concretely, the generated genesis data looks something like this:
  
    > TransCtxt{
    >     cardano: CardanoContext{
    >       leaders:  [ S3, S4, S1, S1, S3, S2, S2, S1, S4, S4, .. ]
    >     , stakes:   [
    >         (S3, 11249999999999992 coin(s))
    >       , (S1, 11250000000000016 coin(s))
    >       , (S2, 11249999999999992 coin(s))
    >       , (S4, 11249999999999992 coin(s))
    >     ]
    >     , balances: [
    >           (AVVM1, 100000 coin(s))
    >         , (AVVM2, 100000 coin(s))
    >         , (AVVM3, 100000 coin(s))
    >         , (AVVM4, 100000 coin(s))
    >         , (AVVM5, 100000 coin(s))
    >         , (AVVM6, 100000 coin(s))
    >         , (AVVM7, 100000 coin(s))
    >         , (AVVM8, 100000 coin(s))
    >         , (AVVM9, 100000 coin(s))
    >         , (AVVM10, 100000 coin(s))
    >         , (P01_1, 37499999999166 coin(s))
    >         , (P02_1, 37499999999166 coin(s))
    >         , (P03_1, 37499999999166 coin(s))
    >         , (P04_1, 37499999999166 coin(s))
    >         , (P05_1, 37499999999166 coin(s))
    >         , (P06_1, 37499999999166 coin(s))
    >         , (P07_1, 37499999999166 coin(s))
    >         , (P08_1, 37499999999166 coin(s))
    >         , (P09_1, 37499999999166 coin(s))
    >         , (P10_1, 37499999999166 coin(s))
    >         , (P11_1, 37499999999166 coin(s))
    >         , (P12_1, 37499999999166 coin(s))
    >         , (R1, 11137499999752500 coin(s))
    >         , (R2, 11137499999752500 coin(s))
    >         , (R3, 11137499999752500 coin(s)P04_1
    >         , (R4, 11137499999752500 coin(s))
    >     ]
    >   }
    >   , actors:  Actors{
    >       rich:  [
    >         Rich{ .., addr: R1 }
    >         ..
    >       , Rich{ .., addr: R4 }
    >     ]
    >     , poor:  [
    >         Poor{ ..,, addrs: [ (.., P01_1) ] }
    >         ..
    >       , Poor{ ..,, addrs: [ (.., P12_1) ] }
    >     ]
    >     , stake: [
    >         Stakeholder{ key: RegularKeyPair{ .., hash: S1 } , del: DelegatedTo{ to:  Rich{ .., addr: R1 }, psk: ProxySk { w = epoch #0, ..  } } }
    >       , Stakeholder{ key: RegularKeyPair{ .., hash: S2 } , del: DelegatedTo{ to:  Rich{ .., addr: R3 }, psk: ProxySk { w = epoch #0, ..  } } }
    >       , Stakeholder{ key: RegularKeyPair{ .., hash: S3 } , del: DelegatedTo{ to:  Rich{ .., addr: R4 }, psk: ProxySk { w = epoch #0, ..  } } }
    >       , Stakeholder{ key: RegularKeyPair{ .., hash: S4 } , del: DelegatedTo{ to:  Rich{ .., addr: R2 }, psk: ProxySk { w = epoch #0, ..  } } }
    >     ]
    >     , avvm:  [
    >         Avvm{ .., addr: AVVM1 }
    >         ..
    >       , Avvm{ .., addr: AVVM10 }
    >     ]
    >   }
    > }
  
    where there is a delegation from each of the stakeholders to one of the
    rich actors.
  
    NOTE: It is somewhat odd that the stakeholders delegate " back " to the rich
    actors. In reality this wouldn't happen, and instead there would be a fourth
    set of actors, with no stake nor any balance, whose sole role is to sign
    blocks. This means that if their keys get compromised, the actual stakeholders
    can then delegate to a new set and the keys of the stakeholders themselves
    never need to be online.
  -------------------------------------------------------------------------------}
  
  -- | Actors in the translation context
  data Actors = Actors {
        actorsRich  :: Map PublicKey Rich
      , actorsPoor  :: Map PublicKey Poor
      , actorsStake :: Map StakeholderId Stakeholder
      , actorsAvvm  :: Map RedeemPublicKey Avvm
      }
    deriving (Show)
  
  -- | A rich actor has a key and a "simple" (non-HD) address
  data Rich = Rich {
        richKey  :: RegularKeyPair
      , richAddr :: Address
      }
    deriving (Show)
  
  -- | A poor actor gets a HD wallet, so it has a keypair per address
  -- (current generation just creates a single address though)
  data Poor = Poor {
        poorKey   :: EncKeyPair
      }
    deriving (Show)
  
  data Stakeholder = Stakeholder {
        stkKey :: RegularKeyPair
      , stkDel :: DelegatedTo Rich
      }
    deriving (Show)
  
  -- | AVVM acount
  data Avvm = Avvm {
        avvmKey  :: RedeemKeyPair
      , avvmSeed :: ByteString
      , avvmAddr :: Address
      }
    deriving (Show)
  
  {-------------------------------------------------------------------------------
    Deriving 'Actors' from 'CardanoContext'
  
    TODO: This derivation is more complicated than it ought to be. The mapping
    from the secret keys to the corresponding addresses is already present in
    generateGenesisData, but it is not returned. I see no choice currently but to
    recompute it. This is unfortunate because it means that when
    'generateGenesisData' changes, we'll be out of sync here. Also, we're assuming
    here that 'tboUseHDAddresses' is true ('useHDAddresses' must be set to true in
    the config yaml file).
  -------------------------------------------------------------------------------}
  
  -- | Compute generated actors
  initActors :: NetworkMagic -> CardanoContext -> Actors
  initActors nm CardanoContext{..} = Actors{..}
    where
      actorsRich  :: Map PublicKey Rich
      actorsPoor  :: Map PublicKey Poor
      actorsStake :: Map StakeholderId Stakeholder
      actorsAvvm  :: Map RedeemPublicKey Avvm
  
      actorsRich  = Map.fromList $ map mkRich  $ gsRichSecrets       ccSecrets
      actorsPoor  = Map.fromList $ map mkPoor  $ gsPoorSecrets       ccSecrets
      actorsStake = Map.fromList $ map mkStake $ gsDlgIssuersSecrets ccSecrets
      actorsAvvm  = Map.fromList $ map mkAvvm  $ gsFakeAvvmSeeds     ccSecrets
  
      -- Intentially not using record wildcards here so that we fail to compile
      -- when the structure of the record changes.
  
      mkRich :: RichSecrets -> (PublicKey, Rich)
      mkRich (RichSecrets richSec _vss) =
          (regKpPub richKey, Rich {..})
        where
          richKey :: RegularKeyPair
          richKey = regularKeyPair richSec
  
          richAddr :: Address
          richAddr = makePubKeyAddressBoot nm (toPublic richSec)
  
      mkPoor :: PoorSecret -> (PublicKey, Poor)
      mkPoor (PoorSecret _) = error err
        where
          err :: Text
          err = sformat (
                "Unexpected unecrypted secret key "
              % "(this is only used in non-HD mode)"
              )
      mkPoor (PoorEncryptedSecret poorSec) = (encKpPub poorKey, Poor {..})
        where
          poorKey :: EncKeyPair
          poorKey = encKeyPair poorSec
  
      mkStake :: SecretKey -> (StakeholderId, Stakeholder)
      mkStake stkSec = (regKpHash stkKey, Stakeholder{..})
        where
          stkKey :: RegularKeyPair
          stkKey = regularKeyPair stkSec
  
          stkDel :: DelegatedTo Rich
          stkDel = DelegatedTo{..}
  
          delTo :: Rich
          delTo = Map.findWithDefault
                       (error ("initActors: delegate not found"))
                       (pskDelegatePk delPSK)
                       actorsRich
  
          delPSK :: ProxySKHeavy
          delPSK = HM.lookupDefault
                     (error ("initActors: issuer not found"))
                     (regKpHash stkKey)
                     (unGenesisDelegation $ gdHeavyDelegation ccData)
  
      mkAvvm :: ByteString -> (RedeemPublicKey, Avvm)
      mkAvvm avvmSeed = (redKpPub, Avvm{..})
        where
          avvmKey :: RedeemKeyPair
          avvmKey = RedeemKeyPair{..}
  
          avvmAddr :: Address
          avvmAddr = makeRedeemAddress nm redKpPub
  
          Just (redKpPub, redKpSec) = redeemDeterministicKeyGen avvmSeed
  
  {-------------------------------------------------------------------------------
    In many cases the difference between rich and poor actors is not important
  -------------------------------------------------------------------------------}
  
  -- | Index the actors by number
  data ActorIx
    = IxRich Int
    | IxPoor Int
    | IxAvvm Int
    -- ^ AVVM refers to the special accounts set up at the start of the Cardano
    -- blockchain that could then be redeemed from, once, for an initial balance.
    -- They can never receive a deposit.
    deriving (Show, Eq, Ord)
  
  -- | Address index of a regular actor
  --
  -- We don't track the difference between the various actors at the type
  -- level to make things a bit more uniform.
  type AddrIx = Int
  
  -- | Address is given by an actor index and an address index
  data Addr = Addr {
        addrActorIx :: ActorIx
      , addrIx      :: AddrIx
      }
    deriving (Show, Eq, Ord)
  
  -- | The maximum size in bytes of the serialized Cardano form of these addresses
  --
  -- This is needed for fee estimation.
  maxAddrSize :: Int
  maxAddrSize = error "TODO: maxAddrSize: not defined!"
  
  -- | Returns true if this is the address of an AVVM account
  isAvvmAddr :: Addr -> Bool
  isAvvmAddr addr =
      case addrActorIx addr of
          IxAvvm _ -> True
          _        -> False
  
  -- | Returns true if this is the address of a poor actor
  isPoorAddr :: Addr -> Bool
  isPoorAddr addr =
      case addrActorIx addr of
          IxPoor _ -> True
          _        -> False
  
  -- | Information about the translation of a DSL address
  data AddrInfo = AddrInfo {
        -- | The master key for the actor owning this address (for HD addresses)
        addrInfoMasterKey :: Maybe EncKeyPair
  
        -- | The key for this particular address
      , addrInfoAddrKey   :: SomeKeyPair
  
        -- | The Cardano address
      , addrInfoCardano   :: Address
      }
  
  -- | Mapping between our addresses and Cardano addresses
  data AddrMap = AddrMap {
        -- | Map from the DSL address to 'AddrInfo'
        addrMap    :: Map Addr AddrInfo
  
        -- | Reverse map from Cardano addresses to DSL addresses
      , addrRevMap :: Map Address Addr
      }
  
  -- | Compute initial address mapping
  initAddrMap :: NetworkMagic -> Actors -> AddrMap
  initAddrMap nm Actors{..} = AddrMap{
        addrMap    = Map.fromList mkMap
      , addrRevMap = Map.fromList $ map (swap . second addrInfoCardano) mkMap
      }
    where
      mkMap :: [(Addr, AddrInfo)]
      mkMap = concat [
                     zipWith mkRich [0..] (Map.elems actorsRich)
          , concat $ zipWith mkPoor [0..] (Map.elems actorsPoor)
          ,          zipWith mkAvvm [0..] (Map.elems actorsAvvm)
          ]
  
      mkRich :: Int -> Rich -> (Addr, AddrInfo)
      mkRich actorIx Rich{..} = (
            Addr (IxRich actorIx) 0
          , AddrInfo {
                addrInfoMasterKey = Nothing
              , addrInfoAddrKey   = KeyPairRegular richKey
              , addrInfoCardano   = richAddr
              }
          )
  
      -- | Adds a number of addresses (`numPoorAddrs`) for each Poor actor
      mkPoor :: Int -> Poor -> [(Addr, AddrInfo)]
      mkPoor actorIx Poor{..} =
          [ poorRawAddr addrIxI (deriveHDAddress' poorSec accId0 addrIxW)
          | (addrIxI, addrIxW) <- addrIxs
          ]
          where
              -- the first HD account and address
              accId0  = accountGenesisIndex
              addrIx0 = wAddressGenesisIndex
              poorSec = encKpEnc poorKey
  
              numPoorAddrs = 10 -- 10 addresses for each Poor actor
              addrIxs = map (\i -> (i, addrIx0 + (fromIntegral i)) )
                            [0 .. numPoorAddrs - 1]
  
              poorRawAddr :: Int -- ^ AddrIx
                          -> (EncKeyPair, Address)
                          -> (Addr, AddrInfo)
              poorRawAddr addrIx' (ekp, addr) = (
                    Addr (IxPoor actorIx) addrIx'
                  , AddrInfo {
                        addrInfoMasterKey = Just poorKey
                      , addrInfoAddrKey   = KeyPairEncrypted ekp
                      , addrInfoCardano   = addr
                      }
                  )
  
              deriveHDAddress' :: EncryptedSecretKey
                               -> Word32 -- ^ account index
                               -> Word32 -- ^ address index
                               -> (EncKeyPair, Address)
              deriveHDAddress' esk accIx' addrIx'
                  = case deriveLvl2KeyPair nm bootstrapEra scp emptyPassphrase esk accIx' addrIx' of
                      Nothing          -> error "impossible"
                      Just (addr, key) -> (encKeyPair key, addr)
                  where
                      bootstrapEra = IsBootstrapEraAddr True
                      scp          = ShouldCheckPassphrase False
  
      mkAvvm :: Int -> Avvm -> (Addr, AddrInfo)
      mkAvvm actorIx Avvm{..} = (
            Addr (IxAvvm actorIx) 0
          , AddrInfo {
                addrInfoMasterKey = Nothing
              , addrInfoAddrKey   = KeyPairRedeem avvmKey
              , addrInfoCardano   = avvmAddr
              }
          )
  
  {-------------------------------------------------------------------------------
    Translation context
  
    The environment we need to be able to do the translation between the DSL
    and Cardano types. Accumulation of the environments above.
  -------------------------------------------------------------------------------}
  
  data TransCtxt = TransCtxt {
        tcCardano :: CardanoContext
      , tcActors  :: Actors
      , tcAddrMap :: AddrMap
      }
  
  initContext :: NetworkMagic -> CardanoContext -> TransCtxt
  initContext nm tcCardano = TransCtxt{..}
    where
      tcActors  = initActors nm tcCardano
      tcAddrMap = initAddrMap nm tcActors
  
  -- | All actor addresses present in the translation context
  transCtxtAddrs :: TransCtxt -> [Addr]
  transCtxtAddrs transCtxt = filter (not . avvm) addrs
      where
          addrs = Map.keys $ addrMap (tcAddrMap transCtxt)
  
          avvm (Addr (IxAvvm _) _) = True
          avvm _                   = False
  
  {-------------------------------------------------------------------------------
    Derived information
  -------------------------------------------------------------------------------}
  
  resolveAddr :: Addr -> TransCtxt -> AddrInfo
  resolveAddr addr TransCtxt{..} =
      fromMaybe
        (error $ sformat ("resolveAddr: " % build % " not found") addr)
        (Map.lookup addr addrMap)
    where
      AddrMap{..} = tcAddrMap
  
  resolveAddress :: Address -> TransCtxt -> Addr
  resolveAddress addr TransCtxt{..} =
      fromMaybe
        (error $ sformat ("resolveAddress: " % build % " not found") addr)
        (Map.lookup addr addrRevMap)
    where
      AddrMap{..} = tcAddrMap
  
  leaderForSlot :: SlotLeaders -> SlotId -> TransCtxt -> Stakeholder
  leaderForSlot leaders slotId TransCtxt{..} = actorsStake Map.! leader
    where
      Actors{..}         = tcActors
      CardanoContext{..} = tcCardano
  
      leader :: StakeholderId
      leader = leaders NE.!! slotIx
  
      slotIx :: Int
      slotIx = fromIntegral $ getSlotIndex (siSlot slotId)
  
  {-------------------------------------------------------------------------------
    Derive block sign info from a 'Stakeholder'
  -------------------------------------------------------------------------------}
  
  -- | Information needed to sign a block
  data BlockSignInfo = BlockSignInfo {
        bsiLeader :: PublicKey    -- ^ Real slot leader
      , bsiKey    :: SecretKey    -- ^ Secret key of the actor signing it
      , bsiPSK    :: ProxySKHeavy -- ^ Prove that the actor may sign the block
      }
  
  -- | 'BlockSignInfo' can be derived from the slot's 'Stakeholder'
  blockSignInfo :: Stakeholder -> BlockSignInfo
  blockSignInfo Stakeholder{..} = BlockSignInfo{..}
    where
      DelegatedTo{..} = stkDel
      Rich{..}        = delTo
  
      bsiLeader = regKpPub stkKey
      bsiKey    = regKpSec richKey
      bsiPSK    = delPSK
  
  blockSignInfoForSlot :: SlotLeaders -> SlotId -> TransCtxt -> BlockSignInfo
  blockSignInfoForSlot leaders slotId =
        blockSignInfo
      . leaderForSlot leaders slotId
  
  {-------------------------------------------------------------------------------
    Pretty-printing
  -------------------------------------------------------------------------------}
  
  instance Buildable Rich where
    build Rich{..} = bprint
        ( "Rich"
        % "{ key:  " % build
        % ", addr: " % build
        % "}"
        )
        richKey
        richAddr
  
  instance Buildable Poor where
    build Poor{..} = bprint
        ( "Poor"
        % "{ key:   " % build
        % "}"
        )
        poorKey
  
  instance Buildable Stakeholder where
    build Stakeholder{..} = bprint
        ( "Stakeholder"
        % "{ key: " % build
        % ", del: " % build
        % "}"
        )
        stkKey
        stkDel
  
  instance Buildable Avvm where
    build Avvm{..} = bprint
        ( "Avvm"
        % "{ key:  " % build
        % ", seed: " % base16F
        % ", addr: " % build
        % "}"
        )
        avvmKey
        avvmSeed
        avvmAddr
  
  instance Buildable Actors where
    build Actors{..} = bprint
        ( "Actors"
        % "{ rich:  " % listJson
        % ", poor:  " % listJson
        % ", stake: " % listJson
        % ", avvm:  " % listJson
        % "}"
        )
        (Map.elems actorsRich)
        (Map.elems actorsPoor)
        (Map.elems actorsStake)
        (Map.elems actorsAvvm)
  
  instance Buildable ActorIx where
    build (IxRich ix) = bprint ("IxRich " % build) ix
    build (IxPoor ix) = bprint ("IxPoor " % build) ix
    build (IxAvvm ix) = bprint ("IxAvvm " % build) ix
  
  instance Buildable Addr where
    build Addr{..} = bprint
        ( "Addr"
        % "{ actorIx: " % build
        % ", addrIx:  " % build
        % "}"
        )
        addrActorIx
        addrIx
  
  -- | We don't show the whole thing, this is for debugging primarily
  instance Buildable CardanoContext where
    build CardanoContext{..} = bprint
        ( "CardanoContext"
        % "{ initLeaders:  " % listJson
        % ", stakes:       " % listJson
        % ", balances:     " % listJson
        % ", utxo:         " % mapJson
        % ", data:         " % build
        % "}"
        )
        ccInitLeaders
        (map (bprint pairF) (HM.toList ccStakes))
        (map (bprint pairF) ccBalances)
        ccUtxo
        ccData
  
  instance Buildable AddrMap where
    build AddrMap{..} = bprint
        ( "AddrMap"
        % "{ revMap: " % mapJson
        % "}"
        )
        addrRevMap
  
  instance Buildable TransCtxt where
    build TransCtxt{..} = bprint
        ( "TransCtxt"
        % "{ cardano: " % build
        % ", actors:  " % build
        % ", addrMap: " % build
        % "}"
        )
        tcCardano
        tcActors
        tcAddrMap
  
  instance Buildable GenesisData where
    build GenesisData{..} = bprint
        ( "GenesisData"
        % "{ bootStakeholders: " % build
        % "}"
        )
        gdBootStakeholders
  