From fc466a824c1e7aaa06fa45734a799661a182be94 Mon Sep 17 00:00:00 2001 From: Russell O'Connor Date: Wed, 24 Dec 2025 15:55:27 -0500 Subject: [PATCH 1/3] Deprecate Broken Jets The specifications for some of the relative time lock jets are nonsensical and based on a gross misunderstanding of how relative time locks work. They should not be used. However, they are already consensus in Liquid so we cannot elimiante them. But what we can do is rename them in order to dissuade their use. Users can and should use other jets to parse the sequence numbers of individual inputs in order to properly validate relative timelocks. --- C/elements/decodeElementsJets.inc | 8 +-- C/elements/elementsJets.c | 8 +-- C/elements/elementsJets.h | 8 +-- C/elements/primitiveEnumJet.inc | 8 +-- C/elements/primitiveJetNode.inc | 64 +++++++++---------- Haskell/Simplicity/Elements/FFI/Jets.hs | 32 +++++----- Haskell/Simplicity/Elements/Jets.hs | 52 +++++++-------- .../Tests/Simplicity/Elements/FFI/Tests.hs | 16 ++--- .../Tests/Simplicity/Elements/Regression.hs | 8 +-- Haskell/Tests/Simplicity/Elements/Tests.hs | 8 +-- Haskell/cbits/elements/jets.c | 8 +-- 11 files changed, 110 insertions(+), 110 deletions(-) diff --git a/C/elements/decodeElementsJets.inc b/C/elements/decodeElementsJets.inc index b2478e45a..8420f64c1 100644 --- a/C/elements/decodeElementsJets.inc +++ b/C/elements/decodeElementsJets.inc @@ -52,12 +52,12 @@ switch (code) { case 1: *result = CHECK_LOCK_HEIGHT; return SIMPLICITY_NO_ERROR; case 2: *result = CHECK_LOCK_TIME; return SIMPLICITY_NO_ERROR; - case 3: *result = CHECK_LOCK_DISTANCE; return SIMPLICITY_NO_ERROR; - case 4: *result = CHECK_LOCK_DURATION; return SIMPLICITY_NO_ERROR; + case 3: *result = BROKEN_DO_NOT_USE_CHECK_LOCK_DISTANCE; return SIMPLICITY_NO_ERROR; + case 4: *result = BROKEN_DO_NOT_USE_CHECK_LOCK_DURATION; return SIMPLICITY_NO_ERROR; case 5: *result = TX_LOCK_HEIGHT; return SIMPLICITY_NO_ERROR; case 6: *result = TX_LOCK_TIME; return SIMPLICITY_NO_ERROR; - case 7: *result = TX_LOCK_DISTANCE; return SIMPLICITY_NO_ERROR; - case 8: *result = TX_LOCK_DURATION; return SIMPLICITY_NO_ERROR; + case 7: *result = BROKEN_DO_NOT_USE_TX_LOCK_DISTANCE; return SIMPLICITY_NO_ERROR; + case 8: *result = BROKEN_DO_NOT_USE_TX_LOCK_DURATION; return SIMPLICITY_NO_ERROR; case 9: *result = TX_IS_FINAL; return SIMPLICITY_NO_ERROR; } break; diff --git a/C/elements/elementsJets.c b/C/elements/elementsJets.c index dfdf5be63..7d4dbf122 100644 --- a/C/elements/elementsJets.c +++ b/C/elements/elementsJets.c @@ -733,14 +733,14 @@ bool simplicity_tx_lock_time(frameItem* dst, frameItem src, const txEnv* env) { } /* tx_lock_distance : ONE |- TWO^16 */ -bool simplicity_tx_lock_distance(frameItem* dst, frameItem src, const txEnv* env) { +bool simplicity_broken_do_not_use_tx_lock_distance(frameItem* dst, frameItem src, const txEnv* env) { (void) src; // src is unused; simplicity_write16(dst, lockDistance(env->tx)); return true; } /* tx_lock_duration : ONE |- TWO^16 */ -bool simplicity_tx_lock_duration(frameItem* dst, frameItem src, const txEnv* env) { +bool simplicity_broken_do_not_use_tx_lock_duration(frameItem* dst, frameItem src, const txEnv* env) { (void) src; // src is unused; simplicity_write16(dst, lockDuration(env->tx)); return true; @@ -761,14 +761,14 @@ bool simplicity_check_lock_time(frameItem* dst, frameItem src, const txEnv* env) } /* check_lock_distance : TWO^16 |- ONE */ -bool simplicity_check_lock_distance(frameItem* dst, frameItem src, const txEnv* env) { +bool simplicity_broken_do_not_use_check_lock_distance(frameItem* dst, frameItem src, const txEnv* env) { (void) dst; // dst is unused; uint_fast16_t x = simplicity_read16(&src); return x <= lockDistance(env->tx); } /* check_lock_duration : TWO^16 |- ONE */ -bool simplicity_check_lock_duration(frameItem* dst, frameItem src, const txEnv* env) { +bool simplicity_broken_do_not_use_check_lock_duration(frameItem* dst, frameItem src, const txEnv* env) { (void) dst; // dst is unused; uint_fast16_t x = simplicity_read16(&src); return x <= lockDuration(env->tx); diff --git a/C/elements/elementsJets.h b/C/elements/elementsJets.h index d6412195c..d6779e38f 100644 --- a/C/elements/elementsJets.h +++ b/C/elements/elementsJets.h @@ -59,12 +59,12 @@ bool simplicity_num_outputs(frameItem* dst, frameItem src, const txEnv* env); bool simplicity_tx_is_final(frameItem* dst, frameItem src, const txEnv* env); bool simplicity_tx_lock_height(frameItem* dst, frameItem src, const txEnv* env); bool simplicity_tx_lock_time(frameItem* dst, frameItem src, const txEnv* env); -bool simplicity_tx_lock_distance(frameItem* dst, frameItem src, const txEnv* env); -bool simplicity_tx_lock_duration(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_broken_do_not_use_tx_lock_distance(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_broken_do_not_use_tx_lock_duration(frameItem* dst, frameItem src, const txEnv* env); bool simplicity_check_lock_height(frameItem* dst, frameItem src, const txEnv* env); bool simplicity_check_lock_time(frameItem* dst, frameItem src, const txEnv* env); -bool simplicity_check_lock_distance(frameItem* dst, frameItem src, const txEnv* env); -bool simplicity_check_lock_duration(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_broken_do_not_use_check_lock_distance(frameItem* dst, frameItem src, const txEnv* env); +bool simplicity_broken_do_not_use_check_lock_duration(frameItem* dst, frameItem src, const txEnv* env); bool simplicity_calculate_issuance_entropy(frameItem* dst, frameItem src, const txEnv* env); bool simplicity_calculate_asset(frameItem* dst, frameItem src, const txEnv* env); bool simplicity_calculate_explicit_token(frameItem* dst, frameItem src, const txEnv* env); diff --git a/C/elements/primitiveEnumJet.inc b/C/elements/primitiveEnumJet.inc index 5e6e6c895..57420913f 100644 --- a/C/elements/primitiveEnumJet.inc +++ b/C/elements/primitiveEnumJet.inc @@ -15,6 +15,10 @@ AND_8, ANNEX_HASH, ASSET_AMOUNT_HASH, BIP_0340_VERIFY, +BROKEN_DO_NOT_USE_CHECK_LOCK_DISTANCE, +BROKEN_DO_NOT_USE_CHECK_LOCK_DURATION, +BROKEN_DO_NOT_USE_TX_LOCK_DISTANCE, +BROKEN_DO_NOT_USE_TX_LOCK_DURATION, BUILD_TAPBRANCH, BUILD_TAPLEAF_SIMPLICITY, BUILD_TAPTWEAK, @@ -27,8 +31,6 @@ CH_16, CH_32, CH_64, CH_8, -CHECK_LOCK_DISTANCE, -CHECK_LOCK_DURATION, CHECK_LOCK_HEIGHT, CHECK_LOCK_TIME, CHECK_SIG_VERIFY, @@ -454,8 +456,6 @@ TOTAL_FEE, TRANSACTION_ID, TX_HASH, TX_IS_FINAL, -TX_LOCK_DISTANCE, -TX_LOCK_DURATION, TX_LOCK_HEIGHT, TX_LOCK_TIME, VERIFY, diff --git a/C/elements/primitiveJetNode.inc b/C/elements/primitiveJetNode.inc index 12731bb79..683de8988 100644 --- a/C/elements/primitiveJetNode.inc +++ b/C/elements/primitiveJetNode.inc @@ -127,6 +127,38 @@ , .targetIx = ty_u , .cost = 49087 /* milli weight units */ } +,[BROKEN_DO_NOT_USE_CHECK_LOCK_DISTANCE] = +{ .tag = JET +, .jet = simplicity_broken_do_not_use_check_lock_distance +, .cmr = {{0x7f78c7a7u, 0x7a25ada2u, 0x23267d23u, 0x9a5922f7u, 0x64b8ac0cu, 0x2fcef68eu, 0xb93c0d92u, 0xda4af515u}} +, .sourceIx = ty_w16 +, .targetIx = ty_u +, .cost = 105 /* milli weight units */ +} +,[BROKEN_DO_NOT_USE_CHECK_LOCK_DURATION] = +{ .tag = JET +, .jet = simplicity_broken_do_not_use_check_lock_duration +, .cmr = {{0x73dac8e2u, 0x5d87eaf3u, 0x82c2a772u, 0x06ad38b9u, 0x384361e7u, 0xd0dc87c0u, 0xfa7af7eau, 0x524597b7u}} +, .sourceIx = ty_w16 +, .targetIx = ty_u +, .cost = 102 /* milli weight units */ +} +,[BROKEN_DO_NOT_USE_TX_LOCK_DISTANCE] = +{ .tag = JET +, .jet = simplicity_broken_do_not_use_tx_lock_distance +, .cmr = {{0x4c7773b8u, 0x18cb7ee5u, 0xf54f925au, 0xad015677u, 0xa043a72fu, 0x316a187cu, 0xc28c696cu, 0xfcb90807u}} +, .sourceIx = ty_u +, .targetIx = ty_w16 +, .cost = 91 /* milli weight units */ +} +,[BROKEN_DO_NOT_USE_TX_LOCK_DURATION] = +{ .tag = JET +, .jet = simplicity_broken_do_not_use_tx_lock_duration +, .cmr = {{0xcc9c64c8u, 0xb6eb4bf0u, 0x9694af5au, 0x35d957a4u, 0x05e66c1bu, 0x35224ed6u, 0x75878918u, 0x452440b2u}} +, .sourceIx = ty_u +, .targetIx = ty_w16 +, .cost = 84 /* milli weight units */ +} ,[BUILD_TAPBRANCH] = { .tag = JET , .jet = simplicity_build_tapbranch @@ -223,22 +255,6 @@ , .targetIx = ty_w8 , .cost = 77 /* milli weight units */ } -,[CHECK_LOCK_DISTANCE] = -{ .tag = JET -, .jet = simplicity_check_lock_distance -, .cmr = {{0x7f78c7a7u, 0x7a25ada2u, 0x23267d23u, 0x9a5922f7u, 0x64b8ac0cu, 0x2fcef68eu, 0xb93c0d92u, 0xda4af515u}} -, .sourceIx = ty_w16 -, .targetIx = ty_u -, .cost = 105 /* milli weight units */ -} -,[CHECK_LOCK_DURATION] = -{ .tag = JET -, .jet = simplicity_check_lock_duration -, .cmr = {{0x73dac8e2u, 0x5d87eaf3u, 0x82c2a772u, 0x06ad38b9u, 0x384361e7u, 0xd0dc87c0u, 0xfa7af7eau, 0x524597b7u}} -, .sourceIx = ty_w16 -, .targetIx = ty_u -, .cost = 102 /* milli weight units */ -} ,[CHECK_LOCK_HEIGHT] = { .tag = JET , .jet = simplicity_check_lock_height @@ -3639,22 +3655,6 @@ , .targetIx = ty_b , .cost = 71 /* milli weight units */ } -,[TX_LOCK_DISTANCE] = -{ .tag = JET -, .jet = simplicity_tx_lock_distance -, .cmr = {{0x4c7773b8u, 0x18cb7ee5u, 0xf54f925au, 0xad015677u, 0xa043a72fu, 0x316a187cu, 0xc28c696cu, 0xfcb90807u}} -, .sourceIx = ty_u -, .targetIx = ty_w16 -, .cost = 91 /* milli weight units */ -} -,[TX_LOCK_DURATION] = -{ .tag = JET -, .jet = simplicity_tx_lock_duration -, .cmr = {{0xcc9c64c8u, 0xb6eb4bf0u, 0x9694af5au, 0x35d957a4u, 0x05e66c1bu, 0x35224ed6u, 0x75878918u, 0x452440b2u}} -, .sourceIx = ty_u -, .targetIx = ty_w16 -, .cost = 84 /* milli weight units */ -} ,[TX_LOCK_HEIGHT] = { .tag = JET , .jet = simplicity_tx_lock_height diff --git a/Haskell/Simplicity/Elements/FFI/Jets.hs b/Haskell/Simplicity/Elements/FFI/Jets.hs index ce038d27e..a97eed352 100644 --- a/Haskell/Simplicity/Elements/FFI/Jets.hs +++ b/Haskell/Simplicity/Elements/FFI/Jets.hs @@ -54,12 +54,12 @@ module Simplicity.Elements.FFI.Jets , tx_is_final , tx_lock_height , tx_lock_time - , tx_lock_distance - , tx_lock_duration + , broken_do_not_use_tx_lock_distance + , broken_do_not_use_tx_lock_duration , check_lock_height , check_lock_time - , check_lock_distance - , check_lock_duration + , broken_do_not_use_check_lock_distance + , broken_do_not_use_check_lock_duration , calculate_issuance_entropy , calculate_asset , calculate_explicit_token @@ -174,12 +174,12 @@ foreign import ccall unsafe "" c_num_outputs :: Ptr FrameItem -> Ptr FrameItem - foreign import ccall unsafe "" c_tx_is_final :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool foreign import ccall unsafe "" c_tx_lock_height :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool foreign import ccall unsafe "" c_tx_lock_time :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool -foreign import ccall unsafe "" c_tx_lock_distance :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool -foreign import ccall unsafe "" c_tx_lock_duration :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_broken_do_not_use_tx_lock_distance :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_broken_do_not_use_tx_lock_duration :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool foreign import ccall unsafe "" c_check_lock_height :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool foreign import ccall unsafe "" c_check_lock_time :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool -foreign import ccall unsafe "" c_check_lock_distance :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool -foreign import ccall unsafe "" c_check_lock_duration :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_broken_do_not_use_check_lock_distance :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool +foreign import ccall unsafe "" c_broken_do_not_use_check_lock_duration :: Ptr FrameItem -> Ptr FrameItem -> Ptr CTxEnv -> IO CBool foreign import ccall unsafe "" c_calculate_issuance_entropy :: Ptr FrameItem -> Ptr FrameItem -> IO CBool foreign import ccall unsafe "" c_calculate_asset :: Ptr FrameItem -> Ptr FrameItem -> IO CBool foreign import ccall unsafe "" c_calculate_explicit_token :: Ptr FrameItem -> Ptr FrameItem -> IO CBool @@ -384,11 +384,11 @@ tx_lock_height = unsafeLocalJet c_tx_lock_height tx_lock_time :: PrimEnv -> () -> Maybe Word32 tx_lock_time = unsafeLocalJet c_tx_lock_time -tx_lock_distance :: PrimEnv -> () -> Maybe Word16 -tx_lock_distance = unsafeLocalJet c_tx_lock_distance +broken_do_not_use_tx_lock_distance :: PrimEnv -> () -> Maybe Word16 +broken_do_not_use_tx_lock_distance = unsafeLocalJet c_broken_do_not_use_tx_lock_distance -tx_lock_duration :: PrimEnv -> () -> Maybe Word16 -tx_lock_duration = unsafeLocalJet c_tx_lock_duration +broken_do_not_use_tx_lock_duration :: PrimEnv -> () -> Maybe Word16 +broken_do_not_use_tx_lock_duration = unsafeLocalJet c_broken_do_not_use_tx_lock_duration check_lock_height :: PrimEnv -> Word32 -> Maybe () check_lock_height = unsafeLocalJet c_check_lock_height @@ -396,11 +396,11 @@ check_lock_height = unsafeLocalJet c_check_lock_height check_lock_time :: PrimEnv -> Word32 -> Maybe () check_lock_time = unsafeLocalJet c_check_lock_time -check_lock_distance :: PrimEnv -> Word16 -> Maybe () -check_lock_distance = unsafeLocalJet c_check_lock_distance +broken_do_not_use_check_lock_distance :: PrimEnv -> Word16 -> Maybe () +broken_do_not_use_check_lock_distance = unsafeLocalJet c_broken_do_not_use_check_lock_distance -check_lock_duration :: PrimEnv -> Word16 -> Maybe () -check_lock_duration = unsafeLocalJet c_check_lock_duration +broken_do_not_use_check_lock_duration :: PrimEnv -> Word16 -> Maybe () +broken_do_not_use_check_lock_duration = unsafeLocalJet c_broken_do_not_use_check_lock_duration calculate_issuance_entropy :: ((Word256, Word32), Word256) -> Maybe Word256 calculate_issuance_entropy = unsafeLocalCoreJet c_calculate_issuance_entropy diff --git a/Haskell/Simplicity/Elements/Jets.hs b/Haskell/Simplicity/Elements/Jets.hs index 5f6711d5b..1903f0e03 100644 --- a/Haskell/Simplicity/Elements/Jets.hs +++ b/Haskell/Simplicity/Elements/Jets.hs @@ -126,12 +126,12 @@ deriving instance Show (SigHashJet a b) data TimeLockJet a b where CheckLockHeight :: TimeLockJet TimeLock.Height () CheckLockTime :: TimeLockJet TimeLock.Time () - CheckLockDistance :: TimeLockJet TimeLock.Distance () - CheckLockDuration :: TimeLockJet TimeLock.Duration () + BrokenDoNotUseCheckLockDistance :: TimeLockJet TimeLock.Distance () + BrokenDoNotUseCheckLockDuration :: TimeLockJet TimeLock.Duration () TxLockHeight :: TimeLockJet () TimeLock.Height TxLockTime :: TimeLockJet () TimeLock.Time - TxLockDistance :: TimeLockJet () TimeLock.Distance - TxLockDuration :: TimeLockJet () TimeLock.Duration + BrokenDoNotUseTxLockDistance :: TimeLockJet () TimeLock.Distance + BrokenDoNotUseTxLockDuration :: TimeLockJet () TimeLock.Duration TxIsFinal :: TimeLockJet () TimeLock.Bit deriving instance Eq (TimeLockJet a b) deriving instance Show (TimeLockJet a b) @@ -249,12 +249,12 @@ specificationSigHash BuildTaptweak = Prog.buildTaptweak specificationTimeLock :: (Assert term, Primitive term) => TimeLockJet a b -> term a b specificationTimeLock CheckLockHeight = TimeLock.checkLockHeight specificationTimeLock CheckLockTime = TimeLock.checkLockTime -specificationTimeLock CheckLockDistance = TimeLock.checkLockDistance -specificationTimeLock CheckLockDuration = TimeLock.checkLockDuration +specificationTimeLock BrokenDoNotUseCheckLockDistance = TimeLock.checkLockDistance +specificationTimeLock BrokenDoNotUseCheckLockDuration = TimeLock.checkLockDuration specificationTimeLock TxLockHeight = TimeLock.txLockHeight specificationTimeLock TxLockTime = TimeLock.txLockTime -specificationTimeLock TxLockDistance = TimeLock.txLockDistance -specificationTimeLock TxLockDuration = TimeLock.txLockDuration +specificationTimeLock BrokenDoNotUseTxLockDistance = TimeLock.txLockDistance +specificationTimeLock BrokenDoNotUseTxLockDuration = TimeLock.txLockDuration specificationTimeLock TxIsFinal = TimeLock.txIsFinal specificationIssuance :: (Assert term, Primitive term) => IssuanceJet a b -> term a b @@ -420,10 +420,10 @@ implementationTimeLock CheckLockTime env x | txIsFinal (envTx env) = guard $ fro | otherwise = guard $ fromWord32 x <= 0 where lock = fromIntegral . sigTxLock . envTx $ env -implementationTimeLock CheckLockDistance env x | fromWord16 x <= fromIntegral (txLockDistance (envTx env)) = Just () - | otherwise = Nothing -implementationTimeLock CheckLockDuration env x | fromWord16 x <= fromIntegral (txLockDuration (envTx env)) = Just () - | otherwise = Nothing +implementationTimeLock BrokenDoNotUseCheckLockDistance env x | fromWord16 x <= fromIntegral (txLockDistance (envTx env)) = Just () + | otherwise = Nothing +implementationTimeLock BrokenDoNotUseCheckLockDuration env x | fromWord16 x <= fromIntegral (txLockDuration (envTx env)) = Just () + | otherwise = Nothing implementationTimeLock TxLockHeight env () | txIsFinal (envTx env) = Just (toWord32 0) | Left l <- parseLock lock = Just . toWord32 $ fromIntegral l | otherwise = Just (toWord32 0) @@ -434,8 +434,8 @@ implementationTimeLock TxLockTime env () | txIsFinal (envTx env) = Just (toWord3 | otherwise = Just (toWord32 0) where lock = fromIntegral . sigTxLock . envTx $ env -implementationTimeLock TxLockDistance env () = Just . toWord16 . fromIntegral $ txLockDistance (envTx env) -implementationTimeLock TxLockDuration env () = Just . toWord16 . fromIntegral $ txLockDuration (envTx env) +implementationTimeLock BrokenDoNotUseTxLockDistance env () = Just . toWord16 . fromIntegral $ txLockDistance (envTx env) +implementationTimeLock BrokenDoNotUseTxLockDuration env () = Just . toWord16 . fromIntegral $ txLockDuration (envTx env) implementationTimeLock TxIsFinal env () = Just $ toBit (txIsFinal (envTx env)) implementationIssuance :: IssuanceJet a b -> PrimEnv -> a -> Maybe b @@ -553,12 +553,12 @@ sigHashCatalogue = book timeLockCatalogue = book [ SomeArrow CheckLockHeight , SomeArrow CheckLockTime - , SomeArrow CheckLockDistance - , SomeArrow CheckLockDuration + , SomeArrow BrokenDoNotUseCheckLockDistance + , SomeArrow BrokenDoNotUseCheckLockDuration , SomeArrow TxLockHeight , SomeArrow TxLockTime - , SomeArrow TxLockDistance - , SomeArrow TxLockDuration + , SomeArrow BrokenDoNotUseTxLockDistance + , SomeArrow BrokenDoNotUseTxLockDuration , SomeArrow TxIsFinal ] issuanceCatalogue = book @@ -671,12 +671,12 @@ putJetBitSigHash BuildTaptweak = putPositive 35 putJetBitTimeLock :: TimeLockJet a b -> DList Bool putJetBitTimeLock CheckLockHeight = putPositive 1 putJetBitTimeLock CheckLockTime = putPositive 2 -putJetBitTimeLock CheckLockDistance = putPositive 3 -putJetBitTimeLock CheckLockDuration = putPositive 4 +putJetBitTimeLock BrokenDoNotUseCheckLockDistance = putPositive 3 +putJetBitTimeLock BrokenDoNotUseCheckLockDuration = putPositive 4 putJetBitTimeLock TxLockHeight = putPositive 5 putJetBitTimeLock TxLockTime = putPositive 6 -putJetBitTimeLock TxLockDistance = putPositive 7 -putJetBitTimeLock TxLockDuration = putPositive 8 +putJetBitTimeLock BrokenDoNotUseTxLockDistance = putPositive 7 +putJetBitTimeLock BrokenDoNotUseTxLockDuration = putPositive 8 putJetBitTimeLock TxIsFinal = putPositive 9 putJetBitIssuance :: IssuanceJet a b -> DList Bool @@ -1294,12 +1294,12 @@ jetCostSigHash BuildTaptweak = cost "BuildTaptweak" jetCostTimeLock :: TimeLockJet a b -> Weight jetCostTimeLock CheckLockHeight = cost "CheckLockHeight" jetCostTimeLock CheckLockTime = cost "CheckLockTime" -jetCostTimeLock CheckLockDistance = cost "CheckLockDistance" -jetCostTimeLock CheckLockDuration = cost "CheckLockDuration" +jetCostTimeLock BrokenDoNotUseCheckLockDistance = cost "CheckLockDistance" +jetCostTimeLock BrokenDoNotUseCheckLockDuration = cost "CheckLockDuration" jetCostTimeLock TxLockHeight = cost "TxLockHeight" jetCostTimeLock TxLockTime = cost "TxLockTime" -jetCostTimeLock TxLockDistance = cost "TxLockDistance" -jetCostTimeLock TxLockDuration = cost "TxLockDuration" +jetCostTimeLock BrokenDoNotUseTxLockDistance = cost "TxLockDistance" +jetCostTimeLock BrokenDoNotUseTxLockDuration = cost "TxLockDuration" jetCostTimeLock TxIsFinal = cost "TxIsFinal" jetCostIssuance :: IssuanceJet a b -> Weight diff --git a/Haskell/Tests/Simplicity/Elements/FFI/Tests.hs b/Haskell/Tests/Simplicity/Elements/FFI/Tests.hs index baa3ed58b..d1ba99879 100644 --- a/Haskell/Tests/Simplicity/Elements/FFI/Tests.hs +++ b/Haskell/Tests/Simplicity/Elements/FFI/Tests.hs @@ -158,14 +158,14 @@ prop_tx_lock_time = forallPrimEnv $ \env -> fast_tx_lock_time env () == tx_lock_ fast_tx_lock_time = testEval (specification (ElementsJet (TimeLockJet TxLockTime))) prop_tx_lock_distance :: Property -prop_tx_lock_distance = forallPrimEnv $ \env -> fast_tx_lock_distance env () == tx_lock_distance env () +prop_tx_lock_distance = forallPrimEnv $ \env -> fast_tx_lock_distance env () == broken_do_not_use_tx_lock_distance env () where - fast_tx_lock_distance = testEval (specification (ElementsJet (TimeLockJet TxLockDistance))) + fast_tx_lock_distance = testEval (specification (ElementsJet (TimeLockJet BrokenDoNotUseTxLockDistance))) prop_tx_lock_duration :: Property -prop_tx_lock_duration = forallPrimEnv $ \env -> fast_tx_lock_duration env () == tx_lock_duration env () +prop_tx_lock_duration = forallPrimEnv $ \env -> fast_tx_lock_duration env () == broken_do_not_use_tx_lock_duration env () where - fast_tx_lock_duration = testEval (specification (ElementsJet (TimeLockJet TxLockDuration))) + fast_tx_lock_duration = testEval (specification (ElementsJet (TimeLockJet BrokenDoNotUseTxLockDuration))) prop_check_lock_height :: Word32 -> Property prop_check_lock_height = \w -> forallPrimEnv $ \env -> fast_check_lock_height env w == check_lock_height env w @@ -178,14 +178,14 @@ prop_check_lock_time = \w -> forallPrimEnv $ \env -> fast_check_lock_time env w fast_check_lock_time = testEval (specification (ElementsJet (TimeLockJet CheckLockTime))) prop_check_lock_distance :: Word16 -> Property -prop_check_lock_distance = \w -> forallPrimEnv $ \env -> fast_check_lock_distance env w == check_lock_distance env w +prop_check_lock_distance = \w -> forallPrimEnv $ \env -> fast_check_lock_distance env w == broken_do_not_use_check_lock_distance env w where - fast_check_lock_distance = testEval (specification (ElementsJet (TimeLockJet CheckLockDistance))) + fast_check_lock_distance = testEval (specification (ElementsJet (TimeLockJet BrokenDoNotUseCheckLockDistance))) prop_check_lock_duration :: Word16 -> Property -prop_check_lock_duration = \w -> forallPrimEnv $ \env -> fast_check_lock_duration env w == check_lock_duration env w +prop_check_lock_duration = \w -> forallPrimEnv $ \env -> fast_check_lock_duration env w == broken_do_not_use_check_lock_duration env w where - fast_check_lock_duration = testEval (specification (ElementsJet (TimeLockJet CheckLockDuration))) + fast_check_lock_duration = testEval (specification (ElementsJet (TimeLockJet BrokenDoNotUseCheckLockDuration))) prop_calculate_issuance_entropy :: ((Word256, Word32), Word256) -> Bool prop_calculate_issuance_entropy = \input -> diff --git a/Haskell/Tests/Simplicity/Elements/Regression.hs b/Haskell/Tests/Simplicity/Elements/Regression.hs index 7f4ac488d..3fd02312b 100644 --- a/Haskell/Tests/Simplicity/Elements/Regression.hs +++ b/Haskell/Tests/Simplicity/Elements/Regression.hs @@ -445,12 +445,12 @@ expected_cmr (ElementsJet (SigHashJet BuildTapbranch)) = 0xcbecf9bce172c50f58595 expected_cmr (ElementsJet (SigHashJet BuildTaptweak)) = 0x38741f80a2bf10f8f8723077c6741cbeae2dcac857901b813725806f21898ee3 expected_cmr (ElementsJet (TimeLockJet CheckLockHeight)) = 0x9e7898d037627134d2bd70c7fca9cba45eaf267d4d09ad50a9ef717a8f2749db expected_cmr (ElementsJet (TimeLockJet CheckLockTime)) = 0x68673d12e2732faa1d39e2136b1406afa098a84c96e8d60502a2dd61c59570bb -expected_cmr (ElementsJet (TimeLockJet CheckLockDistance)) = 0x7f78c7a77a25ada223267d239a5922f764b8ac0c2fcef68eb93c0d92da4af515 -expected_cmr (ElementsJet (TimeLockJet CheckLockDuration)) = 0x73dac8e25d87eaf382c2a77206ad38b9384361e7d0dc87c0fa7af7ea524597b7 +expected_cmr (ElementsJet (TimeLockJet BrokenDoNotUseCheckLockDistance)) = 0x7f78c7a77a25ada223267d239a5922f764b8ac0c2fcef68eb93c0d92da4af515 +expected_cmr (ElementsJet (TimeLockJet BrokenDoNotUseCheckLockDuration)) = 0x73dac8e25d87eaf382c2a77206ad38b9384361e7d0dc87c0fa7af7ea524597b7 expected_cmr (ElementsJet (TimeLockJet TxLockHeight)) = 0xc20257f8e76ecd0ae7ad634f5dfa68ae9a5eded0e2eebe4ee52cb47acfb0264c expected_cmr (ElementsJet (TimeLockJet TxLockTime)) = 0x3ee1900542d01efd4e9a01d4efb1f9dd992ced35b7a752f83da593381538dea4 -expected_cmr (ElementsJet (TimeLockJet TxLockDistance)) = 0x4c7773b818cb7ee5f54f925aad015677a043a72f316a187cc28c696cfcb90807 -expected_cmr (ElementsJet (TimeLockJet TxLockDuration)) = 0xcc9c64c8b6eb4bf09694af5a35d957a405e66c1b35224ed675878918452440b2 +expected_cmr (ElementsJet (TimeLockJet BrokenDoNotUseTxLockDistance)) = 0x4c7773b818cb7ee5f54f925aad015677a043a72f316a187cc28c696cfcb90807 +expected_cmr (ElementsJet (TimeLockJet BrokenDoNotUseTxLockDuration)) = 0xcc9c64c8b6eb4bf09694af5a35d957a405e66c1b35224ed675878918452440b2 expected_cmr (ElementsJet (TimeLockJet TxIsFinal)) = 0x8b3145722470a07de90a28ba89f3f8864261009654ce866cd8eaf76c5d8626eb expected_cmr (ElementsJet (IssuanceJet Issuance)) = 0x5c646312c169c68027979e1bc326c5dc95e5c5168d00d98e9d504a7cde21d768 expected_cmr (ElementsJet (IssuanceJet IssuanceAsset)) = 0x690bf918e1527756cfbbf51c831362143756d52d04e1294f1264950c6267e5c9 diff --git a/Haskell/Tests/Simplicity/Elements/Tests.hs b/Haskell/Tests/Simplicity/Elements/Tests.hs index 64be611e6..253a4434f 100644 --- a/Haskell/Tests/Simplicity/Elements/Tests.hs +++ b/Haskell/Tests/Simplicity/Elements/Tests.hs @@ -188,11 +188,11 @@ prop_tx_lock_time = checkJet (ElementsJet (TimeLockJet TxLockTime)) $ \check -> forallPrimEnv $ \env -> check env () prop_tx_lock_distance :: Property -prop_tx_lock_distance = checkJet (ElementsJet (TimeLockJet TxLockDistance)) +prop_tx_lock_distance = checkJet (ElementsJet (TimeLockJet BrokenDoNotUseTxLockDistance)) $ \check -> forallPrimEnv $ \env -> check env () prop_tx_lock_duration :: Property -prop_tx_lock_duration = checkJet (ElementsJet (TimeLockJet TxLockDuration)) +prop_tx_lock_duration = checkJet (ElementsJet (TimeLockJet BrokenDoNotUseTxLockDuration)) $ \check -> forallPrimEnv $ \env -> check env () prop_check_lock_height :: Property @@ -206,12 +206,12 @@ prop_check_lock_time = checkJet (ElementsJet (TimeLockJet CheckLockTime)) $ \w -> check env (toW32 w) prop_check_lock_distance :: Property -prop_check_lock_distance = checkJet (ElementsJet (TimeLockJet CheckLockDistance)) +prop_check_lock_distance = checkJet (ElementsJet (TimeLockJet BrokenDoNotUseCheckLockDistance)) $ \check -> forallPrimEnv $ \env -> forAll (genBoundaryCases . txLockDistance $ envTx env) $ \w -> check env (toW16 w) prop_check_lock_duration :: Property -prop_check_lock_duration = checkJet (ElementsJet (TimeLockJet CheckLockDuration)) +prop_check_lock_duration = checkJet (ElementsJet (TimeLockJet BrokenDoNotUseCheckLockDuration)) $ \check -> forallPrimEnv $ \env -> forAll (genBoundaryCases . txLockDuration $ envTx env) $ \w -> check env (toW16 w) diff --git a/Haskell/cbits/elements/jets.c b/Haskell/cbits/elements/jets.c index 423c64fb6..6d15bbff6 100644 --- a/Haskell/cbits/elements/jets.c +++ b/Haskell/cbits/elements/jets.c @@ -54,12 +54,12 @@ WRAP_(num_outputs) WRAP_(tx_is_final) WRAP_(tx_lock_height) WRAP_(tx_lock_time) -WRAP_(tx_lock_distance) -WRAP_(tx_lock_duration) +WRAP_(broken_do_not_use_tx_lock_distance) +WRAP_(broken_do_not_use_tx_lock_duration) WRAP_(check_lock_height) WRAP_(check_lock_time) -WRAP_(check_lock_distance) -WRAP_(check_lock_duration) +WRAP_(broken_do_not_use_check_lock_distance) +WRAP_(broken_do_not_use_check_lock_duration) COREWRAP_(calculate_issuance_entropy) COREWRAP_(calculate_asset) COREWRAP_(calculate_explicit_token) From 44ff79fdf0307177b4b07d2c9f6abd43bae60c6d Mon Sep 17 00:00:00 2001 From: Russell O'Connor Date: Wed, 24 Dec 2025 17:06:05 -0500 Subject: [PATCH 2/3] Mark depricated parts of the TxEnv as obsolete These fields are only used to implement broken jets. They only remain here because they are not part of consensus and cannot be removed. --- C/elements/elementsJets.c | 16 +++++++-------- C/elements/env.c | 12 +++++------ C/elements/txEnv.h | 8 +++----- .../Elements/Simplicity/Elements/DataTypes.hs | 14 +++++++------ Haskell/Simplicity/Elements/Jets.hs | 16 +++++++-------- .../Simplicity/Elements/Programs/TimeLock.hs | 20 +++++++++---------- Haskell/Tests/Simplicity/Elements/Tests.hs | 4 ++-- 7 files changed, 45 insertions(+), 45 deletions(-) diff --git a/C/elements/elementsJets.c b/C/elements/elementsJets.c index 7d4dbf122..93b2c5846 100644 --- a/C/elements/elementsJets.c +++ b/C/elements/elementsJets.c @@ -153,12 +153,12 @@ static uint_fast32_t lockTime(const elementsTransaction* tx) { return !tx->isFinal && 500000000U <= tx->lockTime ? tx->lockTime : 0; } -static uint_fast16_t lockDistance(const elementsTransaction* tx) { - return 2 <= tx->version ? tx->lockDistance : 0; +static uint_fast16_t obsolete_lockDistance(const elementsTransaction* tx) { + return 2 <= tx->version ? tx->obsolete_lockDistance : 0; } -static uint_fast16_t lockDuration(const elementsTransaction* tx) { - return 2 <= tx->version ? tx->lockDuration : 0; +static uint_fast16_t obsolete_lockDuration(const elementsTransaction* tx) { + return 2 <= tx->version ? tx->obsolete_lockDuration : 0; } static bool isFee(const sigOutput* output) { @@ -735,14 +735,14 @@ bool simplicity_tx_lock_time(frameItem* dst, frameItem src, const txEnv* env) { /* tx_lock_distance : ONE |- TWO^16 */ bool simplicity_broken_do_not_use_tx_lock_distance(frameItem* dst, frameItem src, const txEnv* env) { (void) src; // src is unused; - simplicity_write16(dst, lockDistance(env->tx)); + simplicity_write16(dst, obsolete_lockDistance(env->tx)); return true; } /* tx_lock_duration : ONE |- TWO^16 */ bool simplicity_broken_do_not_use_tx_lock_duration(frameItem* dst, frameItem src, const txEnv* env) { (void) src; // src is unused; - simplicity_write16(dst, lockDuration(env->tx)); + simplicity_write16(dst, obsolete_lockDuration(env->tx)); return true; } @@ -764,14 +764,14 @@ bool simplicity_check_lock_time(frameItem* dst, frameItem src, const txEnv* env) bool simplicity_broken_do_not_use_check_lock_distance(frameItem* dst, frameItem src, const txEnv* env) { (void) dst; // dst is unused; uint_fast16_t x = simplicity_read16(&src); - return x <= lockDistance(env->tx); + return x <= obsolete_lockDistance(env->tx); } /* check_lock_duration : TWO^16 |- ONE */ bool simplicity_broken_do_not_use_check_lock_duration(frameItem* dst, frameItem src, const txEnv* env) { (void) dst; // dst is unused; uint_fast16_t x = simplicity_read16(&src); - return x <= lockDuration(env->tx); + return x <= obsolete_lockDuration(env->tx); } /* calculate_issuance_entropy : TWO^256 * TWO^32 * TWO^256 |- TWO^256 */ diff --git a/C/elements/env.c b/C/elements/env.c index af39d5223..e1af6e8f5 100644 --- a/C/elements/env.c +++ b/C/elements/env.c @@ -408,12 +408,12 @@ extern elementsTransaction* simplicity_elements_mallocTransaction(const rawEleme copyInput(&input[i], &rawTx->input[i]); if (input[i].sequence < 0xffffffff) { tx->isFinal = false; } if (input[i].sequence < 0x80000000) { - const uint_fast16_t maskedSequence = input[i].sequence & 0xffff; - if (input[i].sequence & ((uint_fast32_t)1 << 22)) { - if (tx->lockDuration < maskedSequence) tx->lockDuration = maskedSequence; - } else { - if (tx->lockDistance < maskedSequence) tx->lockDistance = maskedSequence; - } + const uint_fast16_t maskedSequence = input[i].sequence & 0xffff; + if (input[i].sequence & ((uint_fast32_t)1 << 22)) { + if (tx->obsolete_lockDuration < maskedSequence) tx->obsolete_lockDuration = maskedSequence; + } else { + if (tx->obsolete_lockDistance < maskedSequence) tx->obsolete_lockDistance = maskedSequence; + } } if (input[i].isPegin) { sha256_uchar(&ctx_inputOutpointsHash, 1); diff --git a/C/elements/txEnv.h b/C/elements/txEnv.h index f4493530d..d9a890876 100644 --- a/C/elements/txEnv.h +++ b/C/elements/txEnv.h @@ -224,11 +224,9 @@ typedef struct elementsTransaction { uint_fast32_t numFees; uint_fast32_t version; uint_fast32_t lockTime; - /* lockDuration and lockDistance values are set even when the version is 0 or 1. - * This is similar to lockTime whose value is also set, even when the transaction is final. - */ - uint_fast16_t lockDistance; - uint_fast16_t lockDuration; /* Units of 512 seconds */ + /* These two fields are used to implement broken jets and only remain here for consensus purposes. */ + uint_fast16_t obsolete_lockDistance; + uint_fast16_t obsolete_lockDuration; bool isFinal; } elementsTransaction; diff --git a/Haskell/Elements/Simplicity/Elements/DataTypes.hs b/Haskell/Elements/Simplicity/Elements/DataTypes.hs index 13076e1a8..f9867e14b 100644 --- a/Haskell/Elements/Simplicity/Elements/DataTypes.hs +++ b/Haskell/Elements/Simplicity/Elements/DataTypes.hs @@ -23,7 +23,7 @@ module Simplicity.Elements.DataTypes , SigTx(SigTx), sigTxVersion, sigTxIn, sigTxOut, sigTxLock , putNoWitnessTx, txid , TapEnv(..) - , txIsFinal, txLockDistance, txLockDuration + , txIsFinal, txLockBrokenDistance, txLockBrokenDuration , calculateIssuanceEntropy, calculateAsset, calculateToken , outputAmountsHash, outputNoncesHash, outputScriptsHash , outputRangeProofsHash, outputSurjectionProofsHash, outputsHash, outputHash @@ -322,17 +322,19 @@ txIsFinal tx = all finalSequence (sigTxIn tx) where finalSequence sigin = sigTxiSequence sigin == maxBound -txLockDistance :: SigTx -> Word16 -txLockDistance tx | sigTxVersion tx < 2 = 0 +-- | This function is used in a specification of broken relative timelock jets and should not be used other than for previous consensus reasons. +txLockBrokenDistance :: SigTx -> Word16 +txLockBrokenDistance tx | sigTxVersion tx < 2 = 0 | otherwise = getMax . foldMap distance $ sigTxIn tx where distance sigin = case parseSequence (sigTxiSequence sigin) of Just (Left x) -> Max x _ -> mempty -txLockDuration :: SigTx -> Word16 -txLockDuration tx | sigTxVersion tx < 2 = 0 - | otherwise = getMax . foldMap duration $ sigTxIn tx +-- | This function is used in a specification of broken relative timelock jets and should not be used other than for previous consensus reasons. +txLockBrokenDuration :: SigTx -> Word16 +txLockBrokenDuration tx | sigTxVersion tx < 2 = 0 + | otherwise = getMax . foldMap duration $ sigTxIn tx where duration sigin = case parseSequence (sigTxiSequence sigin) of Just (Right x) -> Max x diff --git a/Haskell/Simplicity/Elements/Jets.hs b/Haskell/Simplicity/Elements/Jets.hs index 1903f0e03..355d544a2 100644 --- a/Haskell/Simplicity/Elements/Jets.hs +++ b/Haskell/Simplicity/Elements/Jets.hs @@ -249,12 +249,12 @@ specificationSigHash BuildTaptweak = Prog.buildTaptweak specificationTimeLock :: (Assert term, Primitive term) => TimeLockJet a b -> term a b specificationTimeLock CheckLockHeight = TimeLock.checkLockHeight specificationTimeLock CheckLockTime = TimeLock.checkLockTime -specificationTimeLock BrokenDoNotUseCheckLockDistance = TimeLock.checkLockDistance -specificationTimeLock BrokenDoNotUseCheckLockDuration = TimeLock.checkLockDuration +specificationTimeLock BrokenDoNotUseCheckLockDistance = TimeLock.brokenCheckLockDistance +specificationTimeLock BrokenDoNotUseCheckLockDuration = TimeLock.brokenCheckLockDuration specificationTimeLock TxLockHeight = TimeLock.txLockHeight specificationTimeLock TxLockTime = TimeLock.txLockTime -specificationTimeLock BrokenDoNotUseTxLockDistance = TimeLock.txLockDistance -specificationTimeLock BrokenDoNotUseTxLockDuration = TimeLock.txLockDuration +specificationTimeLock BrokenDoNotUseTxLockDistance = TimeLock.brokenTxLockDistance +specificationTimeLock BrokenDoNotUseTxLockDuration = TimeLock.brokenTxLockDuration specificationTimeLock TxIsFinal = TimeLock.txIsFinal specificationIssuance :: (Assert term, Primitive term) => IssuanceJet a b -> term a b @@ -420,9 +420,9 @@ implementationTimeLock CheckLockTime env x | txIsFinal (envTx env) = guard $ fro | otherwise = guard $ fromWord32 x <= 0 where lock = fromIntegral . sigTxLock . envTx $ env -implementationTimeLock BrokenDoNotUseCheckLockDistance env x | fromWord16 x <= fromIntegral (txLockDistance (envTx env)) = Just () +implementationTimeLock BrokenDoNotUseCheckLockDistance env x | fromWord16 x <= fromIntegral (txLockBrokenDistance (envTx env)) = Just () | otherwise = Nothing -implementationTimeLock BrokenDoNotUseCheckLockDuration env x | fromWord16 x <= fromIntegral (txLockDuration (envTx env)) = Just () +implementationTimeLock BrokenDoNotUseCheckLockDuration env x | fromWord16 x <= fromIntegral (txLockBrokenDuration (envTx env)) = Just () | otherwise = Nothing implementationTimeLock TxLockHeight env () | txIsFinal (envTx env) = Just (toWord32 0) | Left l <- parseLock lock = Just . toWord32 $ fromIntegral l @@ -434,8 +434,8 @@ implementationTimeLock TxLockTime env () | txIsFinal (envTx env) = Just (toWord3 | otherwise = Just (toWord32 0) where lock = fromIntegral . sigTxLock . envTx $ env -implementationTimeLock BrokenDoNotUseTxLockDistance env () = Just . toWord16 . fromIntegral $ txLockDistance (envTx env) -implementationTimeLock BrokenDoNotUseTxLockDuration env () = Just . toWord16 . fromIntegral $ txLockDuration (envTx env) +implementationTimeLock BrokenDoNotUseTxLockDistance env () = Just . toWord16 . fromIntegral $ txLockBrokenDistance (envTx env) +implementationTimeLock BrokenDoNotUseTxLockDuration env () = Just . toWord16 . fromIntegral $ txLockBrokenDuration (envTx env) implementationTimeLock TxIsFinal env () = Just $ toBit (txIsFinal (envTx env)) implementationIssuance :: IssuanceJet a b -> PrimEnv -> a -> Maybe b diff --git a/Haskell/Simplicity/Elements/Programs/TimeLock.hs b/Haskell/Simplicity/Elements/Programs/TimeLock.hs index 18d61727c..8896b4325 100644 --- a/Haskell/Simplicity/Elements/Programs/TimeLock.hs +++ b/Haskell/Simplicity/Elements/Programs/TimeLock.hs @@ -2,9 +2,9 @@ module Simplicity.Elements.Programs.TimeLock ( txIsFinal , txLockHeight, txLockTime - , txLockDistance, txLockDuration + , brokenTxLockDistance, brokenTxLockDuration , checkLockHeight, checkLockTime - , checkLockDistance, checkLockDuration + , brokenCheckLockDistance, brokenCheckLockDuration , module Simplicity.Programs.TimeLock , Bit ) where @@ -43,16 +43,16 @@ bip68VersionCheck :: (Core term, Primitive term) => term () Bit bip68VersionCheck = scribe (toWord32 2) &&& primitive Version >>> le word32 -- | Implements 'Simplicity.Elements.DataTypes.txLockDistance'. -txLockDistance :: (Core term, Primitive term) => term () Distance -txLockDistance = bip68VersionCheck &&& zero word16 +brokenTxLockDistance :: (Core term, Primitive term) => term () Distance +brokenTxLockDistance = bip68VersionCheck &&& zero word16 >>> match ih (forWhile word32 body >>> copair iden iden) where body = take (drop (primitive InputSequence)) &&& ih >>> match (injl ih) (injr (take parseSequence &&& ih >>> match ih (match (max word16) ih))) -- | Implements 'Simplicity.Elements.DataTypes.txLockDuration'. -txLockDuration :: (Core term, Primitive term) => term () Duration -txLockDuration = bip68VersionCheck &&& zero word16 +brokenTxLockDuration :: (Core term, Primitive term) => term () Duration +brokenTxLockDuration = bip68VersionCheck &&& zero word16 >>> match ih (forWhile word32 body >>> copair iden iden) where body = take (drop (primitive InputSequence)) &&& ih @@ -67,9 +67,9 @@ checkLockTime :: (Assert term, Primitive term) => term Time () checkLockTime = assert (iden &&& (unit >>> txLockTime) >>> le word32) -- | Asserts that the input is less than or equal to the value returned by 'txLockDistance'. -checkLockDistance :: (Assert term, Primitive term) => term Distance () -checkLockDistance = assert (iden &&& (unit >>> txLockDistance) >>> le word16) +brokenCheckLockDistance :: (Assert term, Primitive term) => term Distance () +brokenCheckLockDistance = assert (iden &&& (unit >>> brokenTxLockDistance) >>> le word16) -- | Asserts that the input is less than or equal to the value returned by 'txLockDuration'. -checkLockDuration :: (Assert term, Primitive term) => term Duration () -checkLockDuration = assert (iden &&& (unit >>> txLockDuration) >>> le word16) +brokenCheckLockDuration :: (Assert term, Primitive term) => term Duration () +brokenCheckLockDuration = assert (iden &&& (unit >>> brokenTxLockDuration) >>> le word16) diff --git a/Haskell/Tests/Simplicity/Elements/Tests.hs b/Haskell/Tests/Simplicity/Elements/Tests.hs index 253a4434f..e2bc15b7c 100644 --- a/Haskell/Tests/Simplicity/Elements/Tests.hs +++ b/Haskell/Tests/Simplicity/Elements/Tests.hs @@ -207,12 +207,12 @@ prop_check_lock_time = checkJet (ElementsJet (TimeLockJet CheckLockTime)) prop_check_lock_distance :: Property prop_check_lock_distance = checkJet (ElementsJet (TimeLockJet BrokenDoNotUseCheckLockDistance)) - $ \check -> forallPrimEnv $ \env -> forAll (genBoundaryCases . txLockDistance $ envTx env) + $ \check -> forallPrimEnv $ \env -> forAll (genBoundaryCases . txLockBrokenDistance $ envTx env) $ \w -> check env (toW16 w) prop_check_lock_duration :: Property prop_check_lock_duration = checkJet (ElementsJet (TimeLockJet BrokenDoNotUseCheckLockDuration)) - $ \check -> forallPrimEnv $ \env -> forAll (genBoundaryCases . txLockDuration $ envTx env) + $ \check -> forallPrimEnv $ \env -> forAll (genBoundaryCases . txLockBrokenDuration $ envTx env) $ \w -> check env (toW16 w) prop_calculate_issuance_entropy :: Outpoint -> HashElement -> Bool From a97e57cd96f1ae110d75429043393fca10702a3e Mon Sep 17 00:00:00 2001 From: Russell O'Connor Date: Thu, 25 Dec 2025 20:34:16 -0500 Subject: [PATCH 3/3] Replace Bitcoin Relative Timelock Jets The previous version was not the expected behaviour. This new version is more similar to Bitcoin's CSV op-codes, though there are some subtle difference when passed a 0 argument. --- C/bitcoin/bitcoinJets.c | 34 ++++++++++++++----- C/bitcoin/env.c | 8 ----- C/bitcoin/primitiveJetNode.inc | 8 ++--- C/bitcoin/txEnv.h | 5 --- .../Bitcoin/Simplicity/Bitcoin/DataTypes.hs | 28 +++++++-------- Haskell/Simplicity/Bitcoin/Jets.hs | 28 +++++++++++---- .../Simplicity/Bitcoin/Programs/TimeLock.hs | 23 ++++++------- Haskell/Tests/Simplicity/Bitcoin/Tests.hs | 10 ++++-- 8 files changed, 81 insertions(+), 63 deletions(-) diff --git a/C/bitcoin/bitcoinJets.c b/C/bitcoin/bitcoinJets.c index 850b20e12..0964f0571 100644 --- a/C/bitcoin/bitcoinJets.c +++ b/C/bitcoin/bitcoinJets.c @@ -41,12 +41,26 @@ static uint_fast32_t lockTime(const bitcoinTransaction* tx) { return !tx->isFinal && 500000000U <= tx->lockTime ? tx->lockTime : 0; } -static uint_fast16_t lockDistance(const bitcoinTransaction* tx) { - return 2 <= tx->version ? tx->lockDistance : 0; +static uint_fast16_t lockDistance(const bitcoinTransaction* tx, uint_fast32_t ix) { + simplicity_assert(ix < tx->numInputs); + if (2 <= tx->version && + tx->input[ix].sequence < 0x80000000 && + !(tx->input[ix].sequence & ((uint_fast32_t)1 << 22))) { + return tx->input[ix].sequence & 0xffff; + } else { + return 0; + } } -static uint_fast16_t lockDuration(const bitcoinTransaction* tx) { - return 2 <= tx->version ? tx->lockDuration : 0; +static uint_fast16_t lockDuration(const bitcoinTransaction* tx, uint_fast32_t ix) { + simplicity_assert(ix < tx->numInputs); + if (2 <= tx->version && + tx->input[ix].sequence < 0x80000000 && + !!(tx->input[ix].sequence & ((uint_fast32_t)1 << 22))) { + return tx->input[ix].sequence & 0xffff; + } else { + return 0; + } } /* version : ONE |- TWO^32 */ @@ -312,14 +326,16 @@ bool simplicity_bitcoin_tx_lock_time(frameItem* dst, frameItem src, const txEnv* /* tx_lock_distance : ONE |- TWO^16 */ bool simplicity_bitcoin_tx_lock_distance(frameItem* dst, frameItem src, const txEnv* env) { (void) src; // src is unused; - simplicity_write16(dst, lockDistance(env->tx)); + if (env->tx->numInputs <= env->ix) return false; + simplicity_write16(dst, lockDistance(env->tx, env->ix)); return true; } /* tx_lock_duration : ONE |- TWO^16 */ bool simplicity_bitcoin_tx_lock_duration(frameItem* dst, frameItem src, const txEnv* env) { (void) src; // src is unused; - simplicity_write16(dst, lockDuration(env->tx)); + if (env->tx->numInputs <= env->ix) return false; + simplicity_write16(dst, lockDuration(env->tx, env->ix)); return true; } @@ -340,15 +356,17 @@ bool simplicity_bitcoin_check_lock_time(frameItem* dst, frameItem src, const txE /* check_lock_distance : TWO^16 |- ONE */ bool simplicity_bitcoin_check_lock_distance(frameItem* dst, frameItem src, const txEnv* env) { (void) dst; // dst is unused; + if (env->tx->numInputs <= env->ix) return false; uint_fast16_t x = simplicity_read16(&src); - return x <= lockDistance(env->tx); + return x <= lockDistance(env->tx, env->ix); } /* check_lock_duration : TWO^16 |- ONE */ bool simplicity_bitcoin_check_lock_duration(frameItem* dst, frameItem src, const txEnv* env) { (void) dst; // dst is unused; + if (env->tx->numInputs <= env->ix) return false; uint_fast16_t x = simplicity_read16(&src); - return x <= lockDuration(env->tx); + return x <= lockDuration(env->tx, env->ix); } /* build_tapleaf_simplicity : TWO^256 |- TWO^256 */ diff --git a/C/bitcoin/env.c b/C/bitcoin/env.c index 5d4bcd6b3..85341aff2 100644 --- a/C/bitcoin/env.c +++ b/C/bitcoin/env.c @@ -114,14 +114,6 @@ extern bitcoinTransaction* simplicity_bitcoin_mallocTransaction(const rawBitcoin copyInput(&input[i], &rawTx->input[i]); tx->totalInputValue += input[i].txo.value; if (input[i].sequence < 0xffffffff) { tx->isFinal = false; } - if (input[i].sequence < 0x80000000) { - const uint_fast16_t maskedSequence = input[i].sequence & 0xffff; - if (input[i].sequence & ((uint_fast32_t)1 << 22)) { - if (tx->lockDuration < maskedSequence) tx->lockDuration = maskedSequence; - } else { - if (tx->lockDistance < maskedSequence) tx->lockDistance = maskedSequence; - } - } sha256_hash(&ctx_inputOutpointsHash, &input[i].prevOutpoint.txid); sha256_u32be(&ctx_inputOutpointsHash, input[i].prevOutpoint.ix); sha256_u64be(&ctx_inputValuesHash, input[i].txo.value); diff --git a/C/bitcoin/primitiveJetNode.inc b/C/bitcoin/primitiveJetNode.inc index 95914222a..b292250c4 100644 --- a/C/bitcoin/primitiveJetNode.inc +++ b/C/bitcoin/primitiveJetNode.inc @@ -186,7 +186,7 @@ ,[CHECK_LOCK_DISTANCE] = { .tag = JET , .jet = simplicity_bitcoin_check_lock_distance -, .cmr = {{0xdbaa6fedu, 0xb73b2e31u, 0xa80ccbd1u, 0x4daaa5b8u, 0xce212cdeu, 0x0cbfd96du, 0x5db9715fu, 0xd19f8899u}} +, .cmr = {{0x38fdf7ddu, 0x28538670u, 0xfb34c1bfu, 0xe72f17e2u, 0xca5784f8u, 0x7fed88eau, 0xb584792bu, 0x3974bd18u}} , .sourceIx = ty_w16 , .targetIx = ty_u , .cost = 84 /* milli weight units */ @@ -194,7 +194,7 @@ ,[CHECK_LOCK_DURATION] = { .tag = JET , .jet = simplicity_bitcoin_check_lock_duration -, .cmr = {{0xdde93f33u, 0xf9b9d1d3u, 0xa3f2b3e8u, 0xb90f6d8bu, 0xeffccb45u, 0x81210eeau, 0xf06bd32fu, 0x6319df6eu}} +, .cmr = {{0x77503832u, 0x6eae25c7u, 0x209b2443u, 0x06eaa9f9u, 0x204c7eceu, 0x2dd63b45u, 0x2e10017fu, 0xa4ea53cfu}} , .sourceIx = ty_w16 , .targetIx = ty_u , .cost = 78 /* milli weight units */ @@ -3298,7 +3298,7 @@ ,[TX_LOCK_DISTANCE] = { .tag = JET , .jet = simplicity_bitcoin_tx_lock_distance -, .cmr = {{0x8c0b0c44u, 0x2bc20978u, 0xfa4c2b3du, 0x52a7fe7cu, 0x3f86e3d2u, 0x1b42130eu, 0xbb8dba45u, 0xe0f5e64bu}} +, .cmr = {{0xb6fbaac2u, 0x10a306beu, 0x8b58d0d7u, 0xb1563f62u, 0x2336d2aeu, 0xb56c393du, 0x276445a9u, 0xa22cc4a7u}} , .sourceIx = ty_u , .targetIx = ty_w16 , .cost = 72 /* milli weight units */ @@ -3306,7 +3306,7 @@ ,[TX_LOCK_DURATION] = { .tag = JET , .jet = simplicity_bitcoin_tx_lock_duration -, .cmr = {{0x09b18d5cu, 0x2f08402bu, 0xbcf8c31eu, 0xa43435efu, 0xffd9ea0eu, 0xf8758f76u, 0xbb27272bu, 0x4d60dc62u}} +, .cmr = {{0x53572818u, 0xe7e5b98fu, 0x968c9da7u, 0xdf5090d9u, 0x826f9bcfu, 0x84b63639u, 0x5eea321bu, 0x6909ece9u}} , .sourceIx = ty_u , .targetIx = ty_w16 , .cost = 66 /* milli weight units */ diff --git a/C/bitcoin/txEnv.h b/C/bitcoin/txEnv.h index 1095e05b1..1eb773eae 100644 --- a/C/bitcoin/txEnv.h +++ b/C/bitcoin/txEnv.h @@ -60,11 +60,6 @@ typedef struct bitcoinTransaction { uint_fast32_t numOutputs; uint_fast32_t version; uint_fast32_t lockTime; - /* lockDuration and lockDistance values are set even when the version is 0 or 1. - * This is similar to lockTime whose value is also set, even when the transaction is final. - */ - uint_fast16_t lockDistance; - uint_fast16_t lockDuration; /* Units of 512 seconds */ bool isFinal; } bitcoinTransaction; diff --git a/Haskell/Bitcoin/Simplicity/Bitcoin/DataTypes.hs b/Haskell/Bitcoin/Simplicity/Bitcoin/DataTypes.hs index 8a2b1525a..eec493fe9 100644 --- a/Haskell/Bitcoin/Simplicity/Bitcoin/DataTypes.hs +++ b/Haskell/Bitcoin/Simplicity/Bitcoin/DataTypes.hs @@ -8,7 +8,7 @@ module Simplicity.Bitcoin.DataTypes , putNoWitnessTx, txid , TapEnv(..) , txTotalInputValue, txTotalOutputValue, txFee - , txIsFinal, txLockDistance, txLockDuration + , txIsFinal, txiLockDistance, txiLockDuration , outputValuesHash, outputScriptsHash , outputsHash, outputHash , inputOutpointsHash, inputValuesHash, inputScriptsHash, inputUtxosHash @@ -171,21 +171,17 @@ txIsFinal tx = all finalSequence (sigTxIn tx) where finalSequence sigin = sigTxiSequence sigin == maxBound -txLockDistance :: SigTx -> Word16 -txLockDistance tx | sigTxVersion tx < 2 = 0 - | otherwise = getMax . foldMap distance $ sigTxIn tx - where - distance sigin = case parseSequence (sigTxiSequence sigin) of - Just (Left x) -> Max x - _ -> mempty - -txLockDuration :: SigTx -> Word16 -txLockDuration tx | sigTxVersion tx < 2 = 0 - | otherwise = getMax . foldMap duration $ sigTxIn tx - where - duration sigin = case parseSequence (sigTxiSequence sigin) of - Just (Right x) -> Max x - _ -> mempty +txiLockDistance :: SigTxInput -> Word16 +txiLockDistance sigin = + case parseSequence (sigTxiSequence sigin) of + Just (Left x) -> x + _ -> 0 + +txiLockDuration :: SigTxInput -> Word16 +txiLockDuration sigin = + case parseSequence (sigTxiSequence sigin) of + Just (Right x) -> x + _ -> 0 -- | A hash of all 'txoValues's. outputValuesHash :: SigTx -> Hash256 diff --git a/Haskell/Simplicity/Bitcoin/Jets.hs b/Haskell/Simplicity/Bitcoin/Jets.hs index aaf188940..98f871475 100644 --- a/Haskell/Simplicity/Bitcoin/Jets.hs +++ b/Haskell/Simplicity/Bitcoin/Jets.hs @@ -39,7 +39,7 @@ import qualified Simplicity.Bitcoin.Dag as Dag import Simplicity.Bitcoin.Term import Simplicity.Bitcoin.DataTypes import qualified Simplicity.Bitcoin.JetType -import Simplicity.Bitcoin.Primitive (PrimEnv, PubKey, primEnvHash, envTx, envTap) +import Simplicity.Bitcoin.Primitive (PrimEnv, PubKey, primEnvHash, envTx, envIx, envTap) import qualified Simplicity.Bitcoin.Primitive as Prim import qualified Simplicity.Bitcoin.Serialization.BitString as BitString import qualified Simplicity.Bitcoin.Semantics as Semantics @@ -295,10 +295,16 @@ implementationTimeLock CheckLockTime env x | txIsFinal (envTx env) = guard $ fro | otherwise = guard $ fromWord32 x <= 0 where lock = fromIntegral . sigTxLock . envTx $ env -implementationTimeLock CheckLockDistance env x | fromWord16 x <= fromIntegral (txLockDistance (envTx env)) = Just () - | otherwise = Nothing -implementationTimeLock CheckLockDuration env x | fromWord16 x <= fromIntegral (txLockDuration (envTx env)) = Just () - | otherwise = Nothing +implementationTimeLock CheckLockDistance env x | sigTxVersion (envTx env) < 2 = guard $ fromWord16 x <= 0 + | Just (Left l) <- parseSequence =<< sequence = guard $ fromWord16 x <= fromIntegral l + | otherwise = guard $ fromWord16 x <= 0 + where + sequence = sigTxiSequence <$> (sigTxIn (envTx env) !? (fromIntegral $ envIx env)) +implementationTimeLock CheckLockDuration env x | sigTxVersion (envTx env) < 2 = guard $ fromWord16 x <= 0 + | Just (Right l) <- parseSequence =<< sequence = guard $ fromWord16 x <= fromIntegral l + | otherwise = guard $ fromWord16 x <= 0 + where + sequence = sigTxiSequence <$> (sigTxIn (envTx env) !? (fromIntegral $ envIx env)) implementationTimeLock TxLockHeight env () | txIsFinal (envTx env) = Just (toWord32 0) | Left l <- parseLock lock = Just . toWord32 $ fromIntegral l | otherwise = Just (toWord32 0) @@ -309,8 +315,16 @@ implementationTimeLock TxLockTime env () | txIsFinal (envTx env) = Just (toWord3 | otherwise = Just (toWord32 0) where lock = fromIntegral . sigTxLock . envTx $ env -implementationTimeLock TxLockDistance env () = Just . toWord16 . fromIntegral $ txLockDistance (envTx env) -implementationTimeLock TxLockDuration env () = Just . toWord16 . fromIntegral $ txLockDuration (envTx env) +implementationTimeLock TxLockDistance env () | sigTxVersion (envTx env) < 2 = Just (toWord16 0) + | Just (Left l) <- parseSequence =<< sequence = Just . toWord16 $ fromIntegral l + | otherwise = Just (toWord16 0) + where + sequence = sigTxiSequence <$> (sigTxIn (envTx env) !? (fromIntegral $ envIx env)) +implementationTimeLock TxLockDuration env () | sigTxVersion (envTx env) < 2 = Just (toWord16 0) + | Just (Right l) <- parseSequence =<< sequence = Just . toWord16 $ fromIntegral l + | otherwise = Just (toWord16 0) + where + sequence = sigTxiSequence <$> (sigTxIn (envTx env) !? (fromIntegral $ envIx env)) implementationTimeLock TxIsFinal env () = Just $ toBit (txIsFinal (envTx env)) implementationTransaction :: TransactionJet a b -> PrimEnv -> a -> Maybe b diff --git a/Haskell/Simplicity/Bitcoin/Programs/TimeLock.hs b/Haskell/Simplicity/Bitcoin/Programs/TimeLock.hs index caf868d42..531283868 100644 --- a/Haskell/Simplicity/Bitcoin/Programs/TimeLock.hs +++ b/Haskell/Simplicity/Bitcoin/Programs/TimeLock.hs @@ -13,6 +13,7 @@ import Prelude hiding (Word, all, drop, max, not, take) import Simplicity.Bitcoin.Primitive import Simplicity.Bitcoin.Term +import Simplicity.Bitcoin.Programs.Transaction.Lib import Simplicity.Programs.Arith import Simplicity.Programs.Bit import Simplicity.Programs.Generic @@ -42,21 +43,19 @@ txLockTime = txIsFinal &&& primitive LockTime bip68VersionCheck :: (Core term, Primitive term) => term () Bit bip68VersionCheck = scribe (toWord32 2) &&& primitive Version >>> le word32 --- | Implements 'Simplicity.Bitcoin.DataTypes.txLockDistance'. -txLockDistance :: (Core term, Primitive term) => term () Distance -txLockDistance = bip68VersionCheck &&& zero word16 - >>> match ih (forWhile word32 body >>> copair iden iden) +-- | Computes the relative height timelock or 0 if there is no such timelock. +txLockDistance :: (Assert term, Primitive term) => term () Distance +txLockDistance = bip68VersionCheck &&& (currentSequence >>> parseSequence) + >>> cond (copair (unit >>> z) (copair iden (unit >>> z))) (unit >>> z) where - body = take (drop (primitive InputSequence)) &&& ih - >>> match (injl ih) (injr (take parseSequence &&& ih >>> match ih (match (max word16) ih))) + z = zero word16 --- | Implements 'Simplicity.Bitcoin.DataTypes.txLockDuration'. -txLockDuration :: (Core term, Primitive term) => term () Duration -txLockDuration = bip68VersionCheck &&& zero word16 - >>> match ih (forWhile word32 body >>> copair iden iden) +-- | Computes the relative time timelock or 0 if there is no such timelock. +txLockDuration :: (Assert term, Primitive term) => term () Distance +txLockDuration = bip68VersionCheck &&& (currentSequence >>> parseSequence) + >>> cond (copair (unit >>> z) (copair (unit >>> z) iden)) (unit >>> z) where - body = take (drop (primitive InputSequence)) &&& ih - >>> match (injl ih) (injr (take parseSequence &&& ih >>> match ih (match ih (max word16)))) + z = zero word16 -- | Asserts that the input is less than or equal to the value returned by 'txLockHeight'. checkLockHeight :: (Assert term, Primitive term) => term Height () diff --git a/Haskell/Tests/Simplicity/Bitcoin/Tests.hs b/Haskell/Tests/Simplicity/Bitcoin/Tests.hs index ee5cfbed0..c8804d8b5 100644 --- a/Haskell/Tests/Simplicity/Bitcoin/Tests.hs +++ b/Haskell/Tests/Simplicity/Bitcoin/Tests.hs @@ -4,7 +4,7 @@ import Control.Arrow ((***), (+++)) import qualified Data.ByteString.Char8 as BSC import qualified Data.ByteString.Lazy as BSL import qualified Data.Map as Map -import Data.Maybe (fromMaybe, isJust) +import Data.Maybe (fromMaybe, fromJust, isJust) import Data.Serialize (encode, put, putWord8, putWord32be, runPutLazy) import Data.Vector ((!), (!?), fromList) import Lens.Family2 (review, over, under, view) @@ -150,13 +150,17 @@ prop_check_lock_time = checkJet (BitcoinJet (TimeLockJet CheckLockTime)) prop_check_lock_distance :: Property prop_check_lock_distance = checkJet (BitcoinJet (TimeLockJet CheckLockDistance)) - $ \check -> forallPrimEnv $ \env -> forAll (genBoundaryCases . txLockDistance $ envTx env) + $ \check -> forallPrimEnv $ \env -> forAll (genBoundaryCases . txLockDistance $ env) $ \w -> check env (toW16 w) + where + txLockDistance env = fromIntegral . fromWord16 . fromJust $ implementation (BitcoinJet (TimeLockJet TxLockDistance)) env () :: Word.Word16 prop_check_lock_duration :: Property prop_check_lock_duration = checkJet (BitcoinJet (TimeLockJet CheckLockDuration)) - $ \check -> forallPrimEnv $ \env -> forAll (genBoundaryCases . txLockDuration $ envTx env) + $ \check -> forallPrimEnv $ \env -> forAll (genBoundaryCases . txLockDuration $ env) $ \w -> check env (toW16 w) + where + txLockDuration env = fromIntegral . fromWord16 . fromJust $ implementation (BitcoinJet (TimeLockJet TxLockDuration)) env () :: Word.Word16 prop_build_tapleaf_simplicity :: HashElement -> Bool prop_build_tapleaf_simplicity = \cmr ->