diff --git a/bench/Chainweb/Pact/Backend/PactService.hs b/bench/Chainweb/Pact/Backend/PactService.hs index 6818e5d1bc..222cb5e7e0 100644 --- a/bench/Chainweb/Pact/Backend/PactService.hs +++ b/bench/Chainweb/Pact/Backend/PactService.hs @@ -82,7 +82,7 @@ bench :: RocksDb -> C.Benchmark bench rdb = do C.bgroup "PactService" [ C.bgroup "Pact4" - [ C.bench "1 tx" $ oneBlock pact4Version rdb 1 + $ (const []) [ C.bench "1 tx" $ oneBlock pact4Version rdb 1 -- Temporaily disabled , C.bench "10 txs" $ oneBlock pact4Version rdb 10 , C.bench "20 txs" $ oneBlock pact4Version rdb 20 , C.bench "30 txs" $ oneBlock pact4Version rdb 30 diff --git a/src/Chainweb/Pact/PactService.hs b/src/Chainweb/Pact/PactService.hs index 85ec113f9f..bb0623bcf5 100644 --- a/src/Chainweb/Pact/PactService.hs +++ b/src/Chainweb/Pact/PactService.hs @@ -108,6 +108,7 @@ import Chainweb.Pact.Service.PactQueue (PactQueue, getNextRequest) import Chainweb.Pact.Types import Chainweb.Pact4.SPV qualified as Pact4 import Chainweb.Pact5.SPV qualified as Pact5 +import Chainweb.ForkState (pact4ForkNumber) import Chainweb.Payload import Chainweb.Payload.PayloadStore import Chainweb.Time @@ -817,7 +818,8 @@ execLocal cwtx preflight sigVerify rdepth = pactLabel "execLocal" $ do Left err -> earlyReturn $ review _MetadataValidationFailure $ NonEmpty.singleton $ Text.pack err Right _ -> return () _ -> do - let validated = Pact4.assertCommand pact4Cwtx (validPPKSchemes v cid bh) (isWebAuthnPrefixLegal v cid bh) + let isValidScheme = isValidPPKScheme v cid pact4ForkNumber bh . SchemeV4 + let validated = Pact4.assertCommand pact4Cwtx isValidScheme (isWebAuthnPrefixLegal v cid bh) case validated of Left err -> earlyReturn $ review _MetadataValidationFailure (pure $ displayAssertCommandError err) Right () -> return () @@ -859,7 +861,12 @@ execLocal cwtx preflight sigVerify rdepth = pactLabel "execLocal" $ do let localPact5 = do ph <- view psParentHeader - let pact5RequestKey = Pact5.RequestKey (Pact5.Hash $ Pact4.unHash $ Pact4.toUntypedHash $ Pact4._cmdHash cwtx) + let txCtx = Pact5.TxContext ph noMiner + bh = Pact5.ctxCurrentBlockHeight txCtx + fn = Pact5.ctxParentForkNumber txCtx + spvSupport = Pact5.pactSPV bhdb (_parentHeader ph) + pact5RequestKey = Pact5.RequestKey (Pact5.Hash $ Pact4.unHash $ Pact4.toUntypedHash $ Pact4._cmdHash cwtx) + evalContT $ withEarlyReturn $ \earlyReturn -> do pact5Cmd <- case Pact5.parsePact4Command cwtx of Left (Left errText) -> do @@ -902,14 +909,14 @@ execLocal cwtx preflight sigVerify rdepth = pactLabel "execLocal" $ do review _MetadataValidationFailure $ NonEmpty.singleton $ Text.pack err Right _ -> return () _ -> do - let validated = Pact5.assertCommand pact5Cmd + let isValidScheme = isValidPPKScheme v cid fn bh . SchemeV5 + validated = Pact5.assertCommand pact5Cmd isValidScheme + case validated of Left err -> earlyReturn $ review _MetadataValidationFailure (pure $ displayAssertCommandError err) Right () -> return () - let txCtx = Pact5.TxContext ph noMiner - let spvSupport = Pact5.pactSPV bhdb (_parentHeader ph) case preflight of Just PreflightSimulation -> do -- preflight needs to do additional checks on the metadata @@ -1180,13 +1187,14 @@ execPreInsertCheckReq txs = pactLabel "execPreInsertCheckReq" $ do let parentTime = ParentCreationTime (view blockCreationTime $ _parentHeader ph) currHeight = succ $ view blockHeight $ _parentHeader ph + parentForkNumber = view blockForkNumber $ _parentHeader ph isGenesis = False forM txs $ \tx -> fmap (either Just (\_ -> Nothing)) $ runExceptT $ do -- it's safe to use initialBlockHandle here because it's -- only used to check for duplicate pending txs in a block pact5Tx <- mapExceptT liftIO $ Pact5.validateRawChainwebTx - logger v cid db initialBlockHandle parentTime currHeight isGenesis tx + logger v cid db initialBlockHandle parentTime parentForkNumber currHeight isGenesis tx let logger' = addLabel ("transaction", "attemptBuyGas") logger ExceptT $ Pact5.pactTransaction Nothing $ \pactDb -> runExceptT $ do let txCtx = Pact5.TxContext ph noMiner diff --git a/src/Chainweb/Pact/PactService/Pact4/ExecBlock.hs b/src/Chainweb/Pact/PactService/Pact4/ExecBlock.hs index a0fb97a8c1..8835d6b3e9 100644 --- a/src/Chainweb/Pact/PactService/Pact4/ExecBlock.hs +++ b/src/Chainweb/Pact/PactService/Pact4/ExecBlock.hs @@ -328,7 +328,7 @@ checkTxSigs -> f () checkTxSigs logger v cid bh t = do liftIO $ logFunctionText logger Debug $ "Pact4.checkTxSigs: " <> sshow (Pact4._cmdHash t) - case Pact4.assertValidateSigs validSchemes webAuthnPrefixLegal hsh signers sigs of + case Pact4.assertValidateSigs isValidScheme webAuthnPrefixLegal hsh signers sigs of Right _ -> do pure () Left err -> do @@ -337,7 +337,7 @@ checkTxSigs logger v cid bh t = do hsh = Pact4._cmdHash t sigs = Pact4._cmdSigs t signers = Pact4._pSigners $ Pact4.payloadObj $ Pact4._cmdPayload t - validSchemes = validPPKSchemes v cid bh + isValidScheme = isValidPPKScheme v cid pact4ForkNumber bh . SchemeV4 webAuthnPrefixLegal = isWebAuthnPrefixLegal v cid bh checkCompile diff --git a/src/Chainweb/Pact/PactService/Pact5/ExecBlock.hs b/src/Chainweb/Pact/PactService/Pact5/ExecBlock.hs index 567da7aa93..8611561b8d 100644 --- a/src/Chainweb/Pact/PactService/Pact5/ExecBlock.hs +++ b/src/Chainweb/Pact/PactService/Pact5/ExecBlock.hs @@ -36,6 +36,7 @@ import Chainweb.Pact5.Transaction import Chainweb.Pact5.TransactionExec import Chainweb.Pact5.Types import Chainweb.Payload +import Chainweb.ForkState import Chainweb.Payload.PayloadStore import Chainweb.Time import Chainweb.Utils @@ -328,11 +329,11 @@ continueBlock mpAccess blockInProgress = do cid <- view chainId logger <- view (psServiceEnv . psLogger) dbEnv <- view psBlockDbEnv - let (pHash, pHeight, parentTime) = blockInProgressParent blockInProgress + let (pHash, pForkNumber, pHeight, parentTime) = blockInProgressParent blockInProgress isGenesis <- view psIsGenesis let validate bhi _bha txs = do forM txs $ - runExceptT . validateRawChainwebTx logger v cid dbEnv (_blockInProgressHandle blockInProgress) (ParentCreationTime parentTime) bhi isGenesis + runExceptT . validateRawChainwebTx logger v cid dbEnv (_blockInProgressHandle blockInProgress) (ParentCreationTime parentTime) pForkNumber bhi isGenesis liftIO $ mpaGetBlock mpAccess blockFillState validate (succ pHeight) pHash @@ -478,13 +479,15 @@ validateParsedChainwebTx -> BlockHandle Pact5 -> ParentCreationTime -- ^ reference time for tx validation. + -> ForkNumber + -- ^ Parent Fork number -> BlockHeight -- ^ Current block height -> Bool -- ^ Genesis? -> Pact5.Transaction -> ExceptT InsertError IO () -validateParsedChainwebTx _logger v cid db _blockHandle txValidationTime bh isGenesis tx +validateParsedChainwebTx _logger v cid db _blockHandle txValidationTime fn bh isGenesis tx | isGenesis = pure () | otherwise = do checkUnique tx @@ -532,12 +535,13 @@ validateParsedChainwebTx _logger v cid db _blockHandle txValidationTime bh isGen checkTxSigs :: Pact5.Transaction -> ExceptT InsertError IO () checkTxSigs t = do - case Pact5.assertValidateSigs hsh signers sigs of + case Pact5.assertValidateSigs isValidScheme hsh signers sigs of Right _ -> do pure () Left err -> do throwError $ InsertErrorInvalidSigs (displayAssertValidateSigsError err) where + isValidScheme = isValidPPKScheme v cid fn bh . SchemeV5 hsh = Pact5._cmdHash t sigs = Pact5._cmdSigs t signers = Pact5._pSigners $ view Pact5.payloadObj $ Pact5._cmdPayload t @@ -559,17 +563,19 @@ validateRawChainwebTx -> BlockHandle Pact5 -> ParentCreationTime -- ^ reference time for tx validation. + -> ForkNumber + -- ^ Parent Fork number -> BlockHeight -- ^ Current block height -> Bool -- ^ Genesis? -> Pact4.UnparsedTransaction -> ExceptT InsertError IO Pact5.Transaction -validateRawChainwebTx logger v cid db blockHandle parentTime bh isGenesis tx = do +validateRawChainwebTx logger v cid db blockHandle parentTime fn bh isGenesis tx = do tx' <- either (throwError . InsertErrorPactParseError . either id Pact5.renderText) return $ Pact5.parsePact4Command tx liftIO $ do logDebug_ logger $ "validateRawChainwebTx: parse succeeded" - validateParsedChainwebTx logger v cid db blockHandle parentTime bh isGenesis tx' + validateParsedChainwebTx logger v cid db blockHandle parentTime fn bh isGenesis tx' return $! tx' execExistingBlock @@ -595,7 +601,8 @@ execExistingBlock currHeader payload = do errors <- liftIO $ flip foldMap txs $ \tx -> do errorOrSuccess <- runExceptT $ validateParsedChainwebTx logger v cid db blockHandlePreCoinbase txValidationTime - (view blockHeight currHeader) + (parentBlockHeader ^. blockForkNumber) + (currHeader ^. blockHeight) isGenesis tx case errorOrSuccess of diff --git a/src/Chainweb/Pact/RestAPI/Server.hs b/src/Chainweb/Pact/RestAPI/Server.hs index 80f7ea94ea..be13550df2 100644 --- a/src/Chainweb/Pact/RestAPI/Server.hs +++ b/src/Chainweb/Pact/RestAPI/Server.hs @@ -119,8 +119,9 @@ import qualified Chainweb.TreeDB as TreeDB import Chainweb.Utils import Chainweb.Version import qualified Chainweb.Pact4.Validations as Pact4 -import Chainweb.Version.Guards (isWebAuthnPrefixLegal, validPPKSchemes) +import Chainweb.Version.Guards (isWebAuthnPrefixLegal, isValidPPKScheme) import Chainweb.WebPactExecutionService +import Chainweb.ForkState (pact4ForkNumber) import qualified Pact.JSON.Encode as J import qualified Pact.Parse as Pact4 @@ -718,12 +719,18 @@ barf e = maybe (throwError e) return validateCommand :: ChainwebVersion -> ChainId -> Pact4.Command Text -> Either Text Pact4.Transaction validateCommand v cid (fmap encodeUtf8 -> cmdBs) = case parsedCmd of Right (commandParsed :: Pact4.Transaction) -> - case Pact4.assertCommand commandParsed (validPPKSchemes v cid bh) (isWebAuthnPrefixLegal v cid bh) of + case Pact4.assertCommand commandParsed isValidScheme (isWebAuthnPrefixLegal v cid bh) of Left err -> Left $ "Command failed validation: " <> Pact4.displayAssertCommandError err Right () -> Right commandParsed Left e -> Left $ "Pact parsing error: " <> T.pack e where - bh = maxBound :: BlockHeight + isValidScheme = isValidPPKScheme v cid pact4ForkNumber bh . SchemeV4 + -- It's a Pact4 function, make sure it always execute in the context of Pact4 + bh = case afterFork v Pact5Fork of + ForkAtBlockHeight x -> x - 1 + ForkNever -> maxBound + _ -> error "Incompatible Pact 4 version" + decodeAndParse bs = traverse (Pact4.parsePact) =<< Aeson.eitherDecodeStrict' bs parsedCmd = Pact4.mkPayloadWithText <$> @@ -731,14 +738,15 @@ validateCommand v cid (fmap encodeUtf8 -> cmdBs) = case parsedCmd of -- TODO: all of the functions in this module can instead grab the current block height from consensus -- and pass it here to get a better estimate of what behavior is correct. -validatePact5Command :: ChainwebVersion -> Pact5.Command Text -> Either String Pact5.Transaction -validatePact5Command _v cmdText = case parsedCmd of +validatePact5Command :: ChainwebVersion -> ChainId -> Pact5.Command Text -> Either String Pact5.Transaction +validatePact5Command _v cid cmdText = case parsedCmd of Right (commandParsed :: Pact5.Transaction) -> - if isRight (Pact5.assertCommand commandParsed) + if isRight (Pact5.assertCommand commandParsed isValidScheme) then Right commandParsed else Left "Command failed validation" Left e -> Left $ "Pact parsing error: " ++ Pact5.renderCompactString e where + isValidScheme = isValidPPKScheme _v cid maxBound maxBound . SchemeV5 parsedCmd = Pact5.parseCommand cmdText -- | Validate the length of the request key's underlying hash. diff --git a/src/Chainweb/Pact/Types.hs b/src/Chainweb/Pact/Types.hs index 8dcb5f3d4f..27988e3af0 100644 --- a/src/Chainweb/Pact/Types.hs +++ b/src/Chainweb/Pact/Types.hs @@ -227,6 +227,7 @@ import Chainweb.BlockHeader import Chainweb.BlockHeight import Chainweb.BlockHeaderDB import Chainweb.ChainId +import Chainweb.ForkState import Chainweb.Counter import Chainweb.Mempool.Mempool (TransactionHash, BlockFill, MempoolPreBlockCheck, InsertError) import Chainweb.Miner.Pact @@ -264,6 +265,7 @@ import Chainweb.Payload import Data.ByteString.Short (ShortByteString) import qualified Data.ByteString.Short as SB import qualified Data.Vector as V +--import qualified Pact.Core.Hash as Pact5 import Data.Maybe import Chainweb.BlockCreationTime import qualified Data.Aeson as Aeson @@ -1188,11 +1190,11 @@ instance HasChainId (BlockInProgress pv) where _chainId = _blockInProgressChainId {-# INLINE _chainId #-} -blockInProgressParent :: BlockInProgress pv -> (BlockHash, BlockHeight, BlockCreationTime) +blockInProgressParent :: BlockInProgress pv -> (BlockHash, ForkNumber, BlockHeight, BlockCreationTime) blockInProgressParent bip = maybe - (genesisParentBlockHash v cid, genesisHeight v cid, v ^?! versionGenesis . genesisTime . atChain cid) - (\bh -> (view blockHash bh, view blockHeight bh, view blockCreationTime bh)) + (genesisParentBlockHash v cid, genesisForkState ^. forkNumber, genesisHeight v cid, v ^?! versionGenesis . genesisTime . atChain cid) + (\bh -> (view blockHash bh, view blockForkNumber bh, view blockHeight bh, view blockCreationTime bh)) (_parentHeader <$> _blockInProgressParentHeader bip) where v = _blockInProgressChainwebVersion bip diff --git a/src/Chainweb/Pact4/Validations.hs b/src/Chainweb/Pact4/Validations.hs index d279ec439e..87a681b296 100644 --- a/src/Chainweb/Pact4/Validations.hs +++ b/src/Chainweb/Pact4/Validations.hs @@ -63,8 +63,8 @@ import Chainweb.Pact.Utils (fromPactChainId) import Chainweb.Time (Seconds(..), Time(..), secondsToTimeSpan, scaleTimeSpan, second, add) import Chainweb.Pact4.Transaction import Chainweb.Version -import Chainweb.Version.Guards (isWebAuthnPrefixLegal, validPPKSchemes) - +import Chainweb.Version.Guards (isWebAuthnPrefixLegal, isValidPPKScheme) +import Chainweb.ForkState (pact4ForkNumber) import qualified Pact.Types.Gas as P import qualified Pact.Types.Hash as P import qualified Pact.Types.ChainId as P @@ -89,7 +89,7 @@ assertPreflightMetadata cmd@(P.Command pay sigs hsh) txCtx sigVerify = do bgl <- view psBlockGasLimit let bh = ctxCurrentBlockHeight txCtx - let validSchemes = validPPKSchemes v cid bh + let isValidScheme = isValidPPKScheme v cid pact4ForkNumber bh . SchemeV4 let webAuthnPrefixLegal = isWebAuthnPrefixLegal v cid bh let P.PublicMeta pcid _ gl gp _ _ = P._pMeta pay @@ -104,7 +104,7 @@ assertPreflightMetadata cmd@(P.Command pay sigs hsh) txCtx sigVerify = do , eUnless "Gas price decimal precision too high" $ assertGasPrice gp , eUnless "Network id mismatch" $ assertNetworkId v nid , eUnless "Signature list size too big" $ assertSigSize sigs - , eUnless "Invalid transaction signatures" $ sigValidate validSchemes webAuthnPrefixLegal signers + , eUnless "Invalid transaction signatures" $ sigValidate isValidScheme webAuthnPrefixLegal signers , eUnless "Tx time outside of valid range" $ assertTxTimeRelativeToParent pct cmd ] @@ -112,9 +112,9 @@ assertPreflightMetadata cmd@(P.Command pay sigs hsh) txCtx sigVerify = do Nothing -> Right () Just vs -> Left vs where - sigValidate validSchemes webAuthnPrefixLegal signers + sigValidate isValidScheme webAuthnPrefixLegal signers | Just NoVerify <- sigVerify = True - | otherwise = isRight $ assertValidateSigs validSchemes webAuthnPrefixLegal hsh signers sigs + | otherwise = isRight $ assertValidateSigs isValidScheme webAuthnPrefixLegal hsh signers sigs pct = ParentCreationTime . view blockCreationTime @@ -174,13 +174,13 @@ assertTxSize initialGas gasLimit = initialGas < fromIntegral gasLimit -- transaction hash. -- assertValidateSigs :: () - => [P.PPKScheme] + => (P.PPKScheme -> Bool) -> IsWebAuthnPrefixLegal -> P.PactHash -> [P.Signer] -> [P.UserSig] -> Either AssertValidateSigsError () -assertValidateSigs validSchemes webAuthnPrefixLegal hsh signers sigs = do +assertValidateSigs isValidScheme webAuthnPrefixLegal hsh signers sigs = do let signersLength = length signers let sigsLength = length sigs ebool_ @@ -193,7 +193,7 @@ assertValidateSigs validSchemes webAuthnPrefixLegal hsh signers sigs = do iforM_ (zip sigs signers) $ \pos (sig, signer) -> do ebool_ (InvalidSignerScheme pos) - (fromMaybe P.ED25519 (P._siScheme signer) `elem` validSchemes) + (isValidScheme $ fromMaybe P.ED25519 $ P._siScheme signer) ebool_ (InvalidSignerWebAuthnPrefix pos) (webAuthnPrefixLegal == WebAuthnPrefixLegal || not (P.webAuthnPrefix `Text.isPrefixOf` P._siPubKey signer)) @@ -239,10 +239,10 @@ assertTxNotInFuture (ParentCreationTime (BlockCreationTime txValidationTime)) tx -- | Assert that the command hash matches its payload and -- its signatures are valid, without parsing the payload. -assertCommand :: P.Command (PayloadWithText m c) -> [P.PPKScheme] -> IsWebAuthnPrefixLegal -> Either AssertCommandError () -assertCommand (P.Command pwt sigs hsh) ppkSchemePassList webAuthnPrefixLegal = do +assertCommand :: P.Command (PayloadWithText m c) -> (P.PPKScheme -> Bool) -> IsWebAuthnPrefixLegal -> Either AssertCommandError () +assertCommand (P.Command pwt sigs hsh) isValidScheme webAuthnPrefixLegal = do if isRight assertHash - then first AssertValidateSigsError $ assertValidateSigs ppkSchemePassList webAuthnPrefixLegal hsh signers sigs + then first AssertValidateSigsError $ assertValidateSigs isValidScheme webAuthnPrefixLegal hsh signers sigs else Left InvalidPayloadHash where cmdBS = SBS.fromShort $ payloadBytes pwt diff --git a/src/Chainweb/Pact5/Validations.hs b/src/Chainweb/Pact5/Validations.hs index bdafd74796..279f7444d9 100644 --- a/src/Chainweb/Pact5/Validations.hs +++ b/src/Chainweb/Pact5/Validations.hs @@ -34,6 +34,7 @@ module Chainweb.Pact5.Validations ) where import Control.Lens +import Control.Monad import Data.Decimal (decimalPlaces) import Data.Maybe @@ -62,6 +63,7 @@ import qualified Pact.Parse as Pact4 import Chainweb.Pact5.Types import qualified Chainweb.Pact5.Transaction as Pact5 import Chainweb.Utils (ebool_) +import Chainweb.Version.Guards (isValidPPKScheme) -- | Check whether a local Api request has valid metadata @@ -79,6 +81,7 @@ assertPreflightMetadata cmd@(P.Command pay sigs hsh) txCtx sigVerify = do let P.PublicMeta pcid _ gl gp _ _ = P._pMeta pay nid = P._pNetworkId pay signers = P._pSigners pay + isValidScheme = isValidPPKScheme v cid (ctxParentForkNumber txCtx) (ctxCurrentBlockHeight txCtx) . SchemeV5 let errs = catMaybes [ eUnless "Chain id mismatch" $ assertChainId cid pcid @@ -88,7 +91,7 @@ assertPreflightMetadata cmd@(P.Command pay sigs hsh) txCtx sigVerify = do , eUnless "Gas price decimal precision too high" $ assertGasPrice gp , eUnless "Network id mismatch" $ assertNetworkId v nid , eUnless "Signature list size too big" $ assertSigSize sigs - , eUnless "Invalid transaction signatures" $ sigValidate signers + , eUnless "Invalid transaction signatures" $ sigValidate isValidScheme signers , eUnless "Tx time outside of valid range" $ assertTxTimeRelativeToParent pct cmd ] @@ -96,9 +99,9 @@ assertPreflightMetadata cmd@(P.Command pay sigs hsh) txCtx sigVerify = do Nothing -> Right () Just vs -> Left vs where - sigValidate signers + sigValidate isValidScheme signers | Just NoVerify <- sigVerify = True - | otherwise = isRight $ assertValidateSigs hsh signers sigs + | otherwise = isRight $ assertValidateSigs isValidScheme hsh signers sigs pct = ParentCreationTime . view blockCreationTime @@ -153,11 +156,12 @@ assertTxSize initialGas gasLimit = P.GasLimit initialGas < gasLimit -- transaction hash. -- assertValidateSigs :: () - => P.Hash + => (P.PPKScheme -> Bool) + -> P.Hash -> [P.Signer] -> [P.UserSig] -> Either AssertValidateSigsError () -assertValidateSigs hsh signers sigs = do +assertValidateSigs isValidScheme hsh signers sigs = do let signersLength = length signers let sigsLength = length sigs ebool_ @@ -168,6 +172,10 @@ assertValidateSigs hsh signers sigs = do (signersLength == sigsLength) iforM_ (zip sigs signers) $ \pos (sig, signer) -> do + ebool_ + (InvalidSignerScheme pos) + (isValidScheme $ fromMaybe P.ED25519 $ P._siScheme signer) + case P.verifyUserSig hsh sig signer of Left errMsg -> Left (InvalidUserSig pos (Text.pack errMsg)) Right () -> Right () @@ -209,10 +217,10 @@ assertTxNotInFuture (ParentCreationTime (BlockCreationTime txValidationTime)) tx -- | Assert that the command hash matches its payload and -- its signatures are valid, without parsing the payload. -assertCommand :: Pact5.Transaction -> Either AssertCommandError () -assertCommand cmd = do - _ <- assertHash & _Left .~ InvalidPayloadHash - assertValidateSigs hsh signers (P._cmdSigs cmd) & _Left %~ AssertValidateSigsError +assertCommand :: Pact5.Transaction -> (P.PPKScheme -> Bool) -> Either AssertCommandError () +assertCommand cmd isValidScheme = do + void $ assertHash & _Left .~ InvalidPayloadHash + assertValidateSigs isValidScheme hsh signers (P._cmdSigs cmd) & _Left %~ AssertValidateSigsError where hsh = P._cmdHash cmd pwt = P._cmdPayload cmd diff --git a/src/Chainweb/Version.hs b/src/Chainweb/Version.hs index 7860efa058..13c12d9620 100644 --- a/src/Chainweb/Version.hs +++ b/src/Chainweb/Version.hs @@ -35,7 +35,8 @@ module Chainweb.Version ( -- * Properties of Chainweb Version - Fork(..) + GenericPPKScheme(..) + , Fork(..) , ForkHeight(..) , succByHeight , _ForkAtBlockHeight @@ -78,6 +79,7 @@ module Chainweb.Version , versionVerifierPluginNames , versionQuirks , versionForkNumber + , versionAllowedSignatureSchemes , versionForkVoteCastingLength , genesisBlockPayload , genesisBlockPayloadHash @@ -149,7 +151,7 @@ module Chainweb.Version , latestBehaviorAt , onAllChains , domainAddr2PeerInfo - + , afterFork -- * Internal. Don't use. Exported only for testing -- , headerSizes -- , headerBaseSizeBytes @@ -185,6 +187,9 @@ import Chainweb.MerkleUniverse import Chainweb.Payload import Chainweb.Pact4.Transaction qualified as Pact4 import Chainweb.Pact5.Transaction qualified as Pact5 +import qualified Pact.Types.Scheme as Pact4 (PPKScheme(..)) +import qualified Pact.Core.Scheme as Pact5 (PPKScheme(..)) + import Chainweb.Pact5.InitialGasModel import Chainweb.ForkState import Chainweb.Utils @@ -498,6 +503,12 @@ noQuirks = VersionQuirks { _quirkGasFees = AllChains HM.empty } +-- -------------------------------------------------------------------------- -- +-- Schemes +data GenericPPKScheme = SchemeV4 Pact4.PPKScheme | SchemeV5 Pact5.PPKScheme + deriving stock (Eq, Ord, Generic) + deriving anyclass (NFData) + -- | Chainweb versions are sets of properties that must remain consistent among -- all nodes on the same network. For examples see `Chainweb.Version.Mainnet`, -- `Chainweb.Version.Testnet`, `Chainweb.Version.RecapDevelopment`, and @@ -546,6 +557,8 @@ data ChainwebVersion -- retain in its history at all times. , _versionInitialGasModel :: ChainMap (Rule ForkHeight (InitialGasModel)) -- ^ The initial gas model used for Pact 5 transactions processing + , _versionAllowedSignatureSchemes :: ChainMap (Rule ForkHeight (Set GenericPPKScheme)) + -- ^ Allowed schemes for this version , _versionBootstraps :: [PeerInfo] -- ^ The locations of the bootstrap peers. , _versionGenesis :: VersionGenesis @@ -861,3 +874,8 @@ onAllChains v f = OnChains <$> HM.traverseWithKey (\cid () -> f cid) (HS.toMap (chainIds v)) + +-- | Util function to get the ForkHeight of a given Fork. +-- Assuming that all chains are synchronized with chain 0 +afterFork:: ChainwebVersion -> Fork -> ForkHeight +afterFork v fork = v ^?! versionForks . at fork . _Just . atChain (unsafeChainId 0) \ No newline at end of file diff --git a/src/Chainweb/Version/Development.hs b/src/Chainweb/Version/Development.hs index 401b50d623..c8437f3a06 100644 --- a/src/Chainweb/Version/Development.hs +++ b/src/Chainweb/Version/Development.hs @@ -64,8 +64,10 @@ devnet = ChainwebVersion { _disablePeerValidation = True , _disableMempoolSync = False } - , _versionVerifierPluginNames = AllChains $ Bottom - (minBound, Set.fromList $ map VerifierName ["hyperlane_v3_message", "allow", "signed_list"]) + , _versionAllowedSignatureSchemes = AllChains $ + Bottom (minBound, Set.fromList $ SchemeV5 <$> [minBound .. maxBound]) + , _versionVerifierPluginNames = AllChains $ + Bottom (minBound, Set.fromList $ map VerifierName ["hyperlane_v3_message", "allow", "signed_list"]) , _versionQuirks = noQuirks , _versionForkNumber = 1 , _versionForkVoteCastingLength = 120 * 119 -- 5 days diff --git a/src/Chainweb/Version/Guards.hs b/src/Chainweb/Version/Guards.hs index cd1ef7af8a..296dd19c3c 100644 --- a/src/Chainweb/Version/Guards.hs +++ b/src/Chainweb/Version/Guards.hs @@ -59,7 +59,7 @@ module Chainweb.Version.Guards , maxBlockGasLimit , minimumBlockHeaderHistory , activeInitialGasModel - , validPPKSchemes + , isValidPPKScheme , isWebAuthnPrefixLegal , validKeyFormats , pact5Serialiser @@ -80,12 +80,12 @@ import Chainweb.Version import Chainweb.Pact5.InitialGasModel import Control.Lens import Data.Word (Word64) +import qualified Data.Set as S import Numeric.Natural import Pact.Core.Builtin qualified as Pact5 import Pact.Core.Info qualified as Pact5 import Pact.Core.Serialise qualified as Pact5 import Pact.Types.KeySet (PublicKeyText, ed25519HexFormat, webAuthnFormat) -import Pact.Types.Scheme (PPKScheme(ED25519, WebAuthn)) -- Gets the height which the fork is associated with. -- This may not be the first height at which the associated guard is `True` @@ -361,13 +361,14 @@ activeInitialGasModel v cid fn bh = snd $ ruleZipperHere $ snd where searchKey = ForkAtBlockHeight bh `max` ForkAtForkNumber fn --- | Different versions of Chainweb allow different PPKSchemes. +-- | Different versions of Chainweb allow different PPKSchemes depending on rules -- -validPPKSchemes :: ChainwebVersion -> ChainId -> BlockHeight -> [PPKScheme] -validPPKSchemes v cid bh = - if chainweb221Pact v cid bh - then [ED25519, WebAuthn] - else [ED25519] +isValidPPKScheme :: ChainwebVersion -> ChainId -> ForkNumber -> BlockHeight -> GenericPPKScheme -> Bool +isValidPPKScheme v cid fn bh = flip S.member schemesSet + where + schemesSet = snd $ ruleZipperHere $ snd $ ruleSeek (\h _ -> searchKey >= h) + $ v ^?! versionAllowedSignatureSchemes . atChain cid + searchKey = ForkAtBlockHeight bh `max` ForkAtForkNumber fn isWebAuthnPrefixLegal :: ChainwebVersion -> ChainId -> BlockHeight -> Pact4.IsWebAuthnPrefixLegal isWebAuthnPrefixLegal v cid bh = diff --git a/src/Chainweb/Version/Mainnet.hs b/src/Chainweb/Version/Mainnet.hs index acdcdbc7d6..b266440ff0 100644 --- a/src/Chainweb/Version/Mainnet.hs +++ b/src/Chainweb/Version/Mainnet.hs @@ -7,7 +7,6 @@ module Chainweb.Version.Mainnet(mainnet, pattern Mainnet01) where -import Control.Lens import qualified Data.HashMap.Strict as HM import qualified Data.Set as Set @@ -25,6 +24,8 @@ import P2P.BootstrapNodes import Pact.Types.Runtime (Gas(..)) import Pact.Types.Verifier +import qualified Pact.Types.Scheme as Pact4 (PPKScheme(..)) +import qualified Pact.Core.Scheme as Pact5 (PPKScheme(..)) import qualified Chainweb.BlockHeader.Genesis.Mainnet0Payload as MN0 import qualified Chainweb.BlockHeader.Genesis.Mainnet1Payload as MN1 @@ -160,23 +161,40 @@ mainnet = ChainwebVersion Chainweb32-> AllChains (ForkAtForkNumber 1) , _versionGraphs = - (to20ChainsMainnet, twentyChainGraph) `Above` + (to20ChainsMainnet, twentyChainGraph) + `Above` Bottom (minBound, petersenChainGraph) , _versionBlockDelay = BlockDelay 30_000_000 , _versionWindow = WindowWidth 120 , _versionHeaderBaseSizeBytes = 318 - 110 + , _versionAllowedSignatureSchemes = AllChains $ + (afterFork mainnet Pact5Fork, Set.fromList $ SchemeV5 <$> [Pact5.ED25519, Pact5.WebAuthn]) + `Above` + (afterFork mainnet Chainweb221Pact, Set.fromList $ SchemeV4 <$> [Pact4.ED25519, Pact4.WebAuthn]) + `Above` + Bottom (minBound, Set.singleton $ SchemeV4 Pact4.ED25519) + , _versionMaxBlockGasLimit = - (succByHeight $ mainnet ^?! versionForks . at Chainweb216Pact . _Just . atChain (unsafeChainId 0), Just 180_000) `Above` + (succByHeight $ afterFork mainnet Chainweb216Pact, Just 180_000) + `Above` Bottom (minBound, Nothing) + , _versionInitialGasModel = AllChains $ - (mainnet ^?! versionForks . at Chainweb32 . _Just . atChain (unsafeChainId 0), post32GasModel) `Above` - (succByHeight $ mainnet ^?! versionForks . at Chainweb31 . _Just . atChain (unsafeChainId 0), post31GasModel) `Above` + (afterFork mainnet Chainweb32, post32GasModel) + `Above` + (succByHeight $ afterFork mainnet Chainweb31, post31GasModel) + `Above` Bottom (minBound, pre31GasModel) + , _versionSpvProofRootValidWindow = - (mainnet ^?! versionForks . at Chainweb32 . _Just . atChain (unsafeChainId 0), Just 525_600) `Above` - (succByHeight $ mainnet ^?! versionForks . at Chainweb31 . _Just . atChain (unsafeChainId 0), Nothing) `Above` - (succByHeight $ mainnet ^?! versionForks . at Chainweb231Pact . _Just . atChain (unsafeChainId 0) , Just 20_000) `Above` + (afterFork mainnet Chainweb32, Just 525_600) + `Above` + (succByHeight $ afterFork mainnet Chainweb31, Nothing) + `Above` + (succByHeight $ afterFork mainnet Chainweb231Pact, Just 20_000) + `Above` Bottom (minBound, Nothing) + , _versionBootstraps = domainAddr2PeerInfo mainnetBootstrapHosts , _versionGenesis = VersionGenesis { _genesisBlockTarget = OnChains $ HM.fromList $ concat diff --git a/src/Chainweb/Version/RecapDevelopment.hs b/src/Chainweb/Version/RecapDevelopment.hs index 42e059526d..38904a8b6a 100644 --- a/src/Chainweb/Version/RecapDevelopment.hs +++ b/src/Chainweb/Version/RecapDevelopment.hs @@ -9,7 +9,6 @@ module Chainweb.Version.RecapDevelopment(recapDevnet, pattern RecapDevelopment) import qualified Data.HashMap.Strict as HM import qualified Data.Set as Set -import Control.Lens import Chainweb.BlockCreationTime import Chainweb.BlockHeight @@ -23,6 +22,8 @@ import Chainweb.Utils.Rule import Chainweb.Version import Pact.Types.Verifier +import qualified Pact.Types.Scheme as Pact4 (PPKScheme(..)) +import qualified Pact.Core.Scheme as Pact5 (PPKScheme(..)) import qualified Chainweb.BlockHeader.Genesis.RecapDevelopment0Payload as RDN0 import qualified Chainweb.BlockHeader.Genesis.RecapDevelopment1to9Payload as RDNN @@ -95,7 +96,8 @@ recapDevnet = ChainwebVersion ] , _versionGraphs = - (to20ChainsHeight, twentyChainGraph) `Above` + (to20ChainsHeight, twentyChainGraph) + `Above` Bottom (minBound, petersenChainGraph) , _versionBlockDelay = BlockDelay 30_000_000 @@ -117,10 +119,19 @@ recapDevnet = ChainwebVersion , _versionMaxBlockGasLimit = Bottom (minBound, Just 180_000) , _versionInitialGasModel = AllChains $ - (ForkNever, post32GasModel) `Above` - (succByHeight $ recapDevnet ^?! versionForks . at Chainweb231Pact . _Just . atChain (unsafeChainId 0), post31GasModel) `Above` + (ForkNever, post32GasModel) + `Above` + (succByHeight $ afterFork recapDevnet Chainweb231Pact, post31GasModel) + `Above` Bottom (minBound, pre31GasModel) + , _versionAllowedSignatureSchemes = AllChains $ + (afterFork recapDevnet Pact5Fork, Set.fromList $ SchemeV5 <$> [Pact5.ED25519, Pact5.WebAuthn]) + `Above` + (afterFork recapDevnet Chainweb221Pact, Set.fromList $ SchemeV4 <$> [Pact4.ED25519, Pact4.WebAuthn]) + `Above` + Bottom (minBound, Set.singleton $ SchemeV4 Pact4.ED25519) + , _versionSpvProofRootValidWindow = Bottom (minBound, Nothing) , _versionCheats = VersionCheats { _disablePow = False @@ -132,7 +143,8 @@ recapDevnet = ChainwebVersion , _disableMempoolSync = False } , _versionVerifierPluginNames = AllChains $ - (ForkAtBlockHeight $ BlockHeight 600, Set.fromList $ map VerifierName ["hyperlane_v3_message", "allow", "signed_list"]) `Above` + (ForkAtBlockHeight $ BlockHeight 600, Set.fromList $ map VerifierName ["hyperlane_v3_message", "allow", "signed_list"]) + `Above` Bottom (minBound, mempty) , _versionQuirks = noQuirks , _versionForkNumber = 0 diff --git a/src/Chainweb/Version/Testnet04.hs b/src/Chainweb/Version/Testnet04.hs index 514b80c57c..31040b4a55 100644 --- a/src/Chainweb/Version/Testnet04.hs +++ b/src/Chainweb/Version/Testnet04.hs @@ -9,7 +9,6 @@ module Chainweb.Version.Testnet04(testnet04, pattern Testnet04) where -import Control.Lens import qualified Data.HashMap.Strict as HM import qualified Data.Set as Set @@ -27,6 +26,8 @@ import P2P.BootstrapNodes import Pact.Types.Runtime (Gas(..)) import Pact.Types.Verifier +import qualified Pact.Types.Scheme as Pact4 (PPKScheme(..)) +import qualified Pact.Core.Scheme as Pact5 (PPKScheme(..)) import qualified Chainweb.Pact.Transactions.CoinV3Transactions as CoinV3 import qualified Chainweb.Pact.Transactions.CoinV4Transactions as CoinV4 @@ -140,22 +141,26 @@ testnet04 = ChainwebVersion Chainweb31 -> AllChains ForkNever Chainweb32 -> AllChains ForkNever MigratePlatformShare -> AllChains ForkNever - , _versionGraphs = - (to20ChainsTestnet, twentyChainGraph) `Above` + (to20ChainsTestnet, twentyChainGraph) + `Above` Bottom (minBound, petersenChainGraph) , _versionBlockDelay = BlockDelay 30_000_000 , _versionWindow = WindowWidth 120 , _versionHeaderBaseSizeBytes = 318 - 110 , _versionMaxBlockGasLimit = - (succByHeight $ testnet04 ^?! versionForks . at Chainweb216Pact . _Just . atChain (unsafeChainId 0) , Just 180_000) `Above` + (succByHeight $ afterFork testnet04 Chainweb216Pact, Just 180_000) + `Above` Bottom (minBound, Nothing) , _versionInitialGasModel = AllChains $ - (ForkNever, post32GasModel) `Above` - (succByHeight $ testnet04 ^?! versionForks . at Chainweb231Pact . _Just . atChain (unsafeChainId 0), post31GasModel) `Above` + (ForkNever, post32GasModel) + `Above` + (succByHeight $ afterFork testnet04 Chainweb231Pact, post31GasModel) + `Above` Bottom (minBound, pre31GasModel) , _versionSpvProofRootValidWindow = - (succByHeight $ testnet04 ^?! versionForks . at Chainweb231Pact . _Just . atChain (unsafeChainId 0) , Just 20_000) `Above` + (succByHeight $ afterFork testnet04 Chainweb231Pact, Just 20_000) + `Above` Bottom (minBound, Nothing) , _versionBootstraps = domainAddr2PeerInfo testnet04BootstrapHosts , _versionGenesis = VersionGenesis @@ -198,8 +203,18 @@ testnet04 = ChainwebVersion { _disablePeerValidation = False , _disableMempoolSync = False } - , _versionVerifierPluginNames = AllChains $ (ForkAtBlockHeight $ BlockHeight $ 4_100_681, Set.fromList [VerifierName "hyperlane_v3_message"]) `Above` + , _versionVerifierPluginNames = AllChains $ + (ForkAtBlockHeight $ BlockHeight $ 4_100_681, Set.fromList [VerifierName "hyperlane_v3_message"]) + `Above` Bottom (minBound, mempty) + + , _versionAllowedSignatureSchemes = AllChains $ + (afterFork testnet04 Pact5Fork, Set.fromList $ SchemeV5 <$> [Pact5.ED25519, Pact5.WebAuthn]) + `Above` + (afterFork testnet04 Chainweb221Pact, Set.fromList $ SchemeV4 <$> [Pact4.ED25519, Pact4.WebAuthn]) + `Above` + Bottom (minBound, Set.singleton $ SchemeV4 Pact4.ED25519) + , _versionQuirks = VersionQuirks { _quirkGasFees = onChains [ (unsafeChainId 1, HM.fromList [((BlockHeight 4104500, TxBlockIdx 0), Gas 66_239)]) diff --git a/src/Chainweb/Version/Testnet06.hs b/src/Chainweb/Version/Testnet06.hs index c5c42d2b00..e3d4e4bd59 100644 --- a/src/Chainweb/Version/Testnet06.hs +++ b/src/Chainweb/Version/Testnet06.hs @@ -8,7 +8,6 @@ module Chainweb.Version.Testnet06(testnet06, pattern Testnet06) where import qualified Data.HashMap.Strict as HM -import Control.Lens import qualified Data.Set as Set import Chainweb.BlockCreationTime @@ -108,7 +107,8 @@ testnet06 = ChainwebVersion ] } , _versionInitialGasModel = AllChains $ - (testnet06 ^?! versionForks . at Chainweb32 . _Just . atChain (unsafeChainId 0), post32GasModel) `Above` + (afterFork testnet06 Chainweb32, post32GasModel) + `Above` Bottom (minBound, post31GasModel) , _versionMaxBlockGasLimit = Bottom (minBound, Just 180_000) , _versionSpvProofRootValidWindow = Bottom (minBound, Nothing) @@ -122,8 +122,11 @@ testnet06 = ChainwebVersion , _disableMempoolSync = False } , _versionVerifierPluginNames = AllChains $ - (ForkAtBlockHeight $ BlockHeight 600, Set.fromList $ map VerifierName ["hyperlane_v3_message"]) `Above` + (ForkAtBlockHeight $ BlockHeight 600, Set.fromList $ map VerifierName ["hyperlane_v3_message"]) + `Above` Bottom (minBound, mempty) + , _versionAllowedSignatureSchemes = + AllChains $ Bottom (minBound, Set.empty) , _versionQuirks = noQuirks , _versionForkNumber = 1 , _versionForkVoteCastingLength = 120 * 119 -- 5 days diff --git a/src/Chainweb/WebPactExecutionService.hs b/src/Chainweb/WebPactExecutionService.hs index 0e689c1775..ffc8f04f82 100644 --- a/src/Chainweb/WebPactExecutionService.hs +++ b/src/Chainweb/WebPactExecutionService.hs @@ -68,7 +68,9 @@ newBlockToPayloadWithOutputs (NewBlockPayload _ pwo) = pwo newBlockParent :: NewBlock -> (BlockHash, BlockHeight, BlockCreationTime) -newBlockParent (NewBlockInProgress (ForSomePactVersion _ bip)) = blockInProgressParent bip +newBlockParent (NewBlockInProgress (ForSomePactVersion _ bip)) = (hs, bh, bct) + where + (hs, _ , bh, bct) = blockInProgressParent bip newBlockParent (NewBlockPayload (ParentHeader ph) _) = (view blockHash ph, view blockHeight ph, view blockCreationTime ph) diff --git a/test/lib/Chainweb/Test/Pact5/CmdBuilder.hs b/test/lib/Chainweb/Test/Pact5/CmdBuilder.hs index ded0c3b26f..2672b553bd 100644 --- a/test/lib/Chainweb/Test/Pact5/CmdBuilder.hs +++ b/test/lib/Chainweb/Test/Pact5/CmdBuilder.hs @@ -186,8 +186,10 @@ defaultCmd cid = CmdBuilder -- | Build parsed + verified Pact command -- TODO: Use the new `assertPact4Command` function. buildCwCmd :: (MonadThrow m, MonadIO m) => ChainwebVersion -> CmdBuilder -> m Pact5.Transaction -buildCwCmd v cmd = buildTextCmd v cmd >>= \(c :: Command Text) -> - case validatePact5Command v c of +buildCwCmd v cmd = do + c <- buildTextCmd v cmd + cid <- chainIdFromText $ _cbChainId cmd + case validatePact5Command v cid c of Left err -> throwM $ userError $ "buildCwCmd failed: " ++ err Right cmd' -> return cmd' diff --git a/test/lib/Chainweb/Test/TestVersions.hs b/test/lib/Chainweb/Test/TestVersions.hs index 2d702e062d..cc5e457679 100644 --- a/test/lib/Chainweb/Test/TestVersions.hs +++ b/test/lib/Chainweb/Test/TestVersions.hs @@ -64,6 +64,8 @@ import qualified Pact.Types.Gas as P import Chainweb.Test.Pact5.Utils (pactTxFrom4To5) import Pact.Types.Verifier +import qualified Pact.Types.Scheme as Pact4 (PPKScheme(..)) +import qualified Pact.Core.Scheme as Pact5 (PPKScheme(..)) import qualified Chainweb.Pact.Transactions.CoinV3Transactions as CoinV3 import qualified Chainweb.Pact.Transactions.CoinV4Transactions as CoinV4 @@ -168,6 +170,7 @@ testVersionTemplate v = v & versionMaxBlockGasLimit .~ Bottom (minBound, Just 2_000_000) & versionSpvProofRootValidWindow .~ Bottom (minBound, Just 20) & versionInitialGasModel .~ AllChains (Bottom (minBound, pre31GasModel)) + & versionAllowedSignatureSchemes .~ AllChains (Bottom (minBound, Set.singleton $ SchemeV4 Pact4.ED25519)) & versionBootstraps .~ [testBootstrapPeerInfos] & versionVerifierPluginNames .~ AllChains (Bottom (minBound, mempty)) & versionForkNumber .~ 0 @@ -248,6 +251,7 @@ pact5CheckpointerTestVersion g1 = buildTestVersion $ \v -> v & versionQuirks .~ noQuirks & versionUpgrades .~ AllChains HM.empty & versionGraphs .~ Bottom (minBound, g1) + & versionAllowedSignatureSchemes .~ AllChains (Bottom (minBound, Set.fromList $ SchemeV5 <$> [minBound .. maxBound])) & versionCheats .~ VersionCheats { _disablePow = True , _fakeFirstEpochStart = True @@ -286,6 +290,13 @@ cpmTestVersion g v = v , _genesisBlockTarget = AllChains maxTarget , _genesisTime = AllChains $ BlockCreationTime epoch } + & versionAllowedSignatureSchemes .~ (AllChains $ + (afterFork v Pact5Fork, Set.fromList $ SchemeV5 <$> [Pact5.ED25519, Pact5.WebAuthn]) + `Above` + (afterFork v Chainweb221Pact, Set.fromList $ SchemeV4 <$> [Pact4.ED25519, Pact4.WebAuthn]) + `Above` + Bottom (minBound, Set.singleton $ SchemeV4 Pact4.ED25519)) + & versionUpgrades .~ chainZip HM.union (indexByForkHeights v [ (CoinV2, AllChains (pact4Upgrade Other.transactions)) @@ -505,6 +516,7 @@ pact5InstantCpmTestVersion migrate g = buildTestVersion $ \v -> v , _genesisTime = AllChains $ BlockCreationTime epoch } & versionUpgrades .~ AllChains mempty + & versionAllowedSignatureSchemes .~ AllChains (Bottom (minBound, Set.fromList $ SchemeV5 <$> [minBound .. maxBound])) & versionVerifierPluginNames .~ AllChains (Bottom ( minBound @@ -586,6 +598,7 @@ pact5SlowCpmTestVersion g = buildTestVersion $ \v -> v _ -> AllChains ForkAtGenesis ) & versionQuirks .~ noQuirks + & versionAllowedSignatureSchemes .~ AllChains (Bottom (minBound, Set.fromList $ SchemeV5 <$> [minBound .. maxBound])) & versionGenesis .~ VersionGenesis { _genesisBlockPayload = onChains $ (unsafeChainId 0, IN0.payloadBlock) :