From dd1ece2711b98c61916a0679fb65ab537a82dadd Mon Sep 17 00:00:00 2001 From: jmcardon Date: Fri, 6 Sep 2024 11:21:37 -0400 Subject: [PATCH 01/11] fix verifier codec --- pact/Pact/Core/Evaluate.hs | 2 +- pact/Pact/Core/Verifiers.hs | 11 +++++------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/pact/Pact/Core/Evaluate.hs b/pact/Pact/Core/Evaluate.hs index c97995fe7..f91913c2f 100644 --- a/pact/Pact/Core/Evaluate.hs +++ b/pact/Pact/Core/Evaluate.hs @@ -183,7 +183,7 @@ setupEvalEnv pdb mode msgData gasModel' np spv pd efs = do (PublicKeyText (fromMaybe pubK addr),S.fromList (_sigCapability <$> capList)) mkMsgVerifiers vs = M.fromListWith S.union $ map toPair vs where - toPair (Verifier vfn _ caps) = (vfn, S.fromList caps) + toPair (Verifier vfn _ caps) = (vfn, S.fromList (_sigCapability <$> caps)) evalExec :: ExecutionMode -> PactDb CoreBuiltin Info -> SPVSupport -> GasModel CoreBuiltin -> Set ExecutionFlag -> NamespacePolicy diff --git a/pact/Pact/Core/Verifiers.hs b/pact/Pact/Core/Verifiers.hs index c1e499f9e..c8aede70e 100644 --- a/pact/Pact/Core/Verifiers.hs +++ b/pact/Pact/Core/Verifiers.hs @@ -25,17 +25,16 @@ import GHC.Generics import qualified Pact.JSON.Decode as JD import qualified Pact.JSON.Encode as J -import Pact.Core.Legacy.LegacyPactValue import Pact.Core.Names -import Pact.Core.Capabilities import Pact.Core.PactValue import Pact.Core.StableEncoding +import Pact.Core.Signer data Verifier prf = Verifier { _verifierName :: VerifierName , _verifierProof :: prf - , _verifierCaps :: [CapToken QualifiedName PactValue] + , _verifierCaps :: [SigCapability] } deriving (Eq, Show, Generic, Ord, Functor, Foldable, Traversable) @@ -51,15 +50,15 @@ instance J.Encode a => J.Encode (Verifier a) where build va = J.object [ "name" J..= _verifierName va , "proof" J..= _verifierProof va - , "clist" J..= J.build (J.Array (StableEncoding <$> _verifierCaps va)) + , "clist" J..= J.build (J.Array (_verifierCaps va)) ] instance FromJSON a => FromJSON (Verifier a) where parseJSON = withObject "Verifier" $ \o -> do name <- o .: "name" proof <- o .: "proof" - legacyCaps <- o .: "clist" - return $ Verifier name proof (_unLegacy <$> legacyCaps) + caps <- o .: "clist" + return $ Verifier name proof caps instance J.Encode ParsedVerifierProof where From 77cf66a486e4e0cdfcb7a692d44bcc13b975303c Mon Sep 17 00:00:00 2001 From: jmcardon Date: Mon, 9 Sep 2024 19:41:09 -0400 Subject: [PATCH 02/11] Integration: Add pactdb regression for all tables --- .../Pact/Core/Test/JSONRoundtripTests.hs | 66 +++++ .../Pact/Core/Test/LegacyDBRegression.hs | 240 ++++++++++++++++ pact-tests/Pact/Core/Test/PersistenceTests.hs | 2 +- pact-tests/Pact/Core/Test/SerialiseTests.hs | 2 +- pact-tests/PactCoreTests.hs | 4 + pact-tng.cabal | 4 +- pact/Pact/Core/ChainData.hs | 2 +- pact/Pact/Core/IR/Eval/Direct/ReplBuiltin.hs | 4 +- pact/Pact/Core/Legacy/LegacyPactValue.hs | 263 ------------------ pact/Pact/Core/Names.hs | 45 ++- pact/Pact/Core/Persistence/SQLite.hs | 62 +++-- pact/Pact/Core/Persistence/Types.hs | 2 +- pact/Pact/Core/Repl/Runtime/ReplBuiltin.hs | 4 +- pact/Pact/Core/Serialise.hs | 4 +- pact/Pact/Core/StableEncoding.hs | 200 ++++++++----- pact/Pact/Crypto/Hyperlane.hs | 4 +- test-utils/Pact/Core/Gen.hs | 146 ++++++++-- test-utils/Pact/Core/PactDbRegression.hs | 4 +- 18 files changed, 651 insertions(+), 407 deletions(-) create mode 100644 pact-tests/Pact/Core/Test/JSONRoundtripTests.hs create mode 100644 pact-tests/Pact/Core/Test/LegacyDBRegression.hs delete mode 100644 pact/Pact/Core/Legacy/LegacyPactValue.hs diff --git a/pact-tests/Pact/Core/Test/JSONRoundtripTests.hs b/pact-tests/Pact/Core/Test/JSONRoundtripTests.hs new file mode 100644 index 000000000..b55d5cd1d --- /dev/null +++ b/pact-tests/Pact/Core/Test/JSONRoundtripTests.hs @@ -0,0 +1,66 @@ +{-# LANGUAGE RankNTypes #-} +{-# LANGUAGE ExistentialQuantification #-} + +module Pact.Core.Test.JSONRoundtripTests(tests) where + +import qualified Pact.JSON.Encode as J + +import Pact.Core.StableEncoding +import Pact.Core.Gen + +import Data.Typeable +import Hedgehog +import Test.Tasty +import Test.Tasty.Hedgehog +import qualified Data.Aeson as JD + +data StableEncodingCase + = forall a. (JD.FromJSON (StableEncoding a), J.Encode (StableEncoding a), Typeable a, Eq a, Show a) => StableEncodingCase (Gen a) + +data EncodingCase + = forall a. (JD.FromJSON a, J.Encode a, Typeable a, Eq a, Show a) => EncodingCase (Gen a) + + +testJSONRoundtrip :: EncodingCase -> TestTree +testJSONRoundtrip (EncodingCase gen) = + testProperty testName $ property $ do + v <- forAll gen + JD.decodeStrict (J.encodeStrict v) === Just v + where + testName = "JSON roundtrips for: " <> show (typeNameOfGen gen) + +typeNameOfGen :: Typeable a => Gen a -> String +typeNameOfGen a = show (typeRep (proxyFromGen a)) + where + proxyFromGen :: Gen a -> Proxy a + proxyFromGen _ = Proxy + +testStableEncodingRoundtrip :: StableEncodingCase -> TestTree +testStableEncodingRoundtrip (StableEncodingCase gen) = do + let testName = "JSON roundtrips for StableEncoding: " <> show (typeNameOfGen gen) + testProperty testName $ property $ do + v <- forAll gen + JD.decodeStrict (J.encodeStrict (StableEncoding v)) === Just (StableEncoding v) + + +tests :: TestTree +tests = testGroup "JSON Roundtrips" $ stableEncodings ++ jsonRoundtrips + where + stableEncodings = fmap testStableEncodingRoundtrip $ + [ StableEncodingCase namespaceNameGen + , StableEncodingCase moduleNameGen + , StableEncodingCase qualifiedNameGen + , StableEncodingCase pactValueGen + , StableEncodingCase (guardGen qualifiedNameGen) + , StableEncodingCase keySetNameGen + , StableEncodingCase keySetGen + , StableEncodingCase fullyQualifiedNameGen + , StableEncodingCase (capTokenGen qualifiedNameGen pactValueGen) + , StableEncodingCase publicMetaGen + , StableEncodingCase publicDataGen + , StableEncodingCase rowDataGen + , StableEncodingCase defPactExecGen + ] + jsonRoundtrips = fmap testJSONRoundtrip $ + [ EncodingCase signerGen + ] diff --git a/pact-tests/Pact/Core/Test/LegacyDBRegression.hs b/pact-tests/Pact/Core/Test/LegacyDBRegression.hs new file mode 100644 index 000000000..77c08dd49 --- /dev/null +++ b/pact-tests/Pact/Core/Test/LegacyDBRegression.hs @@ -0,0 +1,240 @@ +{-# LANGUAGE InstanceSigs #-} +{-# LANGUAGE ExistentialQuantification #-} + +module Pact.Core.Test.LegacyDbRegression where + +import Control.Lens +import Control.Applicative +import Control.Monad +import Data.Default +import Data.Text(Text) +import Test.Tasty +import Test.Tasty.HUnit +import System.FilePath +import qualified Data.Text as T + +import Pact.Core.Persistence +import Pact.Core.Builtin +import Pact.Core.Info +import Pact.Core.Names +import Pact.Core.Persistence.SQLite +import Pact.Core.Serialise +import Data.String (IsString(..)) + +import qualified Data.Char as Char +import qualified Text.Megaparsec as MP +import qualified Text.Megaparsec.Char as MP + +import Debug.Trace + +dbPath :: FilePath +dbPath = "pact-tests" "legacy-db-regression" "pact-v1-chain-9.sqlite" + +data SomeDomain + = forall k v. Show k => SomeDomain (Domain k v CoreBuiltin SpanInfo) + +-- Copy pasted from Pact.Core.Names +-- exporting this causes a compliation error in Pact.Core.Principals +identParser :: Parser Text +identParser = do + c1 <- MP.letterChar <|> MP.oneOf specials + rest <- MP.takeWhileP Nothing (\c -> Char.isLetter c || Char.isDigit c || elem c specials) + pure (T.cons c1 rest) + where + specials :: String + specials = "%#+-_&$@<>=^?*!|/~" + +-- Copy pasted from Pact.Core.Names +-- exporting this causes a compliation error in Pact.Core.Principals +moduleNameParser :: Parser ModuleName +moduleNameParser = do + p <- identParser + MP.try (go p <|> pure (ModuleName p Nothing)) + where + go ns = do + _ <- MP.char '.' + p1 <- identParser + pure (ModuleName p1 (Just (NamespaceName ns))) + +type Parser = MP.Parsec () Text + +newtype UnsafeTableName + = UnsafeTableName { _getUnsafeTable :: TableName } + deriving (Eq, Show) + +instance IsString UnsafeTableName where + fromString s = + case reverse (T.splitOn "_" (T.pack s)) of + identRaw:tbl -> + let tbl' = T.intercalate "_" (reverse tbl) + in case (,) <$> MP.parseMaybe moduleNameParser tbl' <*> MP.parseMaybe identParser identRaw of + Just (mn, ident) -> UnsafeTableName (TableName ident mn) + _ -> error "BOOM2" + _ -> error "BOOM" + + -- case MP.parseMaybe parseTableName (T.pack s) of + -- Just s' -> UnsafeTableName s' + -- Nothing -> error "BOOM" + + +parseTableName :: Parser TableName +parseTableName = do + mn <- moduleNameParser + traceM "heheeee" + _ <- MP.char '_' + ident <- identParser + pure (TableName ident mn) + +-- Note: It's an IO PactDb because `withResource` from tasty has a really +-- annoying signature +runTableDecodeRegression :: HasCallStack => IO (PactDb CoreBuiltin SpanInfo) -> SomeDomain -> TestTree +runTableDecodeRegression pdbIO (SomeDomain domain) = testCase testName $ do + pdb <- pdbIO + keys <- ignoreGas def $ _pdbKeys pdb domain + forM_ keys $ \k -> do + v <- ignoreGas def $ _pdbRead pdb domain k + let msg = "Decode failed for table " <> T.unpack (renderDomain domain) <> " at key " <> show k + assertBool msg $ has _Just v + where + testName = "Running regression for table: " <> T.unpack (renderDomain domain) + +allTables :: [SomeDomain] +allTables = + [ SomeDomain DKeySets + , SomeDomain DDefPacts + , SomeDomain DModules + , SomeDomain DNamespaces + ] ++ (SomeDomain <$> userTables) + where + userTables = + DUserTables . _getUnsafeTable <$> + [ "arkade.token_token-table" + , "coin_allocation-table" + , "coin_coin-table" + , "free.KDAG_cumulative-kda-table" + , "free.KDAG_last-id-table" + , "free.KDAG_lock-kda-table" + , "free.KDAG_multiplier-kda-table" + , "free.KDAG_supply-table" + , "free.KDAG_token-table" + , "free.KDG_cumulative-kda-table" + , "free.KDG_last-id-table" + , "free.KDG_lock-kda-table" + , "free.KDG_multiplier-kda-table" + , "free.KDG_supply-table" + , "free.KDG_token-table" + , "free.KGOLD_cumulative-kda-table" + , "free.KGOLD_last-id-table" + , "free.KGOLD_lock-kda-table" + , "free.KGOLD_multiplier-kda-table" + , "free.KGOLD_supply-table" + , "free.KGOLD_token-table" + , "free.SHIB_token-table" + , "free.anedak_token-table" + , "free.babena_cumulative-babe-table" + , "free.babena_cumulative-kda-table" + , "free.babena_emergency-babe-table" + , "free.babena_emergency-kda-table" + , "free.babena_last-id-table" + , "free.babena_lock-babe-table" + , "free.babena_lock-kda-table" + , "free.babena_multiplier-babe-table" + , "free.babena_multiplier-kda-table" + , "free.babena_supply-table" + , "free.babena_token-table" + , "free.backalley-token_allocation-table" + , "free.backalley-token_token-table" + , "free.backalley_allocation-table" + , "free.backalley_token-table" + , "free.bana_token-table" + , "free.corona-inu_token-table" + , "free.corona-token_token-table" + , "free.crankk01_crankk01-token-table" + , "free.dbc-token_token-table" + , "free.docu_token-table" + , "free.elon_token-table" + , "free.fin-us_token-initialization-table" + , "free.fin-us_token-table" + , "free.hyperhub_token-table" + , "free.inu-crew_counts" + , "free.inu-crew_mint" + , "free.inu-crew_nfts" + , "free.inu-crew_price" + , "free.inu-crew_values" + , "free.jodie-inu_token-table" + , "free.jodie-token_token-table" + , "free.kadoge_token-table" + , "free.kapepe-coin_token-table" + , "free.kapybara-token_token-table" + , "free.kimki_token-table" + , "free.kishu-ken_token-table" + , "free.kmp_token-table" + , "free.kpepe_token-table" + , "free.memory-wall_memories" + , "free.phiga-inu_token-table" + , "free.quality-ledger_lots-table" + , "free.quality-ledger_products-table" + , "free.real-kdoge_token-table" + , "free.shatter_token-table" + , "free.sway_token-table" + , "free.timpi_token-table" + , "free.util-random_state-table" + , "free.wiza_base-multiplier-table" + , "free.wiza_mined-wiza-table" + , "free.wiza_staked-table" + , "free.wiza_token-table" + , "free.yeettoken_token-table" + , "hypercent.prod-hype-coin_ledger" + , "kaddex.kdx_contract-lock" + , "kaddex.kdx_mint-cap-table" + , "kaddex.kdx_privileges" + , "kaddex.kdx_special-accounts" + , "kaddex.kdx_supply-table" + , "kaddex.kdx_token-table" + , "kdlaunch.kdswap-token_token-table" + , "kdlaunch.token_token-table" + , "lago.USD2_token-table" + , "lago.kwBTC_token-table" + , "lago.kwUSDC_token-table" + , "mok.token_token-table" + , "n_5a7ccd559b245b7dcbd5259e1ee43d04fbf93eab.kapepe_token-table" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.bubblegum_endtime-table" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.bubblegum_ledger" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.bubblegum_marketplace" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.bubblegum_metadata-table" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.bubblegum_mint-table" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.bubblegum_nft-chains-table" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.bubblegum_supplies" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.bubblegum_vault-count-table" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.bubblegum_vault-table" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.bubblegum_whitelist-table" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-accounts-count-table" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-accounts-table" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-actions-table" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-charters-table" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-links-table" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-membership-ids-table" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-messages-table" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-pools-table" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-proposals-table" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-role-table" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-thresholds-table" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-total-count-table" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-updates-table" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-votes-table" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_daos-table" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_user-proposition-records" + , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_user-vote-records" + , "n_a2fceb4ebd41f3bb808da95d1ca0af9b15cb068c.kadenai-donate_donate" + , "n_a2fceb4ebd41f3bb808da95d1ca0af9b15cb068c.kadenai-donate_values" + , "n_c5a4b8c52f0866d66bc55864998a37cc089db47c.KEKW_token-table" + , "n_df83905bd42ed92e559616bb707f74979a4010e0.bana_token-table" + , "runonflux.flux_ledger" + , "runonflux.testflux_ledger" ] + + +tests :: TestTree +tests = withResource (unsafeCreateSqlitePactDb serialisePact_raw_spaninfo (T.pack dbPath)) + (\(_, db, cache) -> unsafeCloseSqlitePactDb db cache) $ \pdbio -> + testGroup "Legacy PactDb Regression" $ + runTableDecodeRegression (view _1 <$> pdbio) <$> allTables diff --git a/pact-tests/Pact/Core/Test/PersistenceTests.hs b/pact-tests/Pact/Core/Test/PersistenceTests.hs index 1a0483426..006883f04 100644 --- a/pact-tests/Pact/Core/Test/PersistenceTests.hs +++ b/pact-tests/Pact/Core/Test/PersistenceTests.hs @@ -46,7 +46,7 @@ testsWithSerial :: (Show i, Eq b, Eq i, Default i, IsBuiltin b) -> Gen i -> [TestTree] testsWithSerial serial builtins b i = - [ testProperty "KeySet" $ keysetPersistRoundtrip serial builtins (keySetGen undefined) + [ testProperty "KeySet" $ keysetPersistRoundtrip serial builtins keySetGen -- ^ keySetGen does not use its first argument now. We will pass a real argument -- once custom keyset predicate functions are supported. , testProperty "ModuleData" $ moduleDataRoundtrip serial builtins b i diff --git a/pact-tests/Pact/Core/Test/SerialiseTests.hs b/pact-tests/Pact/Core/Test/SerialiseTests.hs index 40f35dd96..279291944 100644 --- a/pact-tests/Pact/Core/Test/SerialiseTests.hs +++ b/pact-tests/Pact/Core/Test/SerialiseTests.hs @@ -100,7 +100,7 @@ serialiseModule = property $ do serialiseKeySet :: Property serialiseKeySet = property $ do - ks <- forAll (keySetGen qualifiedNameGen) + ks <- forAll keySetGen let encoded = _encodeKeySet serialisePact ks case _decodeKeySet serialisePact encoded of diff --git a/pact-tests/PactCoreTests.hs b/pact-tests/PactCoreTests.hs index 52bf4719e..2b0617b29 100644 --- a/pact-tests/PactCoreTests.hs +++ b/pact-tests/PactCoreTests.hs @@ -19,6 +19,8 @@ import qualified Pact.Core.Test.ConTagGolden as ConTagGoldenTests import qualified Pact.Core.Test.DocsTests as DocsTests import qualified Pact.Core.Test.PrincipalTests as PrincipalTests import qualified Pact.Core.Test.SignatureSchemeTests as SignatureSchemeTests +import qualified Pact.Core.Test.JSONRoundtripTests as JSONRoundtripTests +import qualified Pact.Core.Test.LegacyDbRegression as LegacyDbRegression main :: IO () main = do @@ -45,5 +47,7 @@ main = do , docsTests , PrincipalTests.tests , SignatureSchemeTests.tests + , JSONRoundtripTests.tests + , LegacyDbRegression.tests ] diff --git a/pact-tng.cabal b/pact-tng.cabal index 66489dd82..cdb23853c 100644 --- a/pact-tng.cabal +++ b/pact-tng.cabal @@ -253,7 +253,6 @@ library Pact.Core.Repl Pact.Core.SizeOf Pact.Core.StackFrame - Pact.Core.Legacy.LegacyPactValue Pact.Core.Legacy.LegacyCodec Pact.Core.Verifiers Pact.Core.Interpreter @@ -514,6 +513,7 @@ test-suite core-tests , pact-tng , prettyprinter , vector + , megaparsec , tasty , tasty-hunit , tasty-hedgehog @@ -547,6 +547,8 @@ test-suite core-tests , Pact.Core.Test.PrincipalTests , Pact.Core.Test.ConTagGolden , Pact.Core.Test.DocsTests + , Pact.Core.Test.JSONRoundtripTests + , Pact.Core.Test.LegacyDbRegression , Paths_pact_tng , Pact.Core.Test.SignatureSchemeTests if (flag(with-crypto)) diff --git a/pact/Pact/Core/ChainData.hs b/pact/Pact/Core/ChainData.hs index e39aae55e..8750fb2fb 100644 --- a/pact/Pact/Core/ChainData.hs +++ b/pact/Pact/Core/ChainData.hs @@ -107,7 +107,7 @@ data PublicData = PublicData , _pdPrevBlockHash :: !Text -- ^ block hash of preceding block } - deriving (Show, Generic) + deriving (Show, Eq, Generic) makeLenses ''PublicData instance Default PublicData where diff --git a/pact/Pact/Core/IR/Eval/Direct/ReplBuiltin.hs b/pact/Pact/Core/IR/Eval/Direct/ReplBuiltin.hs index d685c970d..833f9de48 100644 --- a/pact/Pact/Core/IR/Eval/Direct/ReplBuiltin.hs +++ b/pact/Pact/Core/IR/Eval/Direct/ReplBuiltin.hs @@ -41,7 +41,7 @@ import Pact.Core.IR.Term import Pact.Core.Info import Pact.Core.Namespace import Pact.Core.ModRefs -import qualified Pact.Core.Legacy.LegacyPactValue as Legacy +import Pact.Core.StableEncoding import qualified Pact.Core.Version as PI import qualified Data.Version as V @@ -220,7 +220,7 @@ envData info b _env = \case [VPactValue pv] -> do -- to mimic prod, we must roundtrip here -- if it fails silently, this is fine. - let pv' = fromMaybe pv (Legacy.roundtripPactValue pv) + let pv' = fromMaybe pv (roundtripStable pv) (replEvalEnv . eeMsgBody) .== pv' return (VString "Setting transaction data") args -> argsError info b args diff --git a/pact/Pact/Core/Legacy/LegacyPactValue.hs b/pact/Pact/Core/Legacy/LegacyPactValue.hs deleted file mode 100644 index cb52a8a11..000000000 --- a/pact/Pact/Core/Legacy/LegacyPactValue.hs +++ /dev/null @@ -1,263 +0,0 @@ -{-# LANGUAGE StandaloneDeriving #-} -{-# LANGUAGE DerivingStrategies #-} -{-# LANGUAGE GeneralizedNewtypeDeriving #-} - -module Pact.Core.Legacy.LegacyPactValue - ( roundtripPactValue - , Legacy(..) - , decodeLegacy - ) where - -import Control.Applicative -import Data.Aeson -import Data.String (IsString (..)) -import Data.Text(Text) - -import qualified Pact.JSON.Encode as J - -import qualified Data.Set as S -import qualified Data.Aeson as A -import qualified Data.Aeson.KeyMap as A - -import Pact.Core.Capabilities -import Pact.Core.Names -import Pact.Core.Guards -import Pact.Core.Literal -import Pact.Core.ModRefs -import Pact.Core.PactValue -import Pact.Core.Legacy.LegacyCodec -import Pact.Core.StableEncoding -import Pact.Core.Persistence.Types(RowData(..)) -import Data.List -import Data.ByteString (ByteString) - -decodeLegacy :: FromJSON (Legacy v) => ByteString -> Maybe v -decodeLegacy = fmap _unLegacy . A.decodeStrict -{-# INLINE decodeLegacy #-} - -newtype Legacy a - = Legacy { _unLegacy :: a } - -data GuardProperty - = GuardArgs - | GuardCgArgs - | GuardCgName - | GuardCgPactId - | GuardFun - | GuardKeys - | GuardKeysetref - | GuardKsn - | GuardModuleName - | GuardName - | GuardNs - | GuardPactId - | GuardPred - | GuardUnknown !String - deriving (Show, Eq, Ord) - -_gprop :: IsString a => Semigroup a => GuardProperty -> a -_gprop GuardArgs = "args" -_gprop GuardCgArgs = "cgArgs" -_gprop GuardCgName = "cgName" -_gprop GuardCgPactId = "cgPactId" -_gprop GuardFun = "fun" -_gprop GuardKeys = "keys" -_gprop GuardKeysetref = "keysetref" -_gprop GuardKsn = "ksn" -_gprop GuardModuleName = "moduleName" -_gprop GuardName = "name" -_gprop GuardNs = "ns" -_gprop GuardPactId = "pactId" -_gprop GuardPred = "pred" -_gprop (GuardUnknown t) = "UNKNOWN_GUARD[" <> fromString t <> "]" - -ungprop :: IsString a => Eq a => Show a => a -> GuardProperty -ungprop "args" = GuardArgs -ungprop "cgArgs" = GuardCgArgs -ungprop "cgName" = GuardCgName -ungprop "cgPactId" = GuardCgPactId -ungprop "fun" = GuardFun -ungprop "keys" = GuardKeys -ungprop "keysetref" = GuardKeysetref -ungprop "ksn" = GuardKsn -ungprop "moduleName" = GuardModuleName -ungprop "name" = GuardName -ungprop "ns" = GuardNs -ungprop "pactId" = GuardPactId -ungprop "pred" = GuardPred -ungprop t = GuardUnknown (show t) - -keyNamef :: Key -keyNamef = "keysetref" - -instance FromJSON (Legacy (CapToken QualifiedName PactValue)) where - parseJSON = withObject "UserToken" $ \o -> do - legacyName <- o .: "name" - legacyArgs <- o .: "args" - pure $ Legacy $ CapToken (_unLegacy legacyName) (_unLegacy <$> legacyArgs) - {-# INLINE parseJSON #-} - -instance FromJSON (Legacy QualifiedName) where - parseJSON = withText "QualifiedName" $ \t -> case parseQualifiedName t of - Just qn -> pure (Legacy qn) - _ -> fail "could not parse qualified name" - {-# INLINE parseJSON #-} - -instance FromJSON (Legacy ModuleName) where - parseJSON = withObject "module name" $ \o -> - fmap Legacy $ - ModuleName - <$> (o .: "name") - <*> (fmap NamespaceName <$> (o .: "namespace")) - {-# INLINE parseJSON #-} - -instance FromJSON (Legacy v) => FromJSON (Legacy (UserGuard QualifiedName v)) where - parseJSON = withObject "UserGuard" $ \o -> - Legacy <$> (UserGuard - <$> (_unLegacy <$> o .: "fun") - <*> (fmap _unLegacy <$> o .: "args")) - {-# INLINE parseJSON #-} - -instance FromJSON (Legacy KeySetName) where - parseJSON v = - Legacy <$> (newKs v <|> oldKs v) - where - oldKs = withText "KeySetName" (pure . (`KeySetName` Nothing)) - newKs = - withObject "KeySetName" $ \o -> KeySetName - <$> o .: "ksn" - <*> (fmap NamespaceName <$> o .:? "ns") - {-# INLINE parseJSON #-} - -instance FromJSON (Legacy v) => FromJSON (Legacy (Guard QualifiedName v)) where - parseJSON v = case props v of - [GuardKeys, GuardPred] -> Legacy . GKeyset . _unLegacy <$> parseJSON v - [GuardKeysetref] -> flip (withObject "KeySetRef") v $ \o -> - Legacy . GKeySetRef . _unLegacy <$> o .: keyNamef - [GuardName, GuardPactId] -> Legacy . GDefPactGuard . _unLegacy <$> parseJSON v - [GuardModuleName, GuardName] -> Legacy . GModuleGuard . _unLegacy <$> parseJSON v - [GuardArgs, GuardFun] -> Legacy . GUserGuard . _unLegacy <$> parseJSON v - [GuardCgArgs, GuardCgName, GuardCgPactId] -> Legacy . GCapabilityGuard . _unLegacy <$> parseJSON v - _ -> fail $ "unexpected properties for Guard: " - <> show (props v) - <> ", " <> show (J.encode v) - where - props (A.Object o) = sort $ ungprop <$> A.keys o - props _ = [] - {-# INLINEABLE parseJSON #-} - -instance FromJSON (Legacy Literal) where - parseJSON n@Number{} = Legacy . LDecimal <$> decoder decimalCodec n - parseJSON (String s) = pure $ Legacy $ LString s - parseJSON (Bool b) = pure $ Legacy $ LBool b - parseJSON o@Object {} = - (Legacy . LInteger <$> decoder integerCodec o) <|> - -- (LTime <$> decoder timeCodec o) <|> - (Legacy . LDecimal <$> decoder decimalCodec o) - parseJSON _t = fail "Literal parse failed" - {-# INLINE parseJSON #-} - -instance FromJSON (Legacy KSPredicate) where - parseJSON = withText "kspredfun" $ \case - "keys-all" -> pure $ Legacy KeysAll - "keys-any" -> pure $ Legacy KeysAny - "keys-2" -> pure $ Legacy Keys2 - t | Just pn <- parseParsedTyName t -> pure $ Legacy (CustomPredicate pn) - | otherwise -> fail "invalid keyset predicate" - {-# INLINE parseJSON #-} - - -instance FromJSON (Legacy KeySet) where - - parseJSON v = - Legacy <$> (withObject "KeySet" keyListPred v <|> keyListOnly) - where - - keyListPred o = KeySet - <$> (S.fromList . fmap PublicKeyText <$> (o .: "keys")) - <*> (maybe KeysAll _unLegacy <$> o .:? "pred") - - keyListOnly = KeySet - <$> (S.fromList . fmap PublicKeyText <$> parseJSON v) - <*> pure KeysAll - {-# INLINE parseJSON #-} - -instance FromJSON (Legacy ModRef) where - parseJSON = withObject "ModRef" $ \o -> - fmap Legacy $ - ModRef <$> (_unLegacy <$> o .: "refName") - <*> (S.fromList . fmap _unLegacy <$> o .: "refSpec") - {-# INLINE parseJSON #-} - -instance FromJSON (Legacy PactValue) where - parseJSON v = fmap Legacy $ - (PLiteral . _unLegacy <$> parseJSON v) <|> - (PList . fmap _unLegacy <$> parseJSON v) <|> - (PGuard . _unLegacy <$> parseJSON v) <|> - (PModRef . _unLegacy <$> parseJSON v) <|> - (PTime <$> decoder timeCodec v) <|> - (PObject . fmap _unLegacy <$> parseJSON v) - {-# INLINE parseJSON #-} - -instance FromJSON (Legacy ModuleGuard) where - parseJSON = withObject "ModuleGuard" $ \o -> - fmap Legacy $ - ModuleGuard <$> (_unLegacy <$> o .: "moduleName") - <*> (o .: "name") - {-# INLINE parseJSON #-} - -instance FromJSON (Legacy DefPactGuard) where - parseJSON = withObject "DefPactGuard" $ \o -> do - fmap Legacy $ - DefPactGuard - <$> (DefPactId <$> o .: "pactId") - <*> o .: "name" - {-# INLINE parseJSON #-} - -instance FromJSON (Legacy v) => FromJSON (Legacy (CapabilityGuard QualifiedName v)) where - parseJSON = withObject "CapabilityGuard" $ \o -> - fmap Legacy $ - CapabilityGuard - <$> (_unLegacy <$> o .: "cgName") - <*> (fmap _unLegacy <$> o .: "cgArgs") - <*> (fmap DefPactId <$> o .: "cgPactId") - {-# INLINE parseJSON #-} - -roundtripPactValue :: PactValue -> Maybe PactValue -roundtripPactValue pv = - _unLegacy <$> A.decodeStrict' (encodeStable pv) - -instance FromJSON (Legacy RowData) where - parseJSON v = - parseVersioned v <|> - -- note: Parsing into `OldPactValue` here defaults to the code used in - -- the old FromJSON instance for PactValue, prior to the fix of moving - -- the `PModRef` parsing before PObject - Legacy . RowData . fmap _unLegacy <$> parseJSON v - where - parseVersioned = withObject "RowData" $ \o -> Legacy . RowData - <$> (fmap (_unRowDataValue._unLegacy) <$> o .: "$d") - {-# INLINE parseJSON #-} - -newtype RowDataValue - = RowDataValue { _unRowDataValue :: PactValue } - deriving (Show, Eq) - -instance FromJSON (Legacy RowDataValue) where - parseJSON v1 = - (Legacy . RowDataValue . PLiteral . _unLegacy <$> parseJSON v1) <|> - (Legacy . RowDataValue . PList . fmap (_unRowDataValue . _unLegacy) <$> parseJSON v1) <|> - parseTagged v1 - where - parseTagged = withObject "tagged RowData" $ \o -> do - (t :: Text) <- o .: "$t" - val <- o .: "$v" - case t of - "o" -> Legacy . RowDataValue . PObject . fmap (_unRowDataValue . _unLegacy) <$> parseJSON val - "g" -> Legacy . RowDataValue . PGuard . fmap (_unRowDataValue) . _unLegacy <$> parseJSON val - "m" -> Legacy . RowDataValue . PModRef <$> parseMR val - _ -> fail "tagged RowData" - parseMR = withObject "tagged ModRef" $ \o -> ModRef - <$> (fmap _unLegacy $ o .: "refName") - <*> (maybe mempty (S.fromList . fmap _unLegacy) <$> o .: "refSpec") - {-# INLINE parseJSON #-} diff --git a/pact/Pact/Core/Names.hs b/pact/Pact/Core/Names.hs index 7e185766c..26e808619 100644 --- a/pact/Pact/Core/Names.hs +++ b/pact/Pact/Core/Names.hs @@ -434,7 +434,7 @@ moduleNameParser = do MP.try (go p <|> pure (ModuleName p Nothing)) where go ns = do - _ <- MP.single '.' + _ <- dot p1 <- identParser pure (ModuleName p1 (Just (NamespaceName ns))) @@ -444,23 +444,38 @@ qualNameParser = do case ns of Just nsn@(NamespaceName nsRaw) -> go n nsn <|> pure (QualifiedName n (ModuleName nsRaw Nothing)) - Nothing -> fail "invalid qualified name" + Nothing -> + fail "invalid qualified name" where go n nsn = do - _ <- MP.single '.' + _ <- dot p1 <- identParser let qual = QualifiedName p1 (ModuleName n (Just nsn)) pure qual +dot :: Parser Char +dot = MP.char '.' + fullyQualNameParser :: Parser FullyQualifiedName fullyQualNameParser = do - QualifiedName n mn <- qualNameParser - h <- MP.char '.' *> (MP.between (MP.char '{') (MP.char '}') $ - MP.takeWhile1P Nothing (\s -> Char.isAlphaNum s || s `elem` ['-', '_'])) - hash' <- case decodeBase64UrlUnpadded (T.encodeUtf8 h) of - Right hash' -> pure $ ModuleHash $ Hash $ SB.toShort hash' - Left _ -> fail "invalid hash encoding" - pure (FullyQualifiedName mn n hash') + qualifier <- identParser + mname <- dot *> identParser + dot *> (withIdent qualifier mname <|> withHash qualifier mname Nothing) + where + withIdent qualifier mname = do + i <- MP.try identParser + dot *> withHash qualifier mname (Just i) + withHash qualifier mname oname = do + h <- MP.between (MP.char '{') (MP.char '}') $ + MP.takeWhile1P Nothing (\s -> Char.isAlphaNum s || s `elem` ['-', '_']) + hash' <- case decodeBase64UrlUnpadded (T.encodeUtf8 h) of + Right hash' -> pure $ ModuleHash $ Hash $ SB.toShort hash' + Left _ -> fail "invalid hash encoding" + case oname of + Just nn -> + pure (FullyQualifiedName (ModuleName mname (Just (NamespaceName qualifier))) nn hash') + Nothing -> + pure (FullyQualifiedName (ModuleName qualifier Nothing) mname hash') -- Here we are parsing either a qualified name, or a bare name -- bare names are just the atom `n`, and qualified names are of the form @@ -477,22 +492,22 @@ parsedTyNameParser = do Nothing -> pure (TBN (BareName n)) where go n nsn = do - _ <- MP.single '.' + _ <- dot p1 <- identParser let qual = QualifiedName p1 (ModuleName n (Just nsn)) pure (TQN qual) parseModuleName :: Text -> Maybe ModuleName -parseModuleName = MP.parseMaybe moduleNameParser +parseModuleName = MP.parseMaybe (moduleNameParser <* MP.eof) parseParsedTyName :: Text -> Maybe ParsedTyName -parseParsedTyName = MP.parseMaybe parsedTyNameParser +parseParsedTyName = MP.parseMaybe (parsedTyNameParser <* MP.eof) parseQualifiedName :: Text -> Maybe QualifiedName -parseQualifiedName = MP.parseMaybe qualNameParser +parseQualifiedName = MP.parseMaybe (qualNameParser <* MP.eof) parseFullyQualifiedName :: Text -> Maybe FullyQualifiedName -parseFullyQualifiedName = MP.parseMaybe fullyQualNameParser +parseFullyQualifiedName = MP.parseMaybe (fullyQualNameParser <* MP.eof) renderDefPactId :: DefPactId -> Text renderDefPactId (DefPactId t) = t diff --git a/pact/Pact/Core/Persistence/SQLite.hs b/pact/Pact/Core/Persistence/SQLite.hs index 2c9537219..62b17c9fd 100644 --- a/pact/Pact/Core/Persistence/SQLite.hs +++ b/pact/Pact/Core/Persistence/SQLite.hs @@ -143,6 +143,12 @@ mkTblStatement db tbl = do readKeysStmt <- SQL.prepare db ("SELECT rowkey FROM \""<> tbl <> "\" ORDER BY txid DESC") pure $ TblStatements insertStmt insertOrUpdateStmt readValueStmt readKeysStmt +addUserTable :: SQL.Database -> IORef StmtCache -> TableName -> IO TblStatements +addUserTable db stmtCache tn = do + stmts <- mkTblStatement db (toUserTable tn) + modifyIORef' stmtCache $ \cache -> + cache { _stmtUserTbl = M.insert tn stmts (_stmtUserTbl cache) } + pure stmts data StmtCache = StmtCache @@ -172,7 +178,7 @@ initializePactDb serial db = do }, stmtsCache) readKeys :: forall k v b i. SQL.Database -> IORef StmtCache -> Domain k v b i -> GasM b i [k] -readKeys _db stmtCache = liftIO . \case +readKeys db stmtCache = liftIO . \case DKeySets -> withStmt (_tblReadKeys . _stmtKeyset <$> readIORef stmtCache) $ \stmt -> do parsedKS <- fmap parseAnyKeysetName <$> collect stmt [] case sequence parsedKS of @@ -180,28 +186,29 @@ readKeys _db stmtCache = liftIO . \case Right v -> pure v DModules -> withStmt (_tblReadKeys . _stmtModules <$> readIORef stmtCache) $ \stmt -> - fmap parseModuleName <$> collect stmt [] >>= \mns -> case sequence mns of + fmap parseModuleName <$> collect stmt [] >>= \mns -> case sequence mns of Nothing -> fail "unexpected decoding" Just mns' -> pure mns' DDefPacts -> withStmt (_tblReadKeys . _stmtDefPact <$> readIORef stmtCache) $ \stmt -> - fmap DefPactId <$> collect stmt [] + fmap DefPactId <$> collect stmt [] DNamespaces -> withStmt (_tblReadKeys . _stmtNamespace <$> readIORef stmtCache) $ \stmt -> - fmap NamespaceName <$> collect stmt [] + fmap NamespaceName <$> collect stmt [] DUserTables tbl -> do - tblCache <- _stmtUserTbl <$> readIORef stmtCache - case M.lookup tbl tblCache of - Nothing -> fail "invariant failure: table unknown" - Just stmt -> withStmt (pure $ _tblReadKeys stmt) $ \s -> fmap RowKey <$> collect s [] + tblCache <- _stmtUserTbl <$> readIORef stmtCache + stmt <- case M.lookup tbl tblCache of + Nothing -> addUserTable db stmtCache tbl + Just s -> pure s + withStmt (pure $ _tblReadKeys stmt) $ \s -> fmap RowKey <$> collect s [] where collect stmt acc = SQL.step stmt >>= \case - SQL.Done -> SQL.reset stmt >> pure acc - SQL.Row -> do - [SQL.SQLText value] <- SQL.columns stmt - collect stmt (value:acc) + SQL.Done -> SQL.reset stmt >> pure acc + SQL.Row -> do + [SQL.SQLText value] <- SQL.columns stmt + collect stmt (value:acc) @@ -273,9 +280,10 @@ write' serial db txId txLog stmtCache wt domain k v = encoded <- _encodeRowData serial v liftIO $ do tblCache <-_stmtUserTbl <$> readIORef stmtCache - case M.lookup tbl tblCache of - Nothing -> fail "invariant failure: table unknown" - Just tblStmts -> withStmt (pure $ _tblInsert tblStmts) $ \stmt -> do + tblStmts <- case M.lookup tbl tblCache of + Nothing -> addUserTable db stmtCache tbl + Just c -> pure c + withStmt (pure $ _tblInsert tblStmts) $ \stmt -> do let RowKey k' = k TxId i <- readIORef txId SQL.bind stmt [SQL.SQLInteger (fromIntegral i), SQL.SQLText k', SQL.SQLBlob encoded] @@ -289,13 +297,14 @@ write' serial db txId txLog stmtCache wt domain k v = encoded <- _encodeRowData serial new liftIO $ do tblCache <-_stmtUserTbl <$> readIORef stmtCache - case M.lookup tbl tblCache of - Nothing -> fail "invariant failure: table unknown" - Just tblStmts -> withStmt (pure $ _tblInsertOrUpdate tblStmts) $ \stmt -> do - let RowKey k' = k - TxId i <- readIORef txId - SQL.bind stmt [SQL.SQLInteger (fromIntegral i), SQL.SQLText k', SQL.SQLBlob encoded] - doWrite stmt (TxLog (renderDomain domain) k' encoded:) + tblStmts <- case M.lookup tbl tblCache of + Nothing -> addUserTable db stmtCache tbl + Just c -> pure c + withStmt (pure $ _tblInsertOrUpdate tblStmts) $ \stmt -> do + let RowKey k' = k + TxId i <- readIORef txId + SQL.bind stmt [SQL.SQLInteger (fromIntegral i), SQL.SQLText k', SQL.SQLBlob encoded] + doWrite stmt (TxLog (renderDomain domain) k' encoded:) DKeySets -> liftIO $ withStmt (_tblInsertOrUpdate . _stmtKeyset <$> readIORef stmtCache) $ \stmt -> do let encoded = _encodeKeySet serial v @@ -344,7 +353,7 @@ write' serial db txId txLog stmtCache wt domain k v = | otherwise -> throwIO E.MultipleRowsReturnedFromSingleWrite read' :: forall k v b i. PactSerialise b i -> SQL.Database -> IORef StmtCache -> Domain k v b i -> k -> GasM b i (Maybe v) -read' serial _db stmtCache domain k = case domain of +read' serial db stmtCache domain k = case domain of DKeySets -> liftIO $ withStmt (_tblReadValue . _stmtKeyset <$> readIORef stmtCache) $ doRead (renderKeySetName k) (\v -> pure (view document <$> _decodeKeySet serial v)) @@ -353,9 +362,10 @@ read' serial _db stmtCache domain k = case domain of DUserTables tbl -> do tblCache <- _stmtUserTbl <$> liftIO (readIORef stmtCache) - case M.lookup tbl tblCache of - Nothing -> error "invariant failure: table unknown" - Just stmt -> liftIO $ withStmt (pure $ _tblReadValue stmt) $ doRead (_rowKey k) (\v -> pure (view document <$> _decodeRowData serial v)) + stmt <- case M.lookup tbl tblCache of + Nothing -> liftIO (addUserTable db stmtCache tbl) + Just s -> pure s + liftIO $ withStmt (pure $ _tblReadValue stmt) $ doRead (_rowKey k) (\v -> pure (view document <$> _decodeRowData serial v)) DDefPacts -> do liftIO $ withStmt (_tblReadValue . _stmtDefPact <$> readIORef stmtCache) $ diff --git a/pact/Pact/Core/Persistence/Types.hs b/pact/Pact/Core/Persistence/Types.hs index 12bfb236c..7ebcdfa93 100644 --- a/pact/Pact/Core/Persistence/Types.hs +++ b/pact/Pact/Core/Persistence/Types.hs @@ -280,7 +280,7 @@ newtype UserTableInfo -- storage in the persistence backend (prefix USER_ and the module name -- to avoid conflicts with any system tables). toUserTable :: TableName -> Text -toUserTable (TableName tbl mn) = "USER_" <> renderModuleName mn <> "_" <> tbl +toUserTable (TableName tbl mn) = renderModuleName mn <> "_" <> tbl renderDomain :: Domain k v b i -> Text renderDomain = \case diff --git a/pact/Pact/Core/Repl/Runtime/ReplBuiltin.hs b/pact/Pact/Core/Repl/Runtime/ReplBuiltin.hs index 13e2014b2..abbcc9217 100644 --- a/pact/Pact/Core/Repl/Runtime/ReplBuiltin.hs +++ b/pact/Pact/Core/Repl/Runtime/ReplBuiltin.hs @@ -41,7 +41,7 @@ import Pact.Core.Persistence import Pact.Core.IR.Term import Pact.Core.Info import Pact.Core.Namespace -import qualified Pact.Core.Legacy.LegacyPactValue as Legacy +import Pact.Core.StableEncoding import qualified Pact.Core.Version as PI import qualified Data.Version as V @@ -224,7 +224,7 @@ envData info b cont handler _env = \case [VPactValue pv] -> do -- to mimic prod, we must roundtrip here -- if it fails silently, this is fine. - let pv' = fromMaybe pv (Legacy.roundtripPactValue pv) + let pv' = fromMaybe pv (roundtripStable pv) (replEvalEnv . eeMsgBody) .== pv' returnCEKValue cont handler (VString "Setting transaction data") args -> argsError info b args diff --git a/pact/Pact/Core/Serialise.hs b/pact/Pact/Core/Serialise.hs index 33889805a..b03459fed 100644 --- a/pact/Pact/Core/Serialise.hs +++ b/pact/Pact/Core/Serialise.hs @@ -31,6 +31,7 @@ import Pact.Core.Guards import Pact.Core.Namespace import Pact.Core.DefPacts.Types import Pact.Core.IR.Term +import Pact.Core.StableEncoding import Control.Lens import qualified Codec.CBOR.Encoding as S @@ -41,7 +42,6 @@ import Codec.CBOR.Read (deserialiseFromBytes) import qualified Pact.Core.Serialise.LegacyPact as LegacyPact import qualified Pact.Core.Serialise.CBOR_V1 as V1 -import qualified Pact.Core.Legacy.LegacyPactValue as LegacyPact import Pact.Core.Info (SpanInfo) import Data.Default @@ -120,7 +120,7 @@ serialisePact = PactSerialise , _encodeRowData = gEncodeRowData , _decodeRowData = \bs -> - LegacyDocument <$> LegacyPact.decodeLegacy bs + LegacyDocument <$> decodeStable bs <|> docDecode bs (\case V1_CBOR -> V1.decodeRowData ) diff --git a/pact/Pact/Core/StableEncoding.hs b/pact/Pact/Core/StableEncoding.hs index 91b8124eb..19ba25dac 100644 --- a/pact/Pact/Core/StableEncoding.hs +++ b/pact/Pact/Core/StableEncoding.hs @@ -1,4 +1,5 @@ {-# LANGUAGE TypeApplications #-} +{-# LANGUAGE GeneralisedNewtypeDeriving #-} {-# LANGUAGE InstanceSigs #-} -- | @@ -9,28 +10,27 @@ module Pact.Core.StableEncoding ( encodeStable , decodeStable + , roundtripStable , StableEncoding(..)) where import Control.Applicative import Control.Monad (guard) -import qualified Data.Aeson.KeyMap as Aeson -import qualified Data.Aeson.Key as AesonKey -import Data.Aeson.Types (Value(Number), Parser) import Data.Bifunctor +import Data.Text(Text) import Data.ByteString (ByteString) import Data.Coerce(coerce) import Data.Decimal (DecimalRaw(..)) import Data.Scientific (Scientific) import qualified Data.Map.Strict as Map import Data.Map.Strict (Map) -import Data.Ratio ((%), denominator) import qualified Data.Set as Set import qualified Data.Text as T import qualified Data.Map.Strict as M import qualified Data.Set as S import qualified Pact.JSON.Decode as JD import qualified Pact.JSON.Encode as J +import Unsafe.Coerce (unsafeCoerce) import Pact.Core.Capabilities import Pact.Core.ChainData @@ -46,6 +46,7 @@ import Pact.Core.Hash import Pact.Core.DefPacts.Types import Pact.Core.PactValue import Pact.Time +import Data.Maybe (fromMaybe) -- | JSON serialization for 'readInteger' and public meta info; -- accepts both a String version (parsed as a Pact integer), @@ -53,6 +54,16 @@ import Pact.Time newtype ParsedInteger = ParsedInteger Integer deriving (Eq,Show,Ord) +encodeStable :: J.Encode (StableEncoding a) => a -> ByteString +encodeStable = J.encodeStrict . StableEncoding +{-# INLINE encodeStable #-} + +decodeStable :: JD.FromJSON (StableEncoding a) => ByteString -> Maybe a +decodeStable = fmap _stableEncoding . JD.decodeStrict' +{-# INLINE decodeStable #-} + +roundtripStable :: (JD.FromJSON (StableEncoding a), J.Encode (StableEncoding a)) => a -> Maybe a +roundtripStable a = _stableEncoding <$> JD.decodeStrict (J.encodeStrict (StableEncoding a)) instance J.Encode ParsedInteger where build (ParsedInteger i) = J.build $ J.Aeson i @@ -69,14 +80,6 @@ instance JD.FromJSON ParsedInteger where StableEncoding pv -> fail $ "Failure parsing integer PactValue object: " ++ show pv parseJSON v = fail $ "Failure parsing integer: " ++ show v - - -encodeStable :: J.Encode (StableEncoding a) => a -> ByteString -encodeStable = J.encodeStrict . StableEncoding - -decodeStable :: JD.FromJSON (StableEncoding a) => ByteString -> Maybe a -decodeStable = fmap _stableEncoding . JD.decodeStrict' - newtype StableEncoding a = StableEncoding { _stableEncoding :: a } deriving (Ord, Eq, Show) @@ -110,14 +113,11 @@ instance J.Encode (StableEncoding Literal) where | otherwise = J.object [ "decimal" J..= T.pack (show d) ] encodeUnit = J.object ["##unit" J..= T.empty] -- TODO: Discuss? isSafeInteger i = i >= -9007199254740991 && i <= 9007199254740991 - {-# INLINABLE build #-} - -instance J.Encode (StableEncoding RowData) where - build (StableEncoding (RowData o)) = J.object - [ "$d" J..= (StableEncoding o) ] + {-# INLINE build #-} -- | Stable encoding of `Guard FullyQualifiedName PactValue` -instance J.Encode (StableEncoding (Guard QualifiedName PactValue)) where +instance (J.Encode (StableEncoding name), J.Encode (StableEncoding v)) + => J.Encode (StableEncoding (Guard name v)) where build (StableEncoding g) = case g of GKeyset ks -> J.build (StableEncoding ks) GKeySetRef ksn -> J.object ["keysetref" J..= StableEncoding ksn] @@ -125,17 +125,20 @@ instance J.Encode (StableEncoding (Guard QualifiedName PactValue)) where GCapabilityGuard cg -> J.build (StableEncoding cg) GModuleGuard mg -> J.build (StableEncoding mg) GDefPactGuard dpg -> J.build (StableEncoding dpg) - {-# INLINABLE build #-} + {-# INLINE build #-} -instance JD.FromJSON (StableEncoding (Guard QualifiedName PactValue)) where +instance (JD.FromJSON (StableEncoding name), JD.FromJSON (StableEncoding v)) + => JD.FromJSON (StableEncoding (Guard name v)) where parseJSON v = fmap StableEncoding ( (GKeyset . _stableEncoding) <$> JD.parseJSON v <|> - (GKeySetRef . _stableEncoding) <$> JD.parseJSON v <|> + (GKeySetRef . _stableEncoding) <$> ksr v <|> (GUserGuard . _stableEncoding) <$> JD.parseJSON v <|> (GCapabilityGuard . _stableEncoding) <$> JD.parseJSON v <|> (GModuleGuard . _stableEncoding) <$> JD.parseJSON v <|> (GDefPactGuard . _stableEncoding) <$> JD.parseJSON v) + where + ksr = JD.withObject "KeySetRef" $ \o -> o JD..: "keysetref" instance JD.FromJSON (StableEncoding KeySet) where parseJSON = JD.withObject "KeySet" $ \o -> do @@ -144,14 +147,19 @@ instance JD.FromJSON (StableEncoding KeySet) where pure $ StableEncoding (KeySet (S.fromList (fmap PublicKeyText keys)) (_stableEncoding pred')) instance JD.FromJSON (StableEncoding KeySetName) where - parseJSON = JD.withObject "KeySetName" $ \o -> do - ns <- o JD..:? "ns" - ksn <- o JD..: "ksn" - pure $ StableEncoding (KeySetName ksn (NamespaceName <$> ns)) + parseJSON v = oldKs v <|> newKs v + where + oldKs = JD.withText "KeySetName" (pure . StableEncoding . (`KeySetName` Nothing)) + newKs = + JD.withObject "KeySetName" $ \o -> do + ns <- o JD..:? "ns" + ksn <- o JD..: "ksn" + pure $ StableEncoding (KeySetName ksn (NamespaceName <$> ns)) -- | Stable encoding of `CapabilityGuard FullyQualifiedName PactValue` -instance J.Encode (StableEncoding (CapabilityGuard QualifiedName PactValue)) where +instance (J.Encode (StableEncoding name), J.Encode (StableEncoding v)) + => J.Encode (StableEncoding (CapabilityGuard name v)) where build (StableEncoding (CapabilityGuard name args mpid)) = J.object [ "cgPactId" J..= fmap StableEncoding mpid , "cgArgs" J..= J.Array (StableEncoding <$> args) @@ -159,13 +167,15 @@ instance J.Encode (StableEncoding (CapabilityGuard QualifiedName PactValue)) whe ] {-# INLINABLE build #-} -instance JD.FromJSON (StableEncoding (CapabilityGuard QualifiedName PactValue)) where +instance (JD.FromJSON (StableEncoding name), JD.FromJSON (StableEncoding v)) + => JD.FromJSON (StableEncoding (CapabilityGuard name v)) where parseJSON = JD.withObject "CapabilityGuard" $ \o -> do name <- o JD..: "cgName" args <- o JD..: "cgArgs" mpid <- o JD..:? "cgPactId" pure $ StableEncoding (CapabilityGuard (_stableEncoding name) (fmap _stableEncoding args) (fmap _stableEncoding mpid)) + {-# INLINE parseJSON #-} instance J.Encode (StableEncoding QualifiedName) where build (StableEncoding qn) = J.build (renderQualName qn) @@ -183,6 +193,12 @@ instance J.Encode (StableEncoding FullyQualifiedName) where t = maybe "" ((<> ".") . _namespaceName) mns <> mn <> "." <> n <> ".{" <> hashToText mh <> "}" {-# INLINABLE build #-} +instance JD.FromJSON (StableEncoding FullyQualifiedName) where + parseJSON = JD.withText "FullyQualifiedName" $ \t -> + case parseFullyQualifiedName t of + Just fqn -> pure (StableEncoding fqn) + Nothing -> fail "could not parse FullyQualifiedName" + -- | Stable encoding of `ModuleGuard` instance J.Encode (StableEncoding ModuleGuard) where build (StableEncoding (ModuleGuard m name)) = J.object @@ -195,7 +211,7 @@ instance JD.FromJSON (StableEncoding ModuleGuard) where parseJSON = JD.withObject "ModuleGuard" $ \o -> do m <- o JD..: "moduleName" name <- o JD..: "name" - pure $ StableEncoding (ModuleGuard (ModuleName m Nothing) name) + pure $ StableEncoding (ModuleGuard (_stableEncoding m) name) -- | Stalbe encoding of `DefPactGuard` instance J.Encode (StableEncoding DefPactGuard) where @@ -213,26 +229,28 @@ instance JD.FromJSON (StableEncoding DefPactGuard) where instance J.Encode (StableEncoding DefPactExec) where build (StableEncoding (DefPactExec sc yield step defPactId continuation stepHasRollback nestedDefPactExec)) = J.object - [ "stepCount" J..= Number (fromIntegral sc) + [ "nested" J..?= if M.null nestedDefPactExec then Nothing else Just (J.Object (convertMap nestedDefPactExec)) + , "executed" J..= (Nothing :: Maybe Bool) -- compat field for prod + , "pactId" J..= StableEncoding defPactId + , "stepHasRollback" J..= stepHasRollback + , "step" J..= J.Aeson step , "yield" J..= fmap StableEncoding yield - , "step" J..= Number (fromIntegral step) - , "defPactId" J..= StableEncoding defPactId , "continuation" J..= StableEncoding continuation - , "stepHasRollback" J..= stepHasRollback - , "nestedDefPactExec" J..= J.Object (convertMap nestedDefPactExec) + , "stepCount" J..= J.Aeson sc ] where convertMap :: Map DefPactId DefPactExec -> Map T.Text (StableEncoding DefPactExec) - convertMap = Map.fromList . fmap (bimap _defPactId StableEncoding) . Map.toList + convertMap = unsafeCoerce instance JD.FromJSON (StableEncoding DefPactExec) where parseJSON = JD.withObject "DefPactExec" $ \o -> do stepCount <- o JD..: "stepCount" + (_ :: Maybe Bool) <- o JD..: "executed" yield <- o JD..:? "yield" step <- o JD..: "step" - defPactId <- o JD..: "defPactId" + defPactId <- o JD..: "pactId" continuation <- o JD..: "continuation" stepHasRollback <- o JD..: "stepHasRollback" - nestedDefPactExec <- o JD..: "nestedDefPactExec" + nestedDefPactExec <- fromMaybe mempty <$> (o JD..:? "nested") pure $ StableEncoding (DefPactExec stepCount @@ -248,7 +266,7 @@ instance JD.FromJSON (StableEncoding DefPactExec) where instance JD.FromJSON (StableEncoding (DefPactContinuation QualifiedName PactValue)) where parseJSON = JD.withObject "DefPactContinuation" $ \o -> do - name <- o JD..: "name" + name <- o JD..: "def" args <- o JD..: "args" pure $ StableEncoding (DefPactContinuation (_stableEncoding name) (_stableEncoding <$> args)) @@ -258,7 +276,7 @@ instance JD.FromJSON (StableEncoding DefPactId) where instance J.Encode (StableEncoding Yield) where build (StableEncoding (Yield data' provenance sourceChain)) = J.object - [ "data" J..= StableEncoding data' + [ "data" J..= (StableEncoding data') , "provenance" J..= fmap StableEncoding provenance , "sourceChain" J..= fmap StableEncoding sourceChain ] @@ -266,10 +284,10 @@ instance J.Encode (StableEncoding Yield) where instance JD.FromJSON (StableEncoding Yield) where parseJSON = JD.withObject "Yield" $ \o -> do - data' <- o JD..: "data" + data' <- fmap _stableEncoding <$> o JD..: "data" provenance <- o JD..:? "provenance" sourceChain <- o JD..:? "sourceChain" - pure $ StableEncoding (Yield (_stableEncoding data') (fmap _stableEncoding provenance) (_stableEncoding <$> sourceChain)) + pure $ StableEncoding (Yield data' (fmap _stableEncoding provenance) (_stableEncoding <$> sourceChain)) instance J.Encode (StableEncoding Provenance) where build (StableEncoding (Provenance chainId moduleHash)) = J.object @@ -292,14 +310,16 @@ instance JD.FromJSON (StableEncoding ChainId) where parseJSON = JD.withText "ChainId" $ \t -> pure $ StableEncoding (ChainId t) -- | Stable encoding of `UserGuard FullyQualifiedName PactValue` -instance J.Encode (StableEncoding (UserGuard QualifiedName PactValue)) where +instance (J.Encode (StableEncoding name), J.Encode (StableEncoding v)) + => J.Encode (StableEncoding (UserGuard name v)) where build (StableEncoding (UserGuard fun args)) = J.object [ "args" J..= J.array (StableEncoding <$> args) , "fun" J..= StableEncoding fun ] {-# INLINABLE build #-} -instance JD.FromJSON (StableEncoding (UserGuard QualifiedName PactValue)) where +instance (JD.FromJSON (StableEncoding name), JD.FromJSON (StableEncoding v)) + => JD.FromJSON (StableEncoding (UserGuard name v)) where parseJSON = JD.withObject "UserGuard" $ \o -> do fun <- o JD..: "fun" args <- o JD..: "args" @@ -318,7 +338,7 @@ instance J.Encode (StableEncoding KeySetName) where instance J.Encode (StableEncoding KeySet) where build (StableEncoding (KeySet keys predFun)) =J.object [ "pred" J..= StableEncoding predFun - , "keys" J..= J.Array (S.map StableEncoding keys) -- TODO: is this valid? + , "keys" J..= J.Array (S.mapMonotonic StableEncoding keys) -- TODO: is this valid? ] {-# INLINABLE build #-} @@ -330,12 +350,6 @@ instance J.Encode (StableEncoding v) => J.Encode (StableEncoding (Map Field v)) c = coerce {-# INLINABLE build #-} -instance JD.FromJSON (StableEncoding (Map Field PactValue)) where - parseJSON = JD.withObject "Map Field PactValue" $ \o -> do - let keyToField k = Field (AesonKey.toText k) - kvs :: Aeson.KeyMap (StableEncoding PactValue) <- traverse JD.parseJSON o - pure $ StableEncoding (Map.mapKeys keyToField $ _stableEncoding <$> Aeson.toMap kvs) - -- | Stable encoding of `KSPredicate FullyQualifiedName` instance J.Encode (StableEncoding KSPredicate) where build (StableEncoding ksp) = case ksp of @@ -348,14 +362,13 @@ instance J.Encode (StableEncoding KSPredicate) where instance JD.FromJSON (StableEncoding KSPredicate) where parseJSON = JD.withText "KSPredicate" parsePredName where - parsePredName :: T.Text -> Parser (StableEncoding KSPredicate) parsePredName txt = case txt of "keys-all" -> pure $ StableEncoding KeysAll "keys-any" -> pure $ StableEncoding KeysAny "keys-2" -> pure $ StableEncoding Keys2 _ -> case parseParsedTyName txt of - Nothing -> fail "invalid keyset predicate" Just parsedName -> pure $ StableEncoding (CustomPredicate parsedName) + Nothing -> fail "invalid keyset predicate" -- | Stable encoding of `PublicKeyText` instance J.Encode (StableEncoding PublicKeyText) where @@ -404,17 +417,8 @@ instance JD.FromJSON (StableEncoding ModRef) where refSpec :: Maybe [StableEncoding ModuleName] <- o JD..:? "refSpec" pure $ StableEncoding (ModRef (_stableEncoding refName) (maybe Set.empty (S.fromList . fmap _stableEncoding) refSpec)) --- | Stable encoding of `UTCTime` --- --- See https://github.com/kadena-io/pact/blob/e72d86749f5d65ac8d6e07a7652dd2ffb468607b/src/Pact/Types/Codec.hs#L150 --- for further details instance J.Encode (StableEncoding UTCTime) where - build (StableEncoding utc) - | denom utc == 1 = J.object [ "time" J..= T.pack (formatTime "%Y-%m-%dT%H:%M:%SZ" utc) ] - | otherwise = J.object [ "timep" J..= T.pack (formatTime "%Y-%m-%dT%H:%M:%S.%vZ" utc) ] - where - denom :: UTCTime -> Integer - denom = denominator . (% 1000) . fromIntegral . toPosixTimestampMicros + build (StableEncoding utc) = encoder timeCodec utc {-# INLINABLE build #-} -- | Stable encoding of `PactValue` @@ -436,6 +440,7 @@ instance JD.FromJSON (StableEncoding PactValue) where (PGuard . _stableEncoding <$> JD.parseJSON v) <|> (PModRef . _stableEncoding <$> JD.parseJSON v) <|> (PTime <$> decoder timeCodec v) <|> + (PCapToken . _stableEncoding <$> JD.parseJSON v) <|> (PObject . fmap _stableEncoding <$> JD.parseJSON v) {-# INLINABLE parseJSON #-} @@ -454,17 +459,21 @@ instance JD.FromJSON (StableEncoding Literal) where pure LUnit parseJSON _t = fail "Literal parse failed" -instance J.Encode (StableEncoding name) => J.Encode (StableEncoding (CapToken name PactValue)) where +instance (J.Encode (StableEncoding name), J.Encode (StableEncoding v)) + => J.Encode (StableEncoding (CapToken name v)) where build (StableEncoding (CapToken name args)) = J.object - [ "ctName" J..= J.build (StableEncoding name) - , "ctArgs" J..= J.build (J.Array (StableEncoding <$> args)) + [ "#ctName" J..= J.build (StableEncoding name) + , "#ctArgs" J..= J.build (J.Array (StableEncoding <$> args)) ] + {-# INLINE build #-} -instance JD.FromJSON (StableEncoding name) => JD.FromJSON (StableEncoding (CapToken name PactValue)) where +instance (JD.FromJSON (StableEncoding name), JD.FromJSON (StableEncoding v)) + => JD.FromJSON (StableEncoding (CapToken name v)) where parseJSON = JD.withObject "CapToken" $ \o -> do - name <- o JD..: "ctName" - args <- o JD..: "ctArgs" + name <- o JD..: "#ctName" + args <- o JD..: "#ctArgs" pure $ StableEncoding (CapToken (_stableEncoding name) (_stableEncoding <$> args)) + {-# INLINE parseJSON #-} -- | Stable encoding of `DefPactContinuation FullyQualifiedName PactValue` @@ -591,3 +600,60 @@ instance JD.FromJSON (StableEncoding PublicData) where instance J.Encode (StableEncoding a) => J.Encode (StableEncoding (Maybe a)) where build (StableEncoding a) = J.build (StableEncoding <$> a) +instance J.Encode (StableEncoding RowData) where + build (StableEncoding (RowData o)) = J.object + [ "$d" J..= (StableEncoding (RowDataValue <$> o)) ] + +-- Legacy instance +instance JD.FromJSON (StableEncoding RowData) where + parseJSON v = + parseVersioned v <|> + StableEncoding . RowData . fmap _stableEncoding <$> JD.parseJSON v + where + -- Note: This is called `parsedVersioned` as a legacy port, but + -- there isn't really any need to parse the row data version here. + -- We are moving to CBOR, so there won't be any versions other than row data + -- v1 + parseVersioned = JD.withObject "RowData" $ \o -> StableEncoding . RowData + <$> (fmap (_unRowDataValue._stableEncoding) <$> o JD..: "$d") + {-# INLINE parseJSON #-} + +instance J.Encode (StableEncoding RowDataValue) where + build (StableEncoding (RowDataValue v)) = case v of + PLiteral t -> J.build (StableEncoding t) + PTime t -> encoder timeCodec t + PList v' -> J.build $ J.Array (StableEncoding . RowDataValue <$> v') + PGuard g -> buildTagged "g" $ J.build (StableEncoding (RowDataValue <$> g)) + PModRef mr -> buildTagged "m" $ J.build (StableEncoding mr) + PCapToken ct -> buildTagged "ct" $ J.build (StableEncoding (RowDataValue <$> ct)) + PObject o -> buildTagged "o" $ J.build (StableEncoding (RowDataValue <$> o)) + where + buildTagged :: Text -> J.Builder -> J.Builder + buildTagged tag o = J.object + [ "$t" J..= tag + , "$v" J..= o ] + +newtype RowDataValue + = RowDataValue { _unRowDataValue :: PactValue } + deriving newtype (Show, Eq) + +instance JD.FromJSON (StableEncoding RowDataValue) where + parseJSON v1 = + (StableEncoding . RowDataValue . PLiteral . _stableEncoding <$> JD.parseJSON v1) <|> + (StableEncoding . RowDataValue . PTime <$> decoder timeCodec v1) <|> + (StableEncoding . RowDataValue . PList . fmap (_unRowDataValue . _stableEncoding) <$> JD.parseJSON v1) <|> + parseTagged v1 + where + parseTagged = JD.withObject "tagged RowData" $ \o -> do + (t :: Text) <- o JD..: "$t" + val <- o JD..: "$v" + case t of + "o" -> StableEncoding . RowDataValue . PObject . fmap (_unRowDataValue . _stableEncoding) <$> JD.parseJSON val + "g" -> StableEncoding . RowDataValue . PGuard . fmap (_unRowDataValue) . _stableEncoding <$> JD.parseJSON val + "ct" -> StableEncoding . RowDataValue . PCapToken . fmap (_unRowDataValue) . _stableEncoding <$> JD.parseJSON val + "m" -> StableEncoding . RowDataValue . PModRef <$> parseMR val + _ -> fail "tagged RowData" + parseMR = JD.withObject "tagged ModRef" $ \o -> ModRef + <$> (fmap _stableEncoding $ o JD..: "refName") + <*> (maybe mempty (S.fromList . fmap _stableEncoding) <$> o JD..: "refSpec") + {-# INLINE parseJSON #-} diff --git a/pact/Pact/Crypto/Hyperlane.hs b/pact/Pact/Crypto/Hyperlane.hs index a48fb0596..30afce871 100644 --- a/pact/Pact/Crypto/Hyperlane.hs +++ b/pact/Pact/Crypto/Hyperlane.hs @@ -47,13 +47,13 @@ import Data.WideWord.Word256 (Word256(..)) import Data.Word (Word8, Word16, Word32) import Ethereum.Misc (keccak256, _getKeccak256Hash, _getBytesN) import Pact.JSON.Decode qualified as J +import Pact.Core.StableEncoding import Pact.Core.Errors import Pact.Core.PactValue import Pact.Core.Names import Pact.Core.Literal import Pact.Core.Hash -import Pact.Core.Legacy.LegacyPactValue import qualified Data.Map as M ---------------------------------------------- @@ -238,7 +238,7 @@ getWord256BE = do tokenMessageToTerm :: TokenMessageERC20 -> Either HyperlaneDecodeError PactValue tokenMessageToTerm tm = do g <- first (const HyperlaneDecodeErrorParseRecipient) - $ fmap (PGuard . _unLegacy) + $ fmap (PGuard . _stableEncoding) $ J.eitherDecode (BL.fromStrict (tmRecipient tm)) let chainId = Text.pack (show (toInteger (tmChainId tm))) pure $ PObject $ M.fromList diff --git a/test-utils/Pact/Core/Gen.hs b/test-utils/Pact/Core/Gen.hs index 6743421ff..68b3db088 100644 --- a/test-utils/Pact/Core/Gen.hs +++ b/test-utils/Pact/Core/Gen.hs @@ -2,12 +2,12 @@ -- and TxLogs. {-# LANGUAGE NamedFieldPuns #-} +{-# LANGUAGE TypeApplications #-} {-# LANGUAGE ApplicativeDo #-} module Pact.Core.Gen where import Control.Applicative -import qualified Data.ByteString.Short as BSS import Data.Decimal import Data.Default (def) import Data.Map.Strict (fromList) @@ -21,10 +21,8 @@ import qualified Hedgehog.Gen as Gen import qualified Hedgehog.Range as Range import Pact.Time -import Pact.Time.Internal(UTCTime(..)) import Pact.Core.Names import Pact.Core.Guards -import Pact.Core.Hash (Hash(..), ModuleHash(..)) import Pact.Core.Type import Pact.Core.Imports (Import(..)) import Pact.Core.IR.Term @@ -34,10 +32,15 @@ import Pact.Core.Literal import Pact.Core.Capabilities import Pact.Core.Persistence import Pact.Core.PactValue +import Pact.Core.Signer +import Pact.Core.Scheme import Pact.Core.DefPacts.Types -import Pact.Core.ChainData (ChainId(..)) -import Pact.Core.Namespace (Namespace(..)) -import Data.Coerce +import Pact.Core.ChainData +import Pact.Core.Namespace +import Pact.Core.Gas +import Pact.Core.ModRefs +import Pact.Core.Hash +import Data.Ratio ((%), denominator) namespaceNameGen :: Gen NamespaceName namespaceNameGen = NamespaceName <$> identGen @@ -56,8 +59,26 @@ moduleNameGen = do publicKeyTextGen :: Gen PublicKeyText publicKeyTextGen = PublicKeyText <$> identGen --- ksPredicateGen :: Gen (KSPredicate n) --- ksPredicateGen = Gen.element [minBound .. maxBound] +ppkSchemeGen :: Gen PPKScheme +ppkSchemeGen = Gen.choice [pure ED25519, pure WebAuthn] + +sigCapabilityGen :: Gen SigCapability +sigCapabilityGen = + SigCapability <$> + (CapToken <$> qualifiedNameGen <*> (Gen.list (Range.linear 0 10) pactValueGen)) + +signerGen :: Gen Signer +signerGen = + Signer <$> Gen.maybe (ppkSchemeGen) + <*> addrGen + <*> Gen.maybe addrGen + <*> Gen.list (Range.linear 0 10) sigCapabilityGen + where + addrGen = Gen.text (Range.singleton 64) Gen.alphaNum + +capTokenGen :: Gen name -> Gen v -> Gen (CapToken name v) +capTokenGen n v = + CapToken <$> n <*> Gen.list (Range.linear 0 10) v keySetNameGen :: Gen KeySetName keySetNameGen = KeySetName <$> identGen <*> Gen.maybe namespaceNameGen @@ -89,13 +110,14 @@ parsedTyNameGen = Gen.choice ] hashGen :: Gen Hash -hashGen = Hash . BSS.toShort . encodeUtf8 <$> identGen +hashGen = pactHash . encodeUtf8 <$> identGen + -- | Generate a keyset, polymorphic over the custom -- predicate function `a`. This particular variant is -- not supported yet, so the argument is unused. -keySetGen :: Gen a -> Gen KeySet -keySetGen _genA = do +keySetGen :: Gen KeySet +keySetGen = do ksKeysList <- Gen.list (Range.linear 1 10) publicKeyTextGen let _ksKeys = Set.fromList ksKeysList _ksPredFun <- Gen.choice @@ -395,34 +417,116 @@ moduleDataGen b i = Gen.choice defPactIdGen :: Gen DefPactId defPactIdGen = DefPactId <$> identGen -userGuardGen :: Int -> Gen (UserGuard FullyQualifiedName PactValue) -userGuardGen depth = do - ident <- fullyQualifiedNameGen - UserGuard ident <$> Gen.list (Range.linear 0 depth) pactValueGen +userGuardGen :: Gen n -> Gen (UserGuard n PactValue) +userGuardGen namegen = do + ident <- namegen + UserGuard ident <$> Gen.list (Range.linear 0 10) pactValueGen + +capGuardGen :: Gen n -> Gen (CapabilityGuard n PactValue) +capGuardGen n = + CapabilityGuard + <$> n + <*> Gen.list (Range.linear 0 10) pactValueGen + <*> Gen.maybe defPactIdGen + +moduleGuardGen :: Gen ModuleGuard +moduleGuardGen = + ModuleGuard + <$> moduleNameGen + <*> identGen + +defpactGuardGen :: Gen DefPactGuard +defpactGuardGen = + DefPactGuard + <$> defPactIdGen + <*> identGen guardGen :: Gen n -> Gen (Guard n PactValue) -guardGen n = Gen.choice [gKeySetGen, gKeySetRefGen] +guardGen n = Gen.recursive Gen.choice + [ gKeySetGen + , gKeySetRefGen + , GModuleGuard <$> moduleGuardGen + , GDefPactGuard <$> defpactGuardGen] + [ GUserGuard <$> userGuardGen n + , GCapabilityGuard <$> capGuardGen n + ] where - gKeySetGen = GKeyset <$> keySetGen n + gKeySetGen = GKeyset <$> keySetGen gKeySetRefGen = GKeySetRef <$> keySetNameGen --- gUserGuardGen = GUserGuard <$> userGuardGen (depth - 1) + +-- | Note: the extra machinery here is because +-- we want the generated UTCTime to be roundtrippable timeGen :: Gen UTCTime -timeGen = - coerce <$> Gen.int64 Range.constantBounded +timeGen = genRoundtripableTimeUTCTime + +-- | Custom generator of arbitrary UTCTime from +-- years 1000-01-1 to 2100-12-31 +genArbitraryUTCTime :: Gen UTCTime +genArbitraryUTCTime = fromPosixTimestampMicros + <$> Gen.int64 (Range.constant (-30610224000000000) 4133894400000000) + +-- | Generate a an arbitrary UTCTime value that can roundtrip via 'Pact.Types.Codec.timeCodec'. +-- +-- See the documentation of 'Pact.Types.Codec.timeCodec' for details. +-- +genRoundtripableTimeUTCTime :: Gen UTCTime +genRoundtripableTimeUTCTime = do + t <- genArbitraryUTCTime + if denom1000 t == 1 && denom t /= 1 + then genRoundtripableTimeUTCTime + else return t + where + -- This works around a bug in the time codec + denom1000 = denominator @Integer . (% 1000) . fromIntegral . toPosixTimestampMicros + denom = denominator @Integer . (% 1000000) . fromIntegral . toPosixTimestampMicros + +modRefGen :: Gen ModRef +modRefGen = + ModRef + <$> moduleNameGen + <*> (Set.fromList <$> Gen.list (Range.constant 0 5) moduleNameGen) + pactValueGen :: Gen PactValue pactValueGen = Gen.recursive Gen.choice [ PLiteral <$> literalGen - , PGuard <$> guardGen qualifiedNameGen , PTime <$> timeGen ] [ PList . Vec.fromList <$> Gen.list (Range.linear 1 5) pactValueGen , PObject <$> (Gen.map (Range.linear 1 5) ((,) <$> fieldGen <*> pactValueGen)) + , PGuard <$> guardGen qualifiedNameGen , PCapToken <$> (CapToken <$> fullyQualifiedNameGen <*> (Gen.list (Range.linear 0 10) pactValueGen)) + , PModRef <$> modRefGen ] +gasLimitGen :: Gen GasLimit +gasLimitGen = + GasLimit . Gas <$> + Gen.word64 Range.constantBounded + +gasPriceGen :: Gen GasPrice +gasPriceGen = GasPrice <$> decimalGen + +publicMetaGen :: Gen PublicMeta +publicMetaGen = + PublicMeta + <$> chainIdGen + <*> identGen + <*> gasLimitGen + <*> gasPriceGen + <*> (TTLSeconds . fromIntegral <$> Gen.word64 (Range.linear 1 100)) + <*> (TxCreationTime . fromIntegral <$> Gen.word64 (Range.linear 1 100)) + +publicDataGen :: Gen PublicData +publicDataGen = + PublicData + <$> publicMetaGen + <*> Gen.word64 (Range.linear 1 100) + <*> Gen.int64 (Range.linear 1 100) + <*> identGen -- todo: is this kosher? + chainIdGen :: Gen ChainId chainIdGen = ChainId <$> identGen diff --git a/test-utils/Pact/Core/PactDbRegression.hs b/test-utils/Pact/Core/PactDbRegression.hs index 41d200e2b..f1db833b0 100644 --- a/test-utils/Pact/Core/PactDbRegression.hs +++ b/test-utils/Pact/Core/PactDbRegression.hs @@ -87,8 +87,8 @@ runPactDbRegression pdb = do txs2 <- _pdbCommitTx pdb -- Tx logs should be emitted in order flip (assertEqual "output of commit") txs2 - [ TxLog "USER_someModule_user1" "key1" rowEnc - , TxLog "USER_someModule_user1" "key1" row2Enc + [ TxLog "someModule_user1" "key1" rowEnc + , TxLog "someModule_user1" "key1" row2Enc , TxLog "SYS:KeySets" "ks1" ksEnc , TxLog "SYS:Modules" "test" mdEnc ] From 5f3f3e72f20d872a778fcc58ab0d8e833a61e757 Mon Sep 17 00:00:00 2001 From: June <38109440+DevopsGoth@users.noreply.github.com> Date: Mon, 9 Sep 2024 18:47:47 -0600 Subject: [PATCH 03/11] downloaded file needed for test --- .github/workflows/applications.yml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/.github/workflows/applications.yml b/.github/workflows/applications.yml index c550f0cdd..7291a69b9 100644 --- a/.github/workflows/applications.yml +++ b/.github/workflows/applications.yml @@ -95,6 +95,11 @@ jobs: ~/.cabal/store dist-newstyle key: ${{ matrix.os }}-${{ matrix.ghc }}-4-cabal + # Needed for a certain test + - name: Download chain9 test file + run: | + curl -L https://chainweb-chain-db.s3.amazonaws.com/test-objects/pact-v1-chain-9.sqlite \\ + -o pact-5/pact-tests/legacy-db-regression/pact-v1-chain-9.sqlite # Build - name: Update package database From 0ba815fb35a8d566ead33b7b1caf8951e777986f Mon Sep 17 00:00:00 2001 From: June <38109440+DevopsGoth@users.noreply.github.com> Date: Mon, 9 Sep 2024 18:50:15 -0600 Subject: [PATCH 04/11] bad slash --- .github/workflows/applications.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/applications.yml b/.github/workflows/applications.yml index 7291a69b9..64ce55e64 100644 --- a/.github/workflows/applications.yml +++ b/.github/workflows/applications.yml @@ -36,6 +36,12 @@ jobs: - name: Checkout repository uses: actions/checkout@v4 + # Needed for a certain test + - name: Download chain9 test file + run: | + curl -L https://chainweb-chain-db.s3.amazonaws.com/test-objects/pact-v1-chain-9.sqlite \ + -o pact-5/pact-tests/legacy-db-regression/pact-v1-chain-9.sqlite + # Haskell Setup - name: Set permissions for .ghcup (ubuntu) if: startsWith(matrix.os, 'ubuntu-') @@ -95,11 +101,6 @@ jobs: ~/.cabal/store dist-newstyle key: ${{ matrix.os }}-${{ matrix.ghc }}-4-cabal - # Needed for a certain test - - name: Download chain9 test file - run: | - curl -L https://chainweb-chain-db.s3.amazonaws.com/test-objects/pact-v1-chain-9.sqlite \\ - -o pact-5/pact-tests/legacy-db-regression/pact-v1-chain-9.sqlite # Build - name: Update package database From 662686cc89827c4e12c5f1ea9bedd0cc42ff4c33 Mon Sep 17 00:00:00 2001 From: June <38109440+DevopsGoth@users.noreply.github.com> Date: Mon, 9 Sep 2024 18:53:21 -0600 Subject: [PATCH 05/11] Update applications.yml --- .github/workflows/applications.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/applications.yml b/.github/workflows/applications.yml index 64ce55e64..3960f367f 100644 --- a/.github/workflows/applications.yml +++ b/.github/workflows/applications.yml @@ -39,6 +39,8 @@ jobs: # Needed for a certain test - name: Download chain9 test file run: | + ls -haltr + mkdir -p pact-5/pact-tests/legacy-db-regression curl -L https://chainweb-chain-db.s3.amazonaws.com/test-objects/pact-v1-chain-9.sqlite \ -o pact-5/pact-tests/legacy-db-regression/pact-v1-chain-9.sqlite From 46eab5911c875815398c6dd7de51cde32a5da6f9 Mon Sep 17 00:00:00 2001 From: jmcardon Date: Mon, 9 Sep 2024 22:28:46 -0400 Subject: [PATCH 06/11] Add auto-download for db regression --- .gitignore | 1 + .../Pact/Core/Test/LegacyDBRegression.hs | 53 +++++++++++++------ pact-tests/PactCoreTests.hs | 4 +- pact-tng.cabal | 10 +--- 4 files changed, 42 insertions(+), 26 deletions(-) diff --git a/.gitignore b/.gitignore index c28b0a8fa..5c2a40a16 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,4 @@ cabal.project.local* .envrc *.sqlite .vscode +*.DS_Store diff --git a/pact-tests/Pact/Core/Test/LegacyDBRegression.hs b/pact-tests/Pact/Core/Test/LegacyDBRegression.hs index 77c08dd49..b4256a29b 100644 --- a/pact-tests/Pact/Core/Test/LegacyDBRegression.hs +++ b/pact-tests/Pact/Core/Test/LegacyDBRegression.hs @@ -1,7 +1,10 @@ {-# LANGUAGE InstanceSigs #-} {-# LANGUAGE ExistentialQuantification #-} -module Pact.Core.Test.LegacyDbRegression where +module Pact.Core.Test.LegacyDBRegression + ( tests + , downloadRegressionDb ) + where import Control.Lens import Control.Applicative @@ -11,6 +14,9 @@ import Data.Text(Text) import Test.Tasty import Test.Tasty.HUnit import System.FilePath +import System.Directory +import qualified Network.HTTP.Simple as Http +import qualified Data.ByteString as B import qualified Data.Text as T import Pact.Core.Persistence @@ -25,10 +31,15 @@ import qualified Data.Char as Char import qualified Text.Megaparsec as MP import qualified Text.Megaparsec.Char as MP -import Debug.Trace -dbPath :: FilePath -dbPath = "pact-tests" "legacy-db-regression" "pact-v1-chain-9.sqlite" +dbFolder :: FilePath +dbFolder = "pact-tests" "legacy-db-regression" + +dbFile :: FilePath +dbFile = "pact-v1-chain-9.sqlite" + +dbFilePath :: FilePath +dbFilePath = dbFolder dbFile data SomeDomain = forall k v. Show k => SomeDomain (Domain k v CoreBuiltin SpanInfo) @@ -62,6 +73,7 @@ newtype UnsafeTableName = UnsafeTableName { _getUnsafeTable :: TableName } deriving (Eq, Show) +-- | Hacky way of being able to provide a table name to our regression instance IsString UnsafeTableName where fromString s = case reverse (T.splitOn "_" (T.pack s)) of @@ -72,18 +84,6 @@ instance IsString UnsafeTableName where _ -> error "BOOM2" _ -> error "BOOM" - -- case MP.parseMaybe parseTableName (T.pack s) of - -- Just s' -> UnsafeTableName s' - -- Nothing -> error "BOOM" - - -parseTableName :: Parser TableName -parseTableName = do - mn <- moduleNameParser - traceM "heheeee" - _ <- MP.char '_' - ident <- identParser - pure (TableName ident mn) -- Note: It's an IO PactDb because `withResource` from tasty has a really -- annoying signature @@ -234,7 +234,26 @@ allTables = tests :: TestTree -tests = withResource (unsafeCreateSqlitePactDb serialisePact_raw_spaninfo (T.pack dbPath)) +tests = withResource (unsafeCreateSqlitePactDb serialisePact_raw_spaninfo (T.pack dbFilePath)) (\(_, db, cache) -> unsafeCloseSqlitePactDb db cache) $ \pdbio -> testGroup "Legacy PactDb Regression" $ runTableDecodeRegression (view _1 <$> pdbio) <$> allTables + + + +-- Function to download a file as a ByteString and save it to a file +downloadFile :: String -> FilePath -> IO () +downloadFile url destination = do + let request = Http.parseRequest_ url + response <- Http.httpBS request + let body = Http.getResponseBody response -- Get the response as a ByteString + B.writeFile destination body -- Write the ByteString to a file + +downloadRegressionDb :: IO () +downloadRegressionDb = do + fileExists <- doesFileExist dbFilePath + unless fileExists $ do + createDirectoryIfMissing True dbFolder + downloadFile "https://chainweb-chain-db.s3.amazonaws.com/test-objects/pact-v1-chain-9.sqlite" dbFilePath + + diff --git a/pact-tests/PactCoreTests.hs b/pact-tests/PactCoreTests.hs index 2b0617b29..fea412e90 100644 --- a/pact-tests/PactCoreTests.hs +++ b/pact-tests/PactCoreTests.hs @@ -2,6 +2,7 @@ module Main where import Test.Tasty + import qualified Pact.Core.Test.CommandTests as CommandTests import qualified Pact.Core.Test.ReplTests as ReplTests import qualified Pact.Core.Test.LexerParserTests as LexerParserTests @@ -20,7 +21,7 @@ import qualified Pact.Core.Test.DocsTests as DocsTests import qualified Pact.Core.Test.PrincipalTests as PrincipalTests import qualified Pact.Core.Test.SignatureSchemeTests as SignatureSchemeTests import qualified Pact.Core.Test.JSONRoundtripTests as JSONRoundtripTests -import qualified Pact.Core.Test.LegacyDbRegression as LegacyDbRegression +import qualified Pact.Core.Test.LegacyDBRegression as LegacyDbRegression main :: IO () main = do @@ -29,6 +30,7 @@ main = do legacyTests <- LegacySerialiseTests.tests commandTests <- CommandTests.tests docsTests <- DocsTests.tests + LegacyDbRegression.downloadRegressionDb defaultMain $ testGroup "pactTests" [ replTests , LexerTests.tests diff --git a/pact-tng.cabal b/pact-tng.cabal index cdb23853c..d0b4cbf13 100644 --- a/pact-tng.cabal +++ b/pact-tng.cabal @@ -361,13 +361,6 @@ executable profile-tx , terminal-progress-bar , neat-interpolation - other-modules: - -- Pact.Core.GasModel.BuiltinsGas - -- Pact.Core.GasModel.InterpreterGas - -- Pact.Core.GasModel.ContractBench - -- Pact.Core.GasModel.Serialization - -- Pact.Core.GasModel.Utils - ghc-options: -Wall -threaded -rtsopts -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints ghc-prof-options: -fprof-auto -fprof-auto-calls default-language: Haskell2010 @@ -530,6 +523,7 @@ test-suite core-tests , lsp-test >= 0.17 , lsp-types , safe-exceptions + , http-conduit other-modules: , Pact.Core.Test.CommandTests , Pact.Core.Test.ReplTests @@ -548,7 +542,7 @@ test-suite core-tests , Pact.Core.Test.ConTagGolden , Pact.Core.Test.DocsTests , Pact.Core.Test.JSONRoundtripTests - , Pact.Core.Test.LegacyDbRegression + , Pact.Core.Test.LegacyDBRegression , Paths_pact_tng , Pact.Core.Test.SignatureSchemeTests if (flag(with-crypto)) From 83f012d2b2498656cb8f034c54124ac85d67ea76 Mon Sep 17 00:00:00 2001 From: DevopsGoth Date: Tue, 10 Sep 2024 00:32:21 -0600 Subject: [PATCH 07/11] check file location --- .github/workflows/applications.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/applications.yml b/.github/workflows/applications.yml index 3960f367f..fff896c5c 100644 --- a/.github/workflows/applications.yml +++ b/.github/workflows/applications.yml @@ -43,7 +43,7 @@ jobs: mkdir -p pact-5/pact-tests/legacy-db-regression curl -L https://chainweb-chain-db.s3.amazonaws.com/test-objects/pact-v1-chain-9.sqlite \ -o pact-5/pact-tests/legacy-db-regression/pact-v1-chain-9.sqlite - + ls -haltrR # Haskell Setup - name: Set permissions for .ghcup (ubuntu) if: startsWith(matrix.os, 'ubuntu-') From d1e7c326ee7124e0528beda984dff5c8562d9c17 Mon Sep 17 00:00:00 2001 From: June <38109440+DevopsGoth@users.noreply.github.com> Date: Tue, 10 Sep 2024 00:34:47 -0600 Subject: [PATCH 08/11] fix path for download yeah I thought so --- .github/workflows/applications.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/applications.yml b/.github/workflows/applications.yml index fff896c5c..39f82970f 100644 --- a/.github/workflows/applications.yml +++ b/.github/workflows/applications.yml @@ -39,11 +39,10 @@ jobs: # Needed for a certain test - name: Download chain9 test file run: | - ls -haltr - mkdir -p pact-5/pact-tests/legacy-db-regression + mkdir -p pact-tests/legacy-db-regression curl -L https://chainweb-chain-db.s3.amazonaws.com/test-objects/pact-v1-chain-9.sqlite \ - -o pact-5/pact-tests/legacy-db-regression/pact-v1-chain-9.sqlite - ls -haltrR + -o pact-tests/legacy-db-regression/pact-v1-chain-9.sqlite + ls -haltrR pact-tests # Haskell Setup - name: Set permissions for .ghcup (ubuntu) if: startsWith(matrix.os, 'ubuntu-') From ca3c9d24d7dc7e0d0252a7196ad28bc1976d7569 Mon Sep 17 00:00:00 2001 From: June <38109440+DevopsGoth@users.noreply.github.com> Date: Tue, 10 Sep 2024 01:01:09 -0600 Subject: [PATCH 09/11] remove ls --- .github/workflows/applications.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/applications.yml b/.github/workflows/applications.yml index 39f82970f..b4c01248d 100644 --- a/.github/workflows/applications.yml +++ b/.github/workflows/applications.yml @@ -42,7 +42,6 @@ jobs: mkdir -p pact-tests/legacy-db-regression curl -L https://chainweb-chain-db.s3.amazonaws.com/test-objects/pact-v1-chain-9.sqlite \ -o pact-tests/legacy-db-regression/pact-v1-chain-9.sqlite - ls -haltrR pact-tests # Haskell Setup - name: Set permissions for .ghcup (ubuntu) if: startsWith(matrix.os, 'ubuntu-') From 7869e557dd9065d5a5c0cc13dbcb68b78821f4a5 Mon Sep 17 00:00:00 2001 From: jmcardon Date: Tue, 10 Sep 2024 12:06:35 -0400 Subject: [PATCH 10/11] Remove LegacyPactDbCheck tool, automatically get table names from sqlite file --- .../Pact/Core/Test/JSONRoundtripTests.hs | 1 + .../Pact/Core/Test/LegacyDBRegression.hs | 215 +++++------------- pact-tests/PactCoreTests.hs | 4 +- pact-tng.cabal | 14 +- pact/Pact/Core/Serialise.hs | 6 +- pact/Pact/Core/StableEncoding.hs | 38 +++- test-utils/Pact/Core/Gen.hs | 5 - tools/LegacyPactDbCheck.hs | 83 ------- 8 files changed, 101 insertions(+), 265 deletions(-) delete mode 100644 tools/LegacyPactDbCheck.hs diff --git a/pact-tests/Pact/Core/Test/JSONRoundtripTests.hs b/pact-tests/Pact/Core/Test/JSONRoundtripTests.hs index b55d5cd1d..9e9e8a293 100644 --- a/pact-tests/Pact/Core/Test/JSONRoundtripTests.hs +++ b/pact-tests/Pact/Core/Test/JSONRoundtripTests.hs @@ -60,6 +60,7 @@ tests = testGroup "JSON Roundtrips" $ stableEncodings ++ jsonRoundtrips , StableEncodingCase publicDataGen , StableEncodingCase rowDataGen , StableEncodingCase defPactExecGen + , StableEncodingCase namespaceGen ] jsonRoundtrips = fmap testJSONRoundtrip $ [ EncodingCase signerGen diff --git a/pact-tests/Pact/Core/Test/LegacyDBRegression.hs b/pact-tests/Pact/Core/Test/LegacyDBRegression.hs index b4256a29b..610d3d9ea 100644 --- a/pact-tests/Pact/Core/Test/LegacyDBRegression.hs +++ b/pact-tests/Pact/Core/Test/LegacyDBRegression.hs @@ -2,10 +2,10 @@ {-# LANGUAGE ExistentialQuantification #-} module Pact.Core.Test.LegacyDBRegression - ( tests - , downloadRegressionDb ) + ( tests ) where +import Control.Exception.Safe import Control.Lens import Control.Applicative import Control.Monad @@ -15,6 +15,7 @@ import Test.Tasty import Test.Tasty.HUnit import System.FilePath import System.Directory +import qualified Database.SQLite3 as SQL import qualified Network.HTTP.Simple as Http import qualified Data.ByteString as B import qualified Data.Text as T @@ -25,11 +26,11 @@ import Pact.Core.Info import Pact.Core.Names import Pact.Core.Persistence.SQLite import Pact.Core.Serialise -import Data.String (IsString(..)) import qualified Data.Char as Char import qualified Text.Megaparsec as MP import qualified Text.Megaparsec.Char as MP +import Data.IORef dbFolder :: FilePath @@ -69,21 +70,16 @@ moduleNameParser = do type Parser = MP.Parsec () Text -newtype UnsafeTableName - = UnsafeTableName { _getUnsafeTable :: TableName } - deriving (Eq, Show) - --- | Hacky way of being able to provide a table name to our regression -instance IsString UnsafeTableName where - fromString s = - case reverse (T.splitOn "_" (T.pack s)) of - identRaw:tbl -> - let tbl' = T.intercalate "_" (reverse tbl) - in case (,) <$> MP.parseMaybe moduleNameParser tbl' <*> MP.parseMaybe identParser identRaw of - Just (mn, ident) -> UnsafeTableName (TableName ident mn) - _ -> error "BOOM2" - _ -> error "BOOM" - +-- | Hacky way of parsing a user table +parseUserTable :: Text -> Maybe TableName +parseUserTable s = + case reverse (T.splitOn "_" s) of + identRaw:tbl -> + let tbl' = T.intercalate "_" (reverse tbl) + in case (,) <$> MP.parseMaybe moduleNameParser tbl' <*> MP.parseMaybe identParser identRaw of + Just (mn, ident) -> Just (TableName ident mn) + _ -> Nothing + _ -> Nothing -- Note: It's an IO PactDb because `withResource` from tasty has a really -- annoying signature @@ -98,147 +94,56 @@ runTableDecodeRegression pdbIO (SomeDomain domain) = testCase testName $ do where testName = "Running regression for table: " <> T.unpack (renderDomain domain) -allTables :: [SomeDomain] -allTables = - [ SomeDomain DKeySets - , SomeDomain DDefPacts - , SomeDomain DModules - , SomeDomain DNamespaces - ] ++ (SomeDomain <$> userTables) +data DBHarness + = DBHarness + { _dbhDB :: SQL.Database + , _dbhPactDb :: PactDb CoreBuiltin SpanInfo + , _dbhStmtCache :: IORef StmtCache + } + +withStmt :: SQL.Database -> Text -> (SQL.Statement -> IO a) -> IO a +withStmt conn sql = bracket (SQL.prepare conn sql) SQL.finalize + +withDb :: (SQL.Database -> IO c) -> IO c +withDb act = + bracket (unsafeCreateSqlitePactDb serialisePact_raw_spaninfo (T.pack dbFilePath)) + (\(_, db, c) -> unsafeCloseSqlitePactDb db c) + (\(_, db, _) -> act db) + +tests :: IO TestTree +tests = do + downloadRegressionDb + userTables <- withDb getUserTables + let allTables = + [ SomeDomain DKeySets + , SomeDomain DDefPacts + , SomeDomain DModules + , SomeDomain DNamespaces + ] ++ [SomeDomain (DUserTables t) | u <- userTables, Just t <- [parseUserTable u]] + pure $ withResource acquireDbHarness releaseDbHarness $ \pdbio -> + testGroup "Legacy PactDb Regression" $ + runTableDecodeRegression (_dbhPactDb <$> pdbio) <$> allTables where - userTables = - DUserTables . _getUnsafeTable <$> - [ "arkade.token_token-table" - , "coin_allocation-table" - , "coin_coin-table" - , "free.KDAG_cumulative-kda-table" - , "free.KDAG_last-id-table" - , "free.KDAG_lock-kda-table" - , "free.KDAG_multiplier-kda-table" - , "free.KDAG_supply-table" - , "free.KDAG_token-table" - , "free.KDG_cumulative-kda-table" - , "free.KDG_last-id-table" - , "free.KDG_lock-kda-table" - , "free.KDG_multiplier-kda-table" - , "free.KDG_supply-table" - , "free.KDG_token-table" - , "free.KGOLD_cumulative-kda-table" - , "free.KGOLD_last-id-table" - , "free.KGOLD_lock-kda-table" - , "free.KGOLD_multiplier-kda-table" - , "free.KGOLD_supply-table" - , "free.KGOLD_token-table" - , "free.SHIB_token-table" - , "free.anedak_token-table" - , "free.babena_cumulative-babe-table" - , "free.babena_cumulative-kda-table" - , "free.babena_emergency-babe-table" - , "free.babena_emergency-kda-table" - , "free.babena_last-id-table" - , "free.babena_lock-babe-table" - , "free.babena_lock-kda-table" - , "free.babena_multiplier-babe-table" - , "free.babena_multiplier-kda-table" - , "free.babena_supply-table" - , "free.babena_token-table" - , "free.backalley-token_allocation-table" - , "free.backalley-token_token-table" - , "free.backalley_allocation-table" - , "free.backalley_token-table" - , "free.bana_token-table" - , "free.corona-inu_token-table" - , "free.corona-token_token-table" - , "free.crankk01_crankk01-token-table" - , "free.dbc-token_token-table" - , "free.docu_token-table" - , "free.elon_token-table" - , "free.fin-us_token-initialization-table" - , "free.fin-us_token-table" - , "free.hyperhub_token-table" - , "free.inu-crew_counts" - , "free.inu-crew_mint" - , "free.inu-crew_nfts" - , "free.inu-crew_price" - , "free.inu-crew_values" - , "free.jodie-inu_token-table" - , "free.jodie-token_token-table" - , "free.kadoge_token-table" - , "free.kapepe-coin_token-table" - , "free.kapybara-token_token-table" - , "free.kimki_token-table" - , "free.kishu-ken_token-table" - , "free.kmp_token-table" - , "free.kpepe_token-table" - , "free.memory-wall_memories" - , "free.phiga-inu_token-table" - , "free.quality-ledger_lots-table" - , "free.quality-ledger_products-table" - , "free.real-kdoge_token-table" - , "free.shatter_token-table" - , "free.sway_token-table" - , "free.timpi_token-table" - , "free.util-random_state-table" - , "free.wiza_base-multiplier-table" - , "free.wiza_mined-wiza-table" - , "free.wiza_staked-table" - , "free.wiza_token-table" - , "free.yeettoken_token-table" - , "hypercent.prod-hype-coin_ledger" - , "kaddex.kdx_contract-lock" - , "kaddex.kdx_mint-cap-table" - , "kaddex.kdx_privileges" - , "kaddex.kdx_special-accounts" - , "kaddex.kdx_supply-table" - , "kaddex.kdx_token-table" - , "kdlaunch.kdswap-token_token-table" - , "kdlaunch.token_token-table" - , "lago.USD2_token-table" - , "lago.kwBTC_token-table" - , "lago.kwUSDC_token-table" - , "mok.token_token-table" - , "n_5a7ccd559b245b7dcbd5259e1ee43d04fbf93eab.kapepe_token-table" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.bubblegum_endtime-table" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.bubblegum_ledger" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.bubblegum_marketplace" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.bubblegum_metadata-table" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.bubblegum_mint-table" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.bubblegum_nft-chains-table" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.bubblegum_supplies" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.bubblegum_vault-count-table" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.bubblegum_vault-table" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.bubblegum_whitelist-table" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-accounts-count-table" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-accounts-table" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-actions-table" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-charters-table" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-links-table" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-membership-ids-table" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-messages-table" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-pools-table" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-proposals-table" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-role-table" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-thresholds-table" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-total-count-table" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-updates-table" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_dao-votes-table" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_daos-table" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_user-proposition-records" - , "n_7763cd0330f59f3c66e431dcd63a2c5c5e2e0b70.dao-hive-factory_user-vote-records" - , "n_a2fceb4ebd41f3bb808da95d1ca0af9b15cb068c.kadenai-donate_donate" - , "n_a2fceb4ebd41f3bb808da95d1ca0af9b15cb068c.kadenai-donate_values" - , "n_c5a4b8c52f0866d66bc55864998a37cc089db47c.KEKW_token-table" - , "n_df83905bd42ed92e559616bb707f74979a4010e0.bana_token-table" - , "runonflux.flux_ledger" - , "runonflux.testflux_ledger" ] + acquireDbHarness :: IO DBHarness + acquireDbHarness = do + (pdb, db, cache) <- unsafeCreateSqlitePactDb serialisePact_raw_spaninfo (T.pack dbFilePath) + pure (DBHarness db pdb cache ) + releaseDbHarness :: DBHarness -> IO () + releaseDbHarness harness = + unsafeCloseSqlitePactDb (_dbhDB harness) (_dbhStmtCache harness) -tests :: TestTree -tests = withResource (unsafeCreateSqlitePactDb serialisePact_raw_spaninfo (T.pack dbFilePath)) - (\(_, db, cache) -> unsafeCloseSqlitePactDb db cache) $ \pdbio -> - testGroup "Legacy PactDb Regression" $ - runTableDecodeRegression (view _1 <$> pdbio) <$> allTables +getUserTables :: SQL.Database -> IO [Text] +getUserTables con = do + withStmt con qry (go []) + where + qry = "select name from sqlite_master where type='table'" + go acc stmt = SQL.step stmt >>= \case + SQL.Done -> pure acc + SQL.Row -> do + [SQL.SQLText tbl] <- SQL.columns stmt + go (tbl: acc) stmt -- Function to download a file as a ByteString and save it to a file diff --git a/pact-tests/PactCoreTests.hs b/pact-tests/PactCoreTests.hs index fea412e90..d08f08f6c 100644 --- a/pact-tests/PactCoreTests.hs +++ b/pact-tests/PactCoreTests.hs @@ -30,7 +30,7 @@ main = do legacyTests <- LegacySerialiseTests.tests commandTests <- CommandTests.tests docsTests <- DocsTests.tests - LegacyDbRegression.downloadRegressionDb + legacyDbRegression <- LegacyDbRegression.tests defaultMain $ testGroup "pactTests" [ replTests , LexerTests.tests @@ -50,6 +50,6 @@ main = do , PrincipalTests.tests , SignatureSchemeTests.tests , JSONRoundtripTests.tests - , LegacyDbRegression.tests + , legacyDbRegression ] diff --git a/pact-tng.cabal b/pact-tng.cabal index d0b4cbf13..cbb42980f 100644 --- a/pact-tng.cabal +++ b/pact-tng.cabal @@ -524,6 +524,7 @@ test-suite core-tests , lsp-types , safe-exceptions , http-conduit + , direct-sqlite other-modules: , Pact.Core.Test.CommandTests , Pact.Core.Test.ReplTests @@ -549,19 +550,6 @@ test-suite core-tests build-depends: pact-tng:pact-crypto --- tools -executable legacyPactDbCheck - import: pact-common - hs-source-dirs: tools - - main-is: LegacyPactDbCheck.hs - - build-depends: - , pact-tng - ghc-options: -Wall -threaded -rtsopts -Wincomplete-record-updates -Wincomplete-uni-patterns -Wredundant-constraints - ghc-prof-options: -fprof-auto -fprof-auto-calls - default-language: Haskell2010 - -- -- tools -- executable pact-server -- import: pact-common diff --git a/pact/Pact/Core/Serialise.hs b/pact/Pact/Core/Serialise.hs index b03459fed..f12c1336d 100644 --- a/pact/Pact/Core/Serialise.hs +++ b/pact/Pact/Core/Serialise.hs @@ -99,21 +99,21 @@ serialisePact = PactSerialise , _encodeKeySet = docEncode V1.encodeKeySet , _decodeKeySet = \bs -> - LegacyDocument <$> LegacyPact.decodeKeySet bs + LegacyDocument <$> decodeStable bs <|> docDecode bs (\case V1_CBOR -> V1.decodeKeySet ) , _encodeDefPactExec = docEncode V1.encodeDefPactExec , _decodeDefPactExec = \bs -> - LegacyDocument <$> LegacyPact.decodeDefPactExec bs + LegacyDocument <$> decodeStable bs <|> docDecode bs (\case V1_CBOR -> V1.decodeDefPactExec ) , _encodeNamespace = docEncode V1.encodeNamespace , _decodeNamespace = \bs -> - LegacyDocument <$> LegacyPact.decodeNamespace bs + LegacyDocument <$> decodeStable bs <|> docDecode bs (\case V1_CBOR -> V1.decodeNamespace ) diff --git a/pact/Pact/Core/StableEncoding.hs b/pact/Pact/Core/StableEncoding.hs index 19ba25dac..5fdbb62c2 100644 --- a/pact/Pact/Core/StableEncoding.hs +++ b/pact/Pact/Core/StableEncoding.hs @@ -47,6 +47,7 @@ import Pact.Core.DefPacts.Types import Pact.Core.PactValue import Pact.Time import Data.Maybe (fromMaybe) +import Pact.Core.Namespace -- | JSON serialization for 'readInteger' and public meta info; -- accepts both a String version (parsed as a Pact integer), @@ -141,10 +142,17 @@ instance (JD.FromJSON (StableEncoding name), JD.FromJSON (StableEncoding v)) ksr = JD.withObject "KeySetRef" $ \o -> o JD..: "keysetref" instance JD.FromJSON (StableEncoding KeySet) where - parseJSON = JD.withObject "KeySet" $ \o -> do - keys <- o JD..: "keys" - pred' <- o JD..: "pred" - pure $ StableEncoding (KeySet (S.fromList (fmap PublicKeyText keys)) (_stableEncoding pred')) + parseJSON v = objKs v <|> keyListOnly v + where + keyListOnly v' = fmap StableEncoding $ + KeySet + <$> fmap (S.mapMonotonic _stableEncoding) (JD.parseJSON v') + <*> pure KeysAll + objKs = + JD.withObject "KeySet" $ \o -> do + keys <- o JD..: "keys" + pred' <- fromMaybe (StableEncoding KeysAll) <$> o JD..:? "pred" + pure $ StableEncoding (KeySet (S.fromList (fmap PublicKeyText keys)) (_stableEncoding pred')) instance JD.FromJSON (StableEncoding KeySetName) where parseJSON v = oldKs v <|> newKs v @@ -375,6 +383,11 @@ instance J.Encode (StableEncoding PublicKeyText) where build (StableEncoding (PublicKeyText pkt)) = J.build pkt {-# INLINABLE build #-} +-- | Stable encoding of `PublicKeyText` +instance JD.FromJSON (StableEncoding PublicKeyText) where + parseJSON = JD.withText "PublicKey" (pure . StableEncoding . PublicKeyText) + {-# INLINABLE parseJSON #-} + -- | Stable encoding of `NamespaceName` instance J.Encode (StableEncoding NamespaceName) where build (StableEncoding (NamespaceName ns)) = J.build ns @@ -383,6 +396,20 @@ instance J.Encode (StableEncoding NamespaceName) where instance JD.FromJSON (StableEncoding NamespaceName) where parseJSON = JD.withText "NamespaceName" $ \t -> pure $ StableEncoding (NamespaceName t) +instance J.Encode (StableEncoding Namespace) where + build (StableEncoding (Namespace nsn user admin)) = J.object + [ "admin" J..= StableEncoding admin + , "user" J..= StableEncoding user + , "name" J..= StableEncoding nsn + ] + +instance JD.FromJSON (StableEncoding Namespace) where + parseJSON = JD.withObject "Namespace" $ \o -> do + StableEncoding admin <- o JD..: "admin" + StableEncoding user <- o JD..: "user" + StableEncoding nsn <- o JD..: "name" + pure (StableEncoding (Namespace nsn user admin)) + -- | Stable encoding of `ModuleName` instance J.Encode (StableEncoding ModuleName) where build (StableEncoding (ModuleName mn ns)) = J.object @@ -600,6 +627,9 @@ instance JD.FromJSON (StableEncoding PublicData) where instance J.Encode (StableEncoding a) => J.Encode (StableEncoding (Maybe a)) where build (StableEncoding a) = J.build (StableEncoding <$> a) +instance JD.FromJSON (StableEncoding a) => JD.FromJSON (StableEncoding (Maybe a)) where + parseJSON v = StableEncoding . fmap _stableEncoding <$> JD.parseJSON v + instance J.Encode (StableEncoding RowData) where build (StableEncoding (RowData o)) = J.object [ "$d" J..= (StableEncoding (RowDataValue <$> o)) ] diff --git a/test-utils/Pact/Core/Gen.hs b/test-utils/Pact/Core/Gen.hs index 68b3db088..3c3f79671 100644 --- a/test-utils/Pact/Core/Gen.hs +++ b/test-utils/Pact/Core/Gen.hs @@ -293,11 +293,6 @@ constValGen t = Gen.choice fqNameRefGen :: Gen (FQNameRef Name) fqNameRefGen = FQName <$> fullyQualifiedNameGen --- defManagedMetaGen :: Gen name -> Gen (DefManagedMeta name) --- defManagedMetaGen genName = Gen.choice --- [ DefManagedMeta <$> liftA2 (,) (Gen.int (Range.linear 0 100)) genText <*> genName --- , pure AutoManagedMeta --- ] defManagedMetaGen :: Gen name -> Gen (DefManagedMeta name) defManagedMetaGen genName = Gen.choice diff --git a/tools/LegacyPactDbCheck.hs b/tools/LegacyPactDbCheck.hs deleted file mode 100644 index 27d9367a0..000000000 --- a/tools/LegacyPactDbCheck.hs +++ /dev/null @@ -1,83 +0,0 @@ -{-# LANGUAGE OverloadedStrings #-} - --- | - -module Main where - -import Control.Exception.Safe -import qualified Database.SQLite3 as SQL -import Data.Text (Text) -import qualified Data.Text as T -import Data.ByteString (ByteString) -import Data.Int (Int64) -import Control.Monad -import Pact.Core.Serialise.LegacyPact - -import System.Environment -import qualified Pact.JSON.Decode as JD - -withStmt :: SQL.Database -> Text -> (SQL.Statement -> IO a) -> IO a -withStmt conn sql = bracket (SQL.prepare conn sql) SQL.finalize - -userTables :: SQL.Database -> IO [Text] -userTables con = withStmt con qry $ go [] - where - qry = "select name from sqlite_master where type='table' and name like '%#-table' escape '#'" - go acc stmt = SQL.step stmt >>= \case - SQL.Done -> pure acc - SQL.Row -> do - [SQL.SQLText tbl] <- SQL.columns stmt - go (tbl: acc) stmt - -data RawRow - = RawRow - { _rrKey :: Text - , _rrTxId :: Int64 - , _rrPayload :: ByteString - } - -getRawData :: SQL.Database -> Text -> IO [RawRow] -getRawData con tbl = withStmt con qry $ go [] - where - qry = "select rowkey,txid,rowdata from [" <> tbl <> "]" - go acc stmt = SQL.step stmt >>= \case - SQL.Done -> pure acc - SQL.Row -> do - [SQL.SQLText key, SQL.SQLInteger txid, SQL.SQLBlob value] <- SQL.columns stmt - go (RawRow key txid value : acc) stmt - - -rawTest :: JD.FromJSON a => SQL.Database -> Text -> (a -> Either String c) -> IO () -rawTest db tbl fromLegacy = do - print tbl - keys <- getRawData db tbl - forM_ keys $ \(RawRow i txid payload) -> case JD.eitherDecodeStrict' payload of - Right lo -> case fromLegacy lo of - Left e -> - putStrLn $ "\t" <> show i <> " " <> show txid <> " " <> e - Right _ -> pure () - Left err -> putStrLn $ "Fatal: decoding into legacy format failed: " - <> show tbl <> " = " <> show i <> " " <> show txid <> " with : " <> err - - - -main :: IO () -main = getArgs >>= \case - [dbstr] -> bracket (SQL.open (T.pack dbstr)) SQL.close $ \db -> do - - rawTest db "SYS:Modules" (runTranslateM . fromLegacyModuleData) - rawTest db "SYS:KeySets" fromLegacyKeySet - rawTest db "SYS:Namespaces" fromLegacyNamespace -- rawTest db "SYS:Pacts" fromLegacyDefPactExec - - tbls <- userTables db - forM_ tbls $ \tbl -> do - print tbl - ud <- getRawData db tbl - forM_ ud $ \(RawRow k txid payload) -> case JD.decodeStrict' payload of - Just lo -> case fromLegacyRowData lo of - Left e -> putStrLn $ "\t" <> show k <> " " <> show txid <> " : " <> e - Right _ -> pure () - Nothing -> putStrLn $ "Fatal: decoding into legacy format failed at: " - <> show k <> " " <> show txid - - _ -> error "Wrong number of arguments, expected 'file.sqlite'" From 2cae64ad55fe550cc758eaa0abca1a7472e66b6e Mon Sep 17 00:00:00 2001 From: chessai Date: Wed, 11 Sep 2024 09:59:10 -0500 Subject: [PATCH 11/11] only build binary bundle --- .github/workflows/nix.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/nix.yml b/.github/workflows/nix.yml index d55f5824e..f1bc8ee80 100644 --- a/.github/workflows/nix.yml +++ b/.github/workflows/nix.yml @@ -39,15 +39,15 @@ jobs: - name: Build and cache artifacts run: | - echo Building the project and its devShell - nix build .#check --log-lines 500 --show-trace --accept-flake-config + #echo Building the project and its devShell + #nix build .#check --log-lines 500 --show-trace --accept-flake-config echo Build the bundle nix build .#pact-binary-bundle --log-lines 500 --show-trace --out-link pact-binary-bundle --accept-flake-config tar -zcvf pact-binary-bundle.${{ matrix.os }}.tar.gz $(readlink pact-binary-bundle) - echo Build the recursive output - nix build .#recursive.allDerivations --log-lines 500 --show-trace --accept-flake-config + #echo Build the recursive output + #nix build .#recursive.allDerivations --log-lines 500 --show-trace --accept-flake-config - name: Publish the bundle uses: actions/upload-artifact@v4