diff --git a/cabal.project b/cabal.project index fb9f3afc16..f017a78841 100644 --- a/cabal.project +++ b/cabal.project @@ -64,3 +64,22 @@ if impl (ghc >= 9.12) -- https://github.com/kapralVV/Unique/issues/11 , Unique:hashable + +source-repository-package + type: git + location: https://github.com/IntersectMBO/ouroboros-network + tag: 5f12333fcaa5931c2bbbcd28582bb8262d02d220 + --sha256: sha256-NMJXzPuTOfdQzHwpRD5j/Q5JUpDPt6yjEPGnIZvTfcc= + subdir: + cardano-client + cardano-ping + monoidal-synchronisation + network-mux + ntp-client + ouroboros-network + ouroboros-network-api + ouroboros-network-framework + ouroboros-network-mock + ouroboros-network-protocols + ouroboros-network-testing + quickcheck-monoids diff --git a/flake.lock b/flake.lock index 6997cbaa1f..20808a65fc 100644 --- a/flake.lock +++ b/flake.lock @@ -3,11 +3,11 @@ "CHaP": { "flake": false, "locked": { - "lastModified": 1747299417, - "narHash": "sha256-VRwq8JRAMnIgoxfgR60ppz37Wo6NixL9BbzwVhBveik=", + "lastModified": 1753277336, + "narHash": "sha256-5vmSiw2m+rpng3KueKDLluUWj1iqLHXYCCVQzFbuK4g=", "owner": "intersectmbo", "repo": "cardano-haskell-packages", - "rev": "10ebc8624f5440c245112f2e3dbf0e123e7e99e5", + "rev": "d37f1bb54e58507c41fbdc946f951df2db2f1f72", "type": "github" }, "original": { @@ -237,11 +237,11 @@ "hackageNix": { "flake": false, "locked": { - "lastModified": 1747268661, - "narHash": "sha256-z+1y/asOg4eOx23SrdMUM2tYhSlBxIFmsx82odczNNk=", + "lastModified": 1753403313, + "narHash": "sha256-w5Db7Tfruv8wGYCWzbhvGOXiQsuhWNj2aHl1Di/d1zs=", "owner": "input-output-hk", "repo": "hackage.nix", - "rev": "232e5cb2402b52c2efd0f58e8ec1e24efcdaa22b", + "rev": "07c82a81c73031f7773d79fcd77ef19cc4db2e64", "type": "github" }, "original": { diff --git a/ouroboros-consensus-cardano/changelog.d/20240808_125131_marcin.wojtowicz_tx_wire_size.md b/ouroboros-consensus-cardano/changelog.d/20240808_125131_marcin.wojtowicz_tx_wire_size.md new file mode 100644 index 0000000000..6d83f5fa2b --- /dev/null +++ b/ouroboros-consensus-cardano/changelog.d/20240808_125131_marcin.wojtowicz_tx_wire_size.md @@ -0,0 +1,21 @@ + + + + +### Breaking + +- Implement txWireSize of TxLimits instantiations for Byron and Shelley diff --git a/ouroboros-consensus-cardano/src/byron/Ouroboros/Consensus/Byron/Ledger/Mempool.hs b/ouroboros-consensus-cardano/src/byron/Ouroboros/Consensus/Byron/Ledger/Mempool.hs index 1a323e58e6..992ec51206 100644 --- a/ouroboros-consensus-cardano/src/byron/Ouroboros/Consensus/Byron/Ledger/Mempool.hs +++ b/ouroboros-consensus-cardano/src/byron/Ouroboros/Consensus/Byron/Ledger/Mempool.hs @@ -130,6 +130,14 @@ instance LedgerSupportsMempool ByronBlock where instance TxLimits ByronBlock where type TxMeasure ByronBlock = IgnoringOverflow ByteSize32 + txWireSize = (+ 2) + -- 2 bytes overhead added by `EncCBOR (AMempoolPayload + -- ByteString)` instance + . fromIntegral + . Strict.length + . CC.mempoolPayloadRecoverBytes + . toMempoolPayload + blockCapacityTxMeasure _cfg st = IgnoringOverflow $ ByteSize32 diff --git a/ouroboros-consensus-cardano/src/shelley/Ouroboros/Consensus/Shelley/Ledger/Mempool.hs b/ouroboros-consensus-cardano/src/shelley/Ouroboros/Consensus/Shelley/Ledger/Mempool.hs index 0adcb65b5d..f261adc6b4 100644 --- a/ouroboros-consensus-cardano/src/shelley/Ouroboros/Consensus/Shelley/Ledger/Mempool.hs +++ b/ouroboros-consensus-cardano/src/shelley/Ouroboros/Consensus/Shelley/Ledger/Mempool.hs @@ -39,7 +39,8 @@ module Ouroboros.Consensus.Shelley.Ledger.Mempool ( import qualified Cardano.Crypto.Hash as Hash import qualified Cardano.Ledger.Allegra.Rules as AllegraEra import Cardano.Ledger.Alonzo.Core (Tx, TxSeq, bodyTxL, eraDecoder, - fromTxSeq, ppMaxBBSizeL, ppMaxBlockExUnitsL, sizeTxF) + fromTxSeq, ppMaxBBSizeL, ppMaxBlockExUnitsL, sizeTxF, + wireSizeTxF) import qualified Cardano.Ledger.Alonzo.Rules as AlonzoEra import Cardano.Ledger.Alonzo.Scripts (ExUnits, ExUnits' (..), pointWiseExUnits, unWrapExUnits) @@ -67,6 +68,7 @@ import Data.Foldable (toList) import Data.Measure (Measure) import Data.Typeable (Typeable) import qualified Data.Validation as V +import Data.Word (Word32) import GHC.Generics (Generic) import GHC.Natural (Natural) import Lens.Micro ((^.)) @@ -85,6 +87,7 @@ import Ouroboros.Consensus.Shelley.Protocol.Abstract (ProtoCrypto) import Ouroboros.Consensus.Util (ShowProxy (..)) import Ouroboros.Consensus.Util.Condense import Ouroboros.Network.Block (unwrapCBORinCBOR, wrapCBORinCBOR) +import Ouroboros.Network.SizeInBytes data instance GenTx (ShelleyBlock proto era) = ShelleyTx !SL.TxId !(Tx era) deriving stock (Generic) @@ -425,18 +428,37 @@ instance MaxTxSizeUTxO ConwayEra where ----- +wrapCBORinCBOROverhead :: Word32 + -- ^ payload size + -> SizeInBytes +wrapCBORinCBOROverhead size = + 2 -- wrapCBORinCBOR's encodeTag 24 + + + -- upper bound for wrapCBORinCBOR's encodeBytes overhead; + -- it is bounded by maximum tx size + case size of + _ | size <= 0x17 -> 1 + | size <= 0xff -> 2 + | size <= 0xffff -> 3 + | otherwise -> 5 + + fromIntegral size + + instance ShelleyCompatible p ShelleyEra => TxLimits (ShelleyBlock p ShelleyEra) where type TxMeasure (ShelleyBlock p ShelleyEra) = IgnoringOverflow ByteSize32 + txWireSize (ShelleyTx _ tx) = wrapCBORinCBOROverhead (tx ^. wireSizeTxF) txMeasure _cfg st tx = runValidation $ txInBlockSize st tx blockCapacityTxMeasure _cfg = txsMaxBytes instance ShelleyCompatible p AllegraEra => TxLimits (ShelleyBlock p AllegraEra) where type TxMeasure (ShelleyBlock p AllegraEra) = IgnoringOverflow ByteSize32 + txWireSize (ShelleyTx _ tx) = wrapCBORinCBOROverhead (tx ^. wireSizeTxF) txMeasure _cfg st tx = runValidation $ txInBlockSize st tx blockCapacityTxMeasure _cfg = txsMaxBytes instance ShelleyCompatible p MaryEra => TxLimits (ShelleyBlock p MaryEra) where type TxMeasure (ShelleyBlock p MaryEra) = IgnoringOverflow ByteSize32 + txWireSize (ShelleyTx _ tx) = wrapCBORinCBOROverhead (tx ^. wireSizeTxF) txMeasure _cfg st tx = runValidation $ txInBlockSize st tx blockCapacityTxMeasure _cfg = txsMaxBytes @@ -547,6 +569,7 @@ instance ( ShelleyCompatible p AlonzoEra ) => TxLimits (ShelleyBlock p AlonzoEra) where type TxMeasure (ShelleyBlock p AlonzoEra) = AlonzoMeasure + txWireSize (ShelleyTx _ tx) = wrapCBORinCBOROverhead (tx ^. wireSizeTxF) txMeasure _cfg st tx = runValidation $ txMeasureAlonzo st tx blockCapacityTxMeasure _cfg = blockCapacityAlonzoMeasure @@ -661,6 +684,7 @@ instance ( ShelleyCompatible p BabbageEra ) => TxLimits (ShelleyBlock p BabbageEra) where type TxMeasure (ShelleyBlock p BabbageEra) = ConwayMeasure + txWireSize (ShelleyTx _ tx) = wrapCBORinCBOROverhead (tx ^. wireSizeTxF) txMeasure _cfg st tx = runValidation $ txMeasureBabbage st tx blockCapacityTxMeasure _cfg = blockCapacityConwayMeasure @@ -668,5 +692,6 @@ instance ( ShelleyCompatible p ConwayEra ) => TxLimits (ShelleyBlock p ConwayEra) where type TxMeasure (ShelleyBlock p ConwayEra) = ConwayMeasure + txWireSize (ShelleyTx _ tx) = wrapCBORinCBOROverhead (tx ^. wireSizeTxF) txMeasure _cfg st tx = runValidation $ txMeasureConway st tx blockCapacityTxMeasure _cfg = blockCapacityConwayMeasure diff --git a/ouroboros-consensus-cardano/src/unstable-byronspec/Ouroboros/Consensus/ByronSpec/Ledger/Mempool.hs b/ouroboros-consensus-cardano/src/unstable-byronspec/Ouroboros/Consensus/ByronSpec/Ledger/Mempool.hs index 339d697731..433e4cbb4a 100644 --- a/ouroboros-consensus-cardano/src/unstable-byronspec/Ouroboros/Consensus/ByronSpec/Ledger/Mempool.hs +++ b/ouroboros-consensus-cardano/src/unstable-byronspec/Ouroboros/Consensus/ByronSpec/Ledger/Mempool.hs @@ -60,6 +60,7 @@ instance TxLimits ByronSpecBlock where type TxMeasure ByronSpecBlock = IgnoringOverflow ByteSize32 -- Dummy values, as these are not used in practice. + txWireSize = const . fromIntegral $ (0 :: Int) blockCapacityTxMeasure _cfg _st = IgnoringOverflow $ ByteSize32 1 txMeasure _cfg _st _tx = pure $ IgnoringOverflow $ ByteSize32 0 diff --git a/ouroboros-consensus-cardano/src/unstable-cardano-testlib/Test/ThreadNet/Infra/TwoEras.hs b/ouroboros-consensus-cardano/src/unstable-cardano-testlib/Test/ThreadNet/Infra/TwoEras.hs index 38625a7bfc..6a4bbf54f6 100644 --- a/ouroboros-consensus-cardano/src/unstable-cardano-testlib/Test/ThreadNet/Infra/TwoEras.hs +++ b/ouroboros-consensus-cardano/src/unstable-cardano-testlib/Test/ThreadNet/Infra/TwoEras.hs @@ -132,11 +132,14 @@ genTestConfig k (EpochSize epochSize1, EpochSize epochSize2) = do Topo.unionNodeTopology oddTopo $ topo0 + txLogicVersion <- elements [minBound..maxBound] + pure TestConfig { initSeed , nodeTopology , numCoreNodes = NumCoreNodes ncn , numSlots + , txLogicVersion } -- | Generate 'setupPartition' diff --git a/ouroboros-consensus-cardano/test/byron-test/Test/Consensus/Byron/Serialisation.hs b/ouroboros-consensus-cardano/test/byron-test/Test/Consensus/Byron/Serialisation.hs index dbb3fee198..efb70fe0f4 100644 --- a/ouroboros-consensus-cardano/test/byron-test/Test/Consensus/Byron/Serialisation.hs +++ b/ouroboros-consensus-cardano/test/byron-test/Test/Consensus/Byron/Serialisation.hs @@ -31,10 +31,13 @@ import Test.Tasty.QuickCheck import Test.Util.Corruption import Test.Util.Orphans.Arbitrary () import Test.Util.Serialisation.Roundtrip +import Test.Util.Serialisation.TxWireSize tests :: TestTree tests = testGroup "Byron" [ roundtrip_all testCodecCfg dictNestedHdr + , testProperty "GenTx.txWireSize.txSubmission" (prop_txWireSize_txSubmission testCodecCfg) + , testProperty "GenTx.txWireSize.tight" (prop_txWireSize (const Nothing) testCodecCfg) , testProperty "BinaryBlockInfo sanity check" prop_byronBinaryBlockInfo diff --git a/ouroboros-consensus-cardano/test/byron-test/Test/ThreadNet/Byron.hs b/ouroboros-consensus-cardano/test/byron-test/Test/ThreadNet/Byron.hs index d2f0502662..ca36de0b21 100644 --- a/ouroboros-consensus-cardano/test/byron-test/Test/ThreadNet/Byron.hs +++ b/ouroboros-consensus-cardano/test/byron-test/Test/ThreadNet/Byron.hs @@ -111,12 +111,14 @@ genTestSetup k numCoreNodes numSlots setupSlotLength = do setupEBBs <- arbitrary initSeed <- arbitrary nodeTopology <- genNodeTopology numCoreNodes + txLogicVersion <- elements [minBound..maxBound] let testConfig = TestConfig { initSeed , nodeTopology , numCoreNodes , numSlots + , txLogicVersion } let params = byronPBftParams k numCoreNodes @@ -154,10 +156,11 @@ tests = testGroup "Byron" $ { setupEBBs = ProduceEBBs , setupK = SecurityParam $ knownNonZeroBounded @10 , setupTestConfig = TestConfig - { initSeed = Seed 0 - , nodeTopology = meshNodeTopology ncn - , numCoreNodes = ncn - , numSlots = NumSlots 24 + { initSeed = Seed 0 + , nodeTopology = meshNodeTopology ncn + , numCoreNodes = ncn + , numSlots = NumSlots 24 + , txLogicVersion = maxBound } , setupNodeJoinPlan = NodeJoinPlan $ Map.fromList [(CoreNodeId 0,SlotNo 0), (CoreNodeId 1,SlotNo 20), (CoreNodeId 2,SlotNo 22)] , setupNodeRestarts = noRestarts @@ -177,10 +180,11 @@ tests = testGroup "Byron" $ { setupEBBs = ProduceEBBs , setupK = SecurityParam $ knownNonZeroBounded @10 , setupTestConfig = TestConfig - { numCoreNodes = ncn - , numSlots = NumSlots 2 - , nodeTopology = meshNodeTopology ncn - , initSeed = Seed 0 + { numCoreNodes = ncn + , numSlots = NumSlots 2 + , nodeTopology = meshNodeTopology ncn + , initSeed = Seed 0 + , txLogicVersion = maxBound } , setupNodeJoinPlan = NodeJoinPlan (Map.fromList [(CoreNodeId 0,SlotNo 0),(CoreNodeId 1,SlotNo 1)]) , setupNodeRestarts = noRestarts @@ -196,10 +200,11 @@ tests = testGroup "Byron" $ { setupEBBs = ProduceEBBs , setupK = SecurityParam $ knownNonZeroBounded @10 , setupTestConfig = TestConfig - { numCoreNodes = ncn - , numSlots = NumSlots 4 - , nodeTopology = meshNodeTopology ncn - , initSeed = Seed 0 + { numCoreNodes = ncn + , numSlots = NumSlots 4 + , nodeTopology = meshNodeTopology ncn + , initSeed = Seed 0 + , txLogicVersion = maxBound } , setupNodeJoinPlan = NodeJoinPlan (Map.fromList [(CoreNodeId 0,SlotNo {unSlotNo = 0}),(CoreNodeId 1,SlotNo {unSlotNo = 3})]) , setupNodeRestarts = noRestarts @@ -218,10 +223,11 @@ tests = testGroup "Byron" $ { setupEBBs = ProduceEBBs , setupK = SecurityParam $ knownNonZeroBounded @5 , setupTestConfig = TestConfig - { numCoreNodes = ncn - , numSlots = NumSlots 7 - , nodeTopology = meshNodeTopology ncn - , initSeed = Seed 0 + { numCoreNodes = ncn + , numSlots = NumSlots 7 + , nodeTopology = meshNodeTopology ncn + , initSeed = Seed 0 + , txLogicVersion = maxBound } , setupNodeJoinPlan = NodeJoinPlan (Map.fromList [(CoreNodeId 0,SlotNo {unSlotNo = 0}),(CoreNodeId 1,SlotNo {unSlotNo = 0})]) , setupNodeRestarts = NodeRestarts (Map.fromList [(SlotNo {unSlotNo = 5},Map.fromList [(CoreNodeId 1,NodeRestart)])]) @@ -241,10 +247,11 @@ tests = testGroup "Byron" $ { setupEBBs = ProduceEBBs , setupK = SecurityParam $ knownNonZeroBounded @5 , setupTestConfig = TestConfig - { numCoreNodes = ncn - , numSlots = NumSlots 58 - , nodeTopology = meshNodeTopology ncn - , initSeed = Seed 0 + { numCoreNodes = ncn + , numSlots = NumSlots 58 + , nodeTopology = meshNodeTopology ncn + , initSeed = Seed 0 + , txLogicVersion = maxBound } , setupNodeJoinPlan = NodeJoinPlan $ Map.fromList [(CoreNodeId 0,SlotNo 3),(CoreNodeId 1,SlotNo 3),(CoreNodeId 2,SlotNo 5),(CoreNodeId 3,SlotNo 57)] , setupNodeRestarts = noRestarts @@ -264,10 +271,11 @@ tests = testGroup "Byron" $ , setupK = SecurityParam $ knownNonZeroBounded @2 , setupTestConfig = TestConfig { numCoreNodes = ncn5 - -- Still fails if I increase numSlots. - , numSlots = NumSlots 54 - , nodeTopology = meshNodeTopology ncn5 - , initSeed = Seed 0 + -- Still fails if I increase numSlots. + , numSlots = NumSlots 54 + , nodeTopology = meshNodeTopology ncn5 + , initSeed = Seed 0 + , txLogicVersion = maxBound } , setupNodeJoinPlan = NodeJoinPlan $ Map.fromList [ (CoreNodeId 0, SlotNo {unSlotNo = 0}) @@ -302,10 +310,11 @@ tests = testGroup "Byron" $ { setupEBBs = ProduceEBBs , setupK = SecurityParam k , setupTestConfig = TestConfig - { numCoreNodes = ncn - , numSlots = NumSlots $ window + slotsPerEpoch + slotsPerRekey + window - , nodeTopology = meshNodeTopology ncn - , initSeed = seed + { numCoreNodes = ncn + , numSlots = NumSlots $ window + slotsPerEpoch + slotsPerRekey + window + , nodeTopology = meshNodeTopology ncn + , initSeed = seed + , txLogicVersion = maxBound } , setupNodeJoinPlan = trivialNodeJoinPlan ncn , setupNodeRestarts = NodeRestarts $ Map.singleton (SlotNo (slotsPerEpoch + mod w window)) (Map.singleton (CoreNodeId 0) NodeRekey) @@ -327,10 +336,11 @@ tests = testGroup "Byron" $ { setupEBBs = NoEBBs , setupK = k , setupTestConfig = TestConfig - { numCoreNodes = ncn - , numSlots = NumSlots 2 - , nodeTopology = meshNodeTopology ncn - , initSeed = Seed 0 + { numCoreNodes = ncn + , numSlots = NumSlots 2 + , nodeTopology = meshNodeTopology ncn + , initSeed = Seed 0 + , txLogicVersion = maxBound } , setupNodeJoinPlan = trivialNodeJoinPlan ncn , setupNodeRestarts = NodeRestarts $ Map.singleton (SlotNo 1) (Map.singleton (CoreNodeId 1) NodeRestart) @@ -348,10 +358,11 @@ tests = testGroup "Byron" $ { setupEBBs = NoEBBs , setupK = SecurityParam $ knownNonZeroBounded @3 , setupTestConfig = TestConfig - { numCoreNodes = ncn4 - , numSlots = NumSlots 72 - , nodeTopology = meshNodeTopology ncn4 - , initSeed = Seed 0 + { numCoreNodes = ncn4 + , numSlots = NumSlots 72 + , nodeTopology = meshNodeTopology ncn4 + , initSeed = Seed 0 + , txLogicVersion = maxBound } , setupNodeJoinPlan = trivialNodeJoinPlan ncn4 , setupNodeRestarts = NodeRestarts (Map.fromList [(SlotNo 59,Map.fromList [(CoreNodeId 3,NodeRekey)])]) @@ -373,10 +384,11 @@ tests = testGroup "Byron" $ { setupEBBs = ProduceEBBs , setupK = SecurityParam $ knownNonZeroBounded @2 , setupTestConfig = TestConfig - { numCoreNodes = ncn3 - , numSlots = NumSlots 84 - , nodeTopology = meshNodeTopology ncn3 - , initSeed = Seed 0 + { numCoreNodes = ncn3 + , numSlots = NumSlots 84 + , nodeTopology = meshNodeTopology ncn3 + , initSeed = Seed 0 + , txLogicVersion = maxBound } , setupNodeJoinPlan = NodeJoinPlan (Map.fromList [(CoreNodeId 0,SlotNo {unSlotNo = 1}),(CoreNodeId 1,SlotNo {unSlotNo = 1}),(CoreNodeId 2,SlotNo {unSlotNo = 58})]) , setupNodeRestarts = NodeRestarts (Map.fromList [(SlotNo {unSlotNo = 58},Map.fromList [(CoreNodeId 2,NodeRekey)])]) @@ -428,11 +440,12 @@ tests = testGroup "Byron" $ { setupEBBs = ProduceEBBs , setupK = SecurityParam $ knownNonZeroBounded @4 , setupTestConfig = TestConfig - { numCoreNodes = NumCoreNodes 3 - , numSlots = NumSlots 96 - , nodeTopology = -- 1 <-> 0 <-> 2 - NodeTopology $ Map.fromList [(CoreNodeId 0,Set.fromList []),(CoreNodeId 1,Set.fromList [CoreNodeId 0]),(CoreNodeId 2,Set.fromList [CoreNodeId 0])] - , initSeed = Seed 0 + { numCoreNodes = NumCoreNodes 3 + , numSlots = NumSlots 96 + , nodeTopology = -- 1 <-> 0 <-> 2 + NodeTopology $ Map.fromList [(CoreNodeId 0,Set.fromList []),(CoreNodeId 1,Set.fromList [CoreNodeId 0]),(CoreNodeId 2,Set.fromList [CoreNodeId 0])] + , initSeed = Seed 0 + , txLogicVersion = maxBound } , setupNodeJoinPlan = NodeJoinPlan $ Map.fromList [(CoreNodeId 0,SlotNo 0),(CoreNodeId 1,SlotNo 0),(CoreNodeId 2,SlotNo 83)] , setupNodeRestarts = NodeRestarts $ Map.fromList [(SlotNo 83,Map.fromList [(CoreNodeId 2,NodeRekey)])] @@ -471,11 +484,12 @@ tests = testGroup "Byron" $ { setupEBBs = ProduceEBBs , setupK = SecurityParam $ knownNonZeroBounded @2 , setupTestConfig = TestConfig - { numCoreNodes = ncn5 - , numSlots = NumSlots 50 - , nodeTopology = -- 3 <-> {0,1,2} <-> 4 - NodeTopology (Map.fromList [(CoreNodeId 0,Set.fromList []),(CoreNodeId 1,Set.fromList [CoreNodeId 0]),(CoreNodeId 2,Set.fromList [CoreNodeId 0, CoreNodeId 1]),(CoreNodeId 3,Set.fromList [CoreNodeId 0,CoreNodeId 1,CoreNodeId 2]),(CoreNodeId 4,Set.fromList [CoreNodeId 0,CoreNodeId 1,CoreNodeId 2])]) - , initSeed = Seed 0 + { numCoreNodes = ncn5 + , numSlots = NumSlots 50 + , nodeTopology = -- 3 <-> {0,1,2} <-> 4 + NodeTopology (Map.fromList [(CoreNodeId 0,Set.fromList []),(CoreNodeId 1,Set.fromList [CoreNodeId 0]),(CoreNodeId 2,Set.fromList [CoreNodeId 0, CoreNodeId 1]),(CoreNodeId 3,Set.fromList [CoreNodeId 0,CoreNodeId 1,CoreNodeId 2]),(CoreNodeId 4,Set.fromList [CoreNodeId 0,CoreNodeId 1,CoreNodeId 2])]) + , initSeed = Seed 0 + , txLogicVersion = maxBound } , setupNodeJoinPlan = NodeJoinPlan (Map.fromList [(CoreNodeId 0,SlotNo {unSlotNo = 0}),(CoreNodeId 1,SlotNo {unSlotNo = 0}),(CoreNodeId 2,SlotNo {unSlotNo = 0}),(CoreNodeId 3,SlotNo {unSlotNo = 37}),(CoreNodeId 4,SlotNo {unSlotNo = 37})]) , setupNodeRestarts = NodeRestarts (Map.fromList [(SlotNo {unSlotNo = 37},Map.fromList [(CoreNodeId 4,NodeRekey)])]) @@ -498,10 +512,11 @@ tests = testGroup "Byron" $ { setupEBBs = NoEBBs , setupK = SecurityParam $ knownNonZeroBounded @2 , setupTestConfig = TestConfig - { numCoreNodes = ncn - , numSlots = NumSlots 41 - , nodeTopology = meshNodeTopology ncn - , initSeed = Seed 0 + { numCoreNodes = ncn + , numSlots = NumSlots 41 + , nodeTopology = meshNodeTopology ncn + , initSeed = Seed 0 + , txLogicVersion = maxBound } , setupNodeJoinPlan = trivialNodeJoinPlan ncn , setupNodeRestarts = NodeRestarts $ Map.singleton (SlotNo 30) $ Map.singleton (CoreNodeId 2) NodeRekey @@ -515,10 +530,11 @@ tests = testGroup "Byron" $ { setupEBBs = NoEBBs , setupK = SecurityParam $ knownNonZeroBounded @7 , setupTestConfig = TestConfig - { numCoreNodes = ncn - , numSlots = NumSlots 10 - , nodeTopology = meshNodeTopology ncn - , initSeed = Seed 0 + { numCoreNodes = ncn + , numSlots = NumSlots 10 + , nodeTopology = meshNodeTopology ncn + , initSeed = Seed 0 + , txLogicVersion = maxBound } , setupNodeJoinPlan = NodeJoinPlan (Map.fromList [(CoreNodeId 0,SlotNo {unSlotNo = 0}),(CoreNodeId 1,SlotNo {unSlotNo = 1})]) , setupNodeRestarts = noRestarts @@ -534,10 +550,11 @@ tests = testGroup "Byron" $ { setupEBBs = NoEBBs , setupK = SecurityParam $ knownNonZeroBounded @9 , setupTestConfig = TestConfig - { numCoreNodes = ncn - , numSlots = NumSlots 1 - , nodeTopology = meshNodeTopology ncn - , initSeed = Seed 0 + { numCoreNodes = ncn + , numSlots = NumSlots 1 + , nodeTopology = meshNodeTopology ncn + , initSeed = Seed 0 + , txLogicVersion = maxBound } , setupNodeJoinPlan = trivialNodeJoinPlan ncn , setupNodeRestarts = noRestarts @@ -559,10 +576,11 @@ tests = testGroup "Byron" $ { setupEBBs = NoEBBs , setupK = SecurityParam $ knownNonZeroBounded @8 , setupTestConfig = TestConfig - { numCoreNodes = ncn - , numSlots = NumSlots 2 - , nodeTopology = meshNodeTopology ncn - , initSeed = Seed 0 + { numCoreNodes = ncn + , numSlots = NumSlots 2 + , nodeTopology = meshNodeTopology ncn + , initSeed = Seed 0 + , txLogicVersion = maxBound } , setupNodeJoinPlan = trivialNodeJoinPlan ncn , setupNodeRestarts = noRestarts @@ -580,10 +598,11 @@ tests = testGroup "Byron" $ { setupEBBs = NoEBBs , setupK = SecurityParam $ knownNonZeroBounded @5 , setupTestConfig = TestConfig - { numCoreNodes = ncn - , numSlots = NumSlots 5 - , nodeTopology = meshNodeTopology ncn - , initSeed = Seed 0 + { numCoreNodes = ncn + , numSlots = NumSlots 5 + , nodeTopology = meshNodeTopology ncn + , initSeed = Seed 0 + , txLogicVersion = maxBound } , setupNodeJoinPlan = NodeJoinPlan $ Map.fromList [ (CoreNodeId 0, SlotNo 2) , (CoreNodeId 1, SlotNo 3) , (CoreNodeId 2, SlotNo 4) , (CoreNodeId 3, SlotNo 4) ] , setupNodeRestarts = noRestarts @@ -597,10 +616,11 @@ tests = testGroup "Byron" $ { setupEBBs = NoEBBs , setupK = SecurityParam $ knownNonZeroBounded @10 , setupTestConfig = TestConfig - { numCoreNodes = ncn - , numSlots = NumSlots 12 - , nodeTopology = meshNodeTopology ncn - , initSeed = Seed 0 + { numCoreNodes = ncn + , numSlots = NumSlots 12 + , nodeTopology = meshNodeTopology ncn + , initSeed = Seed 0 + , txLogicVersion = maxBound } , setupNodeJoinPlan = NodeJoinPlan $ Map.fromList [ (CoreNodeId 0, SlotNo 0) , (CoreNodeId 1, SlotNo 0) , (CoreNodeId 2, SlotNo 10) , (CoreNodeId 3, SlotNo 10) , (CoreNodeId 4, SlotNo 10) ] , setupNodeRestarts = noRestarts @@ -615,10 +635,11 @@ tests = testGroup "Byron" $ { setupEBBs = NoEBBs , setupK = SecurityParam $ knownNonZeroBounded @10 , setupTestConfig = TestConfig - { numCoreNodes = ncn - , numSlots = NumSlots 17 - , nodeTopology = meshNodeTopology ncn - , initSeed = Seed 0 + { numCoreNodes = ncn + , numSlots = NumSlots 17 + , nodeTopology = meshNodeTopology ncn + , initSeed = Seed 0 + , txLogicVersion = maxBound } , setupNodeJoinPlan = NodeJoinPlan $ Map.fromList [(CoreNodeId 0, SlotNo 0) @@ -639,10 +660,11 @@ tests = testGroup "Byron" $ { setupEBBs = ProduceEBBs , setupK = SecurityParam $ knownNonZeroBounded @2 , setupTestConfig = TestConfig - { numCoreNodes = ncn - , numSlots = NumSlots 21 - , nodeTopology = meshNodeTopology ncn - , initSeed = Seed 0 + { numCoreNodes = ncn + , numSlots = NumSlots 21 + , nodeTopology = meshNodeTopology ncn + , initSeed = Seed 0 + , txLogicVersion = maxBound } , setupNodeJoinPlan = NodeJoinPlan $ Map.fromList [ (CoreNodeId 0,SlotNo {unSlotNo = 0}) @@ -689,6 +711,7 @@ tests = testGroup "Byron" $ , (CoreNodeId 4, Set.fromList [CoreNodeId 0, CoreNodeId 1, CoreNodeId 2, CoreNodeId 3]) ] , initSeed = Seed 0 + , txLogicVersion = maxBound } , setupNodeJoinPlan = NodeJoinPlan $ Map.fromList [ (CoreNodeId 0, SlotNo 0) @@ -718,10 +741,11 @@ tests = testGroup "Byron" $ { setupEBBs = ProduceEBBs , setupK = SecurityParam $ knownNonZeroBounded @8 , setupTestConfig = TestConfig - { numCoreNodes = NumCoreNodes 3 - , numSlots = NumSlots 81 - , nodeTopology = meshNodeTopology (NumCoreNodes 3) - , initSeed = Seed 0 + { numCoreNodes = NumCoreNodes 3 + , numSlots = NumSlots 81 + , nodeTopology = meshNodeTopology (NumCoreNodes 3) + , initSeed = Seed 0 + , txLogicVersion = maxBound } , setupNodeJoinPlan = NodeJoinPlan (Map.fromList [(CoreNodeId 0,SlotNo {unSlotNo = 2}),(CoreNodeId 1,SlotNo {unSlotNo = 6}),(CoreNodeId 2,SlotNo {unSlotNo = 9})]) , setupNodeRestarts = noRestarts @@ -736,10 +760,11 @@ tests = testGroup "Byron" $ { setupEBBs = ProduceEBBs , setupK = SecurityParam $ knownNonZeroBounded @2 , setupTestConfig = TestConfig - { numCoreNodes = NumCoreNodes 2 - , numSlots = NumSlots 39 - , nodeTopology = meshNodeTopology (NumCoreNodes 2) - , initSeed = Seed 0 + { numCoreNodes = NumCoreNodes 2 + , numSlots = NumSlots 39 + , nodeTopology = meshNodeTopology (NumCoreNodes 2) + , initSeed = Seed 0 + , txLogicVersion = maxBound } , setupNodeJoinPlan = NodeJoinPlan (Map.fromList [(CoreNodeId 0,SlotNo {unSlotNo = 0}),(CoreNodeId 1,SlotNo {unSlotNo = 33})]) , setupNodeRestarts = noRestarts @@ -770,10 +795,11 @@ tests = testGroup "Byron" $ { setupEBBs = NoEBBs , setupK = SecurityParam $ knownNonZeroBounded @2 , setupTestConfig = TestConfig - { numCoreNodes = NumCoreNodes 3 - , numSlots = NumSlots 21 - , nodeTopology = meshNodeTopology (NumCoreNodes 3) - , initSeed = Seed 0 + { numCoreNodes = NumCoreNodes 3 + , numSlots = NumSlots 21 + , nodeTopology = meshNodeTopology (NumCoreNodes 3) + , initSeed = Seed 0 + , txLogicVersion = maxBound } , setupNodeJoinPlan = NodeJoinPlan (Map.fromList [(CoreNodeId 0,SlotNo {unSlotNo = 0}),(CoreNodeId 1,SlotNo {unSlotNo = 0}),(CoreNodeId 2,SlotNo {unSlotNo = 20})]) , setupNodeRestarts = noRestarts diff --git a/ouroboros-consensus-cardano/test/cardano-test/Test/Consensus/Cardano/Serialisation.hs b/ouroboros-consensus-cardano/test/cardano-test/Test/Consensus/Cardano/Serialisation.hs index 31a42feafe..7bf4d28ea0 100644 --- a/ouroboros-consensus-cardano/test/cardano-test/Test/Consensus/Cardano/Serialisation.hs +++ b/ouroboros-consensus-cardano/test/cardano-test/Test/Consensus/Cardano/Serialisation.hs @@ -26,10 +26,13 @@ import Test.Tasty import Test.Tasty.QuickCheck (Property, testProperty, (===)) import Test.Util.Orphans.Arbitrary () import Test.Util.Serialisation.Roundtrip +import Test.Util.Serialisation.TxWireSize tests :: TestTree tests = testGroup "Cardano" [ testGroup "Examples roundtrip" $ examplesRoundtrip Cardano.Examples.codecConfig Cardano.Examples.examples + , testProperty "GenTx.txWireSize.txSubmission" $ prop_txWireSize_txSubmission testCodecCfg + , testProperty "GenTx.txWireSize.tight" $ prop_txWireSize (const Nothing) testCodecCfg , roundtrip_all_skipping result testCodecCfg dictNestedHdr , testProperty "BinaryBlockInfo sanity check" prop_CardanoBinaryBlockInfo ] diff --git a/ouroboros-consensus-cardano/test/shelley-test/Test/Consensus/Shelley/Serialisation.hs b/ouroboros-consensus-cardano/test/shelley-test/Test/Consensus/Shelley/Serialisation.hs index 251143266e..c540e73d18 100644 --- a/ouroboros-consensus-cardano/test/shelley-test/Test/Consensus/Shelley/Serialisation.hs +++ b/ouroboros-consensus-cardano/test/shelley-test/Test/Consensus/Shelley/Serialisation.hs @@ -5,6 +5,8 @@ module Test.Consensus.Shelley.Serialisation (tests) where +import qualified Cardano.Ledger.MemoBytes.Internal as SL +import qualified Cardano.Ledger.Shelley.Tx.Internal as SL import qualified Codec.CBOR.Write as CBOR import qualified Data.ByteString.Lazy as Lazy import Data.Constraint @@ -26,10 +28,16 @@ import Test.Tasty.QuickCheck import Test.Util.Corruption import Test.Util.Orphans.Arbitrary () import Test.Util.Serialisation.Roundtrip +import Test.Util.Serialisation.TxWireSize + +getTxBytes :: GenTx Block -> Maybe String +getTxBytes (ShelleyTx _ (SL.TxConstr (SL.Memo _ bytes))) = Just $ show bytes tests :: TestTree tests = testGroup "Shelley" [ roundtrip_all testCodecCfg dictNestedHdr + , testProperty "GenTx.txWireSize.txSubmission" (prop_txWireSize_txSubmission testCodecCfg) + , testProperty "GenTx.txWireSize.tight" (prop_txWireSize getTxBytes testCodecCfg) -- Test for real crypto too , testProperty "hashSize real crypto" $ prop_hashSize pReal diff --git a/ouroboros-consensus-diffusion/changelog.d/20240808_125632_marcin.wojtowicz_tx_wire_size.md b/ouroboros-consensus-diffusion/changelog.d/20240808_125632_marcin.wojtowicz_tx_wire_size.md new file mode 100644 index 0000000000..b774e09a01 --- /dev/null +++ b/ouroboros-consensus-diffusion/changelog.d/20240808_125632_marcin.wojtowicz_tx_wire_size.md @@ -0,0 +1,23 @@ + + + + +### Non-Breaking + +- Provide txWireSize to tx-submission protocol + + diff --git a/ouroboros-consensus-diffusion/src/ouroboros-consensus-diffusion/Ouroboros/Consensus/Network/NodeToNode.hs b/ouroboros-consensus-diffusion/src/ouroboros-consensus-diffusion/Ouroboros/Consensus/Network/NodeToNode.hs index 03a006ea88..335beebeaf 100644 --- a/ouroboros-consensus-diffusion/src/ouroboros-consensus-diffusion/Ouroboros/Consensus/Network/NodeToNode.hs +++ b/ouroboros-consensus-diffusion/src/ouroboros-consensus-diffusion/Ouroboros/Consensus/Network/NodeToNode.hs @@ -118,7 +118,11 @@ import Ouroboros.Network.Protocol.TxSubmission2.Client import Ouroboros.Network.Protocol.TxSubmission2.Codec import Ouroboros.Network.Protocol.TxSubmission2.Server import Ouroboros.Network.Protocol.TxSubmission2.Type -import Ouroboros.Network.TxSubmission.Inbound +import Ouroboros.Network.TxSubmission.Inbound.V1 +import Ouroboros.Network.TxSubmission.Inbound.V2 (PeerTxAPI, + TraceTxLogic, TxDecisionPolicy (..), + TxSubmissionLogicVersion (..), defaultTxDecisionPolicy, + txSubmissionInboundV2, withPeer) import Ouroboros.Network.TxSubmission.Mempool.Reader (mapTxSubmissionMempoolReader) import Ouroboros.Network.TxSubmission.Outbound @@ -170,7 +174,13 @@ data Handlers m addr blk = Handlers { , hTxSubmissionServer :: NodeToNodeVersion -> ConnectionId addr - -> TxSubmissionServerPipelined (GenTxId blk) (GenTx blk) m () + -> Either + (TxSubmissionServerPipelined (GenTxId blk) (GenTx blk) m ()) + (PeerTxAPI m (GenTxId blk) (GenTx blk) + -> TxSubmissionServerPipelined (GenTxId blk) (GenTx blk) m ()) + -- ^ Either we use the legacy tx submission protocol or the newest one + -- which require PeerTxAPI. This is decided by + -- 'EnableNewTxSubmissionProtocol' flag. , hKeepAliveClient :: NodeToNodeVersion @@ -211,10 +221,13 @@ mkHandlers :: ) => NodeKernelArgs m addrNTN addrNTC blk -> NodeKernel m addrNTN addrNTC blk + -> TxSubmissionLogicVersion -> Handlers m addrNTN blk mkHandlers - NodeKernelArgs {chainSyncFutureCheck, chainSyncHistoricityCheck, keepAliveRng, miniProtocolParameters, getDiffusionPipeliningSupport} - NodeKernel {getChainDB, getMempool, getTopLevelConfig, getTracers = tracers, getPeerSharingAPI, getGsmState} = + NodeKernelArgs {chainSyncFutureCheck, chainSyncHistoricityCheck, keepAliveRng, miniProtocolParameters, + txSubmissionInitDelay, getDiffusionPipeliningSupport} + NodeKernel {getChainDB, getMempool, getTopLevelConfig, getTracers = tracers, getPeerSharingAPI, getGsmState} + txSubmissionLogicVersion = Handlers { hChainSyncClient = \peer _isBigLedgerpeer dynEnv -> CsClient.chainSyncClient @@ -246,17 +259,35 @@ mkHandlers , hTxSubmissionClient = \version controlMessageSTM peer -> txSubmissionOutbound (contramap (TraceLabelPeer peer) (Node.txOutboundTracer tracers)) - (txSubmissionMaxUnacked miniProtocolParameters) + (NumTxIdsToAck $ getNumTxIdsToReq + $ maxUnacknowledgedTxIds + $ txDecisionPolicy + $ miniProtocolParameters) (mapTxSubmissionMempoolReader txForgetValidated $ getMempoolReader getMempool) version controlMessageSTM , hTxSubmissionServer = \version peer -> - txSubmissionInbound - (contramap (TraceLabelPeer peer) (Node.txInboundTracer tracers)) - (txSubmissionMaxUnacked miniProtocolParameters) - (mapTxSubmissionMempoolReader txForgetValidated $ getMempoolReader getMempool) - (getMempoolWriter getMempool) - version + case txSubmissionLogicVersion of + TxSubmissionLogicV2 -> + Right $ \api -> + txSubmissionInboundV2 + (contramap (TraceLabelPeer peer) + (Node.txInboundTracer tracers)) + txSubmissionInitDelay + (getMempoolWriter getMempool) + api + TxSubmissionLogicV1 -> + Left + $ txSubmissionInbound + (contramap (TraceLabelPeer peer) (Node.txInboundTracer tracers)) + txSubmissionInitDelay + (NumTxIdsToAck $ getNumTxIdsToReq + $ maxUnacknowledgedTxIds + $ txDecisionPolicy + $ miniProtocolParameters) + (mapTxSubmissionMempoolReader txForgetValidated $ getMempoolReader getMempool) + (getMempoolWriter getMempool) + version , hKeepAliveClient = \_version -> keepAliveClient (Node.keepAliveClientTracer tracers) keepAliveRng , hKeepAliveServer = \_version _peer -> keepAliveServer , hPeerSharingClient = \_version controlMessageSTM _peer -> peerSharingClient controlMessageSTM @@ -380,6 +411,7 @@ data Tracers' peer ntnAddr blk e f = Tracers { , tTxSubmission2Tracer :: f (TraceLabelPeer peer (TraceSendRecv (TxSubmission2 (GenTxId blk) (GenTx blk)))) , tKeepAliveTracer :: f (TraceLabelPeer peer (TraceSendRecv KeepAlive)) , tPeerSharingTracer :: f (TraceLabelPeer peer (TraceSendRecv (PeerSharing ntnAddr))) + , tTxLogicTracer :: f (TraceLabelPeer peer (TraceTxLogic peer (GenTxId blk) (GenTx blk))) } instance (forall a. Semigroup (f a)) => Semigroup (Tracers' peer ntnAddr blk e f) where @@ -391,6 +423,7 @@ instance (forall a. Semigroup (f a)) => Semigroup (Tracers' peer ntnAddr blk e f , tTxSubmission2Tracer = f tTxSubmission2Tracer , tKeepAliveTracer = f tKeepAliveTracer , tPeerSharingTracer = f tPeerSharingTracer + , tTxLogicTracer = f tTxLogicTracer } where f :: forall a. Semigroup a @@ -408,6 +441,7 @@ nullTracers = Tracers { , tTxSubmission2Tracer = nullTracer , tKeepAliveTracer = nullTracer , tPeerSharingTracer = nullTracer + , tTxLogicTracer = nullTracer } showTracers :: ( Show blk @@ -427,6 +461,7 @@ showTracers tr = Tracers { , tTxSubmission2Tracer = showTracing tr , tKeepAliveTracer = showTracing tr , tPeerSharingTracer = showTracing tr + , tTxLogicTracer = showTracing tr } {------------------------------------------------------------------------------- @@ -544,7 +579,7 @@ mkApps :: , ShowProxy blk , ShowProxy (Header blk) , ShowProxy (TxId (GenTx blk)) - , ShowProxy (GenTx blk) + , ShowProxy (GenTx blk), HasTxId (GenTx blk), LedgerSupportsMempool blk, Show addrNTN ) => NodeKernel m addrNTN addrNTC blk -- ^ Needed for bracketing only -> Tracers m addrNTN blk e @@ -710,13 +745,31 @@ mkApps kernel Tracers {..} mkCodecs ByteLimits {..} genChainSyncTimeout lopBucke -> m ((), Maybe bTX) aTxSubmission2Server version ResponderContext { rcConnectionId = them } channel = do labelThisThread "TxSubmissionServer" - runPipelinedPeerWithLimits - (contramap (TraceLabelPeer them) tTxSubmission2Tracer) - (cTxSubmission2Codec (mkCodecs version)) - blTxSubmission2 - timeLimitsTxSubmission2 - channel - (txSubmissionServerPeerPipelined (hTxSubmissionServer version them)) + + let runServer serverApi = + runPipelinedPeerWithLimits + (contramap (TraceLabelPeer them) tTxSubmission2Tracer) + (cTxSubmission2Codec (mkCodecs version)) + blTxSubmission2 + timeLimitsTxSubmission2 + channel + (txSubmissionServerPeerPipelined serverApi) + + case hTxSubmissionServer version them of + Left legacyTxSubmissionServer -> + runServer legacyTxSubmissionServer + Right newTxSubmissionServer -> + withPeer (TraceLabelPeer them `contramap` tTxLogicTracer) + (getTxChannelsVar kernel) + (getTxMempoolSem kernel) + defaultTxDecisionPolicy + (getSharedTxStateVar kernel) + (mapTxSubmissionMempoolReader txForgetValidated + $ getMempoolReader (getMempool kernel)) + (getMempoolWriter (getMempool kernel)) + txWireSize + them $ \api -> + runServer (newTxSubmissionServer api) aKeepAliveClient :: NodeToNodeVersion diff --git a/ouroboros-consensus-diffusion/src/ouroboros-consensus-diffusion/Ouroboros/Consensus/Node.hs b/ouroboros-consensus-diffusion/src/ouroboros-consensus-diffusion/Ouroboros/Consensus/Node.hs index 037425f737..3371fe43fb 100644 --- a/ouroboros-consensus-diffusion/src/ouroboros-consensus-diffusion/Ouroboros/Consensus/Node.hs +++ b/ouroboros-consensus-diffusion/src/ouroboros-consensus-diffusion/Ouroboros/Consensus/Node.hs @@ -154,6 +154,10 @@ import Ouroboros.Network.PeerSelection.PeerSharing.Codec import Ouroboros.Network.PeerSelection.RootPeersDNS.PublicRootPeers (TracePublicRootPeers) import Ouroboros.Network.RethrowPolicy +import Ouroboros.Network.TxSubmission.Inbound.V2 + (TxSubmissionLogicVersion) +import Ouroboros.Network.TxSubmission.Inbound.V2.Types + (TxSubmissionInitDelay) import qualified SafeWildCards import System.Exit (ExitCode (..)) import System.FilePath (()) @@ -225,6 +229,11 @@ data RunNodeArgs m addrNTN addrNTC blk p2p = RunNodeArgs { , rnGetUseBootstrapPeers :: STM m UseBootstrapPeers , rnGenesisConfig :: GenesisConfig + + -- | Version of the tx-submission logic to run. + , rnTxSubmissionLogicVersion :: TxSubmissionLogicVersion + + , rnTxSubmissionInitDelay :: TxSubmissionInitDelay } @@ -477,6 +486,7 @@ runWith :: forall m addrNTN addrNTC blk p2p. , Hashable addrNTN -- the constraint comes from `initNodeKernel` , NetworkIO m , NetworkAddr addrNTN + , Show addrNTN ) => RunNodeArgs m addrNTN addrNTC blk p2p -> (NodeToNodeVersion -> addrNTN -> CBOR.Encoding) @@ -587,6 +597,7 @@ runWith RunNodeArgs{..} encAddrNtN decAddrNtN LowLevelRunNodeArgs{..} = llrnPublicPeerSelectionStateVar genesisArgs DiffusionPipeliningOn + rnTxSubmissionInitDelay nodeKernel <- initNodeKernel nodeKernelArgs rnNodeKernelHook registry nodeKernel @@ -637,7 +648,7 @@ runWith RunNodeArgs{..} encAddrNtN decAddrNtN LowLevelRunNodeArgs{..} = (gcChainSyncLoPBucketConfig llrnGenesisConfig) (gcCSJConfig llrnGenesisConfig) (reportMetric Diffusion.peerMetricsConfiguration peerMetrics) - (NTN.mkHandlers nodeKernelArgs nodeKernel) + (NTN.mkHandlers nodeKernelArgs nodeKernel rnTxSubmissionLogicVersion) mkNodeToClientApps :: NodeKernelArgs m addrNTN (ConnectionId addrNTC) blk @@ -837,6 +848,7 @@ mkNodeKernelArgs :: -> StrictSTM.StrictTVar m (PublicPeerSelectionState addrNTN) -> GenesisNodeKernelArgs m blk -> DiffusionPipeliningSupport + -> TxSubmissionInitDelay -> m (NodeKernelArgs m addrNTN (ConnectionId addrNTC) blk) mkNodeKernelArgs registry @@ -856,8 +868,10 @@ mkNodeKernelArgs publicPeerSelectionStateVar genesisArgs getDiffusionPipeliningSupport + txSubmissionInitDelay = do - let (kaRng, psRng) = split rng + let (kaRng, rng') = split rng + (psRng, txRng) = split rng' return NodeKernelArgs { tracers , registry @@ -880,9 +894,11 @@ mkNodeKernelArgs , getUseBootstrapPeers , keepAliveRng = kaRng , peerSharingRng = psRng + , txSubmissionRng = txRng , publicPeerSelectionStateVar , genesisArgs , getDiffusionPipeliningSupport + , txSubmissionInitDelay } -- | We allow the user running the node to customise the 'NodeKernelArgs' diff --git a/ouroboros-consensus-diffusion/src/ouroboros-consensus-diffusion/Ouroboros/Consensus/Node/Tracers.hs b/ouroboros-consensus-diffusion/src/ouroboros-consensus-diffusion/Ouroboros/Consensus/Node/Tracers.hs index 9b5a6eb8db..b37d2b8982 100644 --- a/ouroboros-consensus-diffusion/src/ouroboros-consensus-diffusion/Ouroboros/Consensus/Node/Tracers.hs +++ b/ouroboros-consensus-diffusion/src/ouroboros-consensus-diffusion/Ouroboros/Consensus/Node/Tracers.hs @@ -44,8 +44,10 @@ import Ouroboros.Network.BlockFetch (TraceFetchClientState, import Ouroboros.Network.BlockFetch.Decision.Trace (TraceDecisionEvent) import Ouroboros.Network.KeepAlive (TraceKeepAliveClient) -import Ouroboros.Network.TxSubmission.Inbound +import Ouroboros.Network.TxSubmission.Inbound.V1 (TraceTxSubmissionInbound) +import Ouroboros.Network.TxSubmission.Inbound.V2.Types (TraceTxLogic, + TxSubmissionCounters) import Ouroboros.Network.TxSubmission.Outbound (TraceTxSubmissionOutbound) @@ -63,6 +65,8 @@ data Tracers' remotePeer localPeer blk f = Tracers , txInboundTracer :: f (TraceLabelPeer remotePeer (TraceTxSubmissionInbound (GenTxId blk) (GenTx blk))) , txOutboundTracer :: f (TraceLabelPeer remotePeer (TraceTxSubmissionOutbound (GenTxId blk) (GenTx blk))) , localTxSubmissionServerTracer :: f (TraceLocalTxSubmissionServerEvent blk) + , txLogicTracer :: f (TraceTxLogic remotePeer (GenTxId blk) (GenTx blk)) + , txCountersTracer :: f TxSubmissionCounters , mempoolTracer :: f (TraceEventMempool blk) , forgeTracer :: f (TraceLabelCreds (TraceForgeEvent blk)) , blockchainTimeTracer :: f (TraceBlockchainTimeEvent UTCTime) @@ -88,6 +92,8 @@ instance (forall a. Semigroup (f a)) , txInboundTracer = f txInboundTracer , txOutboundTracer = f txOutboundTracer , localTxSubmissionServerTracer = f localTxSubmissionServerTracer + , txLogicTracer = f txLogicTracer + , txCountersTracer = f txCountersTracer , mempoolTracer = f mempoolTracer , forgeTracer = f forgeTracer , blockchainTimeTracer = f blockchainTimeTracer @@ -121,6 +127,8 @@ nullTracers = Tracers , txInboundTracer = nullTracer , txOutboundTracer = nullTracer , localTxSubmissionServerTracer = nullTracer + , txLogicTracer = nullTracer + , txCountersTracer = nullTracer , mempoolTracer = nullTracer , forgeTracer = nullTracer , blockchainTimeTracer = nullTracer @@ -157,6 +165,8 @@ showTracers tr = Tracers , txInboundTracer = showTracing tr , txOutboundTracer = showTracing tr , localTxSubmissionServerTracer = showTracing tr + , txLogicTracer = showTracing tr + , txCountersTracer = showTracing tr , mempoolTracer = showTracing tr , forgeTracer = showTracing tr , blockchainTimeTracer = showTracing tr diff --git a/ouroboros-consensus-diffusion/src/ouroboros-consensus-diffusion/Ouroboros/Consensus/NodeKernel.hs b/ouroboros-consensus-diffusion/src/ouroboros-consensus-diffusion/Ouroboros/Consensus/NodeKernel.hs index 188ac9b4c2..2a5ed4ef69 100644 --- a/ouroboros-consensus-diffusion/src/ouroboros-consensus-diffusion/Ouroboros/Consensus/NodeKernel.hs +++ b/ouroboros-consensus-diffusion/src/ouroboros-consensus-diffusion/Ouroboros/Consensus/NodeKernel.hs @@ -118,10 +118,13 @@ import Ouroboros.Network.PeerSharing (PeerSharingAPI, newPeerSharingRegistry, ps_POLICY_PEER_SHARE_MAX_PEERS, ps_POLICY_PEER_SHARE_STICKY_TIME) import Ouroboros.Network.Protocol.LocalStateQuery.Type (Target (..)) -import Ouroboros.Network.SizeInBytes -import Ouroboros.Network.TxSubmission.Inbound - (TxSubmissionMempoolWriter) -import qualified Ouroboros.Network.TxSubmission.Inbound as Inbound +import Ouroboros.Network.TxSubmission.Inbound.V1 + (TxSubmissionInitDelay, TxSubmissionMempoolWriter) +import qualified Ouroboros.Network.TxSubmission.Inbound.V1 as Inbound +import Ouroboros.Network.TxSubmission.Inbound.V2.Registry + (SharedTxStateVar, TxChannelsVar, TxMempoolSem, + decisionLogicThreads, newSharedTxStateVar, + newTxChannelsVar, newTxMempoolSem) import Ouroboros.Network.TxSubmission.Mempool.Reader (TxSubmissionMempoolReader) import qualified Ouroboros.Network.TxSubmission.Mempool.Reader as MempoolReader @@ -177,6 +180,18 @@ data NodeKernel m addrNTN addrNTC blk = NodeKernel { , getDiffusionPipeliningSupport :: DiffusionPipeliningSupport , getBlockchainTime :: BlockchainTime m + + -- | Communication channels between `TxSubmission` client mini-protocol and + -- decision logic. + , getTxChannelsVar :: TxChannelsVar m (ConnectionId addrNTN) (GenTxId blk) (GenTx blk) + + -- | Shared state of all `TxSubmission` clients. + -- + , getSharedTxStateVar :: SharedTxStateVar m (ConnectionId addrNTN) (GenTxId blk) (GenTx blk) + + -- | A semaphore used by tx-submission for submitting `tx`s to the mempool. + -- + , getTxMempoolSem :: TxMempoolSem m } -- | Arguments required when initializing a node @@ -199,6 +214,8 @@ data NodeKernelArgs m addrNTN addrNTC blk = NodeKernelArgs { , gsmArgs :: GsmNodeKernelArgs m blk , getUseBootstrapPeers :: STM m UseBootstrapPeers , peerSharingRng :: StdGen + , txSubmissionRng :: StdGen + , txSubmissionInitDelay :: TxSubmissionInitDelay , publicPeerSelectionStateVar :: StrictSTM.StrictTVar m (PublicPeerSelectionState addrNTN) , genesisArgs :: GenesisNodeKernelArgs m blk @@ -222,9 +239,11 @@ initNodeKernel args@NodeKernelArgs { registry, cfg, tracers , btime , gsmArgs , peerSharingRng + , txSubmissionRng , publicPeerSelectionStateVar , genesisArgs , getDiffusionPipeliningSupport + , miniProtocolParameters } = do -- using a lazy 'TVar', 'BlockForging' does not have a 'NoThunks' instance. blockForgingVar :: LazySTM.TMVar m [BlockForging m blk] <- LazySTM.newTMVarIO [] @@ -297,21 +316,25 @@ initNodeKernel args@NodeKernelArgs { registry, cfg, tracers ps_POLICY_PEER_SHARE_STICKY_TIME ps_POLICY_PEER_SHARE_MAX_PEERS + txChannelsVar <- newTxChannelsVar + sharedTxStateVar <- newSharedTxStateVar txSubmissionRng + txMempoolSem <- newTxMempoolSem + case gnkaLoEAndGDDArgs genesisArgs of - LoEAndGDDDisabled -> pure () - LoEAndGDDEnabled lgArgs -> do + LoEAndGDDDisabled -> pure () + LoEAndGDDEnabled varGetLoEFragment -> do varLoEFragment <- newTVarIO $ AF.Empty AF.AnchorGenesis setGetLoEFragment (readTVar varGsmState) (readTVar varLoEFragment) - (lgnkaLoEFragmentTVar lgArgs) + (lgnkaLoEFragmentTVar varGetLoEFragment) void $ forkLinkedWatcher registry "NodeKernel.GDD" $ gddWatcher cfg (gddTracer tracers) chainDB - (lgnkaGDDRateLimit lgArgs) + (lgnkaGDDRateLimit varGetLoEFragment) (readTVar varGsmState) (cschcMap varChainSyncHandles) varLoEFragment @@ -329,6 +352,14 @@ initNodeKernel args@NodeKernelArgs { registry, cfg, tracers fetchClientRegistry blockFetchConfiguration + void $ forkLinkedThread registry "NodeKernel.decisionLogicThreads" $ + decisionLogicThreads + (txLogicTracer tracers) + (txCountersTracer tracers) + (txDecisionPolicy miniProtocolParameters) + txChannelsVar + sharedTxStateVar + return NodeKernel { getChainDB = chainDB , getMempool = mempool @@ -345,6 +376,9 @@ initNodeKernel args@NodeKernelArgs { registry, cfg, tracers = varOutboundConnectionsState , getDiffusionPipeliningSupport , getBlockchainTime = btime + , getTxChannelsVar = txChannelsVar + , getSharedTxStateVar = sharedTxStateVar + , getTxMempoolSem = txMempoolSem } where blockForgingController :: InternalState m remotePeer localPeer blk @@ -795,9 +829,9 @@ getMempoolReader mempool = MempoolReader.TxSubmissionMempoolReader { mempoolTxIdsAfter = \idx -> [ ( txId (txForgetValidated tx) , idx' - , SizeInBytes $ unByteSize32 $ txMeasureByteSize msr + , txWireSize $ txForgetValidated tx ) - | (tx, idx', msr) <- snapshotTxsAfter idx + | (tx, idx', _msr) <- snapshotTxsAfter idx ] , mempoolLookupTx = snapshotLookupTx , mempoolHasTx = snapshotHasTx diff --git a/ouroboros-consensus-diffusion/src/unstable-diffusion-testlib/Test/ThreadNet/General.hs b/ouroboros-consensus-diffusion/src/unstable-diffusion-testlib/Test/ThreadNet/General.hs index 348d183e31..4bf86aad16 100644 --- a/ouroboros-consensus-diffusion/src/unstable-diffusion-testlib/Test/ThreadNet/General.hs +++ b/ouroboros-consensus-diffusion/src/unstable-diffusion-testlib/Test/ThreadNet/General.hs @@ -60,9 +60,11 @@ import Ouroboros.Consensus.Util.IOLike import Ouroboros.Consensus.Util.Orphans () import Ouroboros.Consensus.Util.RedundantConstraints import qualified Ouroboros.Network.Mock.Chain as MockChain +import Ouroboros.Network.TxSubmission.Inbound.V2 + (TxSubmissionLogicVersion (..)) import qualified System.FS.Sim.MockFS as Mock import System.FS.Sim.MockFS (MockFS) -import Test.QuickCheck +import Test.QuickCheck as QC import Test.ThreadNet.Network import Test.ThreadNet.TxGen import Test.ThreadNet.Util @@ -92,11 +94,12 @@ import Test.Util.Time (dawnOfTime) -- (each of which realizes a ledger-protocol combination) influences the -- validity of these data. data TestConfig = TestConfig - { initSeed :: Seed - , nodeTopology :: NodeTopology - , numCoreNodes :: NumCoreNodes - , numSlots :: NumSlots + { initSeed :: Seed + , nodeTopology :: NodeTopology + , numCoreNodes :: NumCoreNodes + , numSlots :: NumSlots -- ^ TODO generate in function of @k@ + , txLogicVersion :: TxSubmissionLogicVersion } deriving (Show) @@ -125,6 +128,7 @@ instance Arbitrary TestConfig where numCoreNodes <- arbitrary nodeTopology <- genNodeTopology numCoreNodes + txLogicVersion <- QC.elements [minBound..maxBound] numSlots <- arbitrary pure TestConfig @@ -132,6 +136,7 @@ instance Arbitrary TestConfig where , nodeTopology , numCoreNodes , numSlots + , txLogicVersion } shrink TestConfig @@ -139,6 +144,7 @@ instance Arbitrary TestConfig where , nodeTopology , numCoreNodes , numSlots + , txLogicVersion } = dropId $ [ TestConfig @@ -146,6 +152,7 @@ instance Arbitrary TestConfig where , nodeTopology = top' , numCoreNodes = n' , numSlots = t' + , txLogicVersion } | n' <- andId shrink numCoreNodes , t' <- andId shrink numSlots @@ -213,6 +220,7 @@ runTestNetwork TestConfig , numSlots , nodeTopology , initSeed + , txLogicVersion } TestConfigB { forgeEbbEnv , future @@ -234,20 +242,21 @@ runTestNetwork TestConfig (BTime.SystemStart dawnOfTime) nullTracer runThreadNetwork systemTime ThreadNetworkArgs - { tnaForgeEbbEnv = forgeEbbEnv - , tnaFuture = future - , tnaJoinPlan = nodeJoinPlan - , tnaMessageDelay = messageDelay - , tnaNodeInfo = nodeInfo - , tnaNumCoreNodes = numCoreNodes - , tnaNumSlots = numSlots - , tnaSeed = initSeed - , tnaMkRekeyM = mkRekeyM - , tnaRestarts = nodeRestarts - , tnaTopology = nodeTopology - , tnaTxGenExtra = txGenExtra - , tnaVersion = networkVersion - , tnaBlockVersion = blockVersion + { tnaForgeEbbEnv = forgeEbbEnv + , tnaFuture = future + , tnaJoinPlan = nodeJoinPlan + , tnaMessageDelay = messageDelay + , tnaNodeInfo = nodeInfo + , tnaNumCoreNodes = numCoreNodes + , tnaNumSlots = numSlots + , tnaSeed = initSeed + , tnaMkRekeyM = mkRekeyM + , tnaRestarts = nodeRestarts + , tnaTopology = nodeTopology + , tnaTxGenExtra = txGenExtra + , tnaVersion = networkVersion + , tnaBlockVersion = blockVersion + , tnaTxLogicVersion = txLogicVersion } {------------------------------------------------------------------------------- diff --git a/ouroboros-consensus-diffusion/src/unstable-diffusion-testlib/Test/ThreadNet/Network.hs b/ouroboros-consensus-diffusion/src/unstable-diffusion-testlib/Test/ThreadNet/Network.hs index 0ef7e84bba..ccf1d42f44 100644 --- a/ouroboros-consensus-diffusion/src/unstable-diffusion-testlib/Test/ThreadNet/Network.hs +++ b/ouroboros-consensus-diffusion/src/unstable-diffusion-testlib/Test/ThreadNet/Network.hs @@ -123,6 +123,10 @@ import Ouroboros.Network.Protocol.Limits (waitForever) import Ouroboros.Network.Protocol.LocalStateQuery.Type import Ouroboros.Network.Protocol.PeerSharing.Type (PeerSharing) import Ouroboros.Network.Protocol.TxSubmission2.Type +import Ouroboros.Network.TxSubmission.Inbound.V2 + (TxSubmissionInitDelay (..), TxSubmissionLogicVersion (..)) +import Ouroboros.Network.TxSubmission.Inbound.V2.Policy + (TxDecisionPolicy (..), defaultTxDecisionPolicy) import qualified System.FS.Sim.MockFS as Mock import System.FS.Sim.MockFS (MockFS) import System.Random (mkStdGen, split) @@ -216,20 +220,21 @@ instance Show (CalcMessageDelay blk) where -- | Parameters for the test node net -- data ThreadNetworkArgs m blk = ThreadNetworkArgs - { tnaForgeEbbEnv :: Maybe (ForgeEbbEnv blk) - , tnaFuture :: Future - , tnaJoinPlan :: NodeJoinPlan - , tnaNodeInfo :: CoreNodeId -> TestNodeInitialization m blk - , tnaNumCoreNodes :: NumCoreNodes - , tnaNumSlots :: NumSlots - , tnaMessageDelay :: CalcMessageDelay blk - , tnaSeed :: Seed - , tnaMkRekeyM :: Maybe (m (RekeyM m blk)) - , tnaRestarts :: NodeRestarts - , tnaTopology :: NodeTopology - , tnaTxGenExtra :: TxGenExtra blk - , tnaVersion :: NodeToNodeVersion - , tnaBlockVersion :: BlockNodeToNodeVersion blk + { tnaForgeEbbEnv :: Maybe (ForgeEbbEnv blk) + , tnaFuture :: Future + , tnaJoinPlan :: NodeJoinPlan + , tnaNodeInfo :: CoreNodeId -> TestNodeInitialization m blk + , tnaNumCoreNodes :: NumCoreNodes + , tnaNumSlots :: NumSlots + , tnaMessageDelay :: CalcMessageDelay blk + , tnaSeed :: Seed + , tnaMkRekeyM :: Maybe (m (RekeyM m blk)) + , tnaRestarts :: NodeRestarts + , tnaTopology :: NodeTopology + , tnaTxGenExtra :: TxGenExtra blk + , tnaVersion :: NodeToNodeVersion + , tnaBlockVersion :: BlockNodeToNodeVersion blk + , tnaTxLogicVersion :: TxSubmissionLogicVersion } {------------------------------------------------------------------------------- @@ -317,6 +322,7 @@ runThreadNetwork systemTime ThreadNetworkArgs , tnaTxGenExtra = txGenExtra , tnaVersion = version , tnaBlockVersion = blockVersion + , tnaTxLogicVersion = txLogicVersion } = withRegistry $ \sharedRegistry -> do mbRekeyM <- sequence mbMkRekeyM @@ -983,7 +989,8 @@ runThreadNetwork systemTime ThreadNetworkArgs let rng = case seed of Seed s -> mkStdGen s - (kaRng, psRng) = split rng + (kaRng, rng') = split rng + (psRng, txRng) = split rng' publicPeerSelectionStateVar <- makePublicPeerSelectionStateVar let nodeKernelArgs = NodeKernelArgs { tracers @@ -1001,11 +1008,13 @@ runThreadNetwork systemTime ThreadNetworkArgs , mempoolCapacityOverride = NoMempoolCapacityBytesOverride , keepAliveRng = kaRng , peerSharingRng = psRng + , txSubmissionRng = txRng , miniProtocolParameters = MiniProtocolParameters { chainSyncPipeliningHighMark = 4, chainSyncPipeliningLowMark = 2, blockFetchPipeliningMax = 10, - txSubmissionMaxUnacked = 1000 -- TODO ? + txDecisionPolicy = + defaultTxDecisionPolicy { maxUnacknowledgedTxIds = 1000 } -- TODO ? } , blockFetchConfiguration = BlockFetchConfiguration { bfcMaxConcurrencyBulkSync = 1 @@ -1033,6 +1042,7 @@ runThreadNetwork systemTime ThreadNetworkArgs gnkaLoEAndGDDArgs = LoEAndGDDDisabled } , getDiffusionPipeliningSupport = DiffusionPipeliningOn + , txSubmissionInitDelay = NoTxSubmissionInitDelay } nodeKernel <- initNodeKernel nodeKernelArgs @@ -1063,7 +1073,7 @@ runThreadNetwork systemTime ThreadNetworkArgs -- The purpose of this test is not testing protocols, so -- returning constant empty list is fine if we have thorough -- tests about the peer sharing protocol itself. - (NTN.mkHandlers nodeKernelArgs nodeKernel) + (NTN.mkHandlers nodeKernelArgs nodeKernel txLogicVersion) -- Create a 'ReadOnlyForker' to be used in 'forkTxProducer'. This function -- needs the read-only forker to elaborate a complete UTxO set to generate diff --git a/ouroboros-consensus-diffusion/test/consensus-test/Test/Consensus/HardFork/Combinator.hs b/ouroboros-consensus-diffusion/test/consensus-test/Test/Consensus/HardFork/Combinator.hs index 014d286490..a84dacbbd4 100644 --- a/ouroboros-consensus-diffusion/test/consensus-test/Test/Consensus/HardFork/Combinator.hs +++ b/ouroboros-consensus-diffusion/test/consensus-test/Test/Consensus/HardFork/Combinator.hs @@ -184,10 +184,11 @@ prop_simple_hfc_convergence testSetup@TestSetup{..} = testConfig :: TestConfig testConfig = TestConfig { - numCoreNodes = ncn - , numSlots = testSetupNumSlots testSetup - , nodeTopology = meshNodeTopology ncn - , initSeed = testSetupSeed + numCoreNodes = ncn + , numSlots = testSetupNumSlots testSetup + , nodeTopology = meshNodeTopology ncn + , initSeed = testSetupSeed + , txLogicVersion = maxBound } where ncn :: NumCoreNodes diff --git a/ouroboros-consensus-diffusion/test/consensus-test/Test/Consensus/HardFork/Combinator/A.hs b/ouroboros-consensus-diffusion/test/consensus-test/Test/Consensus/HardFork/Combinator/A.hs index c6f6d0c9d1..65623f622a 100644 --- a/ouroboros-consensus-diffusion/test/consensus-test/Test/Consensus/HardFork/Combinator/A.hs +++ b/ouroboros-consensus-diffusion/test/consensus-test/Test/Consensus/HardFork/Combinator/A.hs @@ -375,6 +375,7 @@ instance LedgerSupportsMempool BlockA where instance TxLimits BlockA where type TxMeasure BlockA = IgnoringOverflow ByteSize32 + txWireSize = const . fromIntegral $ (0 :: Int) blockCapacityTxMeasure _cfg _st = IgnoringOverflow $ ByteSize32 $ 100 * 1024 -- arbitrary txMeasure _cfg _st _tx = pure $ IgnoringOverflow $ ByteSize32 0 diff --git a/ouroboros-consensus-diffusion/test/consensus-test/Test/Consensus/HardFork/Combinator/B.hs b/ouroboros-consensus-diffusion/test/consensus-test/Test/Consensus/HardFork/Combinator/B.hs index 0e43f6fbf8..85ceaf9e4b 100644 --- a/ouroboros-consensus-diffusion/test/consensus-test/Test/Consensus/HardFork/Combinator/B.hs +++ b/ouroboros-consensus-diffusion/test/consensus-test/Test/Consensus/HardFork/Combinator/B.hs @@ -305,6 +305,7 @@ instance LedgerSupportsMempool BlockB where instance TxLimits BlockB where type TxMeasure BlockB = IgnoringOverflow ByteSize32 + txWireSize = const . fromIntegral $ (0 :: Int) blockCapacityTxMeasure _cfg _st = IgnoringOverflow $ ByteSize32 $ 100 * 1024 -- arbitrary txMeasure _cfg _st _tx = pure $ IgnoringOverflow $ ByteSize32 0 diff --git a/ouroboros-consensus/bench/mempool-bench/Bench/Consensus/Mempool/TestBlock.hs b/ouroboros-consensus/bench/mempool-bench/Bench/Consensus/Mempool/TestBlock.hs index 52ea68d857..cfd32e024c 100644 --- a/ouroboros-consensus/bench/mempool-bench/Bench/Consensus/Mempool/TestBlock.hs +++ b/ouroboros-consensus/bench/mempool-bench/Bench/Consensus/Mempool/TestBlock.hs @@ -223,6 +223,7 @@ instance Ledger.LedgerSupportsMempool TestBlock where instance Ledger.TxLimits TestBlock where type TxMeasure TestBlock = Ledger.IgnoringOverflow Ledger.ByteSize32 + txWireSize = fromIntegral . Ledger.unByteSize32 . txSize -- We tweaked this in such a way that we test the case in which we exceed the -- maximum mempool capacity. The value used here depends on 'txInBlockSize'. blockCapacityTxMeasure _cfg _st = diff --git a/ouroboros-consensus/changelog.d/20240808_101916_marcin.wojtowicz_tx_wire_size.md b/ouroboros-consensus/changelog.d/20240808_101916_marcin.wojtowicz_tx_wire_size.md new file mode 100644 index 0000000000..63ff90e564 --- /dev/null +++ b/ouroboros-consensus/changelog.d/20240808_101916_marcin.wojtowicz_tx_wire_size.md @@ -0,0 +1,25 @@ + + + + +### Breaking + +- Added txWireSize method to TxLimits class to provide + a CBOR-encoded transaction size as it is when transmitted + over the network. + +- Added `msExUnitsMemory` and `msExUnitsSteps` fields to `MempoolSize` diff --git a/ouroboros-consensus/ouroboros-consensus.cabal b/ouroboros-consensus/ouroboros-consensus.cabal index eea42167be..1f9263a177 100644 --- a/ouroboros-consensus/ouroboros-consensus.cabal +++ b/ouroboros-consensus/ouroboros-consensus.cabal @@ -412,6 +412,7 @@ library unstable-consensus-testlib Test.Util.Serialisation.Golden Test.Util.Serialisation.Roundtrip Test.Util.Serialisation.SomeResult + Test.Util.Serialisation.TxWireSize Test.Util.Shrink Test.Util.Slots Test.Util.Split @@ -456,6 +457,7 @@ library unstable-consensus-testlib nothunks, optparse-applicative, ouroboros-consensus, + ouroboros-network, ouroboros-network-api, ouroboros-network-mock, pretty-simple, diff --git a/ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/HardFork/Combinator/Mempool.hs b/ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/HardFork/Combinator/Mempool.hs index 46eacc01d0..85b03b8e6e 100644 --- a/ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/HardFork/Combinator/Mempool.hs +++ b/ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/HardFork/Combinator/Mempool.hs @@ -273,6 +273,21 @@ instance ( CanHardFork xs instance CanHardFork xs => TxLimits (HardForkBlock xs) where type TxMeasure (HardForkBlock xs) = HardForkTxMeasure xs + txWireSize = + \tx -> + let tx' :: NS GenTx xs + tx' = getOneEraGenTx (getHardForkGenTx tx) + -- HFC overhead + -- note that HFC might be disabled, then this gives an upperbound. + overhead | nsToIndex tx' <= 23 = 2 + | otherwise = 3 + + in + (+ overhead) + . hcollapse + . hcmap proxySingle (K . txWireSize) + $ tx' + blockCapacityTxMeasure HardForkLedgerConfig{..} (TickedHardForkLedgerState transition hardForkState) diff --git a/ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Ledger/Dual.hs b/ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Ledger/Dual.hs index 918603eb54..028af218d5 100644 --- a/ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Ledger/Dual.hs +++ b/ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Ledger/Dual.hs @@ -677,6 +677,7 @@ instance Bridge m a => LedgerSupportsMempool (DualBlock m a) where instance Bridge m a => TxLimits (DualBlock m a) where type TxMeasure (DualBlock m a) = TxMeasure m + txWireSize = txWireSize . dualGenTxMain txMeasure DualLedgerConfig{..} TickedDualLedgerState{..} DualGenTx{..} = do mapExcept (inj +++ id) $ txMeasure dualLedgerConfigMain tickedDualLedgerStateMain dualGenTxMain diff --git a/ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Ledger/SupportsMempool.hs b/ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Ledger/SupportsMempool.hs index fa781294de..438c489c29 100644 --- a/ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Ledger/SupportsMempool.hs +++ b/ouroboros-consensus/src/ouroboros-consensus/Ouroboros/Consensus/Ledger/SupportsMempool.hs @@ -45,6 +45,7 @@ import Numeric.Natural import Ouroboros.Consensus.Block.Abstract import Ouroboros.Consensus.Ledger.Abstract import Ouroboros.Consensus.Ledger.Tables.Utils +import Ouroboros.Network.SizeInBytes as Network -- | Generalized transaction -- @@ -305,6 +306,10 @@ class ( Measure (TxMeasure blk) -- | The (possibly multi-dimensional) size of a transaction in a block. type TxMeasure blk + -- | The size of the transaction from the perspective of diffusion layer + -- + txWireSize :: GenTx blk -> Network.SizeInBytes + -- | The various sizes (bytes, Plutus script ExUnits, etc) of a tx /when it's -- in a block/ -- diff --git a/ouroboros-consensus/src/unstable-consensus-testlib/Test/Util/Serialisation/TxWireSize.hs b/ouroboros-consensus/src/unstable-consensus-testlib/Test/Util/Serialisation/TxWireSize.hs new file mode 100644 index 0000000000..23042dd90c --- /dev/null +++ b/ouroboros-consensus/src/unstable-consensus-testlib/Test/Util/Serialisation/TxWireSize.hs @@ -0,0 +1,87 @@ +{-# LANGUAGE FlexibleContexts #-} + +module Test.Util.Serialisation.TxWireSize ( + prop_txWireSize + , prop_txWireSize_txSubmission + ) where + +import Codec.CBOR.Write (toLazyByteString) +import qualified Data.ByteString.Lazy as BSL +import Ouroboros.Consensus.Block.Abstract (CodecConfig) +import Ouroboros.Consensus.Ledger.SupportsMempool (GenTx, + TxLimits (..)) +import Ouroboros.Consensus.Node.NetworkProtocolVersion + (BlockNodeToNodeVersion) +import Ouroboros.Consensus.Node.Serialisation +import Ouroboros.Network.SizeInBytes +import Ouroboros.Network.TxSubmission.Inbound.V2.State + (config_MAX_TX_SIZE_DISCREPANCY) +import Test.Tasty.QuickCheck +import Test.Util.Serialisation.Roundtrip (WithVersion (..)) + + +-- | Verify that `txWriteSize` agrees with the encoded `GenTx` size up to +-- `config_MX_TX_SIZE_DISCREPANCY` allowed by `tx-submission` mini-protocol. +-- +-- NOTE: `tx`s which do not satisfy this property will terminate connections. +-- +prop_txWireSize_txSubmission :: + ( SerialiseNodeToNode blk (GenTx blk) + , TxLimits blk + ) + => CodecConfig blk + -> WithVersion (BlockNodeToNodeVersion blk) (GenTx blk) + -> Property +prop_txWireSize_txSubmission ccfg (WithVersion version tx) = + counterexample ("encoded size " ++ show encSize ++ ", computed size " ++ show cmpSize) + $ counterexample ("diff size " ++ show diffSize) + $ label (show diffSize) + $ fromIntegral (abs diffSize) <= config_MAX_TX_SIZE_DISCREPANCY + where + encSize, cmpSize :: SizeInBytes + + encSize = fromIntegral (BSL.length $ toLazyByteString (encodeNodeToNode ccfg version tx)) + cmpSize = txWireSize tx + + diffSize :: Int + diffSize = fromIntegral encSize - fromIntegral cmpSize + + +-- | Verify that `txWriteSize` is very close to the real tx size. +-- +-- The `txWireSize` doesn't take into account if HFC is enabled or not. If it +-- is enabled, the `wireTxSize` for `GenTx (HardForkBlock xs)` will agree with +-- the encoded size, but if it is disabled it will overestimate the value by HFC +-- envelope (at most 3 bytes, 2 for forcible future) +-- +prop_txWireSize :: + ( SerialiseNodeToNode blk (GenTx blk) + , TxLimits blk + ) + => (GenTx blk -> Maybe String) + -- ^ show tx bytes + -> CodecConfig blk + -> WithVersion (BlockNodeToNodeVersion blk) (GenTx blk) + -> Property +prop_txWireSize getTxBytes ccfg (WithVersion version tx) = + counterexample ("encoded size " ++ show encSize ++ ", computed size " ++ show cmpSize) + $ counterexample ("encoded tx:\n" ++ show encoded) + $ label (show diffSize) + $ case getTxBytes tx of + Just txBytes -> counterexample ("tx bytes:\n" ++ txBytes) + Nothing -> property + $ encSize <= cmpSize + .&&. + encSize + 3 >= cmpSize + + + where + encoded :: BSL.ByteString + encoded = toLazyByteString (encodeNodeToNode ccfg version tx) + + encSize, cmpSize :: SizeInBytes + encSize = fromIntegral (BSL.length encoded) + cmpSize = txWireSize tx + + diffSize :: Int + diffSize = fromIntegral encSize - fromIntegral cmpSize diff --git a/ouroboros-consensus/src/unstable-mock-block/Ouroboros/Consensus/Mock/Ledger/Block.hs b/ouroboros-consensus/src/unstable-mock-block/Ouroboros/Consensus/Mock/Ledger/Block.hs index 143df4ea50..edce469395 100644 --- a/ouroboros-consensus/src/unstable-mock-block/Ouroboros/Consensus/Mock/Ledger/Block.hs +++ b/ouroboros-consensus/src/unstable-mock-block/Ouroboros/Consensus/Mock/Ledger/Block.hs @@ -567,6 +567,7 @@ instance MockProtocolSpecific c ext instance TxLimits (SimpleBlock c ext) where type TxMeasure (SimpleBlock c ext) = IgnoringOverflow ByteSize32 + txWireSize = fromIntegral . unByteSize32 . genTxSize -- Large value so that the Mempool tests never run out of capacity when they -- don't override it. -- diff --git a/ouroboros-consensus/test/consensus-test/Test/Consensus/Mempool/Fairness/TestBlock.hs b/ouroboros-consensus/test/consensus-test/Test/Consensus/Mempool/Fairness/TestBlock.hs index 5b6218380c..91c063adb6 100644 --- a/ouroboros-consensus/test/consensus-test/Test/Consensus/Mempool/Fairness/TestBlock.hs +++ b/ouroboros-consensus/test/consensus-test/Test/Consensus/Mempool/Fairness/TestBlock.hs @@ -117,6 +117,7 @@ instance Ledger.LedgerSupportsMempool TestBlock where instance Ledger.TxLimits TestBlock where type TxMeasure TestBlock = Ledger.IgnoringOverflow Ledger.ByteSize32 + txWireSize = fromIntegral . Ledger.unByteSize32 . txSize . unGenTx blockCapacityTxMeasure _cfg _st = -- The tests will override this value. By using 1, @computeMempoolCapacity@ -- can be exactly what each test requests.