Skip to content

Commit b5158bb

Browse files
committed
Index Byron-era outputs and spends
getBlockTxs returns [] for a Byron block and there is no Tx ByronEra, so the decode entry points indexed nothing from Byron. Read the transactions off byronBlockRaw instead. A Byron output is an address and an ada-only value; a bootstrap address has no credentials, which the query side already handles. The decoder is the only thing that changes. transaction_index is the block's transaction order and spending_input_index the transaction's input order, Byron having no redeemer pointers to align to. Genesis-distributed outputs are in no block, so they stay unindexed. Tested per transaction and from real block bytes: mainnet block 2,160,150 and an epoch boundary block, with ids verified against mainnet.
1 parent d335c0b commit b5158bb

5 files changed

Lines changed: 321 additions & 13 deletions

File tree

cardano-sieve.cabal

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ library
5656
, cardano-api >=11.2
5757
, cardano-ledger-alonzo
5858
, cardano-ledger-babbage
59+
, cardano-ledger-byron
5960
, cardano-ledger-core
6061
, containers ^>=0.7
6162
, http-api-data ^>=0.6
@@ -89,18 +90,26 @@ test-suite cardano-sieve-test
8990
type: exitcode-stdio-1.0
9091
hs-source-dirs: test
9192
main-is: Main.hs
93+
data-files:
94+
test/files/golden/byron-ebb.cbor.gz
95+
test/files/golden/byron-main-block.cbor
96+
9297
build-depends:
9398
, base >=4.14
9499
, bytestring ^>=0.12
95100
, cardano-api >=11.2
96101
, cardano-api:gen >=11.2
102+
, cardano-ledger-binary
103+
, cardano-ledger-byron
97104
, cardano-sieve
98105
, containers ^>=0.7
99106
, directory ^>=1.3
100107
, filepath ^>=1.4
101108
, hedgehog
109+
, ouroboros-consensus:cardano
102110
, sqlite-simple ^>=0.4
103111
, tasty ^>=1.5
104112
, tasty-hedgehog
105113
, tasty-hunit ^>=0.10
106114
, text ^>=2.1
115+
, zlib ^>=0.7

src/Cardano/Sieve/Node/Decode.hs

Lines changed: 81 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,29 +11,40 @@ module Cardano.Sieve.Node.Decode
1111
, selectedOutputs
1212
, spentInputs
1313
, encodeOutputRef
14+
, byronOutputsInTx
15+
, byronTxSpends
1416
)
1517
where
1618

1719
import Cardano.Api
18-
( AddressAny
20+
( Address (ByronAddress)
21+
, AddressAny (AddressByron)
22+
, Block (ByronBlock, ShelleyBlock)
1923
, BlockInMode (BlockInMode)
2024
, ShelleyBasedEra (..)
2125
, Tx (ShelleyTx)
26+
, TxId
2227
, TxIn (TxIn)
2328
, TxIx (TxIx)
2429
, Value
30+
, fromByronLovelace
31+
, fromByronTxIn
2532
, fromLedgerValue
2633
, fromShelleyAddrToAny
2734
, fromShelleyScriptHash
2835
, fromShelleyTxIn
2936
, getBlockTxs
3037
, getTxIdShelley
38+
, lovelaceToValue
3139
, serialiseToRawBytes
3240
, shelleyBasedEraConstraints
3341
)
42+
import Cardano.Api.Consensus qualified as Consensus
3443
import Cardano.Api.Experimental.Tx (TxOut (TxOut))
3544
import Cardano.Api.Ledger qualified as L
3645

46+
import Cardano.Chain.Block qualified as CC
47+
import Cardano.Chain.UTxO qualified as Utxo
3748
import Cardano.Ledger.Alonzo.Core (originalBytes, scriptPrefixTag)
3849
import Cardano.Ledger.Alonzo.Scripts
3950
( AsIx (AsIx)
@@ -82,11 +93,12 @@ data DecodedOutput = DecodedOutput
8293
-- so every output of the same transaction carries the same set; empty if none.
8394
}
8495

85-
-- | Every output of every transaction in a block. Byron blocks contribute
86-
-- nothing ('getBlockTxs' is @[]@ for them).
96+
-- | Every output of every transaction in a block. 'getBlockTxs' is @[]@ for a
97+
-- Byron block and there is no @Tx ByronEra@, so Byron has its own arm.
8798
outputsInBlock :: BlockInMode -> [DecodedOutput]
88-
outputsInBlock (BlockInMode _ block) =
89-
concat (zipWith outputsInTx [0 ..] (getBlockTxs block))
99+
outputsInBlock (BlockInMode _ block) = case block of
100+
ByronBlock b -> concat (zipWith byronOutputsInTx [0 ..] (byronTxs b))
101+
ShelleyBlock{} -> concat (zipWith outputsInTx [0 ..] (getBlockTxs block))
90102

91103
-- | Decode every output of one transaction. Each output carries the
92104
-- transaction's id (in its output reference), its position within the block, and
@@ -190,7 +202,9 @@ outputsInTx txIx (ShelleyTx sbe ledgerTx) =
190202
-- block produced a matched output. Without that condition every datum and
191203
-- script on the chain would be stored no matter how narrow the selectors.
192204
datumsAndScriptsInBlock :: BlockInMode -> DatumsAndScripts
193-
datumsAndScriptsInBlock (BlockInMode _ block) = foldMap datumsAndScriptsInTx (getBlockTxs block)
205+
datumsAndScriptsInBlock (BlockInMode _ block) = case block of
206+
ByronBlock{} -> mempty -- Byron has neither
207+
ShelleyBlock{} -> foldMap datumsAndScriptsInTx (getBlockTxs block)
194208

195209
-- | The datums and scripts of one transaction.
196210
datumsAndScriptsInTx :: Tx era -> DatumsAndScripts
@@ -264,8 +278,10 @@ selectedOutputs selectors blk =
264278
-- for /all/ inputs (an input carries no data to run a selector against); the
265279
-- writer keeps only the ones whose consumed output is tracked.
266280
spentInputs :: RedeemerCapture -> BlockInMode -> [SpentInput]
267-
spentInputs capture (BlockInMode _ block) =
268-
concatMap (txSpends capture) (getBlockTxs block)
281+
spentInputs capture (BlockInMode _ block) = case block of
282+
-- No redeemers in Byron, so 'capture' does not reach this arm.
283+
ByronBlock b -> concatMap byronTxSpends (byronTxs b)
284+
ShelleyBlock{} -> concatMap (txSpends capture) (getBlockTxs block)
269285

270286
-- | The consumed inputs of one transaction, each tagged with the spending
271287
-- transaction's id, the input's index, and (when asked for) the redeemer that
@@ -349,3 +365,60 @@ encodeOutputRef :: TxIn -> ByteString
349365
encodeOutputRef (TxIn txid (TxIx ix)) =
350366
serialiseToRawBytes txid
351367
<> LBS.toStrict (toLazyByteString (word64BE (fromIntegral ix)))
368+
369+
-- * Byron
370+
371+
-- | The transaction payload of a Byron block; the other three payloads
372+
-- (delegation, update proposals, votes) are not outputs. Epoch boundary blocks
373+
-- carry none.
374+
--
375+
-- This order is the @transaction_index@ reported for Byron outputs.
376+
byronTxs :: Consensus.ByronBlock -> [Utxo.ATxAux ByteString]
377+
byronTxs blk = case Consensus.byronBlockRaw blk of
378+
CC.ABOBBoundary _ebb -> []
379+
CC.ABOBBlock b -> Utxo.aUnTxPayload (CC.blockTxPayload b)
380+
381+
-- | The transaction's id. 'Consensus.byronIdTx' hashes its annotated bytes
382+
-- rather than re-serialising. The reference is a detour: cardano-api exposes
383+
-- the Byron id conversion only through 'fromByronTxIn', so the index below is a
384+
-- placeholder.
385+
byronTxId :: Utxo.ATxAux ByteString -> TxId
386+
byronTxId atx =
387+
case fromByronTxIn (Utxo.TxInUtxo (Consensus.byronIdTx atx) 0) of
388+
TxIn txid _ix -> txid
389+
390+
-- | Decode every output of one Byron transaction, given its position in its
391+
-- block. A Byron output is an address and an ada-only value: no datum, no
392+
-- reference script, no metadata. The address is a bootstrap one, so
393+
-- 'Cardano.Sieve.Node.Encode.toStored' stores NULL for both credential columns.
394+
byronOutputsInTx :: Word64 -> Utxo.ATxAux ByteString -> [DecodedOutput]
395+
byronOutputsInTx txIx atx =
396+
zipWith mkOutput [0 ..] (F.toList (Utxo.txOutputs (Utxo.taTx atx)))
397+
where
398+
txid = byronTxId atx
399+
mkOutput ix o =
400+
DecodedOutput
401+
{ doOutputRef = TxIn txid (TxIx ix)
402+
, doTransactionIndex = txIx
403+
, doAddress = AddressByron (ByronAddress (Utxo.txOutAddress o))
404+
, doValue = lovelaceToValue (fromByronLovelace (Utxo.txOutValue o))
405+
, doDatum = Nothing
406+
, doReferenceScriptHash = Nothing
407+
, doMetadataTags = Set.empty
408+
}
409+
410+
-- | The consumed inputs of one Byron transaction. With no redeemers there is no
411+
-- pointer for 'siInputIndex' to line up with, so it is the input's position in
412+
-- the transaction — the order Byron's UTxO rule consumes them in.
413+
byronTxSpends :: Utxo.ATxAux ByteString -> [SpentInput]
414+
byronTxSpends atx =
415+
zipWith mkSpend [0 ..] (F.toList (Utxo.txInputs (Utxo.taTx atx)))
416+
where
417+
txid = serialiseToRawBytes (byronTxId atx)
418+
mkSpend ix i =
419+
SpentInput
420+
{ siConsumed = encodeOutputRef (fromByronTxIn i)
421+
, siSpendingTxId = txid
422+
, siInputIndex = ix
423+
, siRedeemer = Nothing
424+
}

0 commit comments

Comments
 (0)