packages feed

haskoin-store-data 2.0.1 → 2.0.2

raw patch · 4 files changed

+68/−18 lines, 4 filesPVP ok

version bump matches the API change (PVP)

API changes (from Hackage documentation)

+ Haskoin.Store.Data: toTransactionWithId :: Network -> Ctx -> TxHash -> TxData -> Transaction

Files

CHANGELOG.md view
@@ -4,7 +4,18 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html) -## [Unreleased]+## [2.0.2] - 2026-11-14++### Added+- **`toTransactionWithId`**, `toTransaction` for a transaction whose id is+  known, as it is for one read by its id. It serializes nothing:+  serializing and hashing the transaction took a quarter of the time of+  building its API form.++### Changed+- `toTransaction` counts the size and the weight from the lengths of the+  parts of the transaction, instead of serializing it with and without its+  witness. It still serializes it once without the witness, for its id.  ## [2.0.1] - 2026-11-12 
haskoin-store-data.cabal view
@@ -5,7 +5,7 @@ -- see: https://github.com/sol/hpack  name:           haskoin-store-data-version:        2.0.1+version:        2.0.2 synopsis:       Data types and API client of Haskoin Store description:    The types of the data that Haskoin Store serves (blocks, transactions,                 balances, unspent outputs, extended public keys, health checks, events and
src/Haskoin/Store/Data.hs view
@@ -49,6 +49,7 @@     transactionData,     fromTransaction,     toTransaction,+    toTransactionWithId,     StoreInput (..),     isCoinbase,     StoreOutput (..),@@ -1135,9 +1136,20 @@  -- | Build the API form of a stored transaction. The network decides which -- output scripts have an address. A SegWit-shaped script has none on Bitcoin--- Cash, for example.+-- Cash, for example. The id is the hash of the transaction without its+-- witness; where it is known, as it is for a transaction read by its id,+-- 'toTransactionWithId' saves serializing and hashing the transaction. toTransaction :: Network -> Ctx -> TxData -> Transaction-toTransaction net ctx t =+toTransaction net ctx t = toTransactionWithId net ctx (txHash t.tx) t++-- | 'toTransaction' for a transaction whose id is known, given here. It+-- serializes nothing: the size and the weight are counted from the lengths+-- of the parts of the transaction ('txSizes'). Serializing and hashing a+-- transaction took a quarter of the time of building its API form, for+-- bytes that the store had just read. The id is taken as given, so it has+-- to be the id of the transaction.+toTransactionWithId :: Network -> Ctx -> TxHash -> TxData -> Transaction+toTransactionWithId net ctx txid t =   Transaction     { version = t.tx.version,       locktime = t.tx.locktime,@@ -1145,34 +1157,48 @@       deleted = t.deleted,       rbf = t.rbf,       timestamp = t.timestamp,-      txid = TxHash (doubleSHA256 witless),+      txid = txid,       inputs = ins,       outputs = outs,-      size = fromIntegral (B.length full),-      weight = fromIntegral (B.length witless * 3 + B.length full),+      size = fromIntegral full,+      weight = fromIntegral (witless * 3 + full),       fee =         if any isCoinbase ins           then 0           else inv `minus` outv     }   where-    -- Serialize once with the witness and once without it, and only once-    -- if there is no witness, when both are the same bytes. The id of the-    -- transaction is the hash of the bytes without witness, as 'txHash'-    -- computes it.-    full, witless :: ByteString-    full = runPutS (serialize t.tx)-    witless-      | hasWitness t.tx =-          let Tx {..} = t.tx-           in runPutS (serialize Tx {witness = [], ..})-      | otherwise = full+    (full, witless) = txSizes t.tx     ins = zipWith3 f [0 ..] t.tx.inputs (t.tx.witness <> repeat [])     inv = sum (map (.value) ins)     outs = zipWith g [0 ..] t.tx.outputs     outv = sum $ map (.value) outs     f n i w = toInput net ctx i (IntMap.lookup n t.prevs) w     g n o = toOutput net ctx o $ IntMap.lookup n t.spenders++-- | The length of a transaction serialized, and its length without the+-- witness, counted from the lengths of its parts as 'serialize' writes+-- them: the witness, with its marker and flag, only if 'hasWitness', and+-- one stack for each input.+txSizes :: Tx -> (Int, Int)+txSizes tx = (if hasWitness tx then witless + 2 + witnesses else witless, witless)+  where+    witless =+      8+        + list (length tx.inputs)+        + sum [40 + bytes (B.length i.script) | i <- tx.inputs]+        + list (length tx.outputs)+        + sum [8 + bytes (B.length o.script) | o <- tx.outputs]+    witnesses = sum [stack w | w <- take (length tx.inputs) (tx.witness <> repeat [])]+    stack w = list (length w) + sum (map (bytes . B.length) w)+    bytes n = list n + n+    -- the length of a count as a variable integer+    list :: Int -> Int+    list n+      | n < 0xfd = 1+      | n <= 0xffff = 3+      | n <= 0xffffffff = 5+      | otherwise = 9  -- | Inverse of 'toTransaction'. fromTransaction :: Transaction -> TxData
test/Haskoin/Store/DataSpec.hs view
@@ -262,6 +262,19 @@                            fromIntegral (len witless * 3 + len td.tx),                            ws                          )+    it "gives the same with the id given" $+      forAll (arbitraryTxData ctx) $ \td ->+        toTransactionWithId btc ctx (txHash td.tx) td `shouldBe` toTransaction btc ctx td+    it "counts sizes as serializing does, with large counts and pushes" $+      -- counts and lengths of 253 and more take more than a byte+      forAll (arbitraryTxData ctx) $ \td -> forAll (choose (0, 3)) $ \k ->+        let big = B.replicate (0xfc + k * 0x100) 0x51+            Tx {..} = td.tx+            grown = case outputs of+              TxOut v _ : os -> Tx {outputs = TxOut v big : os, ..}+              [] -> Tx {..}+            t = toTransaction btc ctx (td {tx = grown} :: TxData)+         in t.size `shouldBe` fromIntegral (B.length (runPutS (serialize grown)))     -- an output has an address only where the network has that kind     forM_       [ ("a SegWit output an address on Bitcoin", btc, p2wpkh, "\"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4\""),